English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Dr. Tradelove ou como parei de me preocupar e criei um Expert Advisor para autotreinamento

Dr. Tradelove ou como parei de me preocupar e criei um Expert Advisor para autotreinamento

MetaTrader 5Negociação | 7 março 2014, 13:23
1 732 1
Roman Zamozhnyy
Roman Zamozhnyy

Conceito

Depois de criar o Expert Advisor todos nós podemos usar o built-in Strategy Tester para selecionar os parâmetros ideais. Após selecioná-los, executamos o Expert Advisor uma vez e assim que qualquer mudança significativa ocorra, o Expert Advisor é então parado e otimizado repetidamente usando o Strategy Tester, e assim por diante.

Podemos atribuir a reotimização e tomada de decisões de reotimização como um processo para o Expert Advisor sem, naturalmente, interromper seu trabalho?

Uma das soluções para esse problema foi proposta pela Quantum em seu artigo "Adaptive Trading Systems and Their Use in MetaTrader5 Terminal", dedicado ao uso de um sistema de negociação real juntamente com algumas (em número ilimitado) estratégias de negociação virtuais, onde a estratégia selecionada foi até agora a que trouxe o maior lucro. A decisão de mudar a estratégia de negociação é adotada depois de um certo valor de barra fixa ter sido superado.

Eu proponho usar um código algorítimo (GA) genético selecionado pelo joo no artigo "Genetic Algorithms - It's Easy!". Veremos a implementação de tal Expert Advisor (um dos exemplos abaixo é um EA proposto para participação no Automated Trading Championship 2011).


Trabalho em progresso

Por isso, precisamos definir o que o Expert Advisor deve ser capaz de fazer. Em primeiro lugar, e nem é preciso dizer, negociar usando a estratégia selecionada. Em segundo lugar, tomar uma decisão: se é hora de reotimizar (para realizar uma nova otimização dos parâmetros de entrada). E terceiro, para reotimizar utilizando GA. Para começar, vamos rever a reotimização mais simples - existe é uma estratégia que basta selecionarmos os novos parâmetros. Veremos então se pudermos, utilizando GA, selecionar uma outra estratégia em um ambiente de mercado que mudou e nesse caso - como isso pode ser feito.

Além disso, para facilitar a simulação na função de aptidão nós tomamos a decisão de trocar apenas barras concluídas em um único instrumento. Não haverá a adição de posições e fechamentos parciais. Aqueles que preferem usar paradas fixas e também usar trailing stops, por favor, consulte o artigo "Tick Generation Algorithm in MetaTrader5 Strategy Tester" a fim de implementar as verificações Stop Loss e Take Profit na função de aptidão. Vou expandir a inteligente frase abaixo:

Na função de aptidão eu simulei um modo de teste conhecido no Testador como "Apenas preços abertos". MAS! Isso não significa que esta é a única simulação possível do processo de teste na função de aptidão. Pessoas mais cautelosas podem querer implementar um teste de função de aptidão usando o modo "Cada Tick". Para não reinventar a roda, ou fazer "cada tick", gostaria de chamar a sua atenção para um algoritmo existente desenvolvido pela MetaQuotes. Em outras palavras, depois de ter lido este artigo uma pessoa será capaz de simular o modo "Cada Tick" na função de aptidão, que é uma condição necessária para a correta simulação de paradas e tomadas em FF.

Antes de prosseguir para o ponto principal - a implementação da estratégia - vamos rever brevemente os aspectos técnicos e implementar funções auxiliares que definem a abertura de uma nova barra, bem como abertura e fechamento de posições:

//+------------------------------------------------------------------+
//| Define whether a new bar has opened                             |
//+------------------------------------------------------------------+
bool isNewBars()
  {
   CopyTime(s,tf,0,1,curBT);
   TimeToStruct(curBT[0],curT);
   if(tf==PERIOD_M1||
      tf==PERIOD_M2||
      tf==PERIOD_M3||
      tf==PERIOD_M4||
      tf==PERIOD_M5||
      tf==PERIOD_M6||
      tf==PERIOD_M10||
      tf==PERIOD_M12||
      tf==PERIOD_M15||
      tf==PERIOD_M20||
      tf==PERIOD_M30)
      if(curT.min!=prevT.min)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   if(tf==PERIOD_H1||
      tf==PERIOD_H2||
      tf==PERIOD_H3||
      tf==PERIOD_H4||
      tf==PERIOD_H6||
      tf==PERIOD_H8||
      tf==PERIOD_M12)
      if(curT.hour!=prevT.hour)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   if(tf==PERIOD_D1||
      tf==PERIOD_W1)
      if(curT.day!=prevT.day)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   if(tf==PERIOD_MN1)
      if(curT.mon!=prevT.mon)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   return(false);
  }
