Guía práctica de MQL5: Reducción del efecto del sobreajuste y el manejo de la falta de cotizaciones
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:
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:
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.
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
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
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. 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
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
A continuación, puede ver es el resultado 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á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:
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.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/652
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso