Guía práctica de MQL5: Reducción del efecto del sobreajuste y el manejo de la falta de cotizaciones

Anatoli Kazharski | 7 mayo, 2014

Introducción

Creo que muchos traders han confundido varias veces los parámetros óptimos para sus sistemas de trading. De hecho, un algoritmo de trading por sí solo no es suficiente. Hay que ver como se puede usar más. Sea cual sea la estrategia de trading que utilice, sea sencilla o compleja, con uno o varios instrumentos, no se puede evitar la pregunta de qué parámetros elegir para garantizar futuras ganancias.

Tendemos a comprobar los sistemas de trading con parámetros que demostraron buenos resultados durante el período de optimización (backtesting) y durante el período posterior (forward testing). Forward testing de hecho, no es tan necesario. Se pueden obtener resultados pertinentes usando los datos del historial.

Este método da lugar a una pregunta que no puede tener una respuesta definitiva: ¿qué cantidad de datos del historial hay que usar para optimizar un sistema de trading? La cosa es que hay muchas opciones. Todo depende del rango de fluctuaciones del precio que espera capitalizar.

Volviendo a la cantidad del historial necesaria para la optimización, concluimos que los datos disponibles pueden ser muy suficientes para el trading entre horas. Esto no es siempre cierto para plazos más largos. Cuantas más repeticiones de una pauta coherente, es decir, cuantos más trades tengamos, más veraz es el rendimiento del sistema de trading probado que podremos esperar ver en el futuro.

¿Qué ocurre si los datos del precio de un cierto instrumento no son suficientes para conseguir un número suficiente de repeticiones y sentirse más seguro? Respuesta: utilice los datos de todos los instrumentos disponibles.


Ejemplo en NeuroShell DayTrader Professional

Antes de proceder a la programación en MetaTrader 5, repasemos un ejemplo en NeuroShell DayTrader Professional. Ofrece grandes características para optimizar los parámetros de un sistema de trading (compilado con un constructor) para múltiples símbolos. Puede configurar los parámetros necesarios en los ajustes del módulo de trading, optimizar los parámetros para cada símbolo por separado o encontrar un conjunto óptimo de parámetros para todos los símbolos a la vez. Se puede encontrar esta opción en la pestaña Optimization:

La pestaña Optimización en el módulo de trading NeuroShell DayTrader Professional

Fig. 1. La pestaña Optimization en el módulo de trading NeuroShell DayTrader Professional

En nuestro caso, cualquier sistema simple de trading hará que sólo necesitemos comparar los resultados de dos métodos de optimización, por lo que actualmente, la elección del sistema es de poca importancia.

Puede encontrar información acerca de cómo recopilar estrategias de trading en NeuroShell DayTrader Professional en otros artículos de mi blog (puede buscar o puede utilizar tags para encontrar la información relevante). También recomendaría que lea un artículo titulado "Cómo preparar cotizaciones en MetaTrader 5 para otras aplicaciones" que describe y demuestra cómo puede descargar cotizaciones de MetaTrader 5 en el formato compatible con NeuroShell DayTrader Professional mediante un script.

Para hacer esta prueba, preparé los datos obtenidos de las barras diarias para ocho símbolos del año 2000 hasta Enero del 2013:

Lista de símbolos para una prueba en NeuroShell DayTrader Professional

Fig. 2. Lista de símbolos para una prueba en NeuroShell DayTrader Professional

La figura de abajo muestra los resultados de dos optimizaciones. La parte superior muestra el resultado de la optimización donde cada símbolo consigue sus propios parámetros, mientras la parte de abajo muestra el resultado donde los parámetros son comunes para todos los símbolos.

Comparación de los resultados de los modos de optimización de dos parámetros

Fig. 3. Comparación de los resultados de los modos de optimización de dos parámetros

El resultado que muestra los parámetros comunes no parece tan bueno como el de los parámetros diferentes para cada símbolo. Sin embargo, inspira más confianza el hecho de que el sistema de trading pase por una serie de distintos patrones de comportamiento de los precios (volatilidad, número de tendencias/planos) con los mismos parámetros para todos los símbolos.

Continuando con el mismo tema, lógicamente podemos encontrar otro argumento a favor de la optimización que utiliza una cantidad de datos más grande. Bien puede ser que el comportamiento de los precios de un determinado par de divisas, por ejemplo EURUSD, será muy diferente después (en dos, cinco o diez años). Por ejemplo las tendencias de los precios de GBPUSD serán similares al comportamiento del precio anterior de EURUSD y viceversa. Debe estar listo para esto, ya que es así para cualquier instrumento.


Un Ejemplo en MetaTrader 5

Ahora veamos qué parámetros ofrecen modos de optimización en MetaTrader 5. Puede ver abajo todos los símbolos seleccionados en el modo de optimización de Observación del Mercado marcados con una flecha en la lista del menú despegable de los modos de optimización.

