English Русский 中文 Español Deutsch 日本語
preview
Experiências com redes neurais (Parte 2): Otimização inteligente de redes neurais

Experiências com redes neurais (Parte 2): Otimização inteligente de redes neurais

MetaTrader 5Testador | 10 novembro 2022, 13:50
576 0
Roman Poshtar
Roman Poshtar

Introdução

Boa tarde, caros usuários da comunidade MQL5. No último artigo Experiências com redes neurais (Parte 1): Lembrando a geometria, compartilhei com vocês minhas observações e experimentos com redes neurais. Abordamos questões sobre o tipo de dados a serem enviados para a rede neural e discutimos um exemplo simples que utilizava um perceptron para construir um sistema de negociação lucrativo. Tivemos sucesso em alguns pontos, mas surgiram algumas dificuldades no decorrer do trabalho, que tentaremos resolver nesta parte. Hoje também avançaremos para redes neurais mais complexas. Para isso, usaremos a biblioteca do artigo Construindo uma rede neural profunda do zero em linguagem MQL. O autor descreveu detalhadamente os seus princípios de funcionamento, por isso nos concentraremos apenas nos aspectos principais. O principal objetivo deste artigo será o desenvolvimento de um robô de negociação completo que funcione em uma rede neural usando o MetaTrader 5 sem o uso de software de terceiros.


Noções básicas e exemplos

O autor da biblioteca acima mencionada usa uma rede neural profunda, mas proponho começar «reduzindo o apetite» e construindo uma rede com uma estrutura 4-4-3. No total, precisamos de (4 * 4) + 4 + (4 * 3) + 3 = 35 pesos e valores de viés.

Você pode baixar a biblioteca modificada. Eu deliberadamente deixei todas as alterações no código comentadas para que você possa ver como construir suas próprias redes neurais. 

Valores de pesos e vieses:

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 e X são pesos, B e S são parâmetros de viés.

Incluímos a biblioteca de rede neural:

#include <DeepNeuralNetwork2.mqh> 

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

DeepNeuralNetwork dnn(numInput,numHiddenA,numOutput);

Em seguida, veremos dois exemplos do meu artigo anterior: uma forma e um padrão com ângulos. Daremos também uma vista de olhos nos resultados da estratégia do autor da biblioteca. E, finalmente, verificaremos tudo isso com diferentes redes neurais, 4-4-3 e 4-4-4-3.  Ou seja, desenvolvemos seis Expert Advisors de uma só vez.

Transferimos a figura de uma borboleta (envelope). Robô figura Expert Advisor: 

r1


int error=CandlePatterns(ind_In1[1], ind_In1[10], ind_In2[1], ind_In2[10], _xValues);//Вызов функции

int CandlePatterns(double v1,double v2,double v3,double v4,double &xInputs[])//Функция
  {

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

  }

Transferimos o padrão com ângulos de inclinação (4 inclinações do indicador MA 1 e MA 24). Robô 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);//Вызов функции

int CandlePatterns(double v1,double v2,double v3,double v4,double v5,double v6,double &xInputs[])//Функция
  {

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

  }

Ao usar o testador de estratégia, definimos os valores de otimização dos pesos e dos vieses, de -1 a 1 com um incremento de 0,1. Obtemos o resultado 3.68597592780611e+51 passagens. Vamos para a próxima seção.

r3


Resolvendo problemas de otimização

Ao utilizar um EA na forma descrita acima, o testador de estratégia fará pouco mais de 10 000 passagens em modo lento (iteração completa dos parâmetros), o que, em nosso caso, é muito pouco para otimizar a rede neural. Penso que não é apropriado usar o modo rápido (algoritmo genético).

Em princípio, devemos usar apenas uma variável para as passagens, como se de um contador particular se tratasse. Os parâmetros restantes de pesos e vieses são definidos dentro do EA. Para isso, decidi tentar usar um gerador de números aleatórios. Além disso, é necessário lembrar quais variantes utilizamos durante a otimização.

Função geradora de números aleatórios:

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

Para lembrar os parâmetros para a passagem consideramos diversas maneiras, e devo dizer que um array dentro do EA não seria adequado porque seria reinicializado durante cada inicialização do EA, as variáveis globais do terminal também não são apropriadas por causa da grande quantidade de dados. Necessitamos extrair os resultados das passagens realizadas durante o processo de otimização com a possibilidade de leitura dos dados. Decidi usar arquivos CSV.

