Expert Advisor-parte1: Programação de robôs de trade (videoaula)

Finalmente, trataremos a codificação do Expert Advisor (robô ou assistente especializado). As etapas tratadas anteriormente foram fundamentais para melhor compreensão e aprendizado da linguagem MQL5. Neste artigo, veremos como codificar um EA e integrá-lo com o indicador implementado anteriormente.

Vale lembrar que o segredo deste EA está no indicador, não no EA. Logo, é preciso melhorar a eficiência do indicador APF-Color-Base!”

O download do projeto pode ser feito pelo link:
http://aprendizfinanceiro.com.br/APF-Color-Base-EA.zip

Para agilizar o processo de codificação e reduzir a margem de erros, incluímos as bibliotecas internas CPositionInfo (controle de posição), CTrade (operações de trade) e CSymbolInfo (informações de mercado, referentes ao ativo analisado).

#include <Trade\PositionInfo.mqh>
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>

CPositionInfo  m_position;                 // trade position object
CTrade         m_trade;                    // trading object
CSymbolInfo    m_symbol;                   // symbol info object

Como o robô se orienta pelo indicador APF-Color-Base, precisamos obter a cor da barra através de uma chamada iCustom, no bloco OnInit:

hAPF=iCustom(_Symbol,PERIOD_CURRENT,"\\Indicators\\APF-Color-base-1.1.ex5");
if(hAPF==INVALID_HANDLE) {
  Print("Expert: iCustom call: Error code=",GetLastError());
  return(INIT_FAILED);
}

Percebam que a chamada não difere muito dos exemplos que utilizamos no indicador com chamadas iMA e iSAR.

A leitura do sinal (obtenção da cor da barra) foi codificada na função GetSignal.

Mas, para ampliar a segurança das operações, a primeira verificação é referente a tentativa de encerrar operações em aberto (m_trade.PositionClose).

int signal=GetSignal();

if (signal==0 || close_trade) {
   if (count_buy>0 || count_sell>0) {
      int close_pos=0;
      for(int i=PositionsTotal()-1;i>=0;i--)
         if(m_position.SelectByIndex(i))
           if(m_position.Symbol()==Symbol() && m_position.Magic()==m_magic) {
              close_pos=0;
              if (signal==0 || close_trade) {
                  m_trade.PositionClose(m_position.Ticket());
                  close_pos=1;
              }
           }
   }
   return;
}

A função GetSignal retornará a cor da barra: sendo 0 (branco), 1 (verde/long) e 2 (vermelho/short) – vide indicador!

Quando count_buy ou count_sell for maior que 0, certamente existirá uma operação em aberto (Long ou Short). Neste caso, a posição será fechada caso GetSignal retorne 0 (a barra mudou de cor – para branco) ou close_trade seja diferente de zero (para forçar o fechamento).

Confiram o código da função GetSignal:

int GetSignal()
  {
      
      //--- load buf_color_line
      if (CopyBuffer(hAPF,4,0,2,buf_APF) < 0){
         Print("CopyBuffer APF error =",GetLastError());
         return(0);
      }
      
      int vSignal=0;
      if ((buf_APF[0]==1 && buf_APF[1]==0) || (count_buy>0 && buf_APF[0]==1 && buf_APF[1]==1))  vSignal=1;
      if ((buf_APF[0]==2 && buf_APF[1]==0) || (count_sell>0 && buf_APF[0]==2 && buf_APF[1]==2))  vSignal=2;

      return (vSignal);
  }

A cor dos dois últimos candles é obtida através da função do MQL5 CopyBuffer: o primeiro parâmetro identifica o handle do indicador (hAPF), o segundo parâmetro a posição do buffer do indicador (4 é o índice que representa buf_color_line – consulte os mapeamentos feitos com SetIndexBuffer), o terceiro a posição inicial (0 – última barra) e o último corresponde ao número de elementos para copiar (2 – duas barras).

A leitura dos dois últimos candles se faz necessário para comparar uma possível mudança de cor

Já a abertura de posição foi codificada na função OpenBuy (posição comprada) e OpenSell (posição vendida). Conforme exposto anteriormente, utilizamos a biblioteca CTrade para controlar a abertura de posição (m_trade.PositionOpen):

