English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Guia prático do MQL5: Reduzindo o efeito de sobreajuste e lidando com a falta de cotações

Guia prático do MQL5: Reduzindo o efeito de sobreajuste e lidando com a falta de cotações

MetaTrader 5Exemplos | 24 março 2014, 09:24
1 238 0
Anatoli Kazharski
Anatoli Kazharski

Introdução

Acredito que muitos negociantes têm-se intrigado repetidamente sobre os parâmetros ideais para seus sistemas de negociação. Na verdade, um algoritmo de negociação por si só não é suficiente. É preciso ver como ele ainda pode ser usado. Seja qual for a estratégia de negociação que você use, se é simples ou complexa, com um único ou vários instrumentos, você não pode evitar a questão de quais os parâmetros escolher para garantir os lucros futuros.

Estamos propensos a verificar sistemas de negociação com parâmetros que mostraram bons resultados ao longo do período de otimização (teste retrospectivo) e durante o período subsequente (teste progressivo). O teste de progressão, na verdade, não é tão necessário. Os resultados relevantes podem ser obtidos utilizando dados históricos.

Este método ocasiona uma grande questão a qual não pode ser dada uma resposta definitiva: qual a quantidade de dados históricos que deve ser usado para otimizar um sistema de negociação? A questão é que existem muitas opções. Tudo depende da gama de flutuações de preço que você espera para capitalizar.

Voltando para a quantidade de histórico necessário para a otimização, podemos concluir que os dados disponíveis podem ser suficientes para a negociação intra-hora. Isto nem sempre é válido para intervalos de tempo mais longos. Quanto maior o número de repetições de um padrão consistente, ou seja, quanto mais negociações tivermos, mais verdadeiro é o desempenho do sistema de negociação testado que podemos esperar ver no futuro.

E se os dados dos preços de um determinado instrumento não são suficientes para obter um número suficiente de repetições e se sentir mais seguro? Resposta: use dados de todos os instrumentos disponíveis.


Exemplo em NeuroShell DayTrader Professional

Antes de prosseguirmos com a programação em MetaTrader 5, vamos rever um exemplo em NeuroShell DayTrader Professional. Ele oferece ótimos recursos para otimizar os parâmetros de um sistema de negociação (compilado com um construtor) para vários símbolos. Você pode definir os parâmetros necessários nas configurações do módulo de negociação, otimizar os parâmetros para cada símbolo separadamente ou encontrar um conjunto de parâmetros ideais para todos os símbolos de uma só vez. Esta opção pode ser encontrada no guia de otimização:

A guia de otimização no módulo de negociação de NeuroShell DayTrader Professional

Fig. 1. A guia de otimização no módulo de negociação de NeuroShell DayTrader Professional

No nosso caso, qualquer sistema de negociação simples fará com que nós só precisemos comparar resultados de dois métodos de otimização, por isso a escolha do sistema é atualmente de pouca importância.

Você pode encontrar informações sobre como compilar as estratégias de negociação em NeuroShell DayTrader Professional, em outros artigos do meu blog (você pode procurar ou usar os caracteres para localizar a informação relevante). Eu também recomendo que você leia um artigo intitulado "Como preparar cotações do MetaTrader 5 para outros aplicativos", que descreve e demonstra como usando um script você pode baixar as cotações de MetaTrader 5 no formato compatível com NeuroShell DayTrader Professional.

Para fazer este teste, eu preparei dados obtidos a partir de barras diárias de oito símbolos a partir do ano de 2000 até janeiro de 2013:

Lista de símbolo para um teste em NeuroShell DayTrader Professional

Fig. 2. Lista de símbolo para um teste em NeuroShell DayTrader Professional

A figura abaixo mostra dois resultados de otimização. A parte superior apresenta o resultado da otimização, onde cada símbolo recebe seus próprios parâmetros, enquanto que a parte inferior mostra o resultado em que os parâmetros são comuns a todos os símbolos.

Comparação dos resultados de dois modos de otimização de parâmetros