Fig. 4. Modos de optimización en el Probador de estrategias de MetaTrader 5

Fig. 4. Modos de optimización en el Probador de estrategias de MetaTrader 5

Este modo le permite sólo probar un Asesor Experto con los parámetros actuales en cada símbolo, de uno en uno. Los símbolos utilizados para la prueba son los seleccionados actualmente en la ventana de Observación del Mercado. Es decir, la optimización de parámetros no se realiza en este caso. Sin embargo, MetaTrader 5 y MQL5 le permiten aplicar esta idea por su cuenta.

Ahora, debemos ver cómo aplicar este Asesor Experto. La lista de símbolos se proporcionará en un archivo de texto (*.txt). Además, aplicaremos la posibilidad de almacenar varios conjuntos de listas de símbolos. Cada conjunto estará en una sección separada con su propio encabezamiento que representa un número de sección. Los números son necesarios para facilitar la comprobación de la imagen.

Tenga en cuenta que es importante tener el símbolo # delante del número para permitir al Asesor Experto conseguir los datos correctos cuando se llene la matriz de los símbolos. Generalmente, el encabezamiento puede contener cualquier símbolo pero debe tener # siempre. El signo del número puede ser reemplazado por cualquier otro símbolo, según el cual el Asesor Experto determinará/contará las secciones. En ese caso el reemplazo se reflejará en el código.

A continuación, se puede ver el archivo SymbolsList.txt que contiene tres conjuntos de símbolos para probar. Como se muestra, se utilizará este archivo aún más al probar el método.

Fig. 5. Se proporcionan varios conjuntos de símbolos en un fichero de texto para las pruebas

Fig. 5. Se proporcionan varios conjuntos de símbolos en un fichero de texto para las pruebas

En los parámetros externos, agregaremos otro parámetro, SectionOfSymbolList, para indicar el conjunto de símbolos que el Asesor Experto debe utilizar en la prueba actual. Este parámetro toma el valor (desde cero hacia arriba) que define el conjunto de símbolos. Si el valor excede el número de conjuntos disponibles, el Asesor Experto escribirá una entrada correspondiente al registro y sólo se hará la prueba en el símbolo actual.

Se debe situar SymbolsList.txt en el directorio del terminal local en Metatrader 5\MQL5\Files. También se puede colocar en la carpeta común pero en este caso no estará disponible para la optimización del parámetro en MQL5 Cloud Network. Además, para permitir el acceso al archivo y a los indicadores personalizados pertinentes para la prueba, debemos escribir las siguientes líneas al principio del archivo:

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

Nuestro Asesor Experto se basará en el Asesor Experto multidivisa que ya existe, representado en el artículo "Guía práctica de MQL5: El desarrollo de un Asesor Experto multidivisa con un número ilimitado de parámetros". El principio de su estrategia de trading es bastante sencillo pero será suficiente para probar la eficiencia del método. Acabamos de quitar las partes innecesarias, agregamos lo que necesitamos y corregimos el código pertinente existente. Mejoraremos ciertamente nuestro Asesor Experto con el informe que guarda la característica descrita extensamente en el artículo anterior de las series "Guía práctica de MQL5: Escribir el historial de transacciones en un archivo y crear gráficos de balance para cada símbolo en Excel". Los gráficos de balance para todos los símbolos serán también necesarios para evaluar la eficiencia del método en cuestión.

Se deben modificar los parámetros externos del Asesor Experto de la siguiente forma:

//--- 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

Se deben borrar todas las matrices asociadas con los parámetros externos ya que no harán falta y deben ser reemplazadas por las variables externas a través del código completo. Sólo debemos dejar la matriz dinámica de símbolos, InputSymbols[], cuyo tamaño dependerá del número de símbolos usados a partir de uno de los conjuntos en el archivo SymbolsList.txt. Si el Asesor Experto se usa fuera del Probador de Estrategias, el tamaño de esta matriz será igual a 1 ya que el Asesor Experto trabajará con un único símbolo en el modo de tiempo real.

Se deben llevar a cabo también los cambios correspondientes en el archivo de inicialización de la matriz; InitializeArrays.mqh. Es decir, se deben borrar todas las funciones responsables de la inicialización de las matrices de las variables externas. A continuación, se muestra la función InitializeArraySymbols():

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

El código de la función ReadSymbolsFromFile() también se debe modificar. Se usa para leer toda la lista de símbolos mientras que ahora queremos que se lea sólo el conjunto del símbolo especificado. A continuación se muestra el código de la función 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);
  }

Puede ver que están destacados algunos caracteres en el código anterior. Esos códigos contienen la función PrepareArrayForOneSymbol() que prepara simplemente una matriz para un (actual) símbolo en caso de error.

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

