Expert Advisors Basierend auf Beliebten Handelssystemen und Alchemie der Handelsroboter Optimierung

Nikolay Kositsin | 10 März, 2016

Einführung

Die Mehrzahl der Bücher über Forex-Trading bietet in der Regel einfachste Handelssysteme als Lehrmaterial. Aber bis zum heutigen Tag existieren solche Systeme nur als allgemeine Anweisungen, ohne die Umsetzung solcher Handelsstrategien in Form verwendungsfertiger Expert Advisors. Somit ist es jetzt unmöglich abzuschätzen, ob solche Beispiele irgendeinen praktischen Wert beinhalten. Wenn wir durch zahlreiche auf das Schreiben von EAs spezialisierte Foren schauen, können wir zu dem Schluss kommen, dass fast jeder beginnende EA-Autor das Rad neu erfinden und seinen ersten Expert Advisor auf einfachsten Handelssystemen von Anfang an neu entwickeln muss.

Ich halte den Wert solcher Arbeit eher für zweifelhaft und denke, es ist notwendig korrekt entwickelte EAs, basierend auf diesen Handelssystemen, für jeden beginnenden Trader verfügbar zu machen, statt der Notwendigkeit alles von Grund auf neu zu entwickeln. In diesem Artikel möchte ich meine eigene Variante der Problemlösung anbieten. Für den EA-Aufbau verwende ich Indikatoren aus meiner eigenen Bibliothek, die ich bereits verwendet habe in dem Artikel Effective Averaging Algorithms with Minimal Lag: Use in Indicators.



Allgemeines EA Strukturschema


Bevor ich mit der Hauptbeschreibung beginne, möchte ich Ihre Aufmerksamkeit auf die Tatsache lenken, dass alle in dem Artikel enthaltenen EAs nach ein und demselben Schema aufgebaut sind:



Handelssystem basierend auf Änderung der Bewegungsrichtung

Jetzt werde ich versuchen in Einzelheiten den Grundgedanken des angebotenen EA-Strukturschema anhand des Beispiels eines fertigen Expert Advisors zu erklären, mit allen kleineren Details der Arbeit mit ihm. In diesem System ist das Signal zum Kaufen die Änderung der Bewegungsrichtung von fallend zu steigend:

Das Signal zum Verkaufen ist hier die Änderung der Bewegungsrichtung von steigen zu fallend:


Für die Berechnung von Trendsignalen verwenden wir den Bewegungswert auf den dritten, zweiten und ersten Balken. Bewegungswerte auf dem Null-Balken werden nicht berücksichtigt, das heißt, das System arbeitet nur auf geschlossenen Balken. Als eine Bewegung wird der benutzerdefinierte Indikator JFATL verwendet - es ist ein einfacher digitaler FATL-Filter mit zusätzlicher JMA Glättung. Im Internet können Sie häufig Aussagen sehen, dass Einsteigen in einen Trade bei FATL Richtungswechsel mehrere Punkte an Gewinn verspricht, so dass jeder Leser leicht überzeugt werden kann, wie wirksam diese Strategie in der Wirklichkeit ist. Hier ist die Version der Systemumsetzung in Form eines Expert Advisor:


Expert Advisor Code

//+==================================================================+
//|                                                        Exp_1.mq4 |
//|                             Copyright © 2007,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+==================================================================+
#property copyright "Copyright © 2007, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//---- EA INPUT PARAMETERS FOR BUY TRADES 
extern bool   Test_Up = true;//filter of trade calculations direction
extern int    Timeframe_Up = 240;
extern double Money_Management_Up = 0.1;
extern int    Length_Up = 4;  // smoothing depth 
extern int    Phase_Up = 100; // parameter changing in the range 
          //-100 ... +100, influences the quality of the transient process; 
extern int    IPC_Up = 0;/* Selecting prices, on which the indicator will 
be calculated (0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 
6-WEIGHTED, 7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 
11-Heiken Ashi Low, 12-Heiken Ashi High, 13-Heiken Ashi Open, 
14-Heiken Ashi Close.) */
extern int    STOPLOSS_Up = 50;  // stoploss
extern int    TAKEPROFIT_Up = 100; // takeprofit
extern bool   ClosePos_Up = true; // forced position closing allowed
//---- EA INOUT PARAMETERS FOR SELL TRADES 
extern bool   Test_Dn = true;//filter of trade calculations direction
extern int    Timeframe_Dn = 240;
extern double Money_Management_Dn = 0.1;
extern int    Length_Dn = 4;  // smoothing depth 
extern int    Phase_Dn = 100; // parameter changing in the range
         // -100 ... +100, influences the quality of the transient process; 
extern int    IPC_Dn = 0;/* Selecting prices, on which the indicator will 
be calculated (0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 
6-WEIGHTED, 7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 
11-Heiken Ashi Low, 12-Heiken Ashi High, 13-Heiken Ashi Open, 
14-Heiken Ashi Close.) */
extern int   STOPLOSS_Dn = 50;  // stoploss
extern int   TAKEPROFIT_Dn = 100; // takeprofit
extern bool   ClosePos_Dn = true; // forced position closing allowed
//---- Integer variables for the minimum of counted bars
int MinBar_Up, MinBar_Dn;
//+==================================================================+
//| Custom Expert functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- Checking the correctness of Timeframe_Up variable value
   if (Timeframe_Up != 1)
    if (Timeframe_Up != 5)
     if (Timeframe_Up != 15)
      if (Timeframe_Up != 30)
       if (Timeframe_Up != 60)
        if (Timeframe_Up != 240)
         if (Timeframe_Up != 1440)
           Print(StringConcatenate("Timeframe_Up parameter cannot ",  
                                  "be equal to ", Timeframe_Up, "!!!"));
