Otimização automática de EAs no MetaTrader 5

BPASoftware Thai Co. Ltd | 18 dezembro, 2018

Introdução

Nosso EA Buddy Ilan usará 4 parâmetros fundamentais. Seria bom implementar uma auto-otimização semanal desses parâmetros para que o EA correspondesse às mudanças no mercado ao máximo.

Esses parâmetros são:

É impossível iniciar esse processo a cada semana manualmente, por essa razão, tentamos encontrar mecanismos para realizar tarefas repetitivas. Como não encontramos soluções prontas para o MetaTrader 5, decidimos desenvolver nosso próprio mecanismo.

Graças a Igor Maltsev, autor do artigo "Otimização automatizada de um robô de negociação no trading real" para o terminal MetaTrader 4.


Funcionamento



A primeira instância do terminal MetaTrader 5 trabalha 24/7. É nesse terminal que são iniciados o EA Buddy Ilan e o EA em que trabalharemos nesse artigo (EA Optimizer). Esse EA executa o processo de otimização na segunda instância do terminal MetaTrader 5.

No final do processo, o EA Optimizer define os valores otimizados em variáveis globais, que serão lidas pelo EA Buddy Ilan inicializado.

Como programado, a otimização será realizada todos os sábados sem qualquer manipulação manual.


Copiando dados

Como mencionado acima, precisamos de duas instâncias do terminal MetaTrader 5.

A primeira instância é responsável por copiar a configuração, os parâmetros e os arquivos de relatório entre os dois terminais.

Por razões de segurança, como no MetaTrader 5 é impossível acessar arquivos fora da área restrita, usaremos o comando "xcopy" do MS-DOS para copiar dados entre os dois ambientes.

Para fazer isso, usamos uma DLL baseada em bibliotecas do Windows, que declararemos da seguinte forma:

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

Aqui está a chamada para essa função:

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);

Essa função também será chamada para executar otimizações, por exemplo:

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

Para esse EA, deve-se habilitar as importações de DLL:

Importar DLL


Auto-otimização

A plataforma MetaTrader 5 pode ser iniciada com comandos (veja "Execução da plataforma de negociação"). Da mesma forma, podem-se executar tarefas automáticas.

Por exemplo, podemos adicionar o bloco de inicialização "[Tester]" ao arquivo de configuração padrão (common.ini), permitindo executar a auto-otimização ao iniciar o MetaTrader 5.

É precisamente isso o que vamos fazer.


Implementação

O EA Optimizer precisa conhecer os caminhos da instância do MetaTrader 5 a ser usada para teste. Esses caminhos serão listados como parâmetros.

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

Nós já definimos o diretório de trabalho no primeiro terminal do MetaTrader 5: "MQL5\Files\Optimiser".

Em seguida, a função "CopyAndMoveCommonIni()" copia o arquivo de configuração padrão "common.ini" da instância do MetaTrader 5 para teste no diretório de trabalho e muda seu nome para "optimise.ini".

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);

// Esperamos a cópia do arquivo
   Sleep(2500);
   if(ret<32)
     {
      Print("Erro ao copiar o arquivo .ini");
      return false;
     }

// Agora estamos trabalhando na área restrita e podemos usar comandos de arquivo regulares do MetaTrader 5
   string IniFileName="Optimiser\\common.ini";
   string CopyTo="Optimiser\\optimise.ini";

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

Para obter mais informações sobre como funciona a função ShellExecuteW, consulte a documentação da Microsoft para a função. ShellExecuteW. A função não espera a execução do comando do MS-DOS, portanto, é usado um atraso (Sleep 2500).

Agora adicionamos o bloco "Tester" ao arquivo:

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;
  }

Até aqui, nós definimos o EA que otimizaremos ("BuddyIlan"), por isso, esse EA deve estar presente no segundo ambiente. Também estabelecemos o arquivo de parâmetros "BuddyIlanTester.set" (o nome deve diferir do nome do EA), definimos o intervalo (de FromDate a ToDate) e todos os parâmetros necessários para a otimização.

Definimos "ShutdownTerminal=1", o que significa que o terminal será encerrado após o término da otimização.

Adicionalmente, será formado o arquivo "Files\Reports\BuddyIlanReport" com o relatório, sendo que nesse ponto a plataforma adicionará a extensão ".xlm".

Se você iniciar EAs num servidor virtual com recursos de computação limitados, será possível usar agentes de teste remotos ou de nuvem ("UseRemote" ou "UseCloud") para otimização.


Arquivo de parâmetros

Agora, é preciso criar o arquivo de parâmetros definido acima (BuddyIlanTester.set), uma vez que ele conterá os valores de cada parâmetro otimizado do EA (Buddy Ilan).

Os valores padrão desses parâmetros são definidos pelo usuário (definidos como parâmetros):