Fig. 3. Comparação dos resultados de dois modos de otimização de parâmetros

O resultado mostrando os parâmetros comuns não parece tão bom quanto aquele em que os parâmetros são diferentes para cada símbolo. No entanto, ele inspira mais confiança já que o sistema de negociação passa por uma série de vários padrões de comportamento de preços (volatilidade, número de tendências/planos), com os mesmos parâmetros para todos os símbolos.

Continuando no mesmo assunto, podemos encontrar logicamente outro argumento em favor da otimização utilizando uma maior quantidade de dados. Pode muito bem ser, que o comportamento do preço de um determinado par de moedas, por exemplo, EURUSD, será bem diferente depois (em dois, cinco ou dez anos). Por exemplo, a inflação dos preços GBPUSD será semelhante ao comportamento passado do preço do EURUSD e vice-versa. Você deve estar pronto para isso já que é veredito para qualquer instrumento.


Um exemplo em MetaTrader 5

Vamos ver agora quais modos de otimização de parâmetros são oferecidos em MetaTrader 5. Abaixo você pode ver todos os símbolos selecionados no modo de otimização Market Watch com uma seta na lista suspensa de modos de otimização.

Fig. 4. Modo de otimização no Testador de Estratégia do MetaTrader 5

Fig. 4. Modo de otimização no Testador de Estratégia do MetaTrader 5

Este modo permite que você apenas teste um EA com os parâmetros atuais de cada símbolo, um por um. Os símbolos utilizados nos testes são os que estão atualmente selecionados na janela Market Watch. Em outras palavras, a otimização de parâmetros é, neste caso, não realizada. No entanto, o MetaTrader 5 e MQL5 permitem que você implemente esta ideia por si próprio.

Agora, precisamos ver como implementar o tal EA. A lista de símbolos será fornecida em um arquivo de texto (*.txt). Além disso, vamos implementar a possibilidade de armazenamento de vários conjuntos das listas de símbolos. Cada conjunto estará em uma seção separada com seu próprio cabeçalho dispondo de um número de seção. Os números são necessários para facilitar a verificação visual.

Nota-se que é importante ter o símbolo # frente ao número a fim de permitir que o Expert Advisor obtenha o conjunto de dados corretos quando preenche o conjunto de símbolos. Geralmente, o cabeçalho pode conter quaisquer símbolos mas deve ter # em todos os momentos. O sinal do número pode ser substituído por qualquer outro símbolo, segundo o qual o Expert Advisor irá determinar/contar as seções. Neste caso, a substituição terá que ser refletida no código.

Abaixo você pode ver o arquivo SymbolsList.txt que contém três conjuntos de símbolos para o teste. Este arquivo, como mostrado, ainda será utilizado ao testar o método.

Fig. 5. Vários conjuntos de símbolos fornecidos em um arquivo de texto para o teste

Fig. 5. Vários conjuntos de símbolos fornecidos em um arquivo de texto para o teste

Nos parâmetros externos, vamos adicionar outro parâmetro, SectionOfSymbolList, para indicar o conjunto de símbolos que o Expert Advisor deve usar no teste atual. Este parâmetro assume o valor (de zero para cima) que define o conjunto de símbolos. Se o valor exceder o número de conjuntos disponíveis, o Expert Advisor irá compor uma entrada correspondente ao registro e teste só será feito no símbolo atual.

SymbolsList.txt deve estar localizado no diretório do terminal local sob Metatrader 5\MQL5\Files. Ele também pode ser colocado na pasta comum mas, neste caso, não estará disponível para a otimização de parâmetros na rede em nuvem do MQL5. Além disso, para permitir o acesso ao arquivo e aos indicadores personalizados relevantes para testes, precisamos compor as seguintes linhas no início do arquivo:

//--- Allow access to the external file and indicator for optimization in the cloud
#property tester_file      "SymbolsList.txt"
#property tester_indicator "EventsSpy.ex5"

