English Русский 中文 Deutsch 日本語 Português
preview
Experimentos con redes neuronales (Parte 2): Optimización inteligente de una red neuronal

Experimentos con redes neuronales (Parte 2): Optimización inteligente de una red neuronal

MetaTrader 5Probador | 27 octubre 2022, 13:28
657 0
Roman Poshtar
Roman Poshtar

Introducción

Buenas tardes, estimados usuarios de la comunidad MQL5. El artículo anterior, Experimentos con redes neuronales (Parte 1): Recordando la geometría, compartí con ustedes mis observaciones y experimentos con redes neuronales. En los materiales, vimos qué tipo de datos enviar a la red neuronal y analizamos un ejemplo sencillo en el que se utiliza un perceptrón para producir un sistema comercial rentable. Hemos tenido éxito en algunos puntos, pero también han surgido algunas dificultades durante el trabajo, cosa que intentaremos resolver en esta parte. Además, abordaremos redes neuronales más complejas. Para ello, usaremos la biblioteca del artículo Programamos una red neuronal profunda desde cero usando el lenguaje MQL. El autor describió con gran detalle sus principios, por lo que nos centraremos únicamente en los aspectos principales. La tarea principal de este artículo consistirá en desarrollar un robot comercial completo que se ejecute en una red neuronal usando MetaTrader 5 sin utilizar software de terceros.


Conceptos básicos y ejemplos

El autor de la citada biblioteca usa una red neuronal profunda, pero yo sugiero comenzar poco a poco y construir una red con una estructura 4-4-3. Necesitaremos un total de (4 * 4) + 4 + (4 * 3) + 3 = 35 pesos y valores de sesgo.

La biblioteca modificada puede descargarse en la aplicación. He comentado todos los cambios en el código para que pueda ver cómo se construye una red neuronal propia. 

Valores para los pesos y los sesgos:

input double w0=1.0;
input double w1=1.0;
input double w2=1.0;
input double w3=1.0;
input double w4=1.0;
input double w5=1.0;
input double w6=1.0;
input double w7=1.0;
input double w8=1.0;
input double w9=1.0;
input double w10=1.0;
input double w11=1.0;
input double w12=1.0;
input double w13=1.0;
input double w14=1.0;
input double w15=1.0;

input double b0=1.0;
input double b1=1.0;
input double b2=1.0;
input double b3=1.0;

input double x0=1.0;
input double x1=1.0;
input double x2=1.0;
input double x3=1.0;
input double x4=1.0;
input double x5=1.0;
input double x6=1.0;
input double x7=1.0;
input double x8=1.0;
input double x9=1.0;
input double x10=1.0;
input double x11=1.0;
input double x12=1.0;
input double x13=1.0;
input double x14=1.0;
input double x15=1.0;

input double s0=1.0;
input double s1=1.0;
input double s2=1.0;

W y X son los pesos, B y S son los parámetros de sesgo.

Conectamos la biblioteca de redes neuronales:

#include <DeepNeuralNetwork2.mqh> 

int numInput=4;
int numHiddenA = 4;
int numOutput=3;

DeepNeuralNetwork dnn(numInput,numHiddenA,numOutput);

A continuación, veremos dos ejemplos de mi artículo anterior. Una figura y un patrón con ángulos de inclinación. También debemos ver los resultados de la estrategia del autor de la biblioteca. Por último, probaremos todo esto en diferentes redes neuronales: 4-4-3 y 4-4-4-3.  Es decir, vamos a desarrollar seis asesores a la vez.

Transmitimos la figura de la mariposa (envoltorio). Asesor Figure EA: 

r1


int error=CandlePatterns(ind_In1[1], ind_In1[10], ind_In2[1], ind_In2[10], _xValues);//Llamada de la función

int CandlePatterns(double v1,double v2,double v3,double v4,double &xInputs[])//Función
  {

   xInputs[0] = (v1-v2)/Point();
   xInputs[1] = (v3-v4)/Point();
   xInputs[2] = (v1-v4)/Point();
   xInputs[3] = (v3-v2)/Point();
   
   return(1);

  }

Transmitimos un patrón con ángulos de inclinación (4 ángulos de inclinación del indicador MA 1 y MA 24). Asesor Angle EA:

r2


int error=CandlePatterns(ind_In1[1], ind_In1[5], ind_In1[10], ind_In2[1], ind_In2[5], ind_In2[10], _xValues);//Llamada de la función

