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

Finalmente, disponibilizei uma demonstração da codificação de um indicador no Metatrader5 (linguagem MQL5). Resolvi dividir, em pelo menos, duas partes. Na primeira, tratei sobre a estrutura básica e a lógica fundamental para criação de um indicador.

No vídeo seguinte, pretendo demonstrar a codificação de um indicador VWAP e, se possível, finalizarei demonstrando como criar um indicador que ajuste as cores do candlestick de acordo com alguns critérios da análise técnica – será referência para nosso EA.

Caso você não tenha experiência com programação, recomendo a leitura do artigo sobre algoritmos:

Vamos a primeira videoaula sobre a codificação do indicador:

Os exemplos demonstrados estão disponíveis através da url:
http://aprendizfinanceiro.com.br/indicador-aulas1.zip

Na explicação da média móvel, estava com pressa para finalizar (antes de minha câmera digital encerrar o vídeo) e não expliquei corretamente o cálculo da média móvel. Mas, resumindo:

1. Somando preço de fechamentos das últimas 20 barras (period): 
   for (int j=0; j<period; j++) media+=close[i-j]; 

Este código faria algo como 
   media=media+close[i-0]; 
   media=media+close[i-1];
... 
   media=media+close[i-period];

2. A média é o valor da soma dividido pelo período:
   media=media/period;

Procurei ser o mais objetivo possível, cobrindo os pontos principais para codificação.

Vi, em outros vídeos, uma demonstração de codificação voltada para “traders” e não, necessariamente, para programadores

Infelizmente, não acredito que isto seja possível.

Quem não abstrair a lógica de programação, fatalmente terá uma capacidade de codificação muito limitada. Desenhar linhas, por si só, não serve para nada. Portanto, não desprezem os aspectos técnicos demonstrados.

Para auxiliar no entendimento, recomendo assistir o seguinte vídeo:

O vídeo anterior foi produzido pela equipe da DeltaTrader e é bastante didático. Aliás, foi por onde me orientei para começar.

Sei que não é tão simples como muitos gostariam, mas sejam pacientes. Ao abstrair a base de codificação, os próximos passos se tornam cada vez mais fáceis.

Então, cada coisa ao seu tempo! 😉

Sucesso para todos!

Programação de Indicadores: Identificando os últimos negócios antes de desenhar a próxima barra!

Aproveitando que tenho compartilhado informações sobre a codificação de robôs no Metatrader5 (ainda em “indicadores“) e algumas dúvidas ou curiosidades estão surgindo, resolvi demonstrar o código que utilizo para identificar os últimos negócios ou nova barra.

A função isNewBar foi definida, inicialmente, para permitir a identificação de uma nova barra – o meu objetivo era abrir os cálculos a cada nova barra.

Parecia simples, bastava comparar “last_time!=lastbar_time” (ou seja, comparar valor atual com o anterior e retornar true quando os valores não forem iguais).

bool isNewBar(const datetime lastbar_time, const int prev_calc) {
   ...
   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);
      return(true);
   }
   ...
   return(false);
}

O valor de lastbar_time é o horário da barra atual (baseado na abertura, e fornecido pela variável time[i]) – mudará apenas na próxima barra. E last_time é o último horário capturado.

Funciona perfeitamente para a primeira execução do indicador, quando prev_calculated for 0 (com dados históricos). Porém, a lógica não pode ser a mesma quando o indicador estiver analisando os negócios em tempo real.

Suponhamos que você queira trocar a cor do candle quando o preço de abertura for igual ao preço de fechamento (para fechar posição)…

Com os dados históricos (prev_calculated 0), a barra não é processada ticket a ticket. Então, se compararmos open com close assim que a função isNewBar retornar true, teremos a certeza de que open[i] representa o primeiro negócio e close[i] o último (no intervalo de tempo representado pela barra).

Pois é, mas o comportamento não será o mesmo quando prev_calculated for maior que 0 (em tempo real). Neste caso, assim que a próxima barra for desenhada, para o primeiro ticket, teremos open igual a close. Ou seja, a comparação será SEMPRE verdadeira logo que surgir a próxima barra.

Por esta razão, inclui o controle de tempo decorrido (time_match):

   ...
   datetime init_auxtime=TimeCurrent();
   ...
   // 5 minutos
   limit_time=300;
   time_match=(limit_time-(init_auxtime-init_time));
   ...
   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_aux==0) return(true);
   else if (time_match<=1) {
      if (time_match<=0) {
         last_time=lastbar_time;
         init_time=init_auxtime;
         return(false);
      }
      return(true);
    }
    ...

Esta é uma das várias pegadinhas deste tipo de programação! 😉

Confiram, logo a seguir, a função completa:

...
int time_punish=0;
...
bool isNewBar(const datetime lastbar_time, 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,prev_aux=prev_calc;
   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,960);
   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 it is the first call of the function or new bar
   if (last_time!=lastbar_time && trade_open>0) bars_trade++;
   if (last_time==0 || init_time==0 || last_time!=lastbar_time) {
      if (init_auxtime>=init_time+limit_time && init_time>0) prev_aux=0;
      time_punish=1;
      last_time=lastbar_time;
      init_time=init_auxtime;
      if (last_time==0) return(false);
   }

//--- allow after 5 minutes - 1s
   if (time_match>2 && time_match<250) time_punish=0;
   if (prev_aux==0) return(true);
   else if (time_match<=1) {
      if (time_match<=0) {
         last_time=lastbar_time;
         init_time=init_auxtime;
         time_punish=1;
         return(false);
      }
      if (time_punish==0) return(true);
   }
   
//--- if we passed to this line, then the bar is not new; return false
   return(false);
  }

"A primeira definição, com a criação de um objeto do tipo LABEL, exibirá o contador decrescente (time_match) dos segundos decorridos (300s ou 5min)"

O código responsável pelos cálculos fica limitado pela função isNewBar:

if (isNewBar(time[i], last)==true) {
   ...
   // codificação para mudança de cor do candlestick
   if (open[i]==close[i]) buf_color_line[i]=0;
   ...
}