//+------------------------------------------------------------------+
//|  ClosePosition                                                   |
//+------------------------------------------------------------------+
void ClosePosition()
  {
   request.action=TRADE_ACTION_DEAL;
   request.symbol=PositionGetSymbol(0);
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) request.type=ORDER_TYPE_SELL; 
   else request.type=ORDER_TYPE_BUY;
   request.type_filling=ORDER_FILLING_FOK;
   if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
      SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
     {
      request.sl=NULL;
      request.tp=NULL;
      request.deviation=100;
     }
   while(PositionsTotal()>0)
     {
      request.volume=NormalizeDouble(MathMin(PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(PositionGetSymbol(0),SYMBOL_VOLUME_MAX)),2);
      if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
         SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
        {
         if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID);
         else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
        }
      OrderSend(request,result);
      Sleep(10000);
     }
  }
//+------------------------------------------------------------------+
//|  OpenPosition                                                    |
//+------------------------------------------------------------------+
void OpenPosition()
  {
   double vol;
   request.action=TRADE_ACTION_DEAL;
   request.symbol=s;
   request.type_filling=ORDER_FILLING_FOK;
   if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
      SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
     {
      request.sl=NULL;
      request.tp=NULL;
      request.deviation=100;
     }
   vol=MathFloor(AccountInfoDouble(ACCOUNT_FREEMARGIN)*optF*AccountInfoInteger(ACCOUNT_LEVERAGE)
       /(SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP)))*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP);
   vol=MathMax(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MIN));
   vol=MathMin(vol,GetPossibleLots()*0.95);
   if(SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)!=0) vol=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)),2);
   request.volume=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2);
   while(PositionSelect(s)==false)
     {
      if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
         SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
        {
         if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); 
         else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
        }
      OrderSend(request,result);
      Sleep(10000);
      PositionSelect(s);
     }
   while(PositionGetDouble(POSITION_VOLUME)<vol)
     {
      request.volume=NormalizeDouble(MathMin(vol-PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2);
      if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
         SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
        {
         if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID);
         else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
        }
      OrderSend(request,result);
      Sleep(10000);
      PositionSelect(s);
     }
  }
//+------------------------------------------------------------------+

Após cuidadosa consideração, você pode notar três parâmetros importantes na função de abertura posição: as variáveis s e optF e a chamada da função GetPossibleLots():

  • s - instrumento de negociação, uma das variáveis​otimizadas pelo GA,
  • optF - parte do depósito a ser usado para negociação (outra variável otimizada pelo GA),
  • Função GetPossibleLots() - devolve a parte do depósito a ser usado para negociação:
//+------------------------------------------------------------------+
//|  GetPossibleLots                                                 |
//+------------------------------------------------------------------+
double GetPossibleLots()
  {
   request.volume=1.0;
   if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID);
   else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
   OrderCheck(request,check);
   return(NormalizeDouble(AccountInfoDouble(ACCOUNT_FREEMARGIN)/check.margin,2));
  }

Ligeiramente quebrando a ordem da narrativa, apresentamos mais duas funções comuns a todos os Expert Advisors e essenciais no estágio dois:

//+------------------------------------------------------------------+
//|  InitRelDD                                                       |
//+------------------------------------------------------------------+
void InitRelDD()
  {
   ulong DealTicket;
   double curBalance;
   prevBT[0]=D'2000.01.01 00:00:00';
   TimeToStruct(prevBT[0],prevT);
   curBalance=AccountInfoDouble(ACCOUNT_BALANCE);
   maxBalance=curBalance;
   HistorySelect(D'2000.01.01 00:00:00',TimeCurrent());
   for(int i=HistoryDealsTotal();i>0;i--)
     {
      DealTicket=HistoryDealGetTicket(i);
      curBalance=curBalance+HistoryDealGetDouble(DealTicket,DEAL_PROFIT);
      if(curBalance>maxBalance) maxBalance=curBalance;
     }
  }
//+------------------------------------------------------------------+
//|  GetRelDD                                                        |
//+------------------------------------------------------------------+
double GetRelDD()
  {
   if(AccountInfoDouble(ACCOUNT_BALANCE)>maxBalance) maxBalance=AccountInfoDouble(ACCOUNT_BALANCE);
   return((maxBalance-AccountInfoDouble(ACCOUNT_BALANCE))/maxBalance);
  }

O que podemos ver aqui? A primeira função determina o valor máximo do saldo de conta, a segunda função calcula o rebaixamento atual relativo da conta. Suas peculiaridades serão definidas em detalhes na descrição do estágio dois.