int CandlePatterns(double v1,double v2,double v3,double v4,double v5,double v6,double &xInputs[])//Función
  {

   xInputs[0] = (((v1-v2)/Point())/5);
   xInputs[1] = (((v1-v3)/Point())/10);
   xInputs[2] = (((v4-v5)/Point())/5);
   xInputs[3] = (((v4-v6)/Point())/50);
   
   return(1);

  }

Usando el simulador de estrategias, establecemos los valores de optimización y los sesgos de -1 a 1 en incrementos de 0,1. El resultado es 3,68597592780611e+51 pasadas. Sin problemas, pasamos a la siguiente sección.

r3


Solución a los problemas de optimización

Al utilizar un EA en la forma descrita anteriormente, el simulador de estrategias hará poco más de 10.000 pasadas en el modo (Lento (iteración completa de parámetros)), que en nuestro caso es demasiado poco para optimizar la red neuronal. El uso del modo (Rápido (algoritmo genético)) no parece apropiado.

La idea básica es usar una sola variable para las pasadas, una especie de contador. El resto de los pesos y sesgos se establecen dentro del asesor. Para ello, decidí probar un generador de números aleatorios. Además, debemos recordar qué opciones hemos utilizado ya en la optimización.

Función del generador de números aleatorios:

double RNDfromCI(double minp, double maxp) 
{ 
  if(minp == maxp) 
    return (minp); 
  double Minp, Maxp; 
  if(minp > maxp) 
  {
    Minp = maxp; 
    Maxp = minp;
  }
  else 
  {
    Minp = minp; 
    Maxp = maxp;
  }
  return (NormalizeDouble(double(Minp + ((Maxp - Minp) * (double)MathRand() / 32767.0)),1));
}

Consideré varias opciones para almacenar los parámetros de las pasadas, en particular un array dentro del EA no resulta adecuado porque se restablece durante cada inicialización del EA, las variables globales del terminal tampoco son adecuadas debido a la gran cantidad de datos. Los resultados de las pasadas deben obtenerse del proceso de optimización con la posibilidad de leer los datos. Decidí utilizar archivos de tipo CSV.

Introducimos una variable para la optimización:

input int Passages = 10000;

Optimizamos el parámetro Passages de 1 hasta el máximo con incrementos de 1. Experimentando, averiguamos el número máximo de pasadas en el modo de una sola variable. Obtenemos como resultado 100.000.000.  Parece más que suficiente.

r4

 La idea original era dividir el asesor en dos partes, una para la optimización y otra para el comercio, pero creo que dejar todo en uno resultará más cómodo. Añadamos un interruptor de modo:

input bool Optimization = true;

El código básico para la optimización se coloca en la función de inicialización OnInit() del asesor:

if (Optimization==true){
int r=0;
int q=0;

 while(q!=1 && !IsStopped())
 {

string str;
 while(r!=1 && !IsStopped())
 {
   sw0=DoubleToString(RNDfromCI(Min, Max),1);
   sw1=DoubleToString(RNDfromCI(Min, Max),1);
   sw2=DoubleToString(RNDfromCI(Min, Max),1);
   sw3=DoubleToString(RNDfromCI(Min, Max),1);
   sw4=DoubleToString(RNDfromCI(Min, Max),1);
   sw5=DoubleToString(RNDfromCI(Min, Max),1);
   sw6=DoubleToString(RNDfromCI(Min, Max),1);
   sw7=DoubleToString(RNDfromCI(Min, Max),1);
   sw8=DoubleToString(RNDfromCI(Min, Max),1);
   sw9=DoubleToString(RNDfromCI(Min, Max),1);
   sw10=DoubleToString(RNDfromCI(Min, Max),1);
   sw11=DoubleToString(RNDfromCI(Min, Max),1);
   sw12=DoubleToString(RNDfromCI(Min, Max),1);
   sw13=DoubleToString(RNDfromCI(Min, Max),1);
   sw14=DoubleToString(RNDfromCI(Min, Max),1);
   sw15=DoubleToString(RNDfromCI(Min, Max),1);
   
   sb0=DoubleToString(RNDfromCI(Min, Max),1);
   sb1=DoubleToString(RNDfromCI(Min, Max),1);
   sb2=DoubleToString(RNDfromCI(Min, Max),1);
   sb3=DoubleToString(RNDfromCI(Min, Max),1);
   
   sx0=DoubleToString(RNDfromCI(Min, Max),1);
   sx1=DoubleToString(RNDfromCI(Min, Max),1);
   sx2=DoubleToString(RNDfromCI(Min, Max),1);
   sx3=DoubleToString(RNDfromCI(Min, Max),1);
   sx4=DoubleToString(RNDfromCI(Min, Max),1);
   sx5=DoubleToString(RNDfromCI(Min, Max),1);
   sx6=DoubleToString(RNDfromCI(Min, Max),1);
   sx7=DoubleToString(RNDfromCI(Min, Max),1);
   sx8=DoubleToString(RNDfromCI(Min, Max),1);
   sx9=DoubleToString(RNDfromCI(Min, Max),1);
   sx10=DoubleToString(RNDfromCI(Min, Max),1);
   sx11=DoubleToString(RNDfromCI(Min, Max),1);
   
   ss0=DoubleToString(RNDfromCI(Min, Max),1);
   ss1=DoubleToString(RNDfromCI(Min, Max),1);
   ss2=DoubleToString(RNDfromCI(Min, Max),1);
   
   if(StringFind(sw0,".", 0) == -1 ){sw0=sw0+".0";}
   if(StringFind(sw1,".", 0) == -1 ){sw1=sw1+".0";}
   if(StringFind(sw2,".", 0) == -1 ){sw2=sw2+".0";}
   if(StringFind(sw3,".", 0) == -1 ){sw3=sw3+".0";}
   if(StringFind(sw4,".", 0) == -1 ){sw4=sw4+".0";}
   if(StringFind(sw5,".", 0) == -1 ){sw5=sw5+".0";}
   if(StringFind(sw6,".", 0) == -1 ){sw6=sw6+".0";}
   if(StringFind(sw7,".", 0) == -1 ){sw7=sw7+".0";}
   if(StringFind(sw8,".", 0) == -1 ){sw8=sw8+".0";}
   if(StringFind(sw9,".", 0) == -1 ){sw9=sw9+".0";}
   if(StringFind(sw10,".", 0) == -1 ){sw10=sw10+".0";}
   if(StringFind(sw11,".", 0) == -1 ){sw11=sw11+".0";}
   if(StringFind(sw12,".", 0) == -1 ){sw12=sw12+".0";}
   if(StringFind(sw13,".", 0) == -1 ){sw13=sw13+".0";}
   if(StringFind(sw14,".", 0) == -1 ){sw14=sw14+".0";}
   if(StringFind(sw15,".", 0) == -1 ){sw15=sw15+".0";}

   if(StringFind(sb0,".", 0) == -1 ){sb0=sb0+".0";}
   if(StringFind(sb1,".", 0) == -1 ){sb1=sb1+".0";}
   if(StringFind(sb2,".", 0) == -1 ){sb2=sb2+".0";}
   if(StringFind(sb3,".", 0) == -1 ){sb3=sb3+".0";}
   
   if(StringFind(sx0,".", 0) == -1 ){sx0=sx0+".0";}
   if(StringFind(sx1,".", 0) == -1 ){sx1=sx1+".0";}
   if(StringFind(sx2,".", 0) == -1 ){sx2=sx2+".0";}
   if(StringFind(sx3,".", 0) == -1 ){sx3=sx3+".0";}
   if(StringFind(sx4,".", 0) == -1 ){sx4=sx4+".0";}
   if(StringFind(sx5,".", 0) == -1 ){sx5=sx5+".0";}
   if(StringFind(sx6,".", 0) == -1 ){sx6=sx6+".0";}
   if(StringFind(sx7,".", 0) == -1 ){sx7=sx7+".0";}
   if(StringFind(sx8,".", 0) == -1 ){sx8=sx8+".0";}
   if(StringFind(sx9,".", 0) == -1 ){sx9=sx9+".0";}
   if(StringFind(sx10,".", 0) == -1 ){sx10=sx10+".0";}
   if(StringFind(sx11,".", 0) == -1 ){sx11=sx11+".0";}
   
   if(StringFind(ss0,".", 0) == -1 ){ss0=ss0+".0";}
   if(StringFind(ss1,".", 0) == -1 ){ss1=ss1+".0";}
   if(StringFind(ss2,".", 0) == -1 ){ss2=ss2+".0";}
   
   str=sw0+","+sw1+","+sw2+","+sw3+","+sw4+","+sw5+","+sw6+","+sw7+","+sw8+","+sw9+","+sw10+","+sw11+","+sw12+","+sw13+","+sw14+","+sw15
   +","+sb0+","+sb1+","+sb2+","+sb3
   +","+sx0+","+sx1+","+sx2+","+sx3+","+sx4+","+sx5+","+sx6+","+sx7+","+sx8+","+sx9+","+sx10+","+sx11
   +","+ss0+","+ss1+","+ss2;
   if (VerifyPassagesInFile(str)==true)
   {
      ResetLastError();
      filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";");
      if(filehandle!=INVALID_HANDLE)
       {
        Print("FileOpen OK");
        FileSeek(filehandle, 0, SEEK_END);
        FileWrite(filehandle,Passages,sw0,sw1,sw2,sw3,sw4,sw5,sw6,sw7,sw8,sw9,sw10,sw11,sw12,sw13,sw14,sw15,
        sb0,sb1,sb2,sb3,
        sx0,sx1,sx2,sx3,sx4,sx5,sx6,sx7,sx8,sx9,sx10,sx11,
        ss0,ss1,ss2);
        FileClose(filehandle); 
        
   weight[0]=StringToDouble(sw0);
   weight[1]=StringToDouble(sw1);
   weight[2]=StringToDouble(sw2);
   weight[3]=StringToDouble(sw3);
   weight[4]=StringToDouble(sw4);
   weight[5]=StringToDouble(sw5);
   weight[6]=StringToDouble(sw6);
   weight[7]=StringToDouble(sw7);
   weight[8]=StringToDouble(sw8);
   weight[9]=StringToDouble(sw9);
   weight[10]=StringToDouble(sw10);
   weight[11]=StringToDouble(sw11);
   weight[12]=StringToDouble(sw12);
   weight[13]=StringToDouble(sw13);
   weight[14]=StringToDouble(sw14);
   weight[15]=StringToDouble(sw15);
   
   weight[16]=StringToDouble(sb0);
   weight[17]=StringToDouble(sb1);
   weight[18]=StringToDouble(sb2);
   weight[19]=StringToDouble(sb3);
   
   weight[20]=StringToDouble(sx0);
   weight[21]=StringToDouble(sx1);
   weight[22]=StringToDouble(sx2);
   weight[23]=StringToDouble(sx3);
   weight[24]=StringToDouble(sx4);
   weight[25]=StringToDouble(sx5);
   weight[26]=StringToDouble(sx6);
   weight[27]=StringToDouble(sx7);
   weight[28]=StringToDouble(sx8);
   weight[29]=StringToDouble(sx9);
   weight[30]=StringToDouble(sx10);
   weight[31]=StringToDouble(sx11);
   
   weight[32]=StringToDouble(ss0);
   weight[33]=StringToDouble(ss1);
   weight[34]=StringToDouble(ss2);
        
        r=1;
        q=1;
       }
      else 
       {
       Print("FileOpen ERROR, error ",GetLastError());
       q=0;
       }
   }
 }
 
 }
}