input _TradingMode TradingMode = Dynamic;             // Volume fixo ou dinâmico
input double  InpIlanFixedVolume = 0.1;               // Tamanho do volume fixo (se usado)

input int InpNCurrencies=1;                           // Número de instâncias do EA Buddy Ilan na conta

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

input int Glubina=24;                                 // Número das últimas barras para calcular a volatilidade
input int DEL=3;

input int TakeProfit = 40.0;                          // Take-Profit em pontos
input int Stoploss = 1000.0;                          // Stop-Loss em pontos

input bool InpIlanTrailingStop = true;                // Usar trailing-stop
input int InpIlanDistanceTS = 5;                      // Distância do trailing-stop em pontos

input int MaxTrades=10;
input int InpDeviation=10;                            // Desvio máximo permitido em pontos

input bool bSTOFilter = true;                         // Filtro dinâmico de tendência
input bool bSTOTimeFrameFilter = false;               // Filtro dinâmico de timeframe
input int InpMaxTf = 60;                              // Timeframe máximo

A função a seguir aceita 8 argumentos, os 4 primeiros correspondem aos parâmetros otimizados (SL, TP, STOFilter e STOTimeFrameFilter). Se for true, um "Y" será adicionado ao final da string do parâmetro correspondente. Os quatro argumentos a seguir correspondem aos valores já otimizados que precisam ser considerados na próxima otimização.

Como o nome indica, a função também copia o arquivo de parâmetros para o diretório correspondente da instância do terminal MetaTrader 5 usada para teste (MQL5\Profiles\Tester).

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\\";

// Copiamos o arquivo ini para o diretório do testador 
   int ret=ShellExecuteW(0,"Open","xcopy","\""+PathTester+"\" \""+PathProfile+"\" /y","",0);

// Esperamos a cópia do arquivo
   Sleep(2500);
   if(ret<32)
     {
      Print("Erro ao copiar o arquivo de parâmetros");
      return false;
     }
   return true;
  }


Execução da otimização

A função a seguir inicia uma instância de teste do MetaTrader 5, enquanto isso, uma otimização é iniciada automaticamente com os parâmetros especificados. Essa segunda instância gera um arquivo com os resultados, após o qual o terminal será encerrado.

bool StartOptimizer()
  {
// Excluímos o relatório anterior
   FileDelete("Optimiser\\BuddyIlanReport.xml");

// Excluímos o relatório anterior (segunda instância do 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);

// Inicialização do processo de otimização
   int start=ShellExecuteW(0,"Open",sTerminalTesterPath+"\\terminal64.exe","/config:"+TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\Optimiser\\optimise.ini","",0);
   if(start<32)
     {
      Print("Erro ao iniciar o Testador");
      return false;
     }
   Sleep(15000);
   return true;
  }

A maneira mais fácil de saber se otimização acabou a partir do primeiro terminal é verificar a presença do arquivo de relatório.

Após criar o arquivo de relatório, copie-o para o diretório de trabalho.

bool CopyReport()
  {
   int nTry=0;

// Esperamos e copiamos o arquivo de relatório

   while(nTry++<500) // Timeout: 2 horas
     {
      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("Aguardando geração do relatório (%d) ...",nTry);
         Sleep(15000);
        }
      else
        {
         if(FileIsExist("Optimiser\\BuddyIlanReport.xml")==true)
           {
            PrintFormat("Relatório (ret=%d) encontrado ...",ret);
            Sleep(2500);
            return true;
           }
         else
           {
            PrintFormat("Aguardando relatório (%d) ...",nTry);
            Sleep(15000);
           }
        }
     }
   return false;
  }


Leitura de resultados

O arquivo de relatório é gerado no formato XML. Por sorte, temos uma biblioteca para trabalhar com esses dados, escrita por Paul van Hemmen. A biblioteca pode ser baixada nesse link: https://www.mql5.com/pt/code/1998.

Adicionamos essa biblioteca ao nosso EA:

#include <EasyXML\EasyXml.mqh>

Adicionamos uma função e fizemos algumas pequenas alterações na biblioteca para adaptá-la aos nossos arquivos de relatório (veja os arquivos anexados).

//+------------------------------------------------------------------+
//| Load XML by given file                                           |
//+------------------------------------------------------------------+
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));
  }

O acesso aos dados é realizado de forma simples: várias funções permitem analisar os resultados e ler os dados em que estamos interessados.

bool LoadResults( OptimisationType eType )
  {
// Variável Init
   BetterProfit=0.0;

// Carregamento de resultados
   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("Erro");
   return true;
  }

Como precisamos otimizar vários parâmetros cujos resultados terão que ser analisados de forma diferente, precisamos de uma função separada para cada otimização (para os parâmetros SL, TP e STO). Essas são funções recursivas.

Aqui está uma função para analisar os resultados da otimização do parâmetro 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);

            // Iteração de cabeçalhos de coluna
            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="";
        }
     }
  }

Essa função é chamada para cada linha e célula.