Introduzimos uma variável para otimização:

input int Passages = 10000;

Otimizamos o parâmetro Passages de 1 até o máximo em incrementos de 1. Empiricamente, descobrimos o número máximo de passagens no modo variável única. Esse número foi 100 000 000.  Acho que ele chega.

r4

 A minha primeira intenção era dividir o EA em dois, um para otimização e outro para negociação. Mas acho que ter apenas um será mais conveniente. Vamos adicionar um interruptor de modo:

input bool Optimization = true;

O código de otimização principal está localizado na função de inicialização OnInit() do EA:

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

Vamos rever um pouco o código detalhadamente. Algo que achei interessante foi ensinar ao EA a esperar sua vez de abrir o arquivo. Enquanto tentava otimizar vários núcleos de processador simultaneamente, deparava-me com um problema, que era abrir o mesmo arquivo simultaneamente. Resolvi este problema no primeiro laço while - o Expert Advisor não sairá da função OnInit() até que não abra o arquivo para escrita. Notei que os EAs se iniciavam um de cada vez durante a otimização.

Assim, enquanto um núcleo está ocupado com um teste, um segundo núcleo pode ser utilizado pelo EA para abrir o arquivo para escrita. Mais adiante no código, atribuímos parâmetros aleatórios no intervalo de Min e Max para todos os pesos e vieses. Se o número for redondo, adicionamos .0 a ele. Colocamos todos os valores em uma linha str. Verificamos esta linha com a função VerifyPassagesInFile(str) para ver se existe a mesma linha no arquivo. Se não existir, registramos no final do arquivo e preenchemos nossos arrays weight[] com os pesos e vieses aleatórios obtidos.

Código da função para verificar se os parâmetros são idênticos aos 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());
   
//--- прочитаем данные из файла
 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); 
}

Aqui, antes de tudo, verificamos se nosso arquivo FileIsExist(OptimizationFileName) existe. Se não existir, devemos criá-lo. Em seguida, lemos a linha no laço while(!FileIsEnding(filehandle) && !IsStopped()) nas variáveis str1- strN. Colocamos tudo junto na variável str. Comparamos cada string str com a string st recebida na entrada da função. Ao final, retornamos o resultado, haja ou não correspondência.

Parâmetros dos Expert Advisors resultantes:

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

Optimization - interruptor de modos, otimização ou negociação;

Min - valor mínimo dos parâmetros de pesos e de viés; 

Max - valor máximo dos parâmetros de pesos e de viés; 

OptimizationFileName - nome do arquivo para gravação de parâmetros durante a otimização e leitura no modo negociação;

Passages - valor das passagens. Parâmetro para otimização de 1 a 100 000 000 com incrementos max. de 1;

LL - função Softmax que dá 3 resultados com base na soma de 100%. 0,6 equivale a um valor acima de 60%;

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

SetFixLotOrPercent - escolha, lote ou porcentagem do depósito;

LotsOrPercent - lote ou porcentagem dependendo de SetFixLotOrPercent;

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

MaxSpread - spread máximo para abertura de ordem;

Slippage - derrapagem;

Magic - número mágico;

EAComment - comentário às ordens;

Para quem não sabe, por padrão o MetaTrader 5 cria arquivos em sua área restrita. 

Caminho para os arquivos:

C:\Users\Your Username\AppData\Roaming\MetaQuotes\Terminal\Common\Files


Otimizando

Vamos fazer a otimização com esses valores: Período de 12/07/2018 a 12/07/2021; Par de moedas: EURUSDM; Período gráfico H1, a preços de abertura; Número de passagens (Passages) 10 000;

Modo (máximo critério complexo); Robô Angle EA 4-4-3.

a1

Modo (máximo critério complexo); Robô Angle EA 4-4-4-3.

a2

Modo (máximo critério complexo); Robô Figure EA 4-4-3

f1

Modo (máximo critério complexo); Robô Figure EA 4-4-4-3.

f2

Modo (máximo critério complexo); Robô Original EA 4-4-3; Estratégia do autor da biblioteca.

o1

Modo (máximo critério complexo); Robô Original EA 4-4-4-3; Estratégia do autor da biblioteca.

o2