Echemos un vistazo al código con explicaciones. Una tarea interesante ha sido enseñar al asesor a esperar su turno para abrir el archivo. En el modo de optimización, me encontré con el problema de abrir un archivo simultáneamente en el modo de optimización mientras usaba varios núcleos de CPU a la vez. Este problema se resolvió en el primer ciclo while. Es decir, el asesor no saldrá de la función OnInit() hasta que haya abierto el archivo para la escritura. Resulta que los asesores se turnan durante la optimización.

Mientras se realiza la prueba en un núcleo, el asesor puede abrir el archivo para escribir en el segundo núcleo. A continuación, en el código, asignamos a todos los pesos y sesgos parámetros aleatorios en el rango Min y Max. Si el número es redondo, súmale .0. Sumamos todos los valores en una cadena str. Comprobamos esta cadena con VerifyPassagesInFile(str) para ver si la misma cadena está presente en el archivo. Si no es así, la escribiremos al final del archivo y llenaremos nuestros arrays weight[] con los pesos y sesgos aleatorios resultantes.

Código de la función para comprobar si los parámetros son idénticos a los anteriores:

bool VerifyPassagesInFile(string st){
string str="";
string str1="";
string str2="";
string str3="";
string str4="";
string str5="";
string str6="";
string str7="";
string str8="";
string str9="";
string str10="";
string str11="";
string str12="";
string str13="";
string str14="";
string str15="";
string str16="";
string str17="";
string str18="";
string str19="";
string str20="";
string str21="";
string str22="";
string str23="";
string str24="";
string str25="";
string str26="";
string str27="";
string str28="";
string str29="";
string str30="";
string str31="";
string str32="";
string str33="";
string str34="";
string str35="";
string str36="";

if (FileIsExist(OptimizationFileName)==
true ){
   ResetLastError();
   filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";");
   if(filehandle!=INVALID_HANDLE)
     {
      Print("FileCreate OK");
     }
   else Print("FileCreate ERROR, error ",GetLastError());
   FileClose(filehandle); 
}

   ResetLastError();
   filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";");
   if(filehandle!=INVALID_HANDLE)
     {
      Print("FileOpen OK");
     }
   else Print("FileOpen ERROR, error ",GetLastError());
   