//---- Checking the correctness of Timeframe_Dn variable value 
   if (Timeframe_Dn != 1)
    if (Timeframe_Dn != 5)
     if (Timeframe_Dn != 15)
      if (Timeframe_Dn != 30)
       if (Timeframe_Dn != 60)
        if (Timeframe_Dn != 240)
         if (Timeframe_Dn != 1440)
           Print(StringConcatenate("Timeframe_Dn parameter cannot ",  
                                 "be equal to ", Timeframe_Dn, "!!!")); 
//---- Initialization of variables             
   MinBar_Up = 4 + 39 + 30;
   MinBar_Dn = 4 + 39 + 30;                                        
//---- end of initialization
   return(0);
  }
//+==================================================================+
//| Expert Advisor deinitialization function                         |
//+==================================================================+  
int deinit()
  {
//----+
   
    //---- End of EA deinitialization
    return(0);
//----+ 
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Declaration of local variables
   int    bar;
   double Mov[3], dMov12, dMov23;
   //----+ Declaration of static variables
   static int LastBars_Up, LastBars_Dn;
   static bool BUY_Sign, BUY_Stop, SELL_Sign, SELL_Stop;
   
   //----++ CODE FOR LONG POSITIONS
   if (Test_Up) 
    {
      int IBARS_Up = iBars(NULL, Timeframe_Up);
      
      if (IBARS_Up >= MinBar_Up)
       {
         if (LastBars_Up != IBARS_Up)
          {
           //----+ Initialization of variables 
           BUY_Sign = false;
           BUY_Stop = false;
           LastBars_Up = IBARS_Up; 
           
           //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM TO BUFFERS
           for(bar = 1; bar <= 3; bar++)
                     Mov[bar - 1]=                  
                         iCustom(NULL, Timeframe_Up, 
                                "JFatl", Length_Up, Phase_Up, 
                                                   0, IPC_Up, 0, bar);
           
           //----+ DEFINING SIGNALS FOR TRADES 
           dMov12 = Mov[0] - Mov[1];
           dMov23 = Mov[1] - Mov[2]; 
                                          
           if (dMov23 < 0)
              if (dMov12 > 0)
                        BUY_Sign = true;
                          
           if (dMov12 < 0)
                        BUY_Stop = true;                                           
          }
          //----+ EXECUTION OF TRADES
          if (!OpenBuyOrder1(BUY_Sign, 1, Money_Management_Up, 
                                          STOPLOSS_Up, TAKEPROFIT_Up))
                                                                 return(-1);
          if (ClosePos_Up)
                if (!CloseOrder1(BUY_Stop, 1))
                                        return(-1);
        }
     }
     
   //----++ CODE FOR SHORT POSITIONS
   if (Test_Dn) 
    {
      int IBARS_Dn = iBars(NULL, Timeframe_Dn);
      
      if (IBARS_Dn >= MinBar_Dn)
       {
         if (LastBars_Dn != IBARS_Dn)
          {
           //----+ Initialization of variables 
           SELL_Sign = false;
           SELL_Stop = false;
           LastBars_Dn = IBARS_Dn; 
           
           //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM TO BUFFERS        
           for(bar = 1; bar <= 3; bar++)
                     Mov[bar - 1]=                  
                         iCustom(NULL, Timeframe_Dn, 
                                "JFatl", Length_Dn, Phase_Dn, 
                                                   0, IPC_Dn, 0, bar);
           
           //----+ DEFINING SIGNALS FOR TRADES
           dMov12 = Mov[0] - Mov[1];
           dMov23 = Mov[1] - Mov[2]; 
                                           
           if (dMov23 > 0)
              if (dMov12 < 0)
                       SELL_Sign = true;
                          
           if (dMov12 > 0)
                       SELL_Stop = true;                                           
          }
          //----+ EXECUTION OF TRADES
          if (!OpenSellOrder1(SELL_Sign, 2, Money_Management_Dn, 
                                            STOPLOSS_Dn, TAKEPROFIT_Dn))
                                                                   return(-1);
          if (ClosePos_Dn)
                if (!CloseOrder1(SELL_Stop, 2))
                                        return(-1);
        }
     }
//----+ 
    
    return(0);
  }
//+------------------------------------------------------------------+