Ahora todo está listo para probar el método de optimización del parámetro. Pero antes de proceder con la prueba, agreguemos otra serie de datos al informe. Antes, además del balance de todos los símbolos, el archivo del informe contenía todas las disminuciones de máximos locales expresadas en porcentaje. Ahora el informe también cubrirá todas las disminuciones en términos monetarios. Al mismo tiempo, modificaremos la función CreateSymbolBalanceReport() donde se genera el informe.

Se proporciona el código de la función CreateSymbolBalanceReport() a continuación:

//+------------------------------------------------------------------+
//| 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())+"");
  }

Solíamos calcular las disminuciones en la función DrawdownMaximumToString(). Esto ahora lo lleva a cabo la función TesterDrawdownMaximum(), mientras que el valor de disminución se convierte en un carácter que usa la función básica DrawdownToString().

Se muestra el código de la función CreateSymbolBalanceReport() a continuación:

//+------------------------------------------------------------------+
//| 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);
              }
           }
        }
     }
  }

Se muestra el código de la función DrawdownToString() a continuación:

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

Ahora está todo configurado y preparado para probar el Asesor Experto y analizar los resultados. Al principio de este artículo, hemos visto el archivo ya existente. Procedamos de la siguiente manera: optimice los parámetros para los símbolos en el segundo conjunto (hay tres símbolos: EURUSD, AUDUSD y USDCHF) y siguiendo la optimización realice la prueba que usa todos los símbolos del tercer conjunto (siete símbolos en total) para ver los resultados para los símbolos cuyos datos no estaban implicados en la optimización del parámetro.


Optimización de Parámetros y Prueba del Asesor Experto

Se debe configurar el Probador de Estrategias como se muestra a continuación:

Fig. 6. Los ajustes del Probador de Estrategias para la optimización

Fig. 6. Configuración del Probador de Estrategias para la optimización

A continuación, se proporciona la configuración del Asesor Experto para la optimización del los parámetros:

Fig. 7. Configuración del Asesor Experto para la optimización de los parámetros

Fig. 7. Configuración del Asesor Experto para la optimización de los parámetros

Dado que la optimización implica tres símbolos y el aumento de volumen de posición está habilitado para cada uno de ellos, hemos creado el lote mínimo con el fin de abrir una posición y aumentar el volumen de posición. En nuestro caso, el valor es 0,01.

Después de la optimización, seleccionamos el resultado principal por el factor máximo de recuperación y esablecemos el parámetro VolumeIncrease a 0,1 para el lote. El resultado se muestra a continuación:

Fig. 8. El resultado de la prueba en MetaTrader 5

Fig. 8. El resultado de la prueba en MetaTrader 5

A continuación, puede ver es el resultado en Excel 2010:

El resultado de la prueba de tres símbolos en Excel 2010

Fig. 9. El resultado de la prueba de tres símbolos en Excel 2010

La disminución en términos monetarios se muestra con marcas verdes en el gráfico inferior en función de la segunda escala (adicional).

Debería ser consciente de los límites de los gráficos en Excel 2010 (se puede encontrar la lista completa de especificaciones y límites en la página web Excel specifications and limits de Microsoft Office).

Fig. 10. Especificaciones y límites de la representación gráfico en Excel 2010

Fig. 10. Especificaciones y límites de la representación gráfica en Excel 2010

¡La tabla muestra que podemos hacer la prueba para 255 símbolos al mismo tiempo y mostrar todos los resultados en el gráfico! Sólo estamos limitados por los recursos del ordenador.

Hagamos ahora la prueba para siete símbolos del tercer conjunto con los parámetros actuales y comprobemos el resultado:

El resultado de la prueba de siete símbolos en Excel 2010

Fig. 11. El resultado de la prueba de siete símbolos en Excel 2010

Con siete símbolos, tenemos 6901 transacciones. Se actualizan los datos del gráfico rápidamente en Excel 2010.


Conclusión

Creo que el método presentado es digno de mención por el hecho de que incluso una estrategia de trading sencilla como la utilizada mostró buenos resultados. Aquí, debemos tener presente que la optimización sólo se llevó a cabo para tres símbolos de siete. Podemos tratar de mejorar el resultado optimizando los parámetros para todos los símbolos a la vez. Sin embargo, antes que nada, debemos tratar de mejorar la estrategia de trading, o mejor todavía, tener una cartera de varias estrategias de trading. Volveremos a esta idea más adelante.

Eso es todo. Hemos conseguido una herramienta bastante útil para estudiar los resultados de las estrategia de trading multidivisa. Al final del artículo está el archivo zip descargable con los archivos del Asesor Experto para su estudio.

Después de extraer los archivos, ponga la carpeta ReduceOverfittingEA en el directorio MetaTrader 5\MQL5\Experts. Además, hay que colocar el indicador EventsSpy.mq5 en MetaTrader 5\MQL5\Indicators. Hay que poner SymbolsList.txt en MetaTrader 5\MQL5\Files.