Nosso Expert Advisor irá basear-se no Expert Advisor multi-moeda já pronto apresentado no artigo "Guia prático do MQL5: Desenvolvendo um Expert Advisor Multi-Moeda com número de parâmetros ilimitados". A sua estratégia de negociação subjacente é muito simples, mas será suficiente para testar a eficácia do método. Nós só removeremos as partes desnecessárias, adicionaremos o que precisamos e corrigiremos o código relevante existente. Vamos, certamente, melhorar o nosso Expert Advisor com o recurso de relatório de economia amplamente descrito no artigo anterior da série "MQL5 Cookbook: Escrevendo a história de negócios em um arquivo e criando gráficos de balanço para cada símbolo no Excel". Gráficos de balanço para todos os símbolos também serão necessários para avaliar a eficiência do método em questão.

Os parâmetros externos do Expert Advisor devem ser modificados da seguinte maneira:

//--- External parameters of the Expert Advisor
sinput int    SectionOfSymbolList = 1;     // Section number in the symbol lists
sinput bool   UpdateReport        = false; // Report update
sinput string delimeter_00="";   // --------------------------------
sinput long   MagicNumber         = 777;   // Magic number
sinput int    Deviation           = 10;    // Slippage
sinput string delimeter_01="";   // --------------------------------
input  int    IndicatorPeriod     = 5;     // Indicator period
input  double TakeProfit          = 100;   // Take Profit
input  double StopLoss            = 50;    // Stop Loss
input  double TrailingStop        = 10;    // Trailing Stop
input  bool   Reverse             = true;  // Position reversal
input  double Lot                 = 0.1;   // Lot
input  double VolumeIncrease      = 0.1;   // Position volume increase
input  double VolumeIncreaseStep  = 10;    // Volume increase step

Todas as matrizes associadas com os parâmetros externos devem ser excluídas, como não serão necessárias, e ainda devem ser substituídas por variáveis externas ao longo do código inteiro. Apenas devemos abandonar a matriz dinâmica de símbolos, InputSymbols[], cujo tamanho vai depender do número de símbolos usados a partir de um dos conjuntos no arquivo SymbolsList.txt. Se o Expert Advisor for utilizado fora do Testador de Estratégia, o tamanho dessa matriz será igual a 1, como no modo em tempo real, o Expert Advisor vai trabalhar com apenas um símbolo.

As alterações correspondentes também devem ser feitas no arquivo de inicialização da matriz - InitializeArrays.mqh. Ou seja, todas as funções responsáveis pela inicialização das matrizes de variáveis ​externas devem ser deletadas. A função InitializeArraySymbols() parece agora como mostrado abaixo:

//+------------------------------------------------------------------+
//| Filling the array of symbols                                     |
//+------------------------------------------------------------------+
void InitializeArraySymbols()
  {
   int    strings_count  =0;   // Number of strings in the symbol file
   string checked_symbol ="";  // To check the accessibility of the symbol on the trade server
//--- Test mode message
   string message_01="<--- All symbol names in the <- SymbolsList.txt -> file are incorrect ... --->\n"
                     "<--- ... or the value of the \"Section of List Symbols\" parameter is greater, "
                     "than the number of file sections! --->\n"
                     "<--- Therefore we will test only the current symbol. --->";
//--- Real-time mode message
   string message_02="<--- In real-time mode, we only work with the current symbol. --->";
//--- If in real-time mode
   if(!IsRealtime())
     {
      //--- Get the number of strings from the specified symbol set in the file and fill the temporary array of symbols
      strings_count=ReadSymbolsFromFile("SymbolsList.txt");
      //--- Iterate over all symbols from the specified set
      for(int s=0; s<strings_count; s++)
        {
         //--- If the correct string is returned following the symbol check
         if((checked_symbol=GetSymbolByName(temporary_symbols[s]))!="")
           {
            //--- increase the counter
            SYMBOLS_COUNT++;
            //--- set/increase the array size
            ArrayResize(InputSymbols,SYMBOLS_COUNT);
            //--- index with the symbol name
            InputSymbols[SYMBOLS_COUNT-1]=checked_symbol;
           }
        }
     }
//--- If all symbol names were not input correctly or if currently working in real-time mode
   if(SYMBOLS_COUNT==0)
     {
      //--- Real-time mode message
      if(IsRealtime())
         Print(message_02);
      //--- Test mode message
      if(!IsRealtime())
         Print(message_01);
      //--- We will work with the current symbol only
      SYMBOLS_COUNT=1;
      //--- set the array size and
      ArrayResize(InputSymbols,SYMBOLS_COUNT);
      //--- index with the current symbol name
      InputSymbols[0]=_Symbol;
     }
  }