Passando para os Expert Advisors como tal. Uma vez que somos apenas iniciantes, não teremos um Expert Advisor para selecionar uma estratégia, mas aplicaremos rigorosamente dois Expert Advisors com as seguintes estratégias:
  • um negocia utilizando intersecções de médias móveis (Golden Cross - compramos um instrumento, Death Cross - vendemos);
  • o outro é uma simples rede neural que recebe alterações de preços no intervalo de [0..1], ao longo das últimas cinco sessões de negociação.

Algoritmicamente, o trabalho de um Expert Advisor de auto-otimização pode ser exemplificado da seguinte forma:

  1. Inicialização de variáveis ​utilizadas pelo Expert Advisor: define e inicializa os buffers indicadores ou configura a topologia de rede neural (número de camadas/neurônios em uma camada; uma rede neural simples, onde o número de neurônios é o mesmo em todas as camadas é dado como um exemplo), define o período de trabalho. Além disso, provavelmente o passo mais importante - nós chamamos a função Otimização Genética que por sua vez aborda a função mais importante - função de aptidão (adiante - FF).

    IMPORTANTE! Há uma nova FF para cada estratégia de negociação, ou seja, é criada cada vez uma nova, por exemplo, FF para uma única média móvel é completamente diferente da FF para duas médias móveis e difere significativamente da rede neural FF.

    O resultado de desempenho FF em meus Expert Advisors é um equilíbrio máximo desde que o levantamento relativo não ultrapasse o valor crítico definido como uma variável externa (nos nossos exemplos - 0,5). Em outras palavras, se a próxima execução GA der o saldo de 100.000, enquanto que o saldo de abaixamento relativo é -0,6, então FF = 0,0. No seu caso, meu caro leitor, o resultado FF pode trazer critérios completamente diferentes.

    Recolha os resultados de performance do algoritmo genético: por intersecção de médias móveis estes, obviamente, estarão movendo períodos médios, no caso de uma rede neural haverá pesos de sinapse, e o resultado comum para ambos (e para meus outros Expert Advisors) é de um instrumento a ser comercializados até a próxima reotimização e já familiar para nós optF, ou seja, uma parte do depósito a ser usado para negociação. Você é livre para adicionar parâmetros otimizados para sua FF a seu próprio critério, por exemplo, você também pode selecionar prazo ou outros parâmetros...

    O último passo na inicialização é descobrir o valor de saldo máximo da conta. Por que é importante? Porque este é um ponto de partida para a tomada de decisão de reotimização.

    IMPORTANTE! Como a decisão sobre a reotimização é tomada: uma vez que o levantamento relativo de SALDO atinge um certo valor crítico definido como uma variável externa (nos nossos exemplos - 0,2), precisamos reotimizar. Em ordem de não ter um Expert Advisor para implementar a reotimização em cada barra, ao atingir o levantamento crítico, o valor de saldo máximo é substituído por um valor atual.

    Você, meu caro leitor, pode ter um critério totalmente diferente para a implementação de reotimização.

  2. Negociação em andamento.

  3. Após cada posição fechada vamos verificar se o levantamento de equilíbrio atingiu o valor crítico. Se o valor crítico foi atingido, executamos um GA e coletamos os seus resultados de performance (isso é reotimização!)

  4. E nós estamos esperando tanto por um telefonema do diretor estrangeiro pedindo para não falir o mundo, ou (o que é mais frequente) por Stop Out, Chamada de margem, ambulância de emergência...

Veja abaixo uma implementação programada do descrito acima para o Expert Advisor usando a estratégia de médias móveis (código fonte também está disponível) e usando a rede neural - tudo como um código fonte.

O código está disponível sob termos e condições da licença GPL.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   tf=Period();
//---for bar-to-bar test...
   prevBT[0]=D'2001.01.01';
//---... long ago
   TimeToStruct(prevBT[0],prevT);
//--- historical depth (should be set since the optimisation is based on historical data)
   depth=10000;
//--- copies at a time (should be set since the optimisation is based on historical data)
   count=2;
   ArrayResize(LongBuffer,count);
   ArrayResize(ShortBuffer,count);
   ArrayInitialize(LongBuffer,0);
   ArrayInitialize(ShortBuffer,0);
//--- calling the neural network genetic optimisation function
   GA();
//--- getting the optimised neural network parameters and other variables
   GetTrainResults();