Após a otimização, por exemplo depois de 10 000 passagens, podemos continuar preenchendo nosso arquivo com novos parâmetros iniciando um novo processo de otimização. Não se esqueça de salvar previamente o relatório de otimização do testador de estratégia com os parâmetros Passages necessários. Você também precisa limpar o histórico do terminal, caso contrário, o testador exibirá os resultados da otimização que já foi realizada. Eu faço isso com um script do tipo .bat.

del C:\Users\Your Username\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\cache\*.* /q /f /s

for /d %%i em (C:\Users\Seu nome de usuário\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\cache\*) do rd /s /q "%%i"


del C:\Users\[Seu nome de usuário]\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\logs\*.* /q /f /s

for /d %%i em (C:\Users\[Seu nome de usuário]\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\logs\*) do rd /s /q "%%i"

Substitua [Seu nome de usuário] e (36A64B8C79A6163D85E6173B54096685). Você pode abrir com um editor de texto comum. Vou adicionar o script de limpeza em um anexo.


Como usar resultados de otimização

Para verificar os resultados da otimização, altere a opção Optimization para false e defina o número de passagem necessário no parâmetro Passages. Isso pode ser feito simplesmente clicando duas vezes no resultado desejado.

Código para carregar parâmetros de pesos e vieses para teste:

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

Quando a opção Optimization está desabilitada, lemos nosso arquivo. Comparamos o valor da primeira coluna com o valor do parâmetro Passages. Se encontrarmos uma correspondência, atribuímos os valores dos parâmetros pesos e vieses aos nossos arrays weight[]. Assim, podemos testar os melhores resultados.

Vamos realizar testes forward dos resultados obtidos, os três primeiros são os melhores. Os critérios de seleção no meu caso são o fator de lucro máximo e o número de transações superior a 100:

  • Data de teste de 2021.07.12 a 2022.07.12;
  • Cada tick baseado em ticks reais;
  • Depósito inicial 10 000;
  • Período H1;
  • Lote fixo 0,01;
  • Expert Advisor Ângulo EA 4-4-3.

Teste 1:

t1

Teste 2:

t2

Teste 3:

t3

  • Data de teste de 2021.07.12 a 2022.07.12;
  • Cada tick baseado em ticks reais;
  • Depósito inicial 10 000;
  • Período H1;
  • Lote fixo 0,01;
  • Robô Angle EA 4-4-4-3.

Teste 1:

t4

Teste 2:

t5

Teste 3:

t6

  • Data de teste de 2021.07.12 a 2022.07.12;
  • Cada tick baseado em ticks reais;
  • Depósito inicial 10 000;
  • Período H1;
  • Lote fixo 0,01;
  • Robô Figure EA 4-4-3.

Teste 1:

t7

Teste 2:

t8

Teste 3:

t9

  • Data de teste de 2021.07.12 a 2022.07.12;
  • Cada tick baseado em ticks reais;
  • Depósito inicial 10 000;
  • Período H1;
  • Lote fixo 0,01;
  • Robô Figure EA 4-4-4-3.

Teste 1:

t10

Teste 2:

t11

Teste 3:

t12

A seguir, testaremos a estratégia original do autor da biblioteca, para isso usaremos as redes neurais 4-4-3 e 4-4-4-3.

  • Data de teste de 2021.07.12 a 2022.07.12;
  • Cada tick baseado em ticks reais;
  • Depósito inicial 10 000;
  • Período H1;
  • Lote fixo 0,01;
  • Robô Original EA 4-4-3.

Teste 1:

t13

Teste 2:

t14

Teste 3:

t15

  • Data de teste de 2021.07.12 a 2022.07.12;
  • Cada tick baseado em ticks reais;
  • Depósito inicial 10 000;
  • Período H1;
  • Lote fixo 0,01;
  • Robô Original EA 4-4-4-3.

Teste 1:

t16

Teste 2:

t17

Teste 3:

t18