O código da função ReadSymbolsFromFile() também deve ser modificado. Ele costumava ler toda a lista de símbolos enquanto que agora queremos que ele leia apenas o conjunto do símbolo especificado. Abaixo está o código de função modificado:

//+------------------------------------------------------------------+
//| Returning the number of strings (symbols) from the specified     |
//| set in the file and filling the temporary array of symbols       |
//+------------------------------------------------------------------+
//--- When preparing the file, symbols in the list should be separated with a line break
int ReadSymbolsFromFile(string file_name)
  {
   ulong  offset         =0;   // Offset for determining the position of the file pointer
   string delimeter      ="#"; // Identifier of the section start
   string read_line      ="";  // For the check of the read string
   int    limit_count    =0;   // Counter limiting the number of the possibly open charts
   int    strings_count  =0;   // String counter
   int    sections_count =-1;  // Section counter
   
//--- Message 01
   string message_01="<--- The <- "+file_name+" -> file has not been prepared appropriately! --->\n"
                     "<--- The first string does not contain the section number identifier ("+delimeter+")! --->";
//--- Message 02
   string message_02="<--- The <- "+file_name+" -> file has not been prepared appropriately! --->\n"
                     "<--- There is no line break identifier in the last string, --->\n"
                     "<--- so only the current symbol will be involved in testing. --->";
//--- Message 03
   string message_03="<--- The <- "+file_name+" -> file could not be found! --->"
                     "<--- Only the current symbol will be involved in testing. --->";
                     
//--- Open the file (get the handle) for reading in the local directory of the terminal
   int file_handle=FileOpen(file_name,FILE_READ|FILE_ANSI,'\n');
//--- If the file handle has been obtained
   if(file_handle!=INVALID_HANDLE)
     {
      //--- Read until the current position of the file pointer
      //    reaches the end of the file or until the program is deleted
      while(!FileIsEnding(file_handle) || !IsStopped())
        {
         //--- Read until the end of the string or until the program is deleted
         while(!FileIsLineEnding(file_handle) || !IsStopped())
           {
            //--- Read the whole string
            read_line=FileReadString(file_handle);
            //--- If the section number identifier has been found
            if(StringFind(read_line,delimeter,0)>-1)
               //--- Increase the section counter
               sections_count++;
            //--- If the section has been read, exit the function
            if(sections_count>SectionOfSymbolList)
              {
               FileClose(file_handle); // Close the file
               return(strings_count);  // Return the number of strings in the file
              }
            //--- If this is the first iteration and the first string does not contain the section number identifier
            if(limit_count==0 && sections_count==-1)
              {
               PrepareArrayForOneSymbol(strings_count,message_01);
               //--- Close the file
               FileClose(file_handle);
               //--- Return the number of strings in the file
               return(strings_count);
              }
            //--- Increase the counter limiting the number of the possibly open charts
            limit_count++;
            //--- If the limit has been reached
            if(limit_count>=CHARTS_MAX)
              {
               PrepareArrayForOneSymbol(strings_count,message_02);
               //--- Close the file
               FileClose(file_handle);
               //--- Return the number of strings in the file
               return(strings_count);
              }
            //--- Get the position of the pointer
            offset=FileTell(file_handle);
            //--- If this is the end of the string
            if(FileIsLineEnding(file_handle))
              {
               //--- Go to the next string if this is not the end of the file
               //    For this purpose, increase the offset of the file pointer
               if(!FileIsEnding(file_handle))
                  offset++;
               //--- move it to the next string
               FileSeek(file_handle,offset,SEEK_SET);
               //--- If we are not in the specified section of the file, exit the loop
               if(sections_count!=SectionOfSymbolList)
                  break;
               //--- Otherwise,
               else
                 {
                  //--- if the string is not empty
                  if(read_line!="")
                    {
                     //--- increase the string counter
                     strings_count++;
                     //--- increase the size of the array of strings,
                     ArrayResize(temporary_symbols,strings_count);
                     //--- write the string to the current index
                     temporary_symbols[strings_count-1]=read_line;
                    }
                 }
               //--- Exit the loop
               break;
              }
           }
         //--- If this is the end of the file, terminate the entire loop
         if(FileIsEnding(file_handle))
            break;
        }
      //--- Close the file
      FileClose(file_handle);
     }
   else
      PrepareArrayForOneSymbol(strings_count,message_03);
//--- Return the number of strings in the file
   return(strings_count);
  }