//--- leemos los datos desde el archivo
 while(!FileIsEnding(filehandle) && !IsStopped())
 {
  str1=FileReadString(filehandle);
  str2=FileReadString(filehandle);
  str3=FileReadString(filehandle);
  str4=FileReadString(filehandle);
  str5=FileReadString(filehandle);
  str6=FileReadString(filehandle);
  str7=FileReadString(filehandle);
  str8=FileReadString(filehandle);
  str9=FileReadString(filehandle);
  str10=FileReadString(filehandle);
  str11=FileReadString(filehandle);
  str12=FileReadString(filehandle);
  str13=FileReadString(filehandle);
  str14=FileReadString(filehandle);
  str15=FileReadString(filehandle);
  str16=FileReadString(filehandle);
  str17=FileReadString(filehandle);
  str18=FileReadString(filehandle);
  str19=FileReadString(filehandle);
  str20=FileReadString(filehandle);
  str21=FileReadString(filehandle);
  str22=FileReadString(filehandle);
  str23=FileReadString(filehandle);
  str24=FileReadString(filehandle);
  str25=FileReadString(filehandle);
  str26=FileReadString(filehandle);
  str27=FileReadString(filehandle);
  str28=FileReadString(filehandle);
  str29=FileReadString(filehandle);
  str30=FileReadString(filehandle);
  str31=FileReadString(filehandle);
  str32=FileReadString(filehandle);
  str33=FileReadString(filehandle);
  str34=FileReadString(filehandle);
  str35=FileReadString(filehandle);
  str36=FileReadString(filehandle);
    
    str=str2+","+str3+","+str4+","+str5+","+str6+","+str7+","+str8+","+str9+","+str10+","+str11
    +","+str12+","+str13+","+str14+","+str15+","+str16+","+str17+","+str18+","+str19+","+str20+","+str21
    +","+str22+","+str23+","+str24+","+str25+","+str26+","+str27+","+str28+","+str29+","+str30+","+str31+","+str32+","+str33+","+str34+","+str35+","+str36;
  if (str==st){FileClose(filehandle); return(false);}
 }

FileClose(filehandle); 
 
return(true); 
}

Lo primero que debemos hacer aquí es comprobar si nuestro archivo FileIsExist(OptimizationFileName) existe. De lo contrario, crearemos uno. Luego leemos la cadena en el ciclo while(!FileIsEnding(filehandle) && !IsStopped()) en las variables str1- strN. Ponemos todo junto en la variable str. A continuación, comparamos cada cadena str con la cadena entrante st de la función. Al final, retornaremos el resultado, tanto si hay coincidencia como si no.

Parámetros de los asesores resultantes:

"------------Open settings----------------";

Optimization - cambio de modo, optimización o comercio;

Min - valor mínimo de los parámetros de pesos y sesgos; 

Max - valor máximo de los parámetros de pesos y sesgos; 

OptimizationFileName - nombre del archivo para escribir los parámetros durante la optimización y para la lectura durante el modo comercial;

Passages - valor de las pasadas. Parámetro de optimización de 1 a 100.000.000 como máximo en incrementos de 1;

LL - la función Softmax da 3 resultados basados en la suma del 100%. 0,6 equivale a un valor superior al 60%;

"------------Lots settings----------------";

SetFixLotOrPercent - selección, lote o porcentaje del depósito;

LotsOrPercent - lote o porcentaje, dependiendo de SetFixLotOrPercent;

"------------Other settings---------------";

MaxSpread - spread máximo para abrir una orden;

Slippage - deslizamiento;

Magic - número mágico;

EAComment - comentario sobre las órdenes;

Para los que no lo saben. Por defecto, MetaTrader 5 crea archivos en su sandbox. 

Ruta del archivo:

C:\Users\Su nombre de usuario\AppData\Roaming\MetaQuotes\Terminal\Common\Files


Optimización

Realizamos la optimización. El periodo abarca del 2018.07.12 al 2021.07.12. Pareja de divisas EURUSD Marco temporal H1, según precios de apertura. El número de pasadas (Passages) es 10.000.

Modo (criterio de máxima complejidad). Asesor Angle EA 4-4-3