Um Kauf- und Verkaufssignale zu erhalten, werden zwei absolut gleiche unabhängige Algorithmen verwendet, jeder der Algorithmen hat seine eigenen externen Parameter für die Optimierung. Meine eigene Erfahrung belegt, dass die Verwendung dieser Art von Ansatz für dieses EA Verfassen viel rentabler ist als die Variante mit nur einem Algorithmus zum Erkennen von Kauf- und Verkaufssignalen. Diejenigen, die an der Variante mit einer einzelnen Bewegung für Short- und Long-Positionen interessiert sind, können den Algorithmus in dem EXP_0.mq4 EA untersuchen, wir werden weiter den EA mit zwei Bewegungen besprechen. Der Expert Advisor kann eine Position in Long-Richtung und eine Position in Short-Richtung gleichzeitig für ein gehandeltes Paar öffnen. Der EA führt Trades zu Marktbedingungen aus. Trades werden geschlossen durch StopLoss und TakeProfit Ordern. Wenn gegenteilige Trendsignale zu offenen Positionen in dem EA erscheinen, ermöglicht der EA das erzwungene Schließen der Positionen. Die Methode zum Erhalt von Signalen zum Schließen von Trades ist analog zu der Methode zum Erhalt von Einstiegssignalen, aber mit gegenteiliger Eigenschaft.



Inhalte der Lite_EXPERT1.mqh Datei

Aus Gründen der maximalen Wirtschaftlichkeit Ihrer eigenen Arbeit, versuchen Sie die maximale Anzahl universeller benutzerdefinierter Funktionen zu verwenden, wenn Sie Expert Advisors schreiben. Und später stellen Sie einen EA-Code aus verschiedenen Teilen zusammen, wie es in verschiedenen Geräte herstellenden Fabriken gemacht wird, wo jedes neue Produkt die maximale Anzahl an vereinheitlichten und Standard-Details, -Blöcken, -Modulen enthält. Das ist der Grund, weshalb in allen EAs in dem "Ausführung der Trades" Block mehrere universelle benutzerdefinierte Funktionen verwendet werden. Sie werden in einen EA eingefügt mit der #include <Lite_EXPERT1.mqh> Anweisung:

bool OpenBuyOrder1
( 
  bool BUY_Signal, int MagicNumber, 
             double Money_Management, int STOPLOSS, int TAKEPROFIT
)

bool OpenSellOrder1
( 
  bool SELL_Signal, int MagicNumber, 
             double Money_Management, int STOPLOSS, int TAKEPROFIT
)

CountLastTime
(
  int lastError
)

bool CloseOrder1
( 
  bool Stop_Signal, int MagicNumber
)

int StopCorrect
( 
  string symbol, int Stop
)

bool MarginCheck
(
  string symbol, int Cmd, double& Lot
)

double GetFreeMargin()

bool DeleteOrder1
(
  bool& CloseStop, int MagicNumber
)

bool OpenBuyLimitOrder1
(
  bool& Order_Signal, int MagicNumber, 
       double Money_Management, int STOPLOSS, int TAKEPROFIT,
                                      int LEVEL, datetime Expiration
)

bool OpenBuyStopOrder1
(
  bool& Order_Signal, int MagicNumber, 
       double Money_Management, int STOPLOSS, int TAKEPROFIT,
                                      int LEVEL, datetime Expiration
)

bool OpenSellLimitOrder1
(
  bool& Order_Signal, int MagicNumber, 
       double Money_Management, int STOPLOSS, int TAKEPROFIT,
                                      int LEVEL, datetime Expiration
)

bool OpenSellStopOrder1
(
  bool& Order_Signal, int MagicNumber, 
       double Money_Management, int STOPLOSS, int TAKEPROFIT,
                                      int LEVEL, datetime Expiration
)

bool OpenBuyOrder2
( 
  bool BUY_Signal, int MagicNumber, 
             double Money_Management, int STOPLOSS, int TAKEPROFIT
)

bool OpenSellOrder2
( 
  bool SELL_Signal, int MagicNumber, 
             double Money_Management, int STOPLOSS, int TAKEPROFIT
)

bool Make_TreilingStop
(
  int MagicNumber, int TRAILINGSTOP
)

Die OpenBuyOrder1() Funktion öffnet Long-Positionen, wenn sie aufgerufen wird, wenn der Wert der externen Variable BUY_Signal gleich true ist und es keine offenen Positionen gibt, die Identifikations-(Magic)-Nummer dieser gleich dem Wert gleich ist zu der MagicNumber Variable. Die Werte der externen Variablen STOPLOSS und TAKEPROFIT bestimmen entsprechend die Werte von StopLoss und TakeProfit in Punkten. Der Wert der Money_Management Variable kann von Null bis Eins variieren. Die Variable bezeichnet welcher Anteil der verfügbaren Einlage für die Ausführung des Trades verwendet wird. Wenn der Wert der Variable weniger als Null ist, wird die OpenBuyOrder() Funktion ihren Wert als Lot-Größe verwenden! Short-Positionen werden auf analoge Weise geöffnet, wenn die Funktion OpenSellOrder1() aufgerufen wird. Beide Funktionen öffnen Positionen unabhängig voneinander, aber nur ein Befehl zur Trade-Ausführung kann innerhalb von 11 Sekunden an einen Server gesendet werden. Neben der Ausführung von Trades, zeichnen die Funktionen OpenBuyOrder1() und OpenSellOrder1() in einer Protokolldatei die Informationen über geöffnete Trades auf.


Wenn die Stop_Signal Variable den Wert true erhält, wird die CloseOrder1() Funktion, wenn auf sie verwiesen wird, die Position mit der Magic Number gleich dem Wert der Variable MagicNumber schließen.


Die StopCorrect() Funktion akzeptiert als Stop Parameter den StopLoss oder TakeProfit Wert, überprüft seine Entsprechung mit dem minimalen akzeptierten Wert, ändert ihn wenn notwendig in den minimal erlaubten Wert und gibt ihn zurück unter Beachtung möglicher Korrekturen.