Você pode ver que algumas sequências no código acima estão destacadas. Essas sequências contêm a função PrepareArrayForOneSymbol() que simplesmente prepara uma matriz para um símbolo (atual) no caso de erro.

//+------------------------------------------------------------------+
//| Preparing an array for one symbol                                |
//+------------------------------------------------------------------+
void PrepareArrayForOneSymbol(int &strings_count,string message)
  {
//--- Print the message to the log
   Print(message);
//--- Array size
   strings_count=1;
//--- Set the size of the array of symbols
   ArrayResize(temporary_symbols,strings_count);
//--- Write the string with the current symbol name to the current index
   temporary_symbols[0]=_Symbol;
  }

Agora tudo está pronto para testar o método de otimização de parâmetros. Mas antes de prosseguirmos com o teste, vamos adicionar outra série de dados ao relatório. Anteriormente, em adição ao balanço de todos os símbolos, o arquivo de relatório continha todos os levantamentos a partir de máximos locais expressados em porcentagem. Agora, o relatório também irá abranger todos os levantamentos de crédito em termos monetários. Ao mesmo tempo, vamos modificar a função CreateSymbolBalanceReport() onde o relatório é gerado.

O código da função CreateSymbolBalanceReport() é fornecido abaixo:

//+------------------------------------------------------------------+
//| Creating test report on deals in .csv format                     |
//+------------------------------------------------------------------+
void CreateSymbolBalanceReport()
  {
   int    file_handle =INVALID_HANDLE; // File handle
   string path        ="";             // File path

//--- If an error occurred when creating/getting the folder, exit
   if((path=CreateInputParametersFolder())=="")
      return;
//--- Create a file to write data in the common folder of the terminal
   file_handle=FileOpen(path+"\\LastTest.csv",FILE_CSV|FILE_WRITE|FILE_ANSI|FILE_COMMON);
//--- If the handle is valid (file created/opened)
   if(file_handle>0)
     {
      int           digits           =0;   // Number of decimal places in the price
      int           deals_total      =0;   // Number of deals in the specified history
      ulong         ticket           =0;   // Deal ticket
      double        drawdown_max     =0.0; // Drawdown
      double        balance          =0.0; // Balance
      string        delimeter        =","; // Delimiter
      string        string_to_write  ="";  // To generate the string for writing
      static double percent_drawdown =0.0; // Drawdown expressed as percentage
      static double money_drawdown   =0.0; // Drawdown in monetary terms

      //--- Generate the header string
      string headers="TIME,SYMBOL,DEAL TYPE,ENTRY TYPE,VOLUME,"
                     "PRICE,SWAP($),PROFIT($),DRAWDOWN(%),DRAWDOWN($),BALANCE";
      //--- If more than one symbol is involved, modify the header string
      if(SYMBOLS_COUNT>1)
        {
         for(int s=0; s<SYMBOLS_COUNT; s++)
            StringAdd(headers,","+InputSymbols[s]);
        }
      //--- Write the report headers
      FileWrite(file_handle,headers);
      //--- Get the complete history
      HistorySelect(0,TimeCurrent());
      //--- Get the number of deals
      deals_total=HistoryDealsTotal();
      //--- Resize the array of balances according to the number of symbols
      ArrayResize(symbol_balance,SYMBOLS_COUNT);
      //--- Resize the array of deals for each symbol
      for(int s=0; s<SYMBOLS_COUNT; s++)
         ArrayResize(symbol_balance[s].balance,deals_total);
      //--- Iterate in a loop and write the data
      for(int i=0; i<deals_total; i++)
        {
         //--- Get the deal ticket
         ticket=HistoryDealGetTicket(i);
         //--- Get all the deal properties
         GetHistoryDealProperties(ticket,D_ALL);
         //--- Get the number of digits in the price
         digits=(int)SymbolInfoInteger(deal.symbol,SYMBOL_DIGITS);
         //--- Calculate the overall balance
         balance+=deal.profit+deal.swap+deal.commission;
         //--- Calculate the max drawdown from the local maximum
         TesterDrawdownMaximum(i,balance,percent_drawdown,money_drawdown);
         //--- Generate a string for writing using concatenation
         StringConcatenate(string_to_write,
                           deal.time,delimeter,
                           DealSymbolToString(deal.symbol),delimeter,
                           DealTypeToString(deal.type),delimeter,
                           DealEntryToString(deal.entry),delimeter,
                           DealVolumeToString(deal.volume),delimeter,
                           DealPriceToString(deal.price,digits),delimeter,
                           DealSwapToString(deal.swap),delimeter,
                           DealProfitToString(deal.symbol,deal.profit),delimeter,
                           DrawdownToString(percent_drawdown),delimeter,
                           DrawdownToString(money_drawdown),delimeter,
                           DoubleToString(balance,2));
         //--- If more than one symbol is involved, write their balance values
         if(SYMBOLS_COUNT>1)
           {
            //--- Iterate over all symbols
            for(int s=0; s<SYMBOLS_COUNT; s++)
              {
               //--- If the symbols are equal and the deal result is non-zero
               if(deal.symbol==InputSymbols[s] && deal.profit!=0)
                 {
                  //--- Display the deal in the balance for the corresponding symbol
                  //    Take into consideration swap and commission
                  symbol_balance[s].balance[i]=symbol_balance[s].balance[i-1]+
                                               deal.profit+
                                               deal.swap+
                                               deal.commission;
                  //--- Add to the string
                  StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2));
                 }
               //--- Otherwise write the previous value
               else
                 {
                  //--- If the deal type is "Balance" (the first deal)
                  if(deal.type==DEAL_TYPE_BALANCE)
                    {
                     //--- the balance is the same for all symbols
                     symbol_balance[s].balance[i]=balance;
                     StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2));
                    }
                  //--- Otherwise write the previous value to the current index
                  else
                    {
                     symbol_balance[s].balance[i]=symbol_balance[s].balance[i-1];
                     StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2));
                    }
                 }
              }
           }
         //--- Write the generated string
         FileWrite(file_handle,string_to_write);
         //--- Mandatory zeroing out of the variable for the next string
         string_to_write="";
        }
      //--- Close the file
      FileClose(file_handle);
     }