a1

Modo (criterio de máxima complejidad). Asesor Angle EA 4-4-4-3

a2

Modo (criterio de máxima complejidad). Asesor Figure EA 4-4-3

f1

Modo (criterio de máxima complejidad). Asesor Figure EA 4-4-4-3

f2

Modo (criterio de máxima complejidad). Asesor Original EA 4-4-3. Estrategia del autor de la biblioteca.

o1

Modo (criterio de máxima complejidad). Asesor Original EA 4-4-4-3. Estrategia del autor de la biblioteca.

o2

Tras una optimización de, por ejemplo, 10.000 pasadas, podemos seguir rellenando nuestro archivo con nuevos parámetros iniciando un nuevo proceso de optimización. No debemos olvidarnos de guardar de antemano el informe de optimización del Simulador de Estrategias con los parámetros de Passages necesarios. También hay que borrar la historia del terminal, pues de lo contrario el simulador emitirá los resultados de la optimización ya realizada. Haremos esto con la ayuda un script del tipo .bat.

del C:\Users\Su nombre de usuario\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\cache\*.* /q /f /s

for /d %%i in (C:\Users\Su nombre de usuario\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\cache\*) do rd /s /q "%%i"


del C:\Users\Su nombre de usuario\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\logs\*.* /q /f /s

for /d %%i in (C:\Users\Su nombre de usuario\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\logs\*) do rd /s /q "%%i"

Sustituya (Su nombre de usuario) y (36A64B8C79A6163D85E6173B54096685) por los suyos. Podrá abrirlo con un editor de texto normal. Añadiremos el script de limpieza al archivo adjunto.


Usando los resultados de la optimización

Para comprobar los resultados de la optimización, el interruptor Optimization deberá estar en el modo false y deberemos introducir el número de pasada necesario en el parámetro Pasajes. Para ello, bastará con clicar dos veces en el resultado que nos interese.

Código para cargar pesos y sesgos para las pruebas:

if (FileIsExist(OptimizationFileName)==false){
int id = 0;
int i = 0;
string f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30,f31,f32,f33,f34,f35,f36;
 int handle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";");
  if(handle!=INVALID_HANDLE)
   {Print("Loading optimization file.");
   
  while(!FileIsEnding(handle) && !IsStopped())
   {
   f1=FileReadString(handle);
   f2=FileReadString(handle);
   f3=FileReadString(handle);
   f4=FileReadString(handle);
   f5=FileReadString(handle);
   f6=FileReadString(handle);
   f7=FileReadString(handle);
   f8=FileReadString(handle);
   f9=FileReadString(handle);
   f10=FileReadString(handle);
   f11=FileReadString(handle);
   f12=FileReadString(handle);
   f13=FileReadString(handle);
   f14=FileReadString(handle);
   f15=FileReadString(handle);
   f16=FileReadString(handle);
   f17=FileReadString(handle);
   f18=FileReadString(handle);
   f19=FileReadString(handle);
   f20=FileReadString(handle);
   f21=FileReadString(handle);
   f22=FileReadString(handle);
   f23=FileReadString(handle);
   f24=FileReadString(handle);
   f25=FileReadString(handle);
   f26=FileReadString(handle);
   f27=FileReadString(handle);
   f28=FileReadString(handle);
   f29=FileReadString(handle);
   f30=FileReadString(handle);
   f31=FileReadString(handle);
   f32=FileReadString(handle);
   f33=FileReadString(handle);
   f34=FileReadString(handle);
   f35=FileReadString(handle);
   f36=FileReadString(handle);
 
   if (StringToInteger(f1)==Passages){
   weight[0]=StringToDouble(f2);
   Print(weight[0]);
   weight[1]=StringToDouble(f3);
   Print(weight[1]);
   weight[2]=StringToDouble(f4);
   Print(weight[2]);
   weight[3]=StringToDouble(f5);
   weight[4]=StringToDouble(f6);
   weight[5]=StringToDouble(f7);
   weight[6]=StringToDouble(f8);
   weight[7]=StringToDouble(f9);
   weight[8]=StringToDouble(f10);
   weight[9]=StringToDouble(f11);
   weight[10]=StringToDouble(f12);
   weight[11]=StringToDouble(f13);
   weight[12]=StringToDouble(f14);
   weight[13]=StringToDouble(f15);
   weight[14]=StringToDouble(f16);
   weight[15]=StringToDouble(f17);
   
   weight[16]=StringToDouble(f18);
   weight[17]=StringToDouble(f19);
   weight[18]=StringToDouble(f20);
   weight[19]=StringToDouble(f21);
   
   weight[20]=StringToDouble(f22);
   weight[21]=StringToDouble(f23);
   weight[22]=StringToDouble(f24);
   weight[23]=StringToDouble(f25);
   weight[24]=StringToDouble(f26);
   weight[25]=StringToDouble(f27);
   weight[26]=StringToDouble(f28);
   weight[27]=StringToDouble(f29);
   weight[28]=StringToDouble(f30);
   weight[29]=StringToDouble(f31);
   weight[30]=StringToDouble(f32);
   weight[31]=StringToDouble(f33);
   
   weight[32]=StringToDouble(f34);
   weight[33]=StringToDouble(f35);
   weight[34]=StringToDouble(f36);
   
   FileClose(handle);  
   break; 
   }
  
   }  
 FileClose(handle);
   }
   else{
   PrintFormat("Could not open file %s, error code = %d",OptimizationFileName,GetLastError());
   return(INIT_FAILED);
   }
}else{
   PrintFormat("Could not open file %s, error code = %d",OptimizationFileName,GetLastError());
   return(INIT_FAILED);
   }