Se o nó não tiver descendentes, isso significa que ele contém dados, portanto, salvamos esses dados como string, e dividimos no final da linha.

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

Assim, os valores para cada coluna ficam disponíveis na matriz "res[]" e podemos selecionar os resultados desejados.


Corpo do EA

Agora, temos todos os elementos necessários para otimizar nossos quatro parâmetros, busca dos melhores parâmetros e definição desses valores para as variáveis globais correspondentes que serão lidas pelo EA Buddy Ilan.

void OnTimer()
  {
   MqlDateTime dt;

   datetime now=TimeLocal(dt);

// Ao sábado
   if(dt.day_of_week!=6)
     {
      bOptimisationDone=false;
      return;
     }

// Às 6:00
   if(dt.hour<6)
      return;

// Já está feito?
   if(bOptimisationDone==true)
      return;

// Removemos o arquivo anterior "optimise.ini"
   FileDelete("Optimiser\\Optimise.ini");

// Criamos e copiamos o arquivo de configuração do EA para a pasta \MQL5\Profiles\Test (instância para teste)
   if(CreateAndCopyParametersFile(true,false,false,false,0,0,true,false)==false)
      return;

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

// Adicionamos a inicialização [Tester] a optimise.ini - https://www.metatrader5.com/ru/terminal/help/start_advanced/start
   if(AddTesterStanza()==false)
      return;

   Print("=======================\nOtimização SL-1");

// Inicialização da primeira otimização do SL
   StartOptimizer();

// Copiamos o arquivo de relatório para o diretório de trabalho
   if(CopyReport()==false)
      return;

// Analisamos os relatórios   
   if(LoadResults(_SL)==false)
      return;

   Print("=======================\nOtimização STO");

// Criamos um arquivo de parâmetros para a otimização do STO (2 parâmetros otimizados ao mesmo tempo) 
   if(CreateAndCopyParametersFile(false,false,true,true,BetterSL,0,true,false)==false)
      return;

// Execução da otimização STO
   StartOptimizer();

// Copiamos o arquivo de relatório para o diretório de trabalho
   if(CopyReport()==false)
      return;

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

   Print("=======================\nOtimização SL-2");

// Criamos um arquivo de parâmetros para otimização do SL (recálculo com novos valores do parâmetro STO)
   if(CreateAndCopyParametersFile(true,false,false,false,0,0,BetterSTOFilter,BetterSTOTimeFrameFilter)==false)
      return;

// Execução da otimização   
   StartOptimizer();

   if(CopyReport()==false)
      return;

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

   Print("=======================\nOtimização TP");

// Criamos um arquivo de parâmetros para otimização do TP
   if(CreateAndCopyParametersFile(false,true,false,false,BetterSL,0,BetterSTOFilter,BetterSTOTimeFrameFilter)==false)
      return;

// Execução da otimização  
   StartOptimizer();

   if(CopyReport()==false)
      return;

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

// Conclusão

   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);

// Definimos variáveis globais, pois o Expert Advisor BuddyIlan irá ler e usar esses novos valores.

// Se um drawdown de mais de 50% for detectado, o EA parará a negociação
   if(BetterDD>50.0 && GlobalVariableSet(gVarStop,1.0)==false)
     {
      PrintFormat("Erro ao definir a variável global [%s]",gVarStop);
     }

   if(GlobalVariableSet(gVarSL,BetterSL)==false)
     {
      PrintFormat("Erro ao definir a variável global [%s]=%d",gVarSL,BetterSL);
     }

   if(GlobalVariableSet(gVarTP,BetterTP)==false)
     {
      PrintFormat("Erro ao definir a variável global [%s]=%d",gVarTP,BetterTP);
     }

   if(GlobalVariableSet(gVarSTOFilter,(BetterSTOFilter==true)?1.0:0.0)==false)
     {
      PrintFormat("Erro ao definir a variável global [%s]=%.1lf",gVarSTOFilter,(BetterSTOFilter==true)?1.0:0.0);
     }

   if(GlobalVariableSet(gVarSTOTimeFrameFilter,(BetterSTOTimeFrameFilter==true)?1.0:0.0)==false)
     {
      PrintFormat("Erro ao definir a variável global [%s]=%.1lf",gVarSTOTimeFrameFilter,(BetterSTOTimeFrameFilter==true)?1.0:0.0);
     }

   bOptimisationDone=true;
  }

Os nomes das variáveis globais são criados na função OnInit():

int OnInit()
  {
// Variáveis globais

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

O seguinte é todo o processo de otimização:

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) =======================



Fim do artigo

Para implementar esse processo, você precisa de conhecimento mínimo do terminal MetaTrader 5, seus mecanismos de otimização e programação.

O código-fonte desse, o utilitário XML Parser de Paul van Hemmen e o arquivo modificado "EasyXml.mqh" estão anexados ao artigo.

Espero que o artigo seja útil para você.