//--- If the file could not be created/opened, print the appropriate message
   else
      Print("Error creating the file! Error: "+IntegerToString(GetLastError())+"");
  }

Nós usamos para calcular abaixamentos na função DrawdownMaximumToString(). Isto agora é realizado pela função TesterDrawdownMaximum(), enquanto que o valor de abaixamento é convertido em uma sequência utilizando a função DrawdownToString() básica.

O código da função TesterDrawdownMaximum() é como se segue:

//+------------------------------------------------------------------+
//| Returning the max drawdown from the local maximum                |
//+------------------------------------------------------------------+
void TesterDrawdownMaximum(int deal_number,
                           double balance,
                           double &percent_drawdown,
                           double &money_drawdown)
  {
   ulong         ticket =0;   // Deal ticket
   string        str    ="";  // The string to be displayed in the report
//--- To calculate the local maximum and drawdown
   static double max    =0.0;
   static double min    =0.0;
   
//--- If this is the first deal
   if(deal_number==0)
     {
      //--- There is no drawdown yet
      percent_drawdown =0.0;
      money_drawdown   =0.0;
      //--- Set the initial point as the local maximum
      max=balance;
      min=balance;
     }
   else
     {
      //--- If the current balance is greater than in the memory, then...
      if(balance>max)
        {
         //--- Calculate the drawdown using the previous values:
         //    in monetary terms
         money_drawdown=max-min;
         //    expressed as percentage
         percent_drawdown=100-((min/max)*100);
         //--- Update the local maximum
         max=balance;
         min=balance;
        }
      //--- Otherwise
      else
        {
         //--- Return zero value of the drawdown
         money_drawdown=0.0;
         percent_drawdown=0.0;
         //--- Update the minimum
         min=fmin(min,balance);
         //--- If the deal ticket by its position in the list has been obtained, then...
         if((ticket=HistoryDealGetTicket(deal_number))>0)
           {
            //--- ...get the deal comment
            GetHistoryDealProperties(ticket,D_COMMENT);
            //--- Flag of the last deal
            static bool last_deal=false;
            //--- The last deal in the test can be identified by the "end of test" comment
            if(deal.comment=="end of test" && !last_deal)
              {
               //--- Set the flag
               last_deal=true;
               //--- Update the drawdown values:
               //    in monetary terms
               money_drawdown=max-min;
               //    expressed as percentage
               percent_drawdown+=100-((min/max)*100);
              }
           }
        }
     }
  }