Die Zuordnung der MarginCheck() Funktion bezeichnet die Verringerung der Lot-Größe, die in einem geöffneten Trade verwendet wird, bis zu der maximalen Größe., bei der die freie Margin ausreichend ist um einen Trade zu öffnen, für den Fall, dass wenn die freie Margin für die aktuelle Lot-Größe nicht ausreicht.


OpenBuyOrder1(), OpenSellOrder1() und CloseOrder1() werden innerhalb der start() Funktion verwendet, während die StopCorrect() und MarginCheck() Funktionen innerhalb von OpenBuyOrder1() und OpenSellOrder1() verwendet werden.


Bei einem korrekten Abschluss von OpenBuyOrder1(), OpenSellOrder1() und CloseOrder1() geben alle der Funktionen 'true' zurück, wenn ein Fehler während der Ausführung der Funktion auftritt, ist der zurückgegebene Wert 'false'. Alle drei Funktionen: OpenBuyOrder1(), OpenSellOrder1() und CloseOrder1() senden bei der Trade-Ausführung die Werte ihrer externen Variablen BUY_Signal, SELL_Signal und Stop_Signal in 'false'!


Die GetFreeMargin() Funktion gibt die erlaubte freie Margin-Größe des aktuellen Kontos für Gewinn und Verlust zurück, die zum Öffnen der Position verwendet werden kann. Diese Funktion wird verwendet für die Berechnung der Lot-Größe.


Die CountLastTime() Funktion macht die Initialisierung der LastTime Variable, mit Berücksichtigung eines Fehlers, der während der Trade-Ausführung aufgetreten ist. Die Funktion sollte direkt nach der Trade-Ausführung aufgerufen werden, zum Beispiel:

 //----+ Open Buy position    
  ticket = OrderSend(Symb, OP_BUY, Lot, ask, 3, 
            Stoploss, TakeProfit, NULL, MagicNumber, 0, Lime);
  
  //---- Calculating a pause between trade operations
  CountLastTime(GetLastError());

Neben den oben aufgezählten Funktionen, enthält die Datei Lite_EXPERT.mqh vier weitere Funktionen zum Platzieren von Pending Ordern und eine Funktion zum Löschen von Pending Ordern: OpenBuyLimitOrder(), OpenBuyStopOrder1(), OpenSellLimitOrder1(), OpenSellStopOrder1(), DeleteOrder1(). Die Zuordnung externer Variablen dieser Funktionen ist absolut analog und von ihren Namen her verständlich. Die neuen Variablen LEVEL und Expiration sind notwendig um den Abstand in Punkten zum aktuellen Kurs an eine Funktion zu senden, in dem Pending Ordern platziert werden, und das entsprechende Pending Order Ablaufdatum.


Neben allen besprochenen Funktionen, enthält die Dateizwei weitere Funktionen: OpenBuylOrder2() und OpenSellOrder2(), die vollständig analog zu OpenBuyOrder1() und OpenSellOrder1() sind, mit Ausnahme eines Details. Diese Funktionen öffnen Positionen ohne StopLoss und TakeProfit Ordern, und ändern danach bereits geöffnete Positionen und stellen StopLoss und TakeProfit ein. Diese Funktionen sind notwendig für EAs, die entwickelt wurden für den Betrieb bei Brokern, die es dem Client nicht ermöglichen StopLoss und TakeProfit zu platzieren, wenn eine Position am Markt geöffnet wird, wegen der "Market Watch" Ausführungsart. Bei solchen Broker-Unternehmen werden Stop Loss und Take Profit Ordern über die Änderung von geöffnete Positionen platziert.


Und die letzte enthaltene Funktion der Datei ist Make_TrailingStop(). Die Funktion für einen Standard Trailing Stop aus.


Neben den Funktionen, enthält Lite_EXPERT.mqh eine vollständige Variable LastTime, die auf globaler Ebene deklariert ist, weil sie von allen Funktionen zum Öffnen von Ordern verwendet wird.


Nach meiner Meinung, ist dieser Satz an Funktionen sehr zweckmäßig zu in der Praxis des EA Schreibens zu verwenden. Es wird viel Zeit sparen für beginnende EA-Autoren und die Notwendigkeit diesen Code zu schreienb eliminieren. Als ein Beispiel können Sie jede Funktion aus dem angebotenen Satz an Funktionen nehmen:

//+==================================================================+
//| OpenBuyOrder1()                                                  |
//+==================================================================+
bool OpenBuyOrder1
        (bool& BUY_Signal, int MagicNumber, 
                double Money_Management, int STOPLOSS, int TAKEPROFIT)
{
//----+
  if (!BUY_Signal)
           return(true); 
  //---- Checking the expiration of minimal time interval 
                                    //between two trade operations         
  if (TimeCurrent() < LastTime)
                          return(true); 
  int total = OrdersTotal();
  //---- Checking the presence of an opened position 
          //with the magic number equal to Value of MagicNumber variable
  for(int ttt = total - 1; ttt >= 0; ttt--)     
      if (OrderSelect(ttt, SELECT_BY_POS, MODE_TRADES))
                      if (OrderMagicNumber() == MagicNumber)
                                                      return(true); 
  string OrderPrice, Symb = Symbol(); 
  int    ticket, StLOSS, TkPROFIT;
  double LOTSTEP, MINLOT, MAXLOT, MARGINREQUIRED;
  double FreeMargin, LotVel, Lot, ask, Stoploss, TakeProfit;                                                 
                                                      
  //----+ calculating lot size for position opening
  if (Money_Management > 0)
    {        
      MARGINREQUIRED = MarketInfo(Symb, MODE_MARGINREQUIRED);
      if (MARGINREQUIRED == 0.0)
                    return(false);
                    
      LotVel = GetFreeMargin()
               * Money_Management / MARGINREQUIRED;         
    }
  else 
    LotVel = MathAbs(Money_Management);
  //----  
  LOTSTEP = MarketInfo(Symb, MODE_LOTSTEP);
  if (LOTSTEP <= 0)
              return(false);  
  //---- fixing lot size for the nearest standard value
  Lot = LOTSTEP * MathFloor(LotVel / LOTSTEP);  
  
  //----+ checking lot for minimally accepted value
  MINLOT = MarketInfo(Symb, MODE_MINLOT);
  if (MINLOT < 0)
         return(false);
  if (Lot < MINLOT)
          return(true);
          
  //----+ checking lot for maximally accepted value
  MAXLOT = MarketInfo(Symb, MODE_MAXLOT);
  if (MAXLOT < 0)
         return(false);
  if (Lot > MAXLOT)
          Lot = MAXLOT;
          
  //----+ checking if free margin is enough for lot size 
  if (!MarginCheck(Symb, OP_BUY, Lot))
                               return(false);
  if (Lot < MINLOT)
          return(true);
  //----
  ask = NormalizeDouble(Ask, Digits);
  if (ask == 0.0)
          return(false);
  //----             
  StLOSS = StopCorrect(Symb, STOPLOSS);
  if (StLOSS < 0)
          return(false);   
  //----
  Stoploss = NormalizeDouble(ask - StLOSS * Point, Digits);
  if (Stoploss < 0)
         return(false);
  //----       
  TkPROFIT = StopCorrect(Symb, TAKEPROFIT);
  if (TkPROFIT < 0)
          return(false);  
  //----               
  TakeProfit = NormalizeDouble(ask + TkPROFIT * Point, Digits);
  if (TakeProfit < 0)
         return(false);
  
  Print(StringConcatenate
         ("Open for ", Symb,
            " a Buy position with the magic number ", MagicNumber));
            
  //----+ Open Buy position
  ticket = OrderSend(Symb, OP_BUY, Lot, ask, 3, 
            Stoploss, TakeProfit, NULL, MagicNumber, 0, Lime);
  
  //---- Calculating pause between trade operations
  CountLastTime(GetLastError());
  //----
  if(ticket > 0)
   {
     if (OrderSelect(ticket, SELECT_BY_TICKET))
       {
         BUY_Signal = false;
         OpderPrice = DoubleToStr(OrderOpenPrice(), Digits);  
         Print(StringConcatenate(Symb, " BUY order with the ticket No",
                ticket, " and magic number ", OrderMagicNumber(), 
                                         " opened with the price ",OpderPrice));
         return(true);
       }
     else
       {
         Print(StringConcatenate("Failed to open ", Symb, 
            " BUY order with the magic number ", MagicNumber, "!!!"));
         return(true);
       }
    }
  else
    {
      Print(StringConcatenate("Failed to open ", Symb, 
           " BUY order with the magic number ", MagicNumber, "!!!"));
      return(true);
    }
  //---- 
  return(true);
//----+
}

Das Schreiben eines solchen Codes ohne offensichtliche und nicht-offensichtliche Fehler ist ziemlich schwierig und ein langer Job für eine Einsteiger in MQL4. Und die Verwendung eines solchen gebrauchsfertigen Universal Codes (Geschrieben von einem Experten) in seinem Code ist sehr einfach:


          //----+ EXECUTION OF TRADES
          if (!OpenBuyOrder1(BUY_Sign, 1, Money_Management_Up, 
                                          STOPLOSS_Up, TAKEPROFIT_Up))
                                                                 return(-1);
          if (ClosePos_Up)
                if (!CloseOrder1(BUY_Stop, 1))
                                        return(-1);

Nur wenige Zeilen mit universellen, benutzerdefinierten Funktionsaufrufen - und der Code ist fertig! Alles was Sie benötigen, ist einmal zu versten wie die Funktionsaufrufe geschrieben werden. Was wichtiger ist - der EA-Code wird ziemlich einfach und leicht zu verstehen, wodurch sie jede Handelsstrategie in Ihren eigenen Expert Advisors umsetzen können. In dem EA Exp_1.mq4 werden die Funktionen zum Öffnen von Pending Ordern und Trailing Stop nicht verwendet, wodurch MetaEditor während der Kompilierung eine Warnung über das Löschen dieser Funktionen aus dem EA anzeigen wird:



Zusätzliche Erklärungen über den EA-Code