Resumindo, gostaria de destacar que as estratégias Angle EA 4-4-3 e Angle EA 4-4-4-3 funcionaram melhor do que Figure EA 4-4-3e Figure EA 4-4-4-3. Acho que é porque elas usam abordagens não padronizadas para análise de mercado. A otimização com 2 núcleos leva cerca de 20 minutos para um período de 3 anos. Além disso, após os experimentos, surgem várias questões que precisam ser abordadas:

  1. Realizar a otimização em um período mais longo.
  2. Aumentar o número de passagens. Isso é óbvio.
  3. Decidir sobre a melhor estratégia para continuar trabalhando nela.
  4. Desenvolver um algoritmo para trabalhar simultaneamente com um determinado banco de dados de resultados de otimização para negociação, no qual já comecei a pensar. 
  5. Desenvolver um algoritmo para otimização e negociação simultâneas.
  6. Usar períodos gráficos diferentes para determinar o melhor resultado.
  7. Combinar duas ou mais redes neurais com diferentes conjuntos de dados em um Expert Advisor.

    Naturalmente, é necessário um treinamento mais aprofundado para a realização de testes mais completos. Mas acho que demos os primeiros passos e os resultados falam por si mesmos. Cabe dizer que são necessários grandes recursos computacionais para este tipo de experimentação.


    Considerações finais

    Hoje avançamos para redes neurais mais complexas. Fizemos um grande esforço para identificar os dados necessários a serem transferidos para a rede neural. Mas as possibilidades não pararam por aí. Devemos tentar transferir dados de mais indicadores, usar mapeamentos complexos. Espero que isso nos leve a novos sucessos no desenvolvimento de um robô de negociação lucrativo. No final das contas, o MetaTrader 5 é bastante capaz sem software de terceiros. Além disso, desenvolvemos um algoritmo de otimização muito interessante que expandirá nossas capacidades. 

    Por tradição, deixarei para você sozinho realizar uma otimização e um teste forward mais profundos.

    Conteúdo do anexo:

    Angle EA 4-4-3 - советник, стратегия наклон углов индикаторов МА1 и МА24, нейросеть 4-4-3.
    
    Angle EA 4-4-4-3 - советник, стратегия наклон углов индикаторов МА1 и МА24, нейросеть 4-4-4-3.
    
    Figure EA 4-4-3 - советник, стратегия фигура бабочка (конверт) индикаторов МА1 и МА24, нейросеть 4-4-3.
    
    Figure EA 4-4-4-3 - советник, стратегия фигура бабочка (конверт) индикаторов МА1 и МА24, нейросеть 4-4-4-3.
    
    Original EA 4-4-3 - советник, стратегия какой процент от размера свечи составляет каждая из ее частей, нейросеть 4-4-3.
    
    Original EA 4-4-4-3 - советник, стратегия какой процент от размера свечи составляет каждая из ее частей, нейросеть 4-4-4-3.
    
    Очистить.bat - скрипт очистки файлов терминала (Лог и Кеш).
    
    DeepNeuralNetwork – библиотека для нейросети 4-4-4-3.
    
    DeepNeuralNetwork2 – библиотека для нейросети 4-4-3.


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

    Arquivos anexados |
    EA.zip (610.58 KB)
    Redes neurais de maneira fácil (Parte 22): Aprendizado não supervisionado de modelos recorrentes Redes neurais de maneira fácil (Parte 22): Aprendizado não supervisionado de modelos recorrentes
    Continuamos a estudar algoritmos de aprendizado não supervisionado. E agora proponho discutir as particularidades por trás do uso de autocodificadores para treinar modelos recorrentes.
    DoEasy. Controles (Parte 13): Otimizando a interação de objetos WinForms com o mouse, dando início ao desenvolvimento do objeto WinForms TabControl DoEasy. Controles (Parte 13): Otimizando a interação de objetos WinForms com o mouse, dando início ao desenvolvimento do objeto WinForms TabControl
    No artigo, vamos corrigir e otimizar o processamento da aparência dos objetos WinForms após afastar o cursor do mouse do objeto e começar a desenvolver o objeto TabControl WinForms.
    Redes neurais de maneira fácil (Parte 23): Criando uma ferramenta para transferência de aprendizado Redes neurais de maneira fácil (Parte 23): Criando uma ferramenta para transferência de aprendizado
    Nesta série de artigos, já mencionamos a transferência de aprendizado mais de uma vez. Mas até agora o assunto não foi além das menções. Sugiro preencher essa lacuna e dar uma olhada mais de perto na transferência de aprendizado.
    DoEasy. Controles (Parte 12): Objeto base lista, objetos WinForms ListBox e ButtonListBox DoEasy. Controles (Parte 12): Objeto base lista, objetos WinForms ListBox e ButtonListBox
    Neste artigo, criaremos um objeto base para listas de objetos WinForms e dois novos objetos, nomeadamente ListBox e ButtonListBox.