O código da função TesterDrawdownMaximum() é como se segue:

//+------------------------------------------------------------------+
//| Converting drawdown to a string                                  |
//+------------------------------------------------------------------+
string DrawdownToString(double drawdown)
  {
   return((drawdown<=0) ? "" : DoubleToString(drawdown,2));
  }

Agora tudo está configurado e pronto para o teste do Expert Advisor e para a análise dos resultados. Logo no início do artigo, vimos um exemplo de arquivo pronto para uso. Vamos fazer o seguinte: otimizar os parâmetros para os símbolos no segundo conjunto (há três símbolos: EURUSD, AUDUSD e USDCHF), e, seguindo a otimização, executar o teste usando todos os símbolos a partir do terceiro conjunto (sete símbolos no total), a fim de ver os resultados para os símbolos cujos dados não estavam envolvidos na otimização de parâmetros.


Otimizando parâmetros e testando o Expert Advisor

O testador de estratégia precisa ser configurado como mostrado abaixo:

Fig. 6. As configurações do testador de estratégia para a otimização

Fig. 6. As configurações do testador de estratégia para a otimização

As configurações do Expert Advisor para a otimização de parâmetros são fornecidas abaixo:

Fig. 7. As configurações do Expert Advisor para a otimização de parâmetros

Fig. 7. As configurações do Expert Advisor para a otimização de parâmetros

Uma vez que a otimização envolve três símbolos e o aumento do volume de posição é permitido para cada um deles, definimos o lote mínimo para efeitos de abertura de uma posição e o aumento do volume de posição. No nosso caso, o valor é de 0,01.

Após a otimização, nós selecionamos o primeiro resultado através do fator máximo de recuperação e definimos o parâmetro VolumeIncrease em 0,1 para o lote. O resultado é mostrado abaixo:

Fig. 8. O resultado do teste em MetaTrader 5

Fig. 8. O resultado do teste em MetaTrader 5

Abaixo, você pode ver o resultado como mostrado no Excel 2010:

O resultado do teste para três símbolos como mostrado no Excel 2010

Fig. 9. O resultado do teste para três símbolos como mostrado no Excel 2010

O abaixamento em termos monetários é exibido como marcas verdes no gráfico inferior em termos da segunda escala (auxiliar).

Você também deve estar consciente dos limites gráficos no Excel 2010 (a lista completa das especificações e limites pode ser encontrada na página de especificações e limites do Excel do site da Microsoft Office).