Jetzt können wir anfangen über den verbleibenden EA-Code zu reden. Der EA besteht aus zwei fast identischen Algorithmen, wodurch es zum richtigen Verständnis der Einzelheiten ausreichend ist, zum Beispiel, den EA Teil der Positionen öffnet, zu analysieren. Der enthält bereits Kommentare, welche die die Bedeutung einzelner Code-Fragmente erklären. Untersuchen wir also Einzelheiten, die nicht kommentiert sind. Im Initialisierung-Block wird die MinBar_Up Variable initialisiert:

//---- Initialization of variables             
   MinBar_Up = 4 + 39 + 30;

Der Zweck dieser Variable ist es, in dem EA-Speicher die minimale Anzahl an Balken zu speichern, bei weniger Balken ist eine EA-Operation in Long-Richtung unmöglich. Dieser Wert wird durch den Algorithmus des benutzerdefinierten Indikators JFATL.mq4 bestimmt. Für die Berechnung von nur einem Wert des FATL Digital-Filter, werden 39 Chart-Balken benötigt. Um JFATL durch den JMA Algorithmus geglättet zu erhalten, sind mindestens 30 FATL Werte notwendig und der EA Algorithmus der Signalerkennung für Trades drei letzte, aber einen Chartbalken plus den vierten - den Null-Balken. Die Überprüfung, ob die Anzahl der Balken ausreichend für weitere Berechnungen ist, sieht so aus:

if (IBARS_Up >= MinBar_Up)

Die Überprüfung

if (LastBars_Up != IBARS_Up)

ist notwendig, um die Signal-Neuberechnung für den Einstieg in den Markt des EAs bei jedem Tick zu abzustellen. Der EA sollte dies nur bei der Änderung von Balken tun, was hinreichend Computer-Ressourcen und EA-Optimierungszeit spart. Aus diesem Grund wird die LastBars_Up Variable als statische Variable deklariert - um an die Anzahl der Balken auf dem vorherigen Tick der int start() Funktion zu erinnern. Die Initialisierung von BUY_Sign und BUY_Stop zum Einstieg in und Ausstieg aus dem Markt wird nur einmal bei Balkenwechsel durchgeführt, sie sollten den Wert halten, bis der Trade ausgeführt oder geschlossen ist, oder ein weiterer Wechsel der Balken stattfindet. Deshalb sind diese Variablen als statisch deklariert. Ich nehme an, weitere Einzelheiten des EA sind ziemlich klar und können aus dem Code heraus verstanden werden.



Bewegung in dem Expert Advisor Ersetzen


Nun möchte ich die EA Modifizierung zur Verwendung einer anderen Bewegung behandeln. Als Beispiel verwenden wir den benutzerdefinierten Indikator J2JMA.mq4 aus meiner Bibliothek. Ihr Aufruf sieht wie folgt aus:

           //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM INTO BUFFERS
           for(bar = 1; bar <= 3; bar++)
                     Mov[bar - 1]=                  
                         iCustom(NULL, Timeframe_Up, 
                                "J2JMA", Length1_Up, Length2_Up,
                                             Phase1_Up, Phase2_Up,  
                                                  0, IPC_Up, 0, bar);

Die Aufgabe besteht darin, den Block von externen Parametern des EA ein wenig zu ändern (der Algorithmus ist wieder nur zur Hälfte in dem Beispiel beschrieben):


//---- EA INPUT PARAMETERS FOR BUY TRADES
extern bool   Test_Up = true;//filter of trade calculations direction
extern int    Timeframe_Up=240;
extern double Money_Management_Up = 0.1;
extern int    Length1_Up = 4;  // depth of the first smoothing
extern int    Phase1_Up = 100; // parameter of the first smoothing,
       //changing in the range -100 ... +100, influences the quality 
       //of the transient process of averaging;
extern int    Length2_Up = 4;  // depth of the second smoothing
extern int    Phase2_Up = 100; // parameter of the second smoothing,
       //changing in the range -100 ... +100, influences the quality 
       //of the transient process of averaging;

Im Initialisierung-Block sollte der variable Wert geändert werden:


//---- Initialization of variables
   MinBar_Up = 4 + 30 + 30;

Jetzt haben wir zwei aufeinanderfolgende JMA Glättungen, jede benötigt mindestens 30 Balken und 4 Balken für den Berechnungsalgorithmus des EA. Danach sollte in dem Block zur Gewinnung von Quellwerten der benutzerdefinierte Indikator Bezug geändert werden, in den am Anfang des Absatzes platzierten. Für den zweiten Teil des Algorithmus wird alles auf die gleiche Weise getan. Somit können wir nicht nur Bewegungen verwenden, sondern auch Oszillatoren, die manchmal sehr nützlich sein können. Der fertige auf J2JMA basierende EA-Code ist in der EXP_2.mqh Datei enthalten.



EA Optimierung


Jetzt, am Beispiel des gegebenen EA, wollen wir einige Einzelheiten der Optimierung aller EAs besprechen, die in diesem Artikel enthalten sind. Wie bereits erwähnt, gibt es zwei unabhängige Algorithmen in dem EA - zur Arbeit mit Long- und Short-Positionen. Natürlich, es ist komfortabler und schneller den EA nur für Buy oder Sell Richtung zu einem Zeitpunkt zu optimieren. Zu diesem Zweck gibt es zwei externe Variablen des EA - Test_Up und entsprechend Test_Dn.