Con el interruptor Optimization desactivado, leemos nuestro archivo. Comparamos el valor de la primera columna con el valor Passages. Si encontramos una coincidencia, asignaremos los pesos y sesgos a nuestros arrays weight[]. De este modo, podremos efectuar una prueba con los mejores resultados.

Realizaremos una prueba forward con los resultados obtenidos, los tres primeros mejores. Los criterios de selección en mi caso son el factor de beneficio máximo y un número de operaciones superior a 100:

  • Fecha de la prueba, del 2021.07.12 al 2022.07.12
  • Modo (Cada tick basado en ticks reales);
  • Depósito inicial: 10 000.
  • Periodo H1.
  • Lote fijo 0.01.
  • Asesor Angle EA 4-4-3.

Prueba 1:

t1

Prueba 2:

t2

Prueba 3:

t3

  • Fecha de la prueba, del 2021.07.12 al 2022.07.12
  • Modo (Cada tick basado en ticks reales);
  • Depósito inicial: 10 000.
  • Periodo H1.
  • Lote fijo 0.01.
  • Asesor Angle EA 4-4-4-3.

Prueba 1:

t4

Prueba 2:

t5

Prueba 3:

t6

  • Fecha de la prueba, del 2021.07.12 al 2022.07.12
  • Modo (Cada tick basado en ticks reales);
  • Depósito inicial: 10 000.
  • Periodo H1.
  • Lote fijo 0.01.
  • Asesor Figure EA 4-4-3.

Prueba 1:

t7

Prueba 2:

t8

Prueba 3:

t9

  • Fecha de la prueba, del 2021.07.12 al 2022.07.12
  • Modo (Cada tick basado en ticks reales);
  • Depósito inicial: 10 000.
  • Periodo H1.
  • Lote fijo 0.01.
  • Asesor Figure EA 4-4-4-3.

Prueba 1:

t10

Prueba 2:

t11

Prueba 3:

t12

A continuación, probaremos la estrategia original del autor de la biblioteca con las redes neuronales 4-4-3 y 4-4-4-3.

  • Fecha de la prueba, del 2021.07.12 al 2022.07.12
  • Modo (Cada tick basado en ticks reales);
  • Depósito inicial: 10 000.
  • Periodo H1.
  • Lote fijo 0.01.
  • Asesor Original 4-4-3.

Prueba 1:

t13

Prueba 2:

t14

Prueba 3:

t15

  • Fecha de la prueba, del 2021.07.12 al 2022.07.12
  • Modo (Cada tick basado en ticks reales);
  • Depósito inicial: 10 000.
  • Periodo H1.
  • Lote fijo 0.01.
  • Asesor Original 4-4-4-3.

Prueba 1:

t16

Prueba 2:

t17

Prueba 3:

t18