Fig. 10. Especificações e limites gráficos no Excel 2010

Fig. 10. Especificações e limites gráficos no Excel 2010

A tabela mostra que podemos executar o teste para 255 símbolos ao mesmo tempo e exibir todos os resultados no gráfico! Nós só somos limitados por recursos do computador.

Vamos agora executar o teste para sete símbolos a partir do terceiro conjunto com os parâmetros atuais e conferir o resultado:

O resultado do teste para sete símbolos como mostrado no Excel 2010

Fig. 11. O resultado do teste para sete símbolos como mostrado no Excel 2010

Com sete símbolos considerados, temos 6.901 negócios. Os dados no gráfico são atualizados muito rapidamente no Excel 2010.


Conclusão

Acredito que o método introduzido é digno de atenção pelo fato de que até mesmo uma estratégia de negociação simples, como a que usamos, mostrou bons resultados. Aqui, devemos ter em mente que a otimização só foi realizada por três símbolos dos sete. Nós podemos tentar melhorar o resultado otimizando os parâmetros para todos os símbolos de uma só vez. No entanto, acima de tudo, devemos ter como objetivo melhorar o sistema de negociação, ou melhor ainda, ter um portfólio de vários sistemas de negociação. Entraremos em contato com essa ideia mais tarde.

Isso é tudo. Temos uma ferramenta bastante útil para estudar os resultados das estratégias de negociação multi-moeda. Abaixo está o arquivo zip para download com os arquivos do Expert Advisor para sua consideração.

Após extrair os arquivos, coloque a pasta ReduceOverfittingEA dentro do diretório MetaTrader 5\MQL5\Experts. Adiante, o indicador EventsSpy.mq5 deve ser colocado dentro do MetaTrader 5\MQL5\Indicators. SymbolsList.txt deve ser colocado sob MetaTrader 5\MQL5\Files.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/652

Arquivos anexados |
symbolslist.txt (0.1 KB)
eventsspy__1.mq5 (7.59 KB)
Depuração dos programas do MQL5 Depuração dos programas do MQL5
Este artigo é destinado principalmente aos programadores que já tenham aprendido a linguagem, mas ainda não tenham dominado completamente o desenvolvimento do programa. Ele revela algumas técnicas de depuração e apresenta uma experiência combinada do autor e muitos outros programadores.
Guia prático do MQL5: Registrando o histórico de negociações em um arquivo e criando gráficos de saldo para cada símbolo no Excel Guia prático do MQL5: Registrando o histórico de negociações em um arquivo e criando gráficos de saldo para cada símbolo no Excel
Ao me comunicar em vários fóruns, utilizei frequentemente exemplos de meus resultados de teste exibidos como capturas de tela de gráficos do Microsoft Excel. Por muitas vezes me foi pedido para explicar como tais gráficos podem ser criados. Agora, enfim, eu tenho algum tempo para explicar tudo nesse artigo.
Indicador para o gráfico de Ponto e Figura Indicador para o gráfico de Ponto e Figura
Existem vários tipos de gráficos que fornecem informações sobre a situação do mercado atual. Muitos deles, como o Gráfico de Ponto e Figura, são o legado de um passado remoto. O artigo descreve um exemplo do gráfico de Ponto e Figura usando um indicador de tempo real.
Guia prático do MQL5: Desenvolvendo um Consultor Especialista multi-moeda com um número ilimitado de parâmetros Guia prático do MQL5: Desenvolvendo um Consultor Especialista multi-moeda com um número ilimitado de parâmetros
Neste artigo, criaremos um padrão que utiliza um único conjunto de parâmetros para otimização do sistema de negociação, enquanto permite um número ilimitado de parâmetros. A lista de símbolos será criada no arquivo de texto padrão (*.txt). Os parâmetros de entrada para cada símbolo também serão armazenados nos arquivos. Desta forma poderemos ser capazes de contornar a restrição do terminal sobre o número de parâmetros de entrada de um Expert Advisor.