Automatisierte Optimierung eines EAs mit dem MetaTrader 5

BPASoftware Thai Co. Ltd | 23 Oktober, 2018

Einführung

Unser EA BuddyIlan verwendet 4 Hauptparameter, die jede Woche automatisch optimiert werden sollen, um die beste Einstellung für den Markt zu finden.

Diese Parameter sind:

Es ist unrealistisch, diese Art von Prozess jede Woche manuell zu starten, also haben wir nach einem bestehenden Mechanismus gesucht, um wiederholte Aufgaben durchzuführen, aber ohne Erfolg (für MetaTrader 5), also haben wir diesen selbst entwickelt.

Vielen Dank an Igor Malcev, der den Artikel "Automatisierte Optimierung eines Handelsroboters in Realem Handel" für MetaTrader 4 geschrieben hat.

Prinzip

Prozess der Optimierung

Die erste Instanz des MetaTrader 5, auf der der EA BuddyIlan läuft, läuft rund um die Uhr. Der EA, an dem wir heute arbeiten werden (Optimizer EA) und der die Optimierung durchführt, startet auf einer zweiten Instanz des MetaTrader 5.

Am Ende des Prozesses weist der Optimizer EA die optimierten Werte globalen Variablen zu, die vom laufenden EA BuddyIlan gelesen werden.

Die Optimierung wird jeden Samstag ohne weiteren manuellen Eingriff durchgeführt.


Kopieren der Daten

Wie bereits erwähnt, benötigen wir zwei Instanzen des MetaTrader 5.

Die erste Instanz des MetaTrader 5 ist für das Kopieren von Konfigurationen, Parametern und Berichtsdateien zwischen den beiden Instanzen verantwortlich.

Aus Sicherheitsgründen ist der Zugriff auf Dateien außerhalb einer Sandbox unter MetaTrader 5 nicht möglich, daher werden wir den DOS-Befehl "xcopy" verwenden, um die Daten zwischen den beiden Umgebungen zu kopieren.

Um dies zu tun, müssen wir eine Windows-basierte DLL verwenden, die wir wie folgt deklarieren werden:

#import  "shell32.dll"
int ShellExecuteW(int hwnd,string Operation,string
                  File,string Parameters,string Directory,int ShowCmd);
#import

Der Aufruf dieser Funktion geschieht folgendermaßen:

string PathIniFile = sTerminalTesterDataPath + "\\config\\common.ini";
string PathTester=TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\Optimiser\\";

int ret=ShellExecuteW(0,"Open","xcopy","\""+PathIniFile+"\" \""+PathTester+"\" /y","",0);

Dies Funktion wird auch für den Start der Optimierung aufgerufen, z.B.:

int start = ShellExecuteW(0, "Open", sTerminalTesterPath + "\\terminal64.exe", "/config:" + TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\Optimiser\\optimise.ini", "", 0);
if(start<32)
  {
   Print("Failed starting Tester");
   return false;
  }

Der DLL-Import muss für den EA erlaubt werden:

DLL Import


Automatisierte Optimierung

MetaTrader 5 kann über Online-Befehle gestartet werden (siehe: Handelsplattform starten), auch automatische Aufgaben können auf diese Weise gestartet werden.

Beispielsweise kann man in der Standardkonfigurationsdatei (common.ini) einen Abschnitt "[Tester]" hinzufügen, um eine automatische Optimierung beim Start von MetaTrader 5 zu starten.

Das ist es, was wir tun werden.


Umsetzung

Der Optimizer EA muss die Pfade der Instanz des MetaTrader 5 Testers kennen, sie werden als Parameter eingegeben.

input string sTerminalTesterPath = "C:\\Program Files\\ForexTime MT5";
input string sTerminalTesterDataPath="C:\\Users\\BPA\\AppData\\Roaming\\MetaQuotes\\Terminal\\5405B7A2ED87FF45712A041DEF45780";

Wir haben ein Arbeitsverzeichnis definiert: "MQL5\Files\Optimiser" in der ersten MetaTrader 5 Instanz.

Unter der Funktion "CopyAndMoveCommonIni()" wird die Standardkonfigurationsdatei "common.ini" der MetaTrader 5 Tester-Instanz in unser Arbeitsverzeichnis kopiert und in "optimise.ini" umbenannt.

bool CopyAndMoveCommonIni()
  {
   string PathIniFile= sTerminalTesterDataPath+"\\config\\common.ini";
   string PathTester = TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\Optimiser\\";

   int ret=ShellExecuteW(0,"Open","xcopy","\""+PathIniFile+"\" \""+PathTester+"\" /y","",0);

// warten bis Datei kopiert wurde
   Sleep(2500);
   if(ret<32)
     {
      Print("Failed copying ini file");
      return false;
     }

// Wir arbeiten jetzt in der Sandbox, wir können die normalen Dateibefehle von MetaTrader 5 verwenden
   string IniFileName="Optimiser\\common.ini";
   string CopyTo="Optimiser\\optimise.ini";

   return FileMove( IniFileName, 0, CopyTo, 0 );
  }

Interessierte finden unter dieser Adresse weitere Informationen zur Funktion "ShellExecuteW": ShellExecuteW. Diese Funktion wartet nicht auf die Rückkehr der Ausführung des DOS-Befehls, sondern nutzt die Verzögerung (Sleep 2500).

Wir fügen nun den Abschnitt "Tester" in diese Datei ein:

bool AddTesterStanza()
  {
   int filehandle=FileOpen("Optimiser\\Optimise.ini",FILE_READ|FILE_WRITE|FILE_TXT);

   if(filehandle!=INVALID_HANDLE)
     {
      FileSeek(filehandle,0,SEEK_END);

      FileWrite(filehandle,"[Tester]\n",
                "Expert=BuddyIlan\\BuddyIlan\n",
                "ExpertParameters=BuddyIlanTester.set\n",
                "Symbol="+_Symbol+"\n",
                "Period=M15\n",
                "Login=\n",
                "Model=4\n",
                "ExecutionMode=0\n",
                "Optimization=2\n",
                "OptimizationCriterion=0\n",
                "FromDate="+TimeToString(TimeGMT()-InpTesterPeriod*86400,TIME_DATE)+"\n",
                "ToDate="+TimeToString(TimeGMT(),TIME_DATE)+"\n",
                "ForwardMode=0\n",
                "Report=MQL5\\Files\\Reports\\BuddyIlanReport\n",
                "ReplaceReport=1\n",
                "ShutdownTerminal=1\n",
                "Deposit=10000\n",
                "Currency=EURUSD\n",
                "Leverage=1:100\n",
                "UseLocal=1\n",
                "UseRemote=0\n",
                "UseCloud=0\n",
                "Visual=1\n");

      FileClose(filehandle);
     }
   else
     {
      Print("FileOpen, error ",GetLastError());
      return false;
     }
   return true;
  }

In diesem Codeblock definieren wir den zu optimierenden Experten ("BuddyIlan") - dieser EA muss in der zweiten Umgebung vorhanden sein - und die Datei ExpertParameters als "BuddyIlanTester.set" (achten Sie darauf, nicht den Namen des EA zu verwenden), wir setzen den Zeitraum (FromDate - ToDate) und alle für die Optimierung benötigten Parameter.

Wir haben "ShutdownTerminal=1" eingestellt, was bedeutet, dass das Terminal am Ende der Optimierung heruntergefahren wird.

Der Bericht wird in der Datei "Files\Reports\BuddyIlanReport" generiert - die Erweiterung ".xlm" wird von der Plattform hinzugefügt.

Wenn Ihre EAs auf einem virtuellen Server mit geringen CPU-Kapazitäten laufen, können Sie einige Remote- oder Cloud-Agenten (siehe "UseRemote" oder "UseCloud") für den Optimierungsprozess verwenden.


Parameterdatei

Jetzt müssen wir die oben definierte Parameterdatei (BuddyIlanTester.set) erstellen, die die Werte jedes Parameters des EA (BuddyIlan) enthält, den wir optimieren wollen.

Die Standardwerte dieser Parameter werden vom Benutzer festgelegt (definiert als Parameter):

input _TradingMode TradingMode = Dynamic;             // Konstantes oder dynamisches Volumen
input double  InpIlanFixedVolume = 0.1;               // Konstantes Volumen (falls gesetzt)

input int InpNCurrencies=1;                           // Anzahl von Instanzen von BuddyIlan auf diesem Konto

input double  LotExponent = 1.4;
input bool    DynamicPips = true;
input int     DefaultPips = 15;

input int Glubina=24;                                 // Anzahl der Bars zu Berechnung der Volatilität
input int DEL=3;

input int TakeProfit = 40.0;                          // Take Profit (Point)
input int Stoploss = 1000.0;                          // Stop Loss (Point)

input bool InpIlanTrailingStop = true;                // Trailing Stop aktivieren
input int InpIlanDistanceTS = 5;                      // Trailing Stop Abstand (Point)

input int MaxTrades=10;
input int InpDeviation=10;                            // Max. erlaubte Preisabweichung (Points)

input bool bSTOFilter = true;                         // dynamischer Trendfilter
input bool bSTOTimeFrameFilter = false;               // dynamischer Zeitfilter
input int InpMaxTf = 60;                              // Max. Zeitrahmen

Die folgende Funktion akzeptiert 8 Argumente, die ersten 4 entsprechen den zu optimierenden Parametern (SL, TP, STOFilter und STOTimeFrameFilter), wenn wahr (true), wird ein "Y" ans Ende der entsprechenden Parameterzeile gestellt. Die folgenden 4 Argumente entsprechen den bereits optimierten Werten, die wir bei der nächsten Optimierung berücksichtigen wollen.

Wie der Name schon sagt, kopiert diese Funktion auch die Parameterdatei in das Ad-hoc-Verzeichnis (MQL5\Profiles\Tester) der MetaTrader 5 Tester-Instanz.

bool CreateAndCopyParametersFile( bool SL, bool TP, bool STOFilter, bool STOTimeFrameFilter, int SLValue, int TPValue, bool STOFilterValue, bool STOTimeFrameFilterValue )
  {
   int filehandle=FileOpen("Optimiser\\BuddyIlanTester.set",FILE_WRITE|FILE_TXT);

   if(filehandle!=INVALID_HANDLE)
     {
      FileWrite(filehandle,
                "_EA_IDENTIFIER=Buddy Ilan\n",
                "_EA_MAGIC_NUMBER=1111||0||1||10||N\n",
                StringFormat("TradingMode=%d||0||0||0||N\n",TradingMode),
                StringFormat("InpIlanFixedVolume=%lf||0.0||0.000000||0.000000||N\n",InpIlanFixedVolume),
                StringFormat("InpNCurrencies=%d||0||1||10||N\n",InpNCurrencies),
                StringFormat("LotExponent=%lf||0.0||0.000000||0.000000||N\n",LotExponent),
                StringFormat("DynamicPips=%s||false||0||true||N\n",(DynamicPips==true)?"true":"false"),
                StringFormat("DefaultPips=%d||0||1||10||N\n",DefaultPips),
                StringFormat("Glubina=%d||0||1||10||N\n",Glubina),
                StringFormat("DEL=%d||0||1||10||N\n",DEL),

                StringFormat("TakeProfit=%d||30||10||70||%s\n",(TPValue==0)?30:TPValue,(TP==true)?"Y":"N"),
                StringFormat("Stoploss=%d||500||250||1500||%s\n",(SLValue==0)?1000:SLValue,(SL==true)?"Y":"N"),

                StringFormat("InpIlanTrailingStop=%s||false||0||true||N\n",(InpIlanTrailingStop==true)?"true":"false"),
                StringFormat("InpIlanDistanceTS=%d||0||1||10||N\n",InpIlanDistanceTS),
                StringFormat("MaxTrades=%d||0||1||10||N\n",MaxTrades),
                StringFormat("InpDeviation=%d||0||1||10||N\n",InpDeviation),

                StringFormat("bSTOFilter=%s||false||0||true||%s\n",(STOFilterValue==true)?"true":"false",(STOFilter==true)?"Y":"N"),
                StringFormat("bSTOTimeFrameFilter=%s||false||0||true||%s\n",(STOTimeFrameFilterValue==true)?"true":"false",(STOTimeFrameFilter==true)?"Y":"N"),
                StringFormat("InpMaxTf=%d||0||1||10||N\n",InpMaxTf));

      FileClose(filehandle);
     }
   else
     {
      Print("FileOpen BuddyIlanTester.set, error ",GetLastError());
      return false;
     }

   Sleep(1500);

   string PathTester=TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\Optimiser\\BuddyIlanTester.set";
   string PathProfile=sTerminalTesterDataPath+"\\MQL5\\Profiles\\Tester\\";

// kopieren der Ini-Date in das Verzeichnis des Testers 
   int ret=ShellExecuteW(0,"Open","xcopy","\""+PathTester+"\" \""+PathProfile+"\" /y","",0);

// warten bis Datei kopiert wurde
   Sleep(2500);
   if(ret<32)
     {
      Print("Failed copying parameters file");
      return false;
     }
   return true;
  }


Start der Optimierung

Die folgende Funktion startet die Instanz des MetaTrader 5 Testers, die Optimierung wird automatisch mit den von uns angegebenen Parametern gestartet. Diese zweite Instanz erzeugt die Ergebnisdatei und fährt dann herunter.

bool StartOptimizer()
  {
// Löschen des vorherigen Berichts
   FileDelete("Optimiser\\BuddyIlanReport.xml");

// Löschen des vorherigen Berichts (zweite Instanz des MetaTrader 5)
   string PathReport=sTerminalTesterDataPath+"\\MQL5\\Files\\Reports\\BuddyIlanReport.xml";

   ShellExecuteW(0,"Open","cmd.exe"," /C del "+PathReport,"",0);

   Sleep(2500);

   string sTerminalPath=TerminalInfoString(TERMINAL_PATH);

// Start der Optimierung
   int start=ShellExecuteW(0,"Open",sTerminalTesterPath+"\\terminal64.exe","/config:"+TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\Optimiser\\optimise.ini","",0);
   if(start<32)
     {
      Print("Failed starting Tester");
      return false;
     }
   Sleep(15000);
   return true;
  }

Für die erste Instanz des MetaTrader 5 ist es am einfachsten herauszufinden, ob die Optimierung abgeschlossen ist, zu überprüfen, ob die Berichtsdatei vorhanden ist.

Wenn die Berichtsdatei erstellt wird, kopieren wir sie in unser Arbeitsverzeichnis.

bool CopyReport()
  {
   int nTry=0;

// Warten und kopieren der Berichtsdatei

   while(nTry++<500) // Timeout : 2 Stunden
     {
      string PathReport = sTerminalTesterDataPath + "\\MQL5\\Files\\Reports\\BuddyIlanReport.xml";
      string PathTarget = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\Optimiser\\";

      int ret=ShellExecuteW(0,"Open","xcopy","\""+PathReport+"\" \""+PathTarget+"\" /y","",0);

      if(ret<32)
        {
         PrintFormat("Waiting generation report (%d) ...",nTry);
         Sleep(15000);
        }
      else
        {
         if(FileIsExist("Optimiser\\BuddyIlanReport.xml")==true)
           {
            PrintFormat("Report found (ret=%d) ...",ret);
            Sleep(2500);
            return true;
           }
         else
           {
            PrintFormat("Waiting report (%d) ...",nTry);
            Sleep(15000);
           }
        }
     }
   return false;
  }


Ergebnisauswertung

Die Berichtsdatei ist im XML-Format. Glücklicherweise schrieb Paul van Hemmen eine Bibliothek für MetaTrader 5, um auf diese Datenform zuzugreifen; diese Bibliothek ist unter folgender Adresse verfügbar: https://www.mql5.com/de/code/1998 - ihm sei herzlich gedankt.

Wir laden diese Bibliothek in unseren EA wie folgt:

#include <EasyXML\EasyXml.mqh>

Tatsächlich verwenden wir die Funktion unten und modifizierten ein paar Dinge in dieser Bibliothek, um sie unserer Berichtsdatei anzupassen (siehe in den beigefügten Dateien).

//+------------------------------------------------------------------+
//| Laden von XML durch die angegebene Datei                         |
//+------------------------------------------------------------------+
bool CEasyXml::loadXmlFromFullPathFile(string pFilename)
  {
   string sStream;
   int    iStringSize;

   Print("Loading XML File ",pFilename);
   int hFile=FileOpen(pFilename,FILE_ANSI|FILE_READ,0,CP_UTF8);
   if(hFile==INVALID_HANDLE)
     {
      Err=EASYXML_ERR_CONNECTION_FILEOPEN;
      PrintFormat("[%s] Err=%d",pFilename,GetLastError());
      return(Error());
     }

   while(!FileIsEnding(hFile))
     {
      iStringSize = FileReadInteger(hFile, INT_VALUE);
      sStream    += FileReadString(hFile, iStringSize);
     }

   FileClose(hFile);

   return(loadXmlFromString(sStream));
  }

Der Zugriff auf die Daten ist ziemlich einfach, mehrere Funktionen erlauben die Analyse der Ergebnisse und das Lesen der Daten, die uns interessieren.

bool LoadResults( OptimisationType eType )
  {
// Init-Variable
   BetterProfit=0.0;

// Laden der Ergebnisse
   CEasyXml EasyXmlDocument;
   EasyXmlDocument.setDebugging(false);

   if(EasyXmlDocument.loadXmlFromFullPathFile("Optimiser\\BuddyIlanReport.xml")==true)
     {
      str="";
      CEasyXmlNode *RootNode=EasyXmlDocument.getDocumentRoot();
      for(int j=0; j<RootNode.Children().Total(); j++)
        {
         CEasyXmlNode *ChildNode=RootNode.Children().At(j);
         for(int i=0; i<ChildNode.Children().Total(); i++)
           {
            CEasyXmlNode *cNode=ChildNode.Children().At(i);
            if(cNode.getName() == "Worksheet" )
              {
               switch(eType)
                 {
                  case _SL :
                     DisplayNodesSL(cNode);
                     PrintFormat("-> SL=%d (Profit=%.2lf)",BetterSL,BetterProfit);
                     break;

                  case _TP :
                     DisplayNodesTP(cNode);
                     PrintFormat("-> TP=%d (Profit=%.2lf DD=%lf)",BetterTP,BetterProfit,BetterDD);
                     break;

                  case _STO :
                     DisplayNodesSTO(cNode);
                     PrintFormat("-> STOFilter=%s STOTimeFrameFilter=%s (Profit=%.2lf)",(BetterSTOFilter==true)?"true":"false",(BetterSTOTimeFrameFilter==true)?"true":"false",BetterProfit);
                     break;
                 }
               break;
              }
           }
        }
     }
   else
      PrintFormat("Error found");
   return true;
  }

Da wir mehrere Parameter optimieren wollen, analysieren wir das Ergebnis in nicht nur einer Weise, wir brauchen daher spezifischen Funktionen für die Optimierung. (P SL, TP und STO). Diese Funktionen sind rekursiv.

Unten ist diejenige für die Analyse der Ergebnisse der Optimierung von SL:

void DisplayNodesSL( CEasyXmlNode *Node )
  {
   for(int i=0; i<Node.Children().Total(); i++)
     {
      CEasyXmlNode *ChildNode=Node.Children().At(i);

      if(ChildNode.Children().Total()==0)
        {
         str+=ChildNode.getValue()+",";
        }
      else
        {
         DisplayNodesSL(ChildNode);

         if(Node.getName()=="Table" && ChildNode.getName()=="Row")
           {
            string res[];
            StringSplit(str,',',res);

            // Übergehen der Spaltentitel
            if(StringCompare(res[0],"Pass",true)!=0)
              {
               double profit=StringToDouble(res[2]);
               int sl=(int) StringToInteger(res[10]);

               PrintFormat("[%s]  Profit=%.2lf StopLoss=%d DD=%s",str,profit,sl,res[8]);

               if(profit>BetterProfit || (profit==BetterProfit && sl<BetterSL))
                 {
                  BetterProfit=profit;
                  BetterSL=sl;
                 }
              }
           }
         if(Node.getName()=="Table")
            str="";
        }
     }
  }

Diese Funktion wird für jede Zeile und jede Zelle aufgerufen.

Wenn ein Knoten (node) keinen Nachfolger (child) hat, bedeutet das, dass er Daten enthält und wir speichern sie in einer Zeichenkette, die wir später am Ende der Zeile wieder aufspalten werden.

if( ChildNode.Children().Total() == 0 )
  {
   str+=ChildNode.getValue()+",";
  }

Damit befinden sich die Wert jeder Spalte im Array "res[]" und wir können die Ergebnisse nach unseren Wünschen auswählen.

Der Hauptteil des EAs

Wir haben jetzt alle notwendigen Bausteine, um die 4 Parameter zu optimieren, die beste Einstellung auszuwählen und die Werte den entsprechenden globalen Variablen zuzuweisen, die dann vom laufenden EA gelesen werden können.

void OnTimer()
  {
   MqlDateTime dt;

   datetime now=TimeLocal(dt);

// Am Samstag
   if(dt.day_of_week!=6)
     {
      bOptimisationDone=false;
      return;
     }

// Um 6:00 früh
   if(dt.hour<6)
      return;

// Fertig ?
   if(bOptimisationDone==true)
      return;

// Löschen der vorherigen "optimise.ini"
   FileDelete("Optimiser\\Optimise.ini");

// Konfig.-Datei des EAs erstellen und in \MQL5\Profiles\Test (Tester Instance) kopieren.
   if(CreateAndCopyParametersFile(true,false,false,false,0,0,true,false)==false)
      return;

// Kopieren von common.ini -> optimise.ini
   if(CopyAndMoveCommonIni()==false)
      return;

// Ergänzen des Abschnitts [Tester] in optimise.ini - https://www.metatrader5.com/de/terminal/help/start_advanced/start
   if(AddTesterStanza()==false)
      return;

   Print("=======================\nOptimization SL-1");

// Start der ersten Optimierung SL
   StartOptimizer();

// Kopieren der Berichtsdatei in das Arbeitsverzeichnis
   if(CopyReport()==false)
      return;

// Bericht mit der Analyse   
   if(LoadResults(_SL)==false)
      return;

   Print("=======================\nOptimization STO");

// Erstellen der Parameterdatei für die Optimierung von STO (2 Parameter zugleich optimiert) 
   if(CreateAndCopyParametersFile(false,false,true,true,BetterSL,0,true,false)==false)
      return;

// Beginn der Optimierung von STO
   StartOptimizer();

// Kopieren der Berichtsdatei in das Arbeitsverzeichnis
   if(CopyReport()==false)
      return;

   if(LoadResults(_STO)==false)
      return;

   Print("=======================\nOptimization SL-2");

// Erstellen der Parameterdatei für die zweite Optimierung von SL (Neuberechnung mit den neuen Parameterwerten von STO)
   if(CreateAndCopyParametersFile(true,false,false,false,0,0,BetterSTOFilter,BetterSTOTimeFrameFilter)==false)
      return;

// Beginn der Optimierung   
   StartOptimizer();

   if(CopyReport()==false)
      return;

   if(LoadResults(_SL)==false)
      return;

   Print("=======================\nOptimization TP");

// Erstellen der Parameterdatei für die Optimierung von TP
   if(CreateAndCopyParametersFile(false,true,false,false,BetterSL,0,BetterSTOFilter,BetterSTOTimeFrameFilter)==false)
      return;

// Beginn der Optimierung  
   StartOptimizer();

   if(CopyReport()==false)
      return;

   if(LoadResults(_TP)==false)
      return;

// Ende

   PrintFormat("=======================\nSL=%d TP=%d STOFilter=%s STOTimeFrameFilter=%s (Profit=%.2lf DD=%lf)\n=======================",
               BetterSL,BetterTP,(BetterSTOFilter==true)?"true":"false",(BetterSTOTimeFrameFilter==true)?"true":"false",BetterProfit,BetterDD);

// Setzen der globalen Variablen - der laufende EA BuddyIlan wird sie lesen und verwenden

// WQird eine Drawdown von mehr als 50% festgestellt, beendet der EA den Handel
   if(BetterDD>50.0 && GlobalVariableSet(gVarStop,1.0)==false)
     {
      PrintFormat("Error setting Global Variable [%s]",gVarStop);
     }

   if(GlobalVariableSet(gVarSL,BetterSL)==false)
     {
      PrintFormat("Error setting Global Variable [%s]=%d",gVarSL,BetterSL);
     }

   if(GlobalVariableSet(gVarTP,BetterTP)==false)
     {
      PrintFormat("Error setting Global Variable [%s]=%d",gVarTP,BetterTP);
     }

   if(GlobalVariableSet(gVarSTOFilter,(BetterSTOFilter==true)?1.0:0.0)==false)
     {
      PrintFormat("Error setting Global Variable [%s]=%.1lf",gVarSTOFilter,(BetterSTOFilter==true)?1.0:0.0);
     }

   if(GlobalVariableSet(gVarSTOTimeFrameFilter,(BetterSTOTimeFrameFilter==true)?1.0:0.0)==false)
     {
      PrintFormat("Error setting Global Variable [%s]=%.1lf",gVarSTOTimeFrameFilter,(BetterSTOTimeFrameFilter==true)?1.0:0.0);
     }

   bOptimisationDone=true;
  }

Die Namen der globalen Variablen werden in der Funktion OnInit() erstellt:

int OnInit()
  {
// Globale Variable

   gVarStop="BuddyIlan."+_Symbol+".Stop";
   gVarSL = "BuddyIlan." + _Symbol + ".SL";
   gVarTP = "BuddyIlan." + _Symbol + ".TP";
   gVarSTOFilter="BuddyIlan."+_Symbol+".STOFilter";
   gVarSTOTimeFrameFilter="BuddyIlan."+_Symbol+".STOTimeFrameFilter";

Unten ist die ganze Optimierung:

2018.07.07 13:20:15.978 BuddyIlanOptimizer (EURGBP,M15) TERMINAL_PATH = C:\Program Files\MetaTrader 5 - ActivTrades
2018.07.07 13:20:15.978 BuddyIlanOptimizer (EURGBP,M15) TERMINAL_DATA_PATH = C:\Users\BPA\AppData\Roaming\MetaQuotes\Terminal\FE0E65DDB0B7B40DE125080872C34D61
2018.07.07 13:20:15.978 BuddyIlanOptimizer (EURGBP,M15) TERMINAL_COMMONDATA_PATH = C:\Users\BPA\AppData\Roaming\MetaQuotes\Terminal\Common
2018.07.07 13:20:32.586 BuddyIlanOptimizer (EURGBP,M15) =======================
2018.07.07 13:20:32.586 BuddyIlanOptimizer (EURGBP,M15) Optimization SL-1
2018.07.07 13:20:50.439 BuddyIlanOptimizer (EURGBP,M15) Waiting report (1) ...
2018.07.07 13:21:05.699 BuddyIlanOptimizer (EURGBP,M15) Waiting report (2) ...
2018.07.07 13:21:20.859 BuddyIlanOptimizer (EURGBP,M15) Waiting report (3) ...
2018.07.07 13:21:35.952 BuddyIlanOptimizer (EURGBP,M15) Report found (ret=42) ...
2018.07.07 13:21:38.471 BuddyIlanOptimizer (EURGBP,M15) Loading XML File Optimiser\BuddyIlanReport.xml
2018.07.07 13:21:38.486 BuddyIlanOptimizer (EURGBP,M15) [0,11032.2600,1032.2600,3.3406,1.7096,1.5083,0.1558,0,6.2173,309,500,]  Profit=1032.26 StopLoss=500 DD=6.2173
2018.07.07 13:21:38.487 BuddyIlanOptimizer (EURGBP,M15) [2,11463.8000,1463.8000,4.7837,2.0386,0.8454,0.1540,0,15.4222,306,1000,]  Profit=1463.80 StopLoss=1000 DD=15.4222
2018.07.07 13:21:38.487 BuddyIlanOptimizer (EURGBP,M15) [4,11444.1000,1444.1000,4.7348,2.0340,0.8340,0.1529,0,15.4493,305,1500,]  Profit=1444.10 StopLoss=1500 DD=15.4493
2018.07.07 13:21:38.487 BuddyIlanOptimizer (EURGBP,M15) [1,11297.1900,1297.1900,4.2392,1.8414,0.8180,0.1400,0,14.1420,306,750,]  Profit=1297.19 StopLoss=750 DD=14.1420
2018.07.07 13:21:38.487 BuddyIlanOptimizer (EURGBP,M15) [3,11514.0800,1514.0800,4.9158,2.3170,1.4576,0.2055,0,9.3136,308,1250,]  Profit=1514.08 StopLoss=1250 DD=9.3136
2018.07.07 13:21:38.487 BuddyIlanOptimizer (EURGBP,M15) -> SL=1250 (Profit=1514.08)
2018.07.07 13:21:38.487 BuddyIlanOptimizer (EURGBP,M15) =======================
2018.07.07 13:21:38.487 BuddyIlanOptimizer (EURGBP,M15) Optimization STO
2018.07.07 13:22:02.660 BuddyIlanOptimizer (EURGBP,M15) Waiting report (1) ...
2018.07.07 13:22:17.768 BuddyIlanOptimizer (EURGBP,M15) Waiting report (2) ...
2018.07.07 13:22:32.856 BuddyIlanOptimizer (EURGBP,M15) Waiting report (3) ...
2018.07.07 13:22:47.918 BuddyIlanOptimizer (EURGBP,M15) Waiting report (4) ...
2018.07.07 13:23:02.982 BuddyIlanOptimizer (EURGBP,M15) Report found (ret=42) ...
2018.07.07 13:23:05.485 BuddyIlanOptimizer (EURGBP,M15) Loading XML File Optimiser\BuddyIlanReport.xml
2018.07.07 13:23:05.499 BuddyIlanOptimizer (EURGBP,M15) [0,11463.5000,1463.5000,4.4483,2.0614,0.8452,0.1540,0,15.4267,329,false,false,]  Profit=1463.50 false false  DD=15.4267
2018.07.07 13:23:05.499 BuddyIlanOptimizer (EURGBP,M15) [1,11444.1000,1444.1000,4.7348,2.0340,0.8340,0.1529,0,15.4493,305,true,false,]  Profit=1444.10 true false  DD=15.4493
2018.07.07 13:23:05.499 BuddyIlanOptimizer (EURGBP,M15) [2,11430.5300,1430.5300,5.1090,2.1548,0.8917,0.1717,0,14.4493,280,false,true,]  Profit=1430.53 false true  DD=14.4493
2018.07.07 13:23:05.499 BuddyIlanOptimizer (EURGBP,M15) [3,11470.7100,1470.7100,6.2851,1.8978,0.8146,0.1288,0,17.3805,234,true,true,]  Profit=1470.71 true true  DD=17.3805
2018.07.07 13:23:05.499 BuddyIlanOptimizer (EURGBP,M15) -> STOFilter=true STOTimeFrameFilter=true (Profit=1470.71)
2018.07.07 13:23:05.500 BuddyIlanOptimizer (EURGBP,M15) =======================
2018.07.07 13:23:05.500 BuddyIlanOptimizer (EURGBP,M15) Optimization SL-2
2018.07.07 13:23:29.921 BuddyIlanOptimizer (EURGBP,M15) Waiting report (1) ...
2018.07.07 13:23:45.043 BuddyIlanOptimizer (EURGBP,M15) Waiting report (2) ...
2018.07.07 13:24:00.170 BuddyIlanOptimizer (EURGBP,M15) Waiting report (3) ...
2018.07.07 13:24:15.268 BuddyIlanOptimizer (EURGBP,M15) Waiting report (4) ...
2018.07.07 13:24:30.340 BuddyIlanOptimizer (EURGBP,M15) Report found (ret=42) ...
2018.07.07 13:24:32.854 BuddyIlanOptimizer (EURGBP,M15) Loading XML File Optimiser\BuddyIlanReport.xml
2018.07.07 13:24:32.872 BuddyIlanOptimizer (EURGBP,M15) [0,9269.9000,-730.1000,-2.7760,0.7328,-0.3644,-0.0532,0,19.4241,263,500,]  Profit=-730.10 StopLoss=500 DD=19.4241
2018.07.07 13:24:32.872 BuddyIlanOptimizer (EURGBP,M15) [4,11470.7100,1470.7100,6.2851,1.8978,0.8146,0.1288,0,17.3805,234,1500,]  Profit=1470.71 StopLoss=1500 DD=17.3805
2018.07.07 13:24:32.872 BuddyIlanOptimizer (EURGBP,M15) [3,11475.9500,1475.9500,6.2806,1.8995,0.8175,0.1290,0,17.3718,235,1250,]  Profit=1475.95 StopLoss=1250 DD=17.3718
2018.07.07 13:24:32.872 BuddyIlanOptimizer (EURGBP,M15) [2,11400.7500,1400.7500,5.8609,1.8442,0.7759,0.1292,0,17.3805,239,1000,]  Profit=1400.75 StopLoss=1000 DD=17.3805
2018.07.07 13:24:32.872 BuddyIlanOptimizer (EURGBP,M15) [1,10662.5500,662.5500,2.8807,1.3618,0.3815,0.0862,0,16.7178,230,750,]  Profit=662.55 StopLoss=750 DD=16.7178
2018.07.07 13:24:32.873 BuddyIlanOptimizer (EURGBP,M15) -> SL=1250 (Profit=1475.95)
2018.07.07 13:24:32.873 BuddyIlanOptimizer (EURGBP,M15) =======================
2018.07.07 13:24:32.873 BuddyIlanOptimizer (EURGBP,M15) Optimization TP
2018.07.07 13:24:57.175 BuddyIlanOptimizer (EURGBP,M15) Waiting report (1) ...
2018.07.07 13:25:12.311 BuddyIlanOptimizer (EURGBP,M15) Waiting report (2) ...
2018.07.07 13:25:27.491 BuddyIlanOptimizer (EURGBP,M15) Waiting report (3) ...
2018.07.07 13:25:42.613 BuddyIlanOptimizer (EURGBP,M15) Waiting report (4) ...
2018.07.07 13:25:57.690 BuddyIlanOptimizer (EURGBP,M15) Report found (ret=42) ...
2018.07.07 13:26:00.202 BuddyIlanOptimizer (EURGBP,M15) Loading XML File Optimiser\BuddyIlanReport.xml
2018.07.07 13:26:00.219 BuddyIlanOptimizer (EURGBP,M15) [1,11768.5700,1768.5700,8.2259,2.4484,1.1024,0.2233,0,14.1173,215,40,]  Profit=1768.57 TakeProfit=40 DD=14.117300
2018.07.07 13:26:00.219 BuddyIlanOptimizer (EURGBP,M15) [4,12343.5200,2343.5200,13.5464,2.5709,1.3349,0.2519,0,15.0389,173,70,]  Profit=2343.52 TakeProfit=70 DD=15.038900
2018.07.07 13:26:00.219 BuddyIlanOptimizer (EURGBP,M15) [0,11243.4600,1243.4600,5.2913,1.6399,0.6887,0.1039,0,17.3805,235,30,]  Profit=1243.46 TakeProfit=30 DD=17.380500
2018.07.07 13:26:00.219 BuddyIlanOptimizer (EURGBP,M15) [3,12292.3500,2292.3500,11.8162,2.5837,0.9257,0.2538,0,20.4354,194,60,]  Profit=2292.35 TakeProfit=60 DD=20.435400
2018.07.07 13:26:00.219 BuddyIlanOptimizer (EURGBP,M15) [2,12146.3900,2146.3900,11.0639,2.4416,1.2226,0.2292,0,15.0772,194,50,]  Profit=2146.39 TakeProfit=50 DD=15.077200
2018.07.07 13:26:00.219 BuddyIlanOptimizer (EURGBP,M15) -> TP=70 (Profit=2343.52 DD=15.038900)
2018.07.07 13:26:00.219 BuddyIlanOptimizer (EURGBP,M15) =======================
2018.07.07 13:26:00.219 BuddyIlanOptimizer (EURGBP,M15) SL=1250 TP=70 STOFilter=true STOTimeFrameFilter=true (Profit=2343.52 DD=15.038900)
2018.07.07 13:26:00.219 BuddyIlanOptimizer (EURGBP,M15) =======================

Schlussfolgerung

Die Implementierung dieses Prozesses erfordert ein Minimum an Kenntnissen über MetaTrader 5, seine Optimierungsmechanismen und die Programmierung.

Anbei die Quellen dieses EA, die XML-Parser-Dateien von Paul van Hemmen und die modifizierte Datei "EasyXml.mqh".

Ich hoffe, es hilft.