void OpenBuy(double sl,double tp)
  {
   sl=m_symbol.NormalizePrice(sl);
   tp=m_symbol.NormalizePrice(tp);
   int aux_Lots=InpLots;
   
//--- check volume before OrderSend to avoid "not enough money" error (CTrade)
   double check_volume_lot=m_trade.CheckVolume(m_symbol.Name(),aux_Lots,m_symbol.Ask(),ORDER_TYPE_BUY);

   if (count_buy>0) return;
   
   if(check_volume_lot!=0.0) {
      if(m_trade.PositionOpen(m_symbol.Name(),ORDER_TYPE_BUY,aux_Lots,m_symbol.Ask(),sl,tp,"LONG"))
      {
         if (trade_maxgain==0) {
            count_buy++;
            max_trades++;
         }
         Print("PositionOpen() BUY method executed SUCCESSFULLY. Return code=",m_trade.ResultRetcode(),
               " (",m_trade.ResultRetcodeDescription(),")");
         PrintResultTrade(m_trade,m_symbol);

         // preço confirmado pela corretora
         trade_open=m_trade.ResultPrice();
      }
...

   }
...

//---
  }

Cada ordem será enviada à mercado (ORDER_TYPE_BUY). Sendo assim, logo que o indicador sinalizar a mudança de cor, o robô fará a negociação pelo preço negociado naquele instante!

Espero que o artigo seja útil e até a próxima! 😉

Indicadores-parte final: Programação de robôs de trade (videoaula)

Para finalizar a série de artigos sobre a codificação de indicadores no MT5, resolvi compartilhar mais um vídeo explicando as razões que nos levaram modificar o código para abertura de posição simulado pelo indicador.

Quanto maior o número de comparações dos preços do candlestick, menos preciso o algoritmo se mostrava em relação ao “histórico” (quando prev_calculated é igual a 0) e o “testador de estratégia” (a cada negócio) – por incrível que pareça, em um segundo, a estimativa de volume ou a diferença do preço máximo pelo fechamento pode mudar algum critério de decisão.

A única forma de coincidir as comparações históricas com o processamento online (no testador de estratégia ou durante o trade real), é validando os critérios para abertura de posição assim que a nova barra for desenhada e comparar os valores da barra anterior.

A última versão do projeto está disponível no link:
http://aprendizfinanceiro.com.br/APF-Color-Base.zip

Outro indicador que foi adicionado ao código foi o StdDev (desvio padrão). O objetivo é capturar a volatilidade do mercado – o valor do indicador aumenta na medida em que os preços se distanciam da média apurada (período).”

A variável “i” é o índice que representa cada barra processada. Para que possamos “identificar a barra atual (i)” e “comparar a anterior (i-1)“, podemos incluir outro índice (“k“) que será igual a “i-1” apenas quando a operação não estiver aberta – quando trade_open for igual a 0.

    for (int i=start; i < rates_total; i++) {
        HighBuf[i]=high[i];
        CloseBuf[i]=close[i];
        OpenBuf[i]=open[i];
        LowBuf[i]=low[i];
...
             int k=i;
             if (!trade_open) k=i-1;
...
             // Identificação da barra
             bool CANDLE_0          = (open[k]-close[k] == 0);
             bool CANDLE_UP       = (close[k]>open[k] && close[k]-open[k]>=1);
             bool CANDLE_DOWN  = (close[k]=1);
...

O ajuste anterior resolve a questão da comparação da barra anterior, mas precisamos alterar o momento em que as comparações serão liberadas. Este controle é feito pela função isNewBar.

bool isNewBar(const datetime lastbar_time, const int op_type, const int prev_calc)
  {
//--- memorize the time of opening of the last bar in the static variable
   static datetime last_time=0;
   static datetime init_time=0;
   
   datetime init_auxtime=TimeCurrent();

   int limit_time=0;
   long time_match=0;
   
   limit_time=300;
   time_match=(limit_time-(init_auxtime-init_time));
   if (time_match<0) time_match=0;
   
   string label_name1="Seconds", label_text="Seconds... "+(string) (long)time_match;
   ObjectDelete(0, label_name1);
   ObjectCreate(0, label_name1, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0,label_name1,OBJPROP_XDISTANCE,ChartGetInteger(0,CHART_WIDTH_IN_PIXELS,0)-100);
   ObjectSetInteger(0,label_name1,OBJPROP_YDISTANCE,30);
   ObjectSetInteger(0,label_name1,OBJPROP_COLOR,YellowGreen);
   ObjectSetString(0,label_name1,OBJPROP_TEXT,label_text);
   
   if (last_time==0 || init_time==0 || last_time!=lastbar_time) {
      last_time=lastbar_time;
      init_time=init_auxtime;
      if (last_time==0) return(false);
      if (prev_calc==0 || op_type==1) return(true);
      return(false);
   }

   if (op_type==2 && time_match<=2) return(true);
   
   return(false);
  }

Todas as atualizações do projeto serão disponibilizadas no github:
https://github.com/betolj/Trade-MT5

Para quem ainda não estiver muito confortável com a codificação em MQL5, recomendo dedicar tempinho para acessar o seguinte canal (bastante didático):
https://www.youtube.com/channel/UCMEarUKP3ALctWTDxccj4Yg

Em breve, começaremos abordar a codificação do Expert Advisor!