//--- getting the account drawdown
   InitRelDD();
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(isNewBars()==true)
     {
      bool trig=false;
      CopyBuffer(MAshort,0,0,count,ShortBuffer);
      CopyBuffer(MAlong,0,0,count,LongBuffer);
      if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1])
        {
         if(PositionsTotal()>0)
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               ClosePosition();
               trig=true;
              }
           }
        }
      if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1])
        {
         if(PositionsTotal()>0)
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               ClosePosition();
               trig=true;
              }
           }
        }
      if(trig==true)
        {
         //--- if the account drawdown has exceeded the allowable value:
         if(GetRelDD()>maxDD)
           {
            //--- calling the neural network genetic optimisation function
            GA();
            //--- getting the optimised neural network parameters and other variables
            GetTrainResults();
            //--- readings of the drawdown will from now on be based on the current balance instead of the maximum balance
            maxBalance=AccountInfoDouble(ACCOUNT_BALANCE);
           }
        }
      CopyBuffer(MAshort,0,0,count,ShortBuffer);
      CopyBuffer(MAlong,0,0,count,LongBuffer);
      if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1])
        {
         request.type=ORDER_TYPE_SELL;
         OpenPosition();
        }
      if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1])
        {
         request.type=ORDER_TYPE_BUY;
         OpenPosition();
        }
     };
  }
//+------------------------------------------------------------------+
//| Preparing and calling the genetic optimizer                      |
//+------------------------------------------------------------------+
void GA()
  {
//--- number of genes (equal to the number of optimised variables), 
//--- all of them should be specified in the FitnessFunction())
   GeneCount      =OptParamCount+2;    
//--- number of chromosomes in a colony
   ChromosomeCount=GeneCount*11;
//--- minimum search range
   RangeMinimum   =0.0;
//--- maximum search range
   RangeMaximum   =1.0;
//--- search pitch
   Precision      =0.0001;
//--- 1 is a minimum, anything else is a maximum
   OptimizeMethod =2;                                                 
   ArrayResize(Chromosome,GeneCount+1);
   ArrayInitialize(Chromosome,0);
//--- number of epochs without any improvement
   Epoch          =100;                                               
//--- ratio of replication, natural mutation, artificial mutation, gene borrowing, 
//--- crossingover, interval boundary displacement ratio, every gene mutation probabilty, %
   UGA(100.0,1.0,1.0,1.0,1.0,0.5,1.0);                                
  }