En resumen, las estrategias Angle EA 4-4-3 y Angle EA 4-4-4-3 han funcionado mejor que Figure EA 4-4-3 y Figure EA 4-4-4-3, creo que debido al uso de enfoques no estándar aplicados en el análisis del mercado. La optimización con 2 núcleos tarda unos 20 minutos para un periodo de 3 años. Los experimentos también plantean una serie de cuestiones que debemos abordar:

  1. Realizar la optimización durante un periodo más largo.
  2. Aumentar el número de pasadas. Obviamente.
  3. Decidir la mejor estrategia a seguir en lo sucesivo.
  4. Desarrollar un algoritmo para trabajar simultáneamente con alguna base de datos con los resultados de optimización para el trading, que ya he empezado a pensar. 
  5. Desarrollar un algoritmo para la optimización y el comercio simultáneos.
  6. Usar diferentes marcos temporales para determinar el mejor resultado.
  7. Combinar dos o más redes neuronales con diferentes conjuntos de datos en el mismo asesor.

    Por supuesto, necesitaremos un entrenamiento más profundo para realizar pruebas completas, pero creo que ya hemos sentado las bases y los resultados hablan por sí mismos. Además, se requieren grandes recursos informáticos para este tipo de experimentación.


    Conclusión

    Hoy hemos abordado el trabajo con redes neuronales más complejas. Asimismo, hemos dedicado mucho esfuerzo a la identificación de los datos a transmitir a la red neuronal. Pero las posibilidades no terminan ahí. Debemos intentar transferir los datos de más indicadores, utilizar enlaces complejos. Espero que así alcancemos nuevos éxitos en el desarrollo de un robot comercial rentable. Resulta que MetaTrader 5 es más que capaz de prescindir del software de terceros. Además, hemos desarrollado un algoritmo de optimización muy interesante que mejorará nuestras capacidades. 

    Como viene siendo tradición, dejaremos al lector que haga una optimización más profunda y realice pruebas forward por su cuenta.

    Contenido del anexo:

    Angle EA 4-4-3 - asesor, estrategia de inclinación de los ángulos de los indicadores МА1 y МА24, red neuronal 4-4-3.
    
    Angle EA 4-4-4-3 - asesor, estrategia de inclinación de los ángulos de los indicadores МА1 y МА24, red neuronal 4-4-4-3.
    
    Figure EA 4-4-3 - asesor, estrategia de figura de mariposa (envoltorio) de los indicadores МА1 y МА24, red neuronal 4-4-3.
    
    Figure EA 4-4-4-3 - asesor, estrategia de figura de mariposa (envoltorio) de los indicadores МА1 y МА24, red neuronal 4-4-4-3.
    
    Original EA 4-4-3 - asesor, estrategia basada en el porcentaje del tamaño de la vela que constituye cada una de sus partes, red neuronal 4-4-3.
    
    Original EA 4-4-4-3 - asesor, estrategia basada en el porcentaje del tamaño de la vela que constituye cada una de sus partes, red neuronal 4-4-4-3.
    
    Clear.bat - script para limpiar los archivos del terminal (Log y Cash).
    
    DeepNeuralNetwork – biblioteca para la red neuronal 4-4-4-3.
    
    DeepNeuralNetwork2 – biblioteca para la red neuronal 4-4-3.
    


    Traducción del ruso hecha por MetaQuotes Ltd.
    Artículo original: https://www.mql5.com/ru/articles/11186

    Archivos adjuntos |
    EA.zip (610.58 KB)
    DoEasy. Elementos de control (Parte 12): Objeto de lista básico, objetos WinForms ListBox y ButtonListBox DoEasy. Elementos de control (Parte 12): Objeto de lista básico, objetos WinForms ListBox y ButtonListBox
    En este artículo, crearemos un objeto de lista básico de objetos WinForms y dos nuevos objetos: ListBox y ButtonListBox.
    Redes neuronales: así de sencillo (Parte 22): Aprendizaje no supervisado de modelos recurrentes Redes neuronales: así de sencillo (Parte 22): Aprendizaje no supervisado de modelos recurrentes
    Continuamos analizando los algoritmos de aprendizaje no supervisado. Hoy hablaremos sobre el uso de autocodificadores en el entrenamiento de modelos recurrentes.
    Indicador CCI. Tres pasos para la transformación Indicador CCI. Tres pasos para la transformación
    En este artículo, intentaremos realizar cambios adicionales en el indicador CCI. Estos cambios afectarán a la propia lógica del indicador, hasta el punto de que podremos ver este indicador en la ventana del gráfico principal.
    Redes neuronales: así de sencillo (Parte 21): Autocodificadores variacionales (VAE) Redes neuronales: así de sencillo (Parte 21): Autocodificadores variacionales (VAE)
    En el anterior artículo, vimos el algoritmo del autocodificador. Como cualquier otro algoritmo, tiene ventajas y desventajas. En la implementación original, el autocodificador se encarga de dividir los objetos de la muestra de entrenamiento tanto como sea posible. Y en este artículo, en cambio, hablaremos de cómo solucionar algunas de sus deficiencias.