Durch die Zuweisung des 'false' Wertes zu einer dieser logischen Variablen, schließen wir alle Berechnungen in dieser Richtung aus und als Ergebnis benötigt die Optimierung weniger Zeit. Nach der Optimierung der EA-Parameter in eine Richtung, können wir die Werte der Test_up und Test_Dn Variablen in die gegenteilige ändern und den EA in die gegenteilige Richtung optimieren. Und nur danach 'true' zu beiden Variablen zuweisen und das Ergebnis testen. Der Vorgang von Optimierung uns Test ist ziemlich gut erklärt in dem Artikel Experten-Testen im Client Terminal MetaTrader 4. Der Einblick, die Optimierung für diesen EA wird wie in ihm beschrieben ausgeführt. Starten Sie den Strategietester, laden Sie den EA, wählen Sie ein Paar, Paar-Periode, Optimierungsmethode und Zeitrahmen für die Optimierung:

danach wählen Sie "Expert Advisor Optionen" und gehen zu "Test":


Hier bestimmen Sie die Größe der Einlage, wählen eine Richtung (Positionen > Long oder Short) zur Optimierung und wählen den genetischen Algorithmus zu Optimierung. Danach gehen Sie zu der Registerkarte "Eingaben":



Hier weisen Sie 'true' einer der beiden externen Variablen Test_Up und Test_Dn zu, und 'false' zu der zweiten. Dann weisen Sie an Timeframe_Up und Timeframe_DN Variablen-Werte von Chart-Perioden zu, auf denen die Optimierung durchgeführt wird, in Money_Management_Up und Money_Management_Dn bestimmen Sie den Teil der Einlage, der zur Ausführung von Buy- und Sell-Trades entsprechend verwendet wird.
Für verbleibende externe Variablen setzen Sie Änderungsgrenzen während der Optimierung. Danach kennzeichnen Sie optimierte externe Variablen. Schließen Sie die Registerkarte mit einem Klick auf "OK" und starten Sie die Optimierung mit einem Klick auf "Test starten" im Tester. Nachdem die Optimierung abgeschlossen ist




gehen Sie zu der "Ergebnisse" Registerkarte im Tester:



und laden die befriedigenden Optimierungsergebnisse in den Strategietester. Danach führen Sie den gleichen Vorgang in die gegenteilige Richtung durch. Als Ergebnis erhalten wir einen EA mit hochgeladenen Parametern, der rentabel in dem Zeitrahmen der Optimierung ist. Beachten Sie, dass die EA-Eigenschaften sich von den oben gezeigten unterscheiden:



Es ist nicht sehr praktisch, das Fenster in dieser Form zu verwenden, und die maximierte Form wird in solchen Fällen eher bevorzugt. Das Client Terminal ermöglich die Maximierung des Fensters nicht, also wurde eine zusätzliche Datei zum Maximieren erstellt OpenExp.exe). Diese Datei maximiert das Eigenschaften-Fenster von EAs des Namens, dessen Dateien von Exp_ c starten (Groß-/Kleinschreibung beachten). Zur Verwendung der Datei sollte sie aus einem Verzeichnis gestartet werden, danach wird das Programm-Modul auf das Erscheinen des EA-Eigenschaften-Fenster warten und seine Größe ändern, in diesem Moment ist es empfehlenswert die Maus nicht zu bewegen.


Aber jeder EA-Autor, der mit Testen beginnt, hat vielleicht viele Fragen über verschiedene Einzelheiten des Testens. Zunächst entscheiden wir welcher Chart-Zeitrahmen zum Testen und Optimieren in unserem Fall verwendet wird. Meiner Meinung nach, werden Sie kaum eine eindeutige Antwort hören. Es ist zu berücksichtigen, dass je größer der der Chart-Zeitrahmen des EA-Betriebs ist, desto stabiler sind die im Handelssystem verwendeten sichtbaren Regelmäßigkeiten. Es ist wie im Live-Trading. Aber auf lange Sicht ist das Optimalste das Ergebnis, das nur durch ernsthafte Analyse und Vergleiche verschiedener Test- und Optimierungsvarianten erhalten werden kann. Das gleiche gilt für die gewählte Sicherheit der Optimierung. Jedes Finanzinstrument hat seine besonderen Eigenschaften. Jetzt über die Modellierung. Die Erfahrung zeigt, dass EAs die Signale zur Trade-Ausführung in dem Moment des Balken-Wechsels erhalten, ziemlich gut optimiert sind, wenn sie auf Kontrollpunkte modelliert sind, ohne hinreichende Qualitäts- und Quantitätsverluste während der Optimierung. Natürlich, solche Aussagen sollten über die eigene Erfahrung überprüft werden. Ich bin überzeugt, dass es nutzlos ist einen solchen EA mit der Modellierung aller Ticks zu optimieren. Sprechen wir nun über einen weiteren wichtigen Parameter - der Zeitraum, in dem die Optimierung durchgeführt wird:



Hier sind verschieden Werte möglich, abhängig von dem Zweck der EA-Optimierung. Hier ist es wichtig, dass Sie die verfügbaren Historie-Daten nicht überschreiten können, andernfalls können Sie das Folgende haben:

Die untere Grenze aller Historie-Daten, verfügbar in der Historiendatenbank, ist die folgende:




Natürlich können Fehler der folgenden Art in dem Betrieb des Expert Advisor und eingeschlossener Indikatoren während der Optimierung und dem Test:

Die Fehler sind nicht mit dem EA selbst verbunden! Es liegt einfach daran, dass der Optimierungszeitraum aus dem gewählt werden muss, was vorhanden ist, und nicht was gewünscht ist!

Jetzt werde ich einige Erklärungen über die "Eingaben" Registerkarte eines Expert Advisor geben. Auch hier analysieren wir nur einen Teil von ihnen - für Long-Positionen. Ich habe bereits Test_Up erwähnt. Die Bedeutung des Timframe_Up Parameters ist klar. Money_Management_Up wurde auch bereits erklärt. Analysieren wir nun den Length_Up Parameter. Die Bedeutung dieses Parameters ist analog zu dem Periode Parameter des Einfachen Moving Average. Dieser Parameter kann Werte von Null bis unendlich haben. Es gibt keinen Grund den oberen Parameter auf mehr als 150 festzulegen, in den meisten Fällen ist es einfacher auf einen höheren Zeitrahmen zu wechseln. Beachten Sie bei der Optimierung dieses Parameters, dass je größer der Parameter ist, desto mehr stabile und langfristige Trends werden durch den JFATL Indikator erkannt. Somit haben wir in diesem Handelssystem eine nicht-offensichtliche Beziehung zwischen dem Length_Up Parameter und STOPLOSS_Up und TAKEPROFIT_Up. Logisch, Stop Loss und Take Profit hängen direkt an Length_Up. Natürlich, wir können Ordern unabhängig von Length_Up platzieren., aber in solchen Fällen wird die Rentabilität eines Handelssystems nicht von den Systemeigenschaften bestimmt, sondern durch die aktuelle Marktsituation, die in diesem Handelssystem nicht definiert ist! Die Bedeutung dieser Aussage lässt sich mit dem foglenden Beispiel verstehen. Angenommen wir haben es geschafft sehr gute EA-Test-Ergebnisse zu erhalten, während der Optimierung dieses EA auf EURUSD mit den folgenden Parametern:

extern bool   Test_Up = true;
extern int    Timeframe_Up = 1;
extern double Money_Management_Up = 0.1;
extern int    Length_Up = 4;
extern int    Phase_Up = 100;
extern int    IPC_Up = 0;
extern int    STOPLOSS_Up = 100;
extern int    TAKEPROFIT_Up = 200;
extern bool   ClosePos_Up = false;

Tatsache ist, JFATL mit Length_Up gleich vier ist ein Indikator für sehr schnelle Trends. Dieser kombiniert mit einem Minuten-Chart, auf dem der EA arbeitet, gibt einem solchen System die Möglichkeit das Ausmaß der Kursänderung sogar in zehn-fünfzehn Punkten zu fixieren, weshalb die hervorragenden Testergebnisse mit solch großem Stop Loss und Take Profit Werten nur die Tatsache bezeichnen, dass der Markt während der Optimierung einen starken Trend erlebt hat, der durch das System selbst nicht erkannt wurde. Somit sollte klar sein, dass nach dem Hochladen dieser Parameter der EA kaum solche guten Ergebnisse zeigen wird. Können Sie jedoch durch die Verwendung anderer Werkzeuge das Vorhandensein eines starken Trends am Markt erkennen, kann die Verwendung solcher Parameter gerechtfertigt sein.


Der Bereich der Wertänderung der Variable Phase_Up ist von -100 bis +100. Sind die Wert gleich -100, sind temporäre Vorgänge der JFATL Bewegung von minimalem Charaktere, aber meine Erfahrung zeigt, dass die besten Optimierungsergebnisse erreicht werden, wenn der Wert +100 ist. Die ICP_Up Variable bestimmt, welcher Kurs für die weitere Verarbeitung durch den JFATL Algorithmus verwendet wird. Die Verwendung der ClosePos_Up Variable ermöglicht das erzwungene Schließen von Positionen, wenn ein Trend gegen eine offene Position beginnt. Es sollte beachtet werden, dass, wenn Take Profit und Stop Loss zu weit vom Markt entfernt platziert sind, alle Positionen bei Moving Signalen geschlossen werden und TP und SL keinen Einfluss auf das Trading haben, wenn der Wert von ClosePos_Up gleich 'true' ist!



Fazit


Hier muss ich die Erklärung beenden, das am Ende des Artikels besprochene Thema Optimierung ist zu lang. Was also übrig ist, wird im nächsten Artikel beschrieben. Der Hauptzweck des ersten Artikels ist, dem Leser meine eigenen Methoden des Schreibens von Expert Advisors zu zeigen, und einen leichteren, eher universellen Weg des Aufbaus eines EA-Codes mit sehr wenig Erfahrung im EA-Schreiben aufzuzeigen. Diese Aufgabe wurde in dem Artikel gelöst, nehme ich an. In dem nächsten Artikel werde ich einige Features zur Analyse von Optimierungsergebnissen erklären und werde Ihnen eins für das Handelssystem anbieten. Für die in dem Artikel als Beispiel enthaltenen EAs beachten Sie bitte, dass sie zu einfach sind und kaum für vollwertiges automatisiertes Trading verwendet werden können. Sie können jedoch sehr nützlich als Werkzeug für die Automatisierung einzelner Handelsvorgänge sein, wenn ein Trader in manchen Momenten das Client Terminal verlässt.