//+------------------------------------------------------------------+
//| Fitness function for neural network genetic optimizer:           | 
//| selecting a pair, optF, synapse weights;                         |
//| anything can be optimised but it is necessary                    |
//| to carefully monitor the number of genes                         |
//+------------------------------------------------------------------+
void FitnessFunction(int chromos)
  {
   int    b;
//--- is there an open position?
   bool   trig=false;
//--- direction of an open position
   string dir="";
//--- opening price
   double OpenPrice=0;
//--- intermediary between a gene colony and optimised parameters
   int    z;
//--- current balance
   double t=cap;
//--- maximum balance
   double maxt=t;
//--- absolute drawdown
   double aDD=0;
//--- relative drawdown
   double rDD=0.000001;
//--- fitness function proper
   double ff=0;
//--- GA is selecting a pair
   z=(int)MathRound(Colony[GeneCount-1][chromos]*12);
   switch(z)
     {
      case  0: {s="AUDUSD"; break;};
      case  1: {s="AUDUSD"; break;};
      case  2: {s="EURAUD"; break;};
      case  3: {s="EURCHF"; break;};
      case  4: {s="EURGBP"; break;};
      case  5: {s="EURJPY"; break;};
      case  6: {s="EURUSD"; break;};
      case  7: {s="GBPCHF"; break;};
      case  8: {s="GBPJPY"; break;};
      case  9: {s="GBPUSD"; break;};
      case 10: {s="USDCAD"; break;};
      case 11: {s="USDCHF"; break;};
      case 12: {s="USDJPY"; break;};
      default: {s="EURUSD"; break;};
     }
   MAshort=iMA(s,tf,(int)MathRound(Colony[1][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   MAlong =iMA(s,tf,(int)MathRound(Colony[2][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   dig=MathPow(10.0,(double)SymbolInfoInteger(s,SYMBOL_DIGITS));
   
//--- GA is selecting the optimal F
   optF=Colony[GeneCount][chromos];                                   
   
   leverage=AccountInfoInteger(ACCOUNT_LEVERAGE);
   contractSize=SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE);
   b=MathMin(Bars(s,tf)-1-count-MaxMAPeriod,depth);
   
//--- for a neural network using historical data - where the data is copied from
   for(from=b;from>=1;from--) 
     {
      CopyBuffer(MAshort,0,from,count,ShortBuffer);
      CopyBuffer(MAlong,0,from,count,LongBuffer);
      if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1])
        {
         if(trig==false)
           {
            CopyOpen(s,tf,from,count,o);
            OpenPrice=o[1];
            dir="SELL";
            trig=true;
           }
         else
           {
            if(dir=="BUY")
              {
               CopyOpen(s,tf,from,count,o);
               if(t>0) t=t+t*optF*leverage*(o[1]-OpenPrice)*dig/contractSize; else t=0;
               if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t;
               if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt;
               OpenPrice=o[1];
               dir="SELL";
               trig=true;
              }
           }
        }
      if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1])
        {
         if(trig==false)
           {
            CopyOpen(s,tf,from,count,o);
            OpenPrice=o[1];
            dir="BUY";
            trig=true;
           }
         else
           {
            if(dir=="SELL")
              {
               CopyOpen(s,tf,from,count,o);
               if(t>0) t=t+t*optF*leverage*(OpenPrice-o[1])*dig/contractSize; else t=0;
               if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t;
               if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt;
               OpenPrice=o[1];
               dir="BUY";
               trig=true;
              }
           }
        }
     }
   if(rDD<=trainDD) ff=t; else ff=0.0;
   AmountStartsFF++;
   Colony[0][chromos]=ff;
  }

//+---------------------------------------------------------------------+
//| getting the optimized neural network parameters and other variables |
//| should always be equal to the number of genes                       |
//+---------------------------------------------------------------------+
void GetTrainResults()
  {
//---  intermediary between a gene colony and optimised parameters
   int z;                                                             
   MAshort=iMA(s,tf,(int)MathRound(Chromosome[1]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   MAlong =iMA(s,tf,(int)MathRound(Chromosome[2]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   CopyBuffer(MAshort,0,from,count,ShortBuffer);
   CopyBuffer(MAlong,0,from,count,LongBuffer);
//--- save the best pair
   z=(int)MathRound(Chromosome[GeneCount-1]*12);                      
   switch(z)
     {
      case  0: {s="AUDUSD"; break;};
      case  1: {s="AUDUSD"; break;};
      case  2: {s="EURAUD"; break;};
      case  3: {s="EURCHF"; break;};
      case  4: {s="EURGBP"; break;};
      case  5: {s="EURJPY"; break;};
      case  6: {s="EURUSD"; break;};
      case  7: {s="GBPCHF"; break;};
      case  8: {s="GBPJPY"; break;};
      case  9: {s="GBPUSD"; break;};
      case 10: {s="USDCAD"; break;};
      case 11: {s="USDCHF"; break;};
      case 12: {s="USDJPY"; break;};
      default: {s="EURUSD"; break;};
     }
//--- saving the best optimal F
   optF=Chromosome[GeneCount];                                        
  }
//+------------------------------------------------------------------+

Vejamos a função principal do algoritmo - a função de aptidão.

A ideia por trás de um Expert Advisor de auto-otimização é baseada na simulação do processo de negociação (como no padrão Tester por MetaQuotes) dentro de um período de tempo (digamos, um histórico de 10.000 barras) na função de aptidão, o qual recebe uma entrada de variáveis​otimizadas a partir do Algoritmo Genético (função GA()). No caso de um algoritmo baseado na intersecção de médias móveis, as variáveis​ otimizadas incluem:

  • instrumento (no estrangeiro - um par de moedas); sim, é um Expert Advisor de múltiplas moedas e o Algoritmo Genético seleciona um instrumento (uma vez que o código foi retirado do Expert Advisor proposto para a participação no Campeonato, os pares que tem correspondência com os pares de moedas do Campeonato, em geral, não pode haver qualquer instrumento citado por um corretor).
    Nota: infelizmente, o Expert Advisor não pode obter a lista par da janela MarketWatch no modo de teste (esclarecemos aqui graças aos usuários MetaQuotes - Sem chance!). Portanto, se você deseja executar o Expert Advisor no testador separadamente para estrangeiros e ações, apenas especifique os seus próprios instrumentos na FF e função GetTrainResults ().
  • parte do depósito para ser usado na negociação;
  • períodos de duas médias móveis.

Os exemplos abaixo mostram uma variável de um Expert Advisor PARA TESTE!

O código NEGOCIAçãO REAL pode ser significativamente simplificado usando a lista de instrumentos a partir da janela Market Watch.

A fim de fazer isso na FF e função GetTrainResults () com comentários "// --- GA selecionando par" e "//--- salvando o melhor par" basta escrever:

//--- GA is selecting a pair
  z=(int)MathRound(Colony[GeneCount-1][chromos]*(SymbolsTotal(true)-1));
  s=SymbolName(z,true);

Assim, no início da FF nós especificamos e inicializaremos, se necessário, as variáveis​ para simulação da negociação baseada em histórico. Na próxima fase, coletaremos diferentes valores das variáveis otimizadas a partir do Algoritmo Genético, por exemplo desta linha "OoptF=Colony [GeneCount] [cromos]; o valor da parte de depósito é transferido para a FF de GA.

Verificamos ainda, o número disponível de barras no histórico e iniciando com 10000 barras ou a primeira barra disponível simulamos o processo de recebimento de cotas no modo "Apenas preços abertos" e tomada de decisões comerciais:

  • Copie os valores de médias móveis de buffers;
  • Verifique, se é uma Death Cross.
  • Se existe uma Death Cross e não há posições abertas (if(trig==false)) - abrir uma posição de venda SELL virtual (basta lembrar o preço de abertura e direção);
  • Se existe uma Death Cross e uma posição de compra BUY aberta (if(dir=="BUY")) - assumir o preço de abertura da barra e prestar a atenção para as três linhas muito importantes, como segue:
  1. Simular fechamento de uma posição e mudar no saldo: o saldo atual é aumentado por um valor de saldo atual, que é multiplicado pela parte do depósito a ser negociado, multiplicado pela diferença entre os preços aberto e fechado, e multiplicado pela preço pip (bruto);
  2. Verifique se o saldo atual atingiu o máximo ao longo da histórico da simulação de negociação, se não, calcular o levantamento de saldo máximo em dinheiro;
  3. Converta o levantamento de saldo calculado anteriormente em dinheiro em levantamento de saldo relativo;
  • Abra uma posição virtual de venda SELL (apenas lembre o preço aberto e a direção);
  • Faça verificações e cálculos semelhantes para a Golden Cross.

Depois de passar por todo o histórico disponível e simulação da negociação virtual, calcule o valor final de FF: se o levantamento de saldo relativo calculado for inferior do que o conjunto de testes, então FF = saldo, caso contrário FF = 0. Algoritmo Genético visa a maximização da função de aptidão!

Depois de tudo, dando vários valores dos instrumentos, partes de depósito e períodos de médias móveis, o Algoritmo Genético encontrará os valores que maximizam o saldo ao levantamento relativo mínimo (mínimo é definido pelo usuário).


Conclusão

Aqui está uma breve conclusão: é fácil criar um Expert Advisor de autotreinamento, a parte difícil é descobrir o que inserir (o importante é uma ideia, a implementação é apenas uma questão de técnica).

Em antecipação à pergunta de pessimistas - "Isto funciona?", eu tenho uma resposta - sim; a minha palavra para os otimistas - este não é o Santo Graal.

Qual é a diferença fundamental entre o método proposto e pelo Quantum? Ele pode ser melhor exemplificado pela comparação dos Expert Advisors que utilizam MA's:

  1. A decisão sobre os períodos de MA está no Sistema de Negociação Adaptável e deve ser realizada antes da compilação e rigorosamente codificada, e a escolha só é possível fora deste número limitado de variáveis; não tomamos qualquer decisão sobre os períodos antes da compilação no Expert Advisor Geneticamente Otimizado, esta decisão será feita por GA e o número de variáveis é limitado apenas pelo senso comum.
  2. A negociação virtual no Sistema de Negociação Adaptável é bar-to-bar (barra a barra); mas raramente é assim no Expert Advisor Geneticamente Otimizado - e somente após a ocorrência de condições para reotimização. O desempenho do computador sobre o aumento do número de estratégias, parâmetros, instrumentos pode ser um fator limitante para o Sistema de negociação adaptável.


Anexo

Aqui está o que nós temos se a rede neural for executada no Testador sem qualquer otimização e com base em gráficos diários, a partir de 01.01.2010:

Relatório do testador de estratégia
MetaQuotes-Demo (Build 523)
Expert Advisor: ANNExemplo
Símbolo: EURUSD
Período: Diário (01.01.2010 - 30.09.2011)
Parâmetros de entrada: trainDD=0.9
maxDD=0.1
Corretor: Alpari NZ Limitado
Moeda: USD
Depósito inicial: 10 000.00
Alavancagem: 1:100
Resultados
Qualidade do histórico: 100%
Barras: 454 Ticks: 2554879
Lucro líquido total: -9 094.49 Lucro bruto: 29 401.09 Perda bruta: -38 495.58
Fator de lucro: 0,76 Retorno esperado: -20,53 Nível de margem: 732,30%
Fator de recuperação: -0,76 índice Sharpe: -0,06 Resultado onTester: 0
Levantamento de saldo:
Levantamento de saldo abs.: 9 102.56 Levantamento de saldo máximo: 11 464.70 (92.74%) Levantamento de saldo relativo: 92.74% (11 464.70)
Levantamento de patrimônio:
Levantamento de patrimônio abs.: 9 176.99 Levantamento máximo de patrimônio: 11 904.00 (93.53%) Levantamento de patrimônio relativo: 93.53% (11 904.00)
Total de negociações: 443 Negociações curtas (vencidas, %): 7 (14.29%) Negociações longas (vencidas, %): 436 (53.44%)
Total de negociações: 886 Negócios lucrativos (% do total): 234 (52.82%) Negócios perdidos (% do total): 209 (47.18%)
Negócio com maior lucro: 1 095.57 Negociação com maior perda: -1 438.85
Lucro comercial médio: 125,65 Perda comercial média: -184,19
Máximos ganhos consecutivos (lucro em dinheiro): 8 (397.45) Máximas perdas consecutivas (perdas em dinheiro): 8 (-1 431.44)
Máximo lucro consecutivo (contagem de vitórias): 1 095.57 (1) Máximas perdas consecutivas (contagem de perdas): -3 433.21 (6)
Médias de ganhos consecutivos: 2 Médias de perdas consecutivas: 2

e aqui abaixo são as três variáveis da reotimização para escolher:

primeiro...

Tempo Negócio Símbolo Tipo Direção Volume Preço Ordem Troca Lucro Saldo
01.01.2010 00:00 1 saldo 0,00 10 000.00 10 000.00
04.01.2010 00:00 2 AUDUSD compra em 0,90 0,89977 2 0,00 0,00 10 000.00
05.01.2010 00:00 3 AUDUSD venda fora 0,90 0,91188 3 5,67 1 089.90 11 095.57
05.01.2010 00:00 4 AUDUSD compra em 0,99 0,91220 4 0,00 0,00 11 095.57
06.01.2010 00:00 5 AUDUSD venda fora 0,99 0,91157 5 6,24 -62,37 11 039.44
06.01.2010 00:00 6 AUDUSD compra em 0,99 0,91190 6 0,00 0,00 11 039.44
07.01.2010 00:00 7 AUDUSD venda fora 0,99 0,91924 7 18,71 726,66 11 784.81


segundo...

Tempo Negócio Símbolo Tipo Direção Volume Preço Ordem Comissão Troca Lucro Saldo
19.05.2010 00:00 189 AUDUSD venda fora 0,36 0,86110 189 0,00 2,27 -595,44 4 221.30
19.05.2010 00:00 190 EURAUD venda em 0,30 1,41280 190 0,00 0,00 0,00 4 221.30
20.05.2010 00:00 191 EURAUD compra fora 0,30 1,46207 191 0,00 7,43 -1 273.26 2 955.47
20.05.2010 00:00 192 AUDUSD compra em 0,21 0,84983 192 0,00 0,00 0,00 2 955.47


terceiro

Tempo Negócio Símbolo Tipo Direção Volume Preço Ordem Troca Lucro Saldo
16.06.2010 00:00 230 GBPCHF compra em 0,06 1,67872 230 0,00 0,00 2 128.80
17.06.2010 00:00 231 GBPCHF venda fora 0,06 1,66547 231 0,13 -70,25 2 058.68
17.06.2010 00:00 232 GBPCHF compra em 0,06 1,66635 232 0,00 0,00 2 058.68
18.06.2010 00:00 233 GBPCHF venda fora 0,06 1,64705 233 0,04 -104,14 1 954.58
18.06.2010 00:00 234 AUDUSD compra em 0,09 0,86741 234 0,00 0,00 1 954.58
21.06.2010 00:00 235 AUDUSD venda fora 0,09 0,87184 235 0,57 39,87 1 995.02
21.06.2010 00:00 236 AUDUSD compra em 0,09 0,88105 236 0,00 0,00 1 995.02
22.06.2010 00:00 237 AUDUSD venda fora 0,09 0,87606 237 0,57 -44,91 1 950.68
22.06.2010 00:00 238 AUDUSD compra em 0,09 0,87637 238 0,00 0,00 1 950.68
23.06.2010 00:00 239 AUDUSD venda fora 0,09 0,87140 239 0,57 -44,73 1 906.52
23.06.2010 00:00 240 AUDUSD compra em 0,08 0,87197 240 0,00 0,00 1 906.52
24.06.2010 00:00 241 AUDUSD venda fora 0,08 0,87385 241 1,51 15,04 1 923.07
24.06.2010 00:00 242 AUDUSD compra em 0,08 0,87413 242 0,00 0,00 1 923.07
25.06.2010 00:00 243 AUDUSD venda fora 0,08 0,86632 243 0,50 -62,48 1 861.09
25.06.2010 00:00 244 AUDUSD compra em 0,08 0,86663 244 0,00 0,00 1 861.09
28.06.2010 00:00 245 AUDUSD venda fora 0,08 0,87375 245 0,50 56,96 1 918.55
28.06.2010 00:00 246 AUDUSD compra em 0,08 0,87415 246 0,00 0,00 1 918.55
29.06.2010 00:00 247 AUDUSD venda fora 0,08 0,87140 247 0,50 -22,00 1 897.05
29.06.2010 00:00 248 AUDUSD compra em 0,08 0,87173 248 0,00 0,00 1 897.05
01.07.2010 00:00 249 AUDUSD venda fora 0,08 0,84053 249 2,01 -249,60 1 649.46
01.07.2010 00:00 250 EURGBP venda em 0,07 0,81841 250 0,00 0,00 1 649.46
02.07.2010 00:00 251 EURGBP compra fora 0,07 0,82535 251 -0,04 -73,69 1 575.73
02.07.2010 00:00 252 EURGBP venda em 0,07 0,82498 252 0,00 0,00 1 575.73
05.07.2010 00:00 253 EURGBP compra fora 0,07 0,82676 253 -0,04 -18,93 1 556.76
05.07.2010 00:00 254 EURGBP venda em 0,06 0,82604 254 0,00 0,00 1 556.76
06.07.2010 00:00 255 EURGBP compra fora 0,06 0,82862 255 -0,04 -23,43 1 533.29


P.S. Como lição de casa: não selecione apenas os parâmetros de um determinado sistema, mas também selecione o sistema que melhor se adapte ao mercado em um dado momento (sugestão - a partir do banco de sistemas).

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

Arquivos anexados |
anntrainlib.mqh (9.76 KB)
matrainlib.mqh (8.94 KB)
ugalib.mqh (33.26 KB)
annexample.mq5 (4.32 KB)
maexample.mq5 (4.22 KB)
musthavelib.mqh (8.14 KB)
Últimos Comentários | Ir para discussão (1)
Airton Raposo
Airton Raposo | 21 abr 2016 em 18:49

Boa tarde!

Grande matéria!

Ao ler seu post me deu um estalo!

Depois de passar um bom tempo otimizando resultados do robô percebi o que era óbvio, uma estratégia somente será válida por um determinado tempo, o tempo em que o mercado se mantiver, mas com o tempo o mercado muda e a estratégia que há um mês atrás era boa já não é tanto, me obrigando a realizar novos testes com parâmetros para encontrar os períodos e outras configurações mais adaptáveis ao mercado atual...

A minha ideia é que o robô possa realizar sozinho em um período de tempo novas configurações mais adaptáveis ao mercado e ele escolher a melhor delas e essa pré definida por mim.

Minha dúvida é se tem como fazer com que o robô faça esse "auto-treinamento" a cada xxx semanas ou xx meses e ele mesmo se atualize com o melhor valor escolhido mediante as variaveis externas escolhidas nas configurações de parâmetros.

O "Tudo ou Nada" Estratégia Forex O "Tudo ou Nada" Estratégia Forex
O objetivo deste artigo é o de criar a estratégia de negociação mais simples que implementa o princípio de jogo "Tudo ou Nada". Não queremos criar um Consultor Especialista rentável - o objetivo é aumentar o depósito inicial várias vezes com a maior probabilidade possível. é possível ter êxito no ForEx ou perder tudo sem saber nada sobre a análise técnica e sem usar nenhum indicador?
Como Adicionar Novas Linguagens de IU na Plataforma MetaTrader 5 Como Adicionar Novas Linguagens de IU na Plataforma MetaTrader 5
A interface do usuário da plataforma MetaTrader 5 é traduzido em vários idiomas. Não se preocupe se a sua língua materna não está entre as suportados. Você pode facilmente completar a tradução usando o utilitário especial MetaTrader MultiLanguage Pack, oferecido pela MetaQuotes Corp. gratuitamente para todos os clientes. Neste artigo mostraremos alguns exemplos de como adicionar linguagens de interface de usuário para a plataforma do MetaTrader 5.
Criando Consultores Especialistas em minutos usando a árvore EA: Parte Um Criando Consultores Especialistas em minutos usando a árvore EA: Parte Um
EA Tree é o primeiro construtor do Consultor Especialista do MetaTrader MQL5 com recurso de arrastar e soltar. Você pode criar um MQL5 complexo usando uma interface gráfica do usuário muito fácil de usar. Na árvore EA, Consultores Especialistas são criados por ligação de caixas juntas. As caixas podem conter funções MQL5, indicadores técnicos, indicadores personalizados ou valores. Usando as "três caixas", o EA Tree gera o código MQL5 do Expert Advisor.
Aplicação da transformada de Fisher e da transformada inversa de Fisher à análise de mercado no MetaTrader 5 Aplicação da transformada de Fisher e da transformada inversa de Fisher à análise de mercado no MetaTrader 5
Sabemos que a função de densidade de probabilidade (PDF) de um ciclo de mercado não se parece com uma curva de Gauss e sim com uma PDF de onda senoidal e que a maioria dos indicadores supõe que a PDF de ciclo de mercado seja uma curva de Gauss, precisamos encontrar uma maneira de "corrigir" isso. A solução é utilizar a transformada de Fisher. A transformada de Fisher faz com que a PDF de qualquer forma de onde se aproxime a uma onda de Gauss. Este artigo descreve a matemática por trás da transformada de Fisher e da transformada inversa de Fisher e sua aplicação a negociação. Um módulo de sinal de negócio proprietário com base na transformada inversa de Fisher é apresentada e avaliada.