English Русский 中文 Español Deutsch 日本語
LifeHack para traders: preparemos "fast-food" de indicadores

LifeHack para traders: preparemos "fast-food" de indicadores

MetaTrader 5Integração | 27 fevereiro 2018, 11:13
2 162 0
Vladimir Karputov
Vladimir Karputov

Se é proibido, mas você está realmente com vontade, então pode fazê-lo. 

Provérbio russo


Simplicidade vs Confiabilidade

Em 2005, foi realizado o lançamento oficial do MetaTrader 4, nele a simples linguagem de script MQL-II foi substituída pela MQL4. É engraçado se lembrar, mas, no início, os traders receberam a nova linguagem com duas pedras na mão, nos fóruns, eles debatiam e acusavam o desenvolvedor MetaQuotes Software Corp. de ter criado uma linguagem muito complexa e impossível de aprender.

Agora, depois de 12 anos, os traders da atualidade acham incompreensível toda queixa sobre a complexidade da MQL4, mas a história se repete. Pois, alguns deles dizem que, ao contrário da MQL4, a MQL5 é difícil de estudar e complexa no desenvolvimento de estratégias. Ao longo dos anos, o nível geral da programação de robôs aumentou significativamente, graças à coragem de desenvolvedores que seguiram em frente e criaram ferramentas ainda mais poderosas para a linguagem C ++, a fim de realizar soluções para o trading algorítmico. A nova MQL5 permite os programadores verificarem cuidadosamente os resultados de todas as operações, conforme a necessidade, usando a RAM, o que é feito especialmente para o processamento de transações. Na antiga MQL4, antes de ser levada até o nível da MQL5, não existiam tantas funcionalidades desse tipo. A própria sintaxe era menos rigorosa, inclusive.

Acredito que o debate sobre a dificuldade do aprendizado da linguagem MQL5 terminará em breve e será parte da história; porém, como muitos traders têm saudades da MQL4, tentaremos mostrá-lhes que aparência podem ter as conhecidas funções MQL4 se forem realizadas em MQL5.

Se você estiver mudando para a MQL5 agora, este artigo é para você, porque, por um lado, o acesso aos dados dos indicadores e às séries é realizado na nossa conhecida linguagem MQL4, por outro lado, toda a realização é escrita em MQL5. Todas as funções são o mais claras quanto possível e são perfeitamente adequadas para uma depuração passo a passo.


1. Será que, em MQL5, se pode trabalhar com os indicadores em estilo MQL4?

A principal diferença no trabalho com indicadores é que, na MQL4, a linha de consulta de dados de indicador é, em essência, o comando para criar um indicador ( iMACD(NULL,0,12,26,9,PRICE_CLOSE ) e, em seguida, o pedido de dados do buffer de indicador ( MODE_MAIN ) e índice ( 1 ) necessários.

//+------------------------------------------------------------------+
//|                                                        iMACd.mq4 |
//|                              Copyright © 2018, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2018, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.00"
#property strict
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   double macd_main_1=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1);
  }
//+------------------------------------------------------------------+

No total, é apenas uma linha e apenas um passo.

Em MQL5, o análogo deste código contém várias etapas:

  • declaração da variável em que será armazenado o identificador do indicador;
  • criação e verificação do identificador do indicador;
  • função separada que dá o valor do indicador.
//+------------------------------------------------------------------+
//|                                                        iMACD.mq5 |
//|                              Copyright © 2018, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2018, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.000"

int    handle_iMACD;                         // variable for storing the handle of the iMACD indicator
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create handle of the indicator iMACD
   handle_iMACD=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
//--- if the handle is not created 
   if(handle_iMACD==INVALID_HANDLE)
     {
      //--- tell about the failure and output the error code 
      PrintFormat("Failed to create handle of the iMACD indicator for the symbol %s/%s, error code %d",
                  Symbol(),
                  EnumToString(Period()),
                  GetLastError());
      //--- the indicator is stopped early 
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   double macd_main_1=iMACDGet(MAIN_LINE,1);
  }
//+------------------------------------------------------------------+
//| Get value of buffers for the iMACD                               |
//|  the buffer numbers are the following:                           |
//|   0 - MAIN_LINE, 1 - SIGNAL_LINE                                 |
//+------------------------------------------------------------------+
double iMACDGet(const int buffer,const int index)
  {
   double MACD[1];
//--- reset error code 
   ResetLastError();
//--- fill a part of the iMACDBuffer array with values from the indicator buffer that has 0 index 
   if(CopyBuffer(handle_iMACD,buffer,index,1,MACD)<0)
     {
      //--- if the copying fails, tell the error code 
      PrintFormat("Failed to copy data from the iMACD indicator, error code %d",GetLastError());
      //--- quit with zero result - it means that the indicator is considered as not calculated 
      return(0.0);
     }
   return(MACD[0]);
  }
//+------------------------------------------------------------------+

Reescrevemos o mesmo código em MQL4.

Numa função, realizamos a criação do ID do indicador e a aquisição de dados a partir do indicador:

//+------------------------------------------------------------------+
//| iMACD function in MQL4 notation                                  |
//+------------------------------------------------------------------+
double iMACD(
             string              symbol,              // symbol name 
             ENUM_TIMEFRAMES     period,              // period 
             int                 fast_ema_period,     // period for Fast average calculation 
             int                 slow_ema_period,     // period for Slow average calculation 
             int                 signal_period,       // period for their difference averaging 
             ENUM_APPLIED_PRICE  applied_price,       // type of price or handle 
             int                 buffer,              // buffer
             int                 shift                // shift
             )
  {
   double result=NULL;
//---
   int handle=iMACD(symbol,period,fast_ema_period,slow_ema_period,signal_period,
                    applied_price);
   double val[1];
   int copied=CopyBuffer(handle,buffer,shift,1,val);
   if(copied>0)
      result=val[0];
   return(result);
  }

E agora ATENÇÃO! Após escrever esta função, criaremos o ID do indicador EM CADA tick. A documentação não recomenda abusar de tal "criatividade". Eis aqui o que diz o guia Funções para trabalhar com indicadores técnicos:

Não se deve acessar os dados imediatamente após serem criados, uma vez que o cálculo dos valores do indicador precisa de algum tempo. Portanto, o melhor é criar o ID do indicador na OnInit().

Por que esse código funciona e não consome memória? A resposta é encontrada na mesma seção, abaixo:

Comentário. No que diz respeito a um só programa MQL5, ao usar repetidamente a função do indicador com os mesmos parâmetros, o contador aumenta em 1, uma vez, isto é, não faz com que o contador de referências aumente muitas vezes. No entanto, recomenda-se obter o ID do indicador na função Oninit() ou no construtor da classe, usando os ID obtidos, noutras funções. O contador de referências diminui ao deinicializar o programa MQL5.

Em outras palavras, a MQL5 é concebida de forma ideal: ela controla a criação de identificadores e não permite criar repetidamente o mesmo indicador com os mesmos parâmetros. Quando se tenta realizar reiteradamente uma cópia do identificador, simplesmente é retornado o ID do indicador criado anteriormente, com as configurações correspondentes. No entanto, recomenda-se obter os identificadores de vez na OnInit(). Veremos o porquê um pouco para a frente.

Atenção, não se pode verificar se a criação do ID é correta, uma vez que não existe tal verificação.

Agora, o código que obtém os valores do indicador iMACD será parecido com isto:

//+------------------------------------------------------------------+
//|                                           MACD MQL4 style EA.mq5 |
//|                              Copyright © 2018, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2018, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.000"

#define MODE_MAIN 0
#define MODE_SIGNAL 1
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   double macd_main_1=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1);
  }
//+------------------------------------------------------------------+
//| iMACD function in MQL4 notation                                  |
//+------------------------------------------------------------------+
double iMACD(
             string              symbol,              // symbol name 
             ENUM_TIMEFRAMES     period,              // period 
             int                 fast_ema_period,     // period for Fast average calculation 
             int                 slow_ema_period,     // period for Slow average calculation 
             int                 signal_period,       // period for their difference averaging 
             ENUM_APPLIED_PRICE  applied_price,       // type of price or handle 
             int                 buffer,              // buffer
             int                 shift                // shift
             )
  {
   double result=NULL;
//---
   int handle=iMACD(symbol,period,fast_ema_period,slow_ema_period,signal_period,
                    applied_price);
   double val[1];
   int copied=CopyBuffer(handle,buffer,shift,1,val);
   if(copied>0)
      result=val[0];
   return(result);
  }
//+------------------------------------------------------------------+

ATENÇÃO: o desejo de usar os indicadores em estilo MQL4 nos priva de opções para verificar o valor de retorno, porque todas as funções em estilo MQL4 retornam APENAS valores double. Apresentaremos uma possível solução na seção 1.1.

Até agora tudo parece bastante complicado, por isso levamos o bloco define e a função double iMACD() a um arquivo "IndicatorsMQL5.mqh" separado que colocamos numa pasta separada "[data folder]\MQL5\Include\SimpleCall". Dessa maneira, o código se torna bastante curto. Atenção, nós incluímos o arquivo "IndicatorsMQL5.mqh". Isso significa que, ao usar o MACD, o nome das linhas de indicador deve ser enviado na forma de MQL5 MAIN_LINE, ao invés de MQL4 MODE_MAIN:

//+------------------------------------------------------------------+
//|                                     MACD MQL4 style EA short.mq5 |
//|                              Copyright © 2018, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2018, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.000"
#include <SimpleCall\IndicatorsMQL5.mqh>
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   double macd_main_1=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MAIN_LINE,1);
   Comment("MACD, main buffer, index 1: ",DoubleToString(macd_main_1,Digits()+1));
  }
//+------------------------------------------------------------------+

Eu inseri "Comment" exclusivamente para verificação. No testador, pode-se confrontar o trabalho iniciando "MACD MQL4 style EA short.mq5", em modo visual, e colocando o cursor na barra com o índice #1:

"MACD MQL4 style EA short.mh5" in tester

Fig. 1. "MACD MQL4 style EA short.mh5" in tester

1.1. Sutilezas ao trabalhar com "IndicatorsXXXX.mqh"


Processamento de erros no valor de retorno

Todos os indicadores enviam seus dados como double. Este é o problema de enviar uma mensagem ao usuário quando não é possível obter os dados do indicador. Esta situação pode se apresentar quando recusada a criação do indicador (por exemplo, ao definir um símbolo inexistente) ou surgido um erro de cópia ao chamar CopyBuffer.

Em caso de erro, simplesmente enviar "0.0" não é uma boa opção, porque para muitos indicadores "0.0" é um valor bastante comum (por exemplo, no caso do MACD). Retornar a constante EMPTY_VALUE (que, aliás, tem o valor DBL_MAX) também não é uma opção, porque o indicador Fractals preenche os índices do buffer com os valores EMPTY_VALUE, o que significa que, para ele, isso não é um erro.

Resta apenas a hipótese de enviar "Not a Number", isto é, NaN. Para fazer isso, a nível global, é declarada a variável "NaN" que, justamente, é inicializada por um "Not a Number":

double NaN=double("nan");
//+------------------------------------------------------------------+
//| iAC function in MQL4 notation                                    |
//+------------------------------------------------------------------+
double   iAC(
             string                       symbol,              // symbol name 
             ENUM_TIMEFRAMES              timeframe,           // timeframe 
             int                          shift                // shift
             )
  {
   double result=NaN;
//---
   int handle=iAC(symbol,timeframe);
   if(handle==INVALID_HANDLE)
     {
      Print(__FUNCTION__,": INVALID_HANDLE error=",GetLastError());
      return(result);
     }
   double val[1];
   int copied=CopyBuffer(handle,0,shift,1,val);
   if(copied>0)
      result=val[0];
   else
      Print(__FUNCTION__,": CopyBuffer error=",GetLastError());
   return(result);
  }

A vantagem desta abordagem é que, em caso de erro, é retornada NaN, enquanto false é o resultado da comparação desta variável com qualquer número.

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- exemplo comparativo de NaN
   double NaN=double("nan");
   double a=10.3;
   double b=-5;
   double otherNaN=double("nan");
   Print("NaN>10.3=",NaN>a);
   Print("NaN<-5=",NaN<b);
   Print("(NaN==0)=",NaN==0);
   Print("(NaN==NaN)=",NaN==otherNaN);
//--- resultado
   NaN>10.3=false
   NaN<-5=false
   (NaN==0)=false
   (NaN==NaN)=false
//---
  }

Portanto, se quisermos usar essas funções em estilo MQL4, é necessário realizar operações de negociação, somente se o resultado da comparação é true. No entanto, neste caso, eu, pessoalmente, insisto em verificar o valor de retorno usando a função MathIsValidNumber

Identificadores de linhas do indicadores em MQL4 e MQL5

Há um problema de compatibilidade em termos de constantes que descrevem a linha do indicador. Por exemplo, peguemos o iAlligator:

  • MQL4: 1 - MODE_GATORJAW, 2 - MODE_GATORTEETH, 3 - MODE_GATORLIPS
  • MQL5: 0 - GATORJAW_LINE,   1 - GATORTEETH_LINE,   2 - GATORLIPS_LINE 

No final das contas, o problema é que, na função "IndicatorsXXXX.mqh", a linha do indicador vem como um número. E, por exemplo, quando este número é igual a 1, não se pode saber se o usuário esteve trabalhando em estilo MQL4 (e se referia a 1 - MODE_GATORJAW) ou se esteve trabalhando em estilo MQL5 (e se referia a uma linha completamente diferente do indicador 1 - GATORTEETH_LINE).

Devido a isso, foi tomada a decisão de criar dois arquivos anexados quase gêmeos: "IndicatorsMQL4.mqh" e "IndicatorsMQL5.mqh". A diferença entre eles é que o arquivo "IndicatorsMQL4.mqh" compreende as linas dos indicadores APENAS em estilo MQL4, enquanto o arquivo "IndicatorsMQL5.mqh" - APENAS em estilo MQL5. Sendo que, no indicador "IndicatorsMQL4.mqh", a conversão da linha do indicador, no parâmetro de entrada, é realizada diretamente nas funções iADX, iAlligator, quer dizer, é impossível realizar estas conversões em #define.

Passo a explicar o porquê de tal proibição pegando como exemplos o iBands e iEnvelopes:

//+------------------------------------------------------------------+
//| iBands function in MQL4 notation                                 |
//|   The buffer numbers are the following:                          |
//|      MQL4 0 - MODE_MAIN, 1 - MODE_UPPER, 2 - MODE_LOWER          |
//|      MQL5 0 - BASE_LINE, 1 - UPPER_BAND, 2 - LOWER_BAND          |
//+------------------------------------------------------------------+
double   iBands(
...
//+------------------------------------------------------------------+
//| iEnvelopes function in MQL4 notation                             |
//|   The buffer numbers are the following:                          |
//|      MQL4 0 - MODE_MAIN,  1 - MODE_UPPER, 2 - MODE_LOWER         | 
//|      MQL5 0 - UPPER_LINE, 1 - LOWER_LINE,        -/-             |
//+------------------------------------------------------------------+
double   iEnvelopes(

Em MQL4, MODE_UPPER para o indicador Bands é convertido em 1, enquanto para o indicador Envelopes - em 0.

2. O que acontece com o consumo de memória quando, em cada tick, trabalhamos com indicadores em estilo MQL4?

Comparemos o consumo de memória de dois experts: "iMACD.mq5" é um EA com acesso a indicadores, enquanto o "MACD MQL4 style EA short.mq5" é um EA com acesso a indicadores em estilo MQL4. Nas configurações do terminal, o número máximo de barras, na janela, está definido como "100 000". Criamos dois perfis de 14 gráficos:

  • perfil do "iMACd" - o EA "iMACd.mq5" é anexado a 13 gráficos, cada um deles tem o timeframe М30;
  • perfil do "MACD MQL4 style EA short" - o EA "MACD MQL4 style EA short.mq5" é anexado a 13 gráficos.

No gráfico catorze, estará o indicador "Terminal memory used.mq5". A cada 10 segundos, ele imprime o indicador TERMINAL_MEMORY_USED.

Vamos comparar dois valores, isto é, a quantidade de memória (RAM) usada pelo terminal (dados do gerenciador de tarefas) e o identificador impresso TERMINAL_MEMORY_USED. O controle durará 10 minutos, se houver consumo excessivo de memória, veremos isso. A principal condição é que, após executar o terminal, não se deve mexer em nada: nem abrir novas abas, nem ler o bate-papo.

Perfil Gerenciador de Tarefas TERMINAL_MEMORY_USED Gerenciador de Tarefas (após 10 minutos) TERMINAL_MEMORY_USED (após 10 minutos)
iMACd 279.7 MB 745 MB 279.7 MB 745 MB
MACD MQL4 style EA short 279.9 MB 745 MB 280.0 MB 745 MB

Agora, modificamos o teste: após 10 minutos, mudamos o timeframe dos 13 gráficos para o timeframe H1.

Perfil Gerenciador de Tarefas TERMINAL_MEMORY_USED Gerenciador de Tarefas (após 10 minutos) TERMINAL_MEMORY_USED (após 10 minutos)
iMACd 398.0 MB 869 MB 398.3 MB 869 MB
MACD MQL4 style EA short 319.2 MB 874 MB 330.5 MB 874 MB

Tabela de resultados para maior clareza sobre o consumo de memória:

Perfil Gerenciador de tarefas
(M30), Mb
TERMINAL_MEMORY_USED
(M30), Mb
Gerenciador de tarefas
(H1), Mb
TERMINAL_MEMORY_USED
(H1), Mb

início após 10 min. início após 10 min. início após 10 min. início após 10 min.
iMACd 279.7 279.7 745 745 398.0 869 398.3 869
MACD MQL4 style EA short 279.9 280.0 745 745 319.2 874 330.5 874

3. Nova vida do EA MACD Sample.mq4

Verificamos a velocidade de execução, o consumo de memória e os correspondentes trades do EA [data folder]\MQL4\Experts\MACD Sample.mq4 (que escrevemos em MQL5, mas em estilo MQL4, isto é, como "MACD MQL4 style EA short.mq5") e o EA [data folder]\MQL5\Experts\Examples\MACD\MACD Sample.mq5.

3.1. Alteramos o EA "MACD Sample.mq5", quer dizer, só vamos receber um valor de cada vez

"MACD Sample.mq5" da distribuição padrão recebe imediatamente dois valores do indicador:

//+------------------------------------------------------------------+
//| main function returns true if any position processed             |
//+------------------------------------------------------------------+
bool CSampleExpert::Processing(void)
  {
//--- refresh rates
   if(!m_symbol.RefreshRates())
      return(false);
//--- refresh indicators
   if(BarsCalculated(m_handle_macd)<2 || BarsCalculated(m_handle_ema)<2)
      return(false);
   if(CopyBuffer(m_handle_macd,0,0,2,m_buff_MACD_main)  !=2 ||
      CopyBuffer(m_handle_macd,1,0,2,m_buff_MACD_signal)!=2 ||
      CopyBuffer(m_handle_ema,0,0,2,m_buff_EMA)         !=2)
      return(false);
//   m_indicators.Refresh();
//--- to simplify the coding and speed up access
//--- data are put into internal variables
   m_macd_current   =m_buff_MACD_main[0];
   m_macd_previous  =m_buff_MACD_main[1];
   m_signal_current =m_buff_MACD_signal[0];
   m_signal_previous=m_buff_MACD_signal[1];
   m_ema_current    =m_buff_EMA[0];
   m_ema_previous   =m_buff_EMA[1];

Após isto, à variável são atribuídos os dados de matrizes de dimensão "2". Por que é feito precisamente dessa maneria? É óbvio que, ao copiar pelo menos um ou dois valores de cada vez, nós ainda vamos usar CopyBuffer. Mas, ao copiar dois valores de vez, é poupada uma operação de gravação na matriz.

No entanto, o EA "MACD Sample.mq4" recebe, de cada vez, um valor do indicador:

//--- to simplify the coding and speed up access data are put into internal variables
   MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0);
   MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1);
   SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0);
   SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1);
   MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
   MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);

Dois vezes é consultada a linha principal do MACD, dois vezes - a linha de sinal do MACD e dois vezes - Moving Average. Por isso, o EA "MACD Sample.mq5" deve ter a mesma aparência. Chamamos esta versão do expert de "MACD Sample One value at a time.mq5". Veja as alterações em que obtemos um valor de cada vez:

//--- refresh indicators
   if(BarsCalculated(m_handle_macd)<2 || BarsCalculated(m_handle_ema)<2)
      return(false);
//   if(CopyBuffer(m_handle_macd,0,0,2,m_buff_MACD_main)  !=2 ||
//      CopyBuffer(m_handle_macd,1,0,2,m_buff_MACD_signal)!=2 ||
//      CopyBuffer(m_handle_ema,0,0,2,m_buff_EMA)         !=2)
//      return(false);
//   m_indicators.Refresh();
//--- to simplify the coding and speed up access
//--- data are put into internal variables
   CopyBuffer(m_handle_macd,0,0,1,m_buff_MACD_main);
   m_macd_current=m_buff_MACD_main[0];
   CopyBuffer(m_handle_macd,0,1,1,m_buff_MACD_main);
   m_macd_previous=m_buff_MACD_main[0];
   CopyBuffer(m_handle_macd,1,0,1,m_buff_MACD_signal);
   m_signal_current=m_buff_MACD_signal[0];
   CopyBuffer(m_handle_macd,1,1,1,m_buff_MACD_signal);
   m_signal_previous=m_buff_MACD_signal[0];
   CopyBuffer(m_handle_ema,0,0,1,m_buff_EMA);
   m_ema_current=m_buff_EMA[0];
   CopyBuffer(m_handle_ema,0,1,1,m_buff_EMA);
   m_ema_previous=m_buff_EMA[0];

Este código é armazenado no EA "MACD Sample One value at a time.mq5", anexado ao artigo.

3.2. Convertemos o expert "MACD Sample.mq4" em código MQL5

Para que, no expert advisor, seja possível usar os indicadores em estilo MQL4, bem como trabalhar com posições e operar, anexamos o arquivo "IndicatorsMQL4.mqh" (lembre que este arquivo, ao trabalhar com indicadores, compreende apenas os nomes das linhas de indicador MQL4) e as classes de negociação CPositionInfoCTradeCSymbolInfo e CAccountInfo. Além disso, para acessar corretamente o "IndicatorsMQL4.mqh", ao EA é necessário adicionar o bloco dos defin, isto é, os nomes das linas de indicador:

#property description " and the indicators are accessed in the style of MQL4"
#define MODE_MAIN    0
#define MODE_SIGNAL  1 
#include <SimpleCall\IndicatorsMQL4.mqh>
//---
#include <Trade\PositionInfo.mqh>
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>  
#include <Trade\AccountInfo.mqh>
CPositionInfo  m_position;                   // trade position object
CTrade         m_trade;                      // trading object
CSymbolInfo    m_symbol;                     // symbol info object
CAccountInfo   m_account;                    // account info wrapper
//---
input double TakeProfit    =50;

Adicionalmente, para construir cotações de três ou cinco dígitos, é necessário um multiplicador especial:

input double MACDCloseLevel=2;
input int    MATrendPeriod =26;
//---
double       m_adjusted_point;               // point value adjusted for 3 or 5 points
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

Para obter os preços atuais, eu uso o objeto m_symbol da classe de negociação CSymbolInfo:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(!m_symbol.Name(Symbol())) // sets symbol name
      return(INIT_FAILED);
   RefreshRates();

O método RefreshRates() atualiza os preços e garante que não haja preços com valores "0.0":

//+------------------------------------------------------------------+
//| Refreshes the symbol quotes data                                 |
//+------------------------------------------------------------------+
bool RefreshRates(void)
  {
//--- refresh rates
   if(!m_symbol.RefreshRates())
     {
      Print("RefreshRates error");
      return(false);
     }
//--- protection against the return value of "zero"
   if(m_symbol.Ask()==0 || m_symbol.Bid()==0)
      return(false);
//---
   return(true);
  }

A inicialização do multiplicador m_adjusted_point é realizada na OnInit(), após a inicialização do objeto m_symbol:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(!m_symbol.Name(Symbol())) // sets symbol name
      return(INIT_FAILED);
   RefreshRates();
   //--- tuning for 3 or 5 digits
   int digits_adjust=1;
   if(m_symbol.Digits()==3 || m_symbol.Digits()==5)
      digits_adjust=10;
   m_adjusted_point=m_symbol.Point()*digits_adjust;
//---
   return(INIT_SUCCEEDED);
  }

Em OnTick(), graças ao arquivo anexado "IndicatorsMQL4Style.mqh", acessamos o arquivo em estilo MQL4:

   if(!RefreshRates())
      return;
//--- to simplify the coding and speed up access data are put into internal variables
   MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MAIN_LINE,0);
   MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MAIN_LINE,1);
   SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,SIGNAL_LINE,0);
   SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,SIGNAL_LINE,1);
   MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
   MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);

3.2.1. Trabalhando com posições

Para obter um resultado adequado, definimos a ausência de posições como

   total=PositionsTotal();
   if(total<1)
     {

Porém, esta abordagem não é inteiramente correta, porque não leva em conta a presença de posições em outros símbolos e/ou com outros identificadores (mágicas).

3.2.2. As posições Buy são abertas com ajuda do método Buy da classe de negociação CTrade, e com o método ResultDeal desta classe verificamos que seja corretamente executada. ResultDeal retorna o bilhete da transação, se ela for feita.

      //--- check for long position (BUY) possibility
      if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious && 
         MathAbs(MacdCurrent)>(MACDOpenLevel*m_adjusted_point) && MaCurrent>MaPrevious)
        {
         m_trade.Buy(Lots,m_symbol.Name(),m_symbol.Ask(),
                     0.0,
                     m_symbol.NormalizePrice(m_symbol.Ask()+TakeProfit*m_adjusted_point),
                     "macd sample");
         if(m_trade.ResultDeal()!=0)
            Print("BUY position opened : ",m_trade.ResultPrice());
         else
            Print("Error opening BUY position : ",m_trade.ResultRetcodeDescription());
         return;
        }

Repare que o preço, no pedido de negociação, é normalizado pelo método NormalizePrice da classe de negociação CSymbolInfo. Este método permite levar em conta a quantização, isto é, a alteração mínima no preço e o número de dígitos depois do ponto decimal. 

Para abertura da posição Sell, utilizamos métodos semelhantes.

3.2.3. Bloco de rastreamento de posições: fechamento ou modificação.

O ciclo em si ocorre entre o número total de posições menos um e zero. Para poder trabalhar com as posições, é preciso, primeiro, selecionar uma de acordo com o índice na lista geral:

   for(int i=PositionsTotal()-1;i>=0;i--)
      if(m_position.SelectByIndex(i)) // selects the position by index for further access to its properties

Fechamento de posição ocorre por meio do método PositionClose, enquanto a modificação através do PositionModify. Repare que, a cada modificação, novamente é utilizado o método de normalização de preços NormalizePrice da classe de negociação CSymbolInfo.

Bloco inteiro de rastreamento de posições:

//--- it is important to enter the market correctly, but it is more important to exit it correctly...   
   for(int i=PositionsTotal()-1;i>=0;i--)
      if(m_position.SelectByIndex(i)) // selects the position by index for further access to its properties
         if(m_position.Symbol()==m_symbol.Name())
           {
            //--- long position is opened
            if(m_position.PositionType()==POSITION_TYPE_BUY)
              {
               //--- should it be closed?
               if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && 
                  MacdCurrent>(MACDCloseLevel*m_adjusted_point))
                 {
                  //--- close position and exit
                  if(!m_trade.PositionClose(m_position.Ticket()))
                     Print("PositionClose error ",m_trade.ResultRetcodeDescription());
                  return;
                 }
               //--- check for trailing stop
               if(TrailingStop>0)
                 {
                  if(m_position.PriceCurrent()-m_position.PriceOpen()>m_adjusted_point*TrailingStop)
                    {
                     if(m_position.StopLoss()<m_symbol.Bid()-m_adjusted_point*TrailingStop)
                       {
                        //--- modify position and exit
                        if(!m_trade.PositionModify(m_position.Ticket(),
                           m_symbol.NormalizePrice(m_position.PriceCurrent()-m_adjusted_point*TrailingStop),
                           m_position.TakeProfit()))
                           Print("PositionModify error ",m_trade.ResultRetcodeDescription());
                        return;
                       }
                    }
                 }
              }

            if(m_position.PositionType()==POSITION_TYPE_SELL)
              {
               //--- should it be closed?
               if(MacdCurrent<0 && MacdCurrent>SignalCurrent && 
                  MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*m_adjusted_point))
                 {
                  //--- close position and exit
                  if(!m_trade.PositionClose(m_position.Ticket()))
                     Print("PositionClose error ",m_trade.ResultRetcodeDescription());
                  return;
                 }
               //--- check for trailing stop
               if(TrailingStop>0)
                 {
                  if((m_position.PriceOpen()-m_position.PriceCurrent())>(m_adjusted_point*TrailingStop))
                    {
                     if((m_position.StopLoss()>(m_symbol.Ask()+m_adjusted_point*TrailingStop)) || (m_position.StopLoss()==0.0))
                       {
                        //--- modify position and exit
                        if(!m_trade.PositionModify(m_position.Ticket(),
                           m_symbol.NormalizePrice(m_symbol.Ask()+m_adjusted_point*TrailingStop),
                           m_position.TakeProfit()))
                           Print("PositionModify error ",m_trade.ResultRetcodeDescription());
                        return;
                       }
                    }
                 }
              }
           }

Isso tudo são alterações, o arquivo final "MACD Sample 4 to 5 MQL4 style.mq5" se encontra anexado no final deste artigo.

3.3. Comparamos a velocidade de execução de EAs com base no MACD

Na comparação, participarão:

  • "MACD Sample.mq5" - EA da distribuição padrão com acesso correto aos indicadores
  • "MACD Sample One value at a time.mq5" - análogo do "MACD Sample.mq5", em que, de cada vez, obtemos um valor dos indicadores
  • "MACD Sample 4 to 5 MQL4 style.mq5" - EA MQL4, reescrito em MQL5 com alterações mínimas e com acesso aos indicadores em estilo MQL4

O teste foi realizado em USDJPY, M30, de 2017.02.01 a 2018.01.16, no servidor MetaQuotes-Demo. Após cada teste (alteração quer do EA quer do modo de geração de ticks), o terminal era reinicializado. Configuração do computador:

Windows 10 (build 16299) x64, IE 11, UAC, Intel Core i3-3120M  @ 2.50GHz, Memory: 4217 / 8077 Mb, Disk: 335 / 464 Gb, GMT+2

№ p/p Expert Advisor Cada tick baseado em ticks reais Todos os ticks OHLC


Hora do teste Trades Transações Hora do teste Trades Transações Hora do teste Trades Transações
 1  MACD Sample.mq5  0:01:19.485  122  244  0:00:53.750  122  244  0:00:03.735  119  238
 2  MACD Sample One value at a time.mq5  0:01:20.344  122  244  0:00:56.297  122  244  0:00:03.687  119  238
 3  MACD Sample 4 to 5 MQL4 style.mq5  0:02:37.422  122  244  0:01:52.171  122  244  0:00:06.312  119  238

Em modo "Todos os ticks", os três experts têm demonstrado os mesmos gráficos:

MACD Sample

Fig. 2. MACD Sample XXXX no testador de estratégias


CONCLUSÃO: o EA "MACD Sample 4 to 5 MQL4 style.mq5", com acesso aos indicadores em estilo MQL4 é duas vezes mais lento do que EAs semelhantes com acesso correto a indicadores.

3.4. Comparemos o consumo de memória de EAs com base no MACD

Para isso, são usados os mesmos 14 gráficos, como no ponto 2. O que acontecerá com o consumo de memória quando, em cada tick, trabalhemos com indicadores em estilo MQL4? No primeiro gráfico, mantem-se o indicador "Terminal memory used.mq5". Ele, a cada 10 segundos, imprime o identificador TERMINAL_MEMORY_USED, enquanto EAs são fixados aos 13 restantes, por turno. Antes de cada medição, o indicador era reinicializado.

№ p/p Expert Advisor Gerenciador de tarefas, MB TERMINAL_MEMORY_USED, MB
 1  MACD Sample.mq5  334.6  813
 2  MACD Sample One value at a time.mq5  335.8  813
 3  MACD Sample 4 to 5 MQL4 style.mq5  342.2  818

CONCLUSÃO: Quanto ao consumo de memória, o EA baseado no MACD com acesso correto aos indicadores é comparável ao EA baseado no MACD com acesso ao indicador em estilo MQL4. Ou seja, eles consomem quase a mesma quantidade de memória.


4. Nova vida do EA [data folder]\MQL4\Experts\Moving Average.mq4

Como, no capítulo 3, nós convertemos MQL4 em MQL5, eu proponho, no caso com Movinge Average.mq4, simplesmente alterar o EA Moving Average.mq5 mediante conexão do arquivo "IndicatorsMQL5.mqh"

#property version   "1.00"
#include <SimpleCall\IndicatorsMQL5.mqh>

#include <Trade\Trade.mqh>

e substituição de CopyBuffer

//--- get current Moving Average 
   double   ma[1];
   if(CopyBuffer(ExtHandle,0,0,1,ma)!=1)
     {
      Print("CopyBuffer from iMA failed, no data");
      return;
     }

pelo estilo MQL4 de acesso aos indicadores:

//--- get Moving Average 
   ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);

O uso dos indicadores em estilo MQL4 nos deixa com apenas uma chance para testar o resultado da operação, isto é, comparar os dados com zero. Com isto em mente, a entrada final nos blocos "CheckForOpen" e "CheckForClose" era como segue:

//--- get current Moving Average 
   double   ma[1];
   if(CopyBuffer(ExtHandle,0,0,1,ma)!=1)
     {
      Print("CopyBuffer from iMA failed, no data");
      return;
     }

e será a seguinte:

//--- get current Moving Average 
   double   ma[1];
   ma[0]=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);
//if(CopyBuffer(ExtHandle,0,0,1,ma)!=1)
   if(ma[0]==0.0)
     {
      //Print("CopyBuffer from iMA failed, no data");
      Print("Get iMA in MQL4 style failed, no data");
      return;
     }

Estas são todas as mudanças que conservamos no EA "Moving Average MQL4 style.mq5". Advisor é anexado no final do artigo. Medimos da produtividade e consumo de memória entre o "Moving Average.mq5" convencional e o "Moving Average MQL4 style.mq5". 

Lembre que os testes foram realizados em equipamentos

Windows 10 (build 16299) x64, IE 11, UAC, Intel Core i3-3120M  @ 2.50GHz, Memory: 4217 / 8077 Mb, Disk: 335 / 464 Gb, GMT+2

e, após cada teste, o terminal foi reinicializado. Testamos em EURUSD, M15 de 2017.02.01 a 2018.01.16, no servidor MetaQuotes-Demo.

№ p/p Expert Advisor Cada tick baseado em ticks reais Todos os ticks OHLC


Hora do teste Trades Transações Hora do teste Trades Transações Hora do teste Trades Transações
 1  Moving Average.mq5  0:00:33.359  1135  2270  0:00:22.562  1114  2228  0:00:02.531  1114  2228
 2  Moving Average MQL4 style.mq5  0:00:34.984  1135  2270  0:00:23.750  1114  2228  0:00:02.578  1114  2228

CONCLUSÃO: é provável que, no MACD Sample, ao acessar os indicadores em estilo MQL4, o kernel da MQL5 deva ter procurado entre dois identificadores, perdendo tempo.

No caso do EA Moving Average, ao usar o indicador em estilo MQL4, o kernel da MQL5 não perde tempo procurando o ID necessário, porque ele é o único. 

Comparamos o consumo de memória de EAs com base no Moving Average

Para isso, são usados os mesmos 14 gráficos, como no ponto 2. No primeiro gráfico, mantem-se o indicador "Terminal memory used.mq5". Ele, a cada 10 segundos, imprime o identificador TERMINAL_MEMORY_USED, enquanto EAs são fixados aos 13 restantes, por turno. Antes de cada medição, o indicador era reinicializado.

№ p/p Expert Advisor Gerenciador de tarefas, MB TERMINAL_MEMORY_USED, MB
 1  Moving Average.mq5  295.6  771
 2  Moving Average MQL4 style.mq5  283.6  760

CONCLUSÃO: o consumo de memória é quase idêntico. Pequenas diferenças podem ser atribuídas à "vida interior" do terminal: atualizações de notícias, etc.


5. Análogos de séries iXXXX

Como fizemos a recepção dos valores dos indicadores em estilo MQL4, neste mesmo momento, escrevemos a função de seção Acesso a TimeSeries e indicadores. A realização será em [data folder]\MQL5\Include\SimpleCall\Series.mqh.

Lista de funções em "Series.mqh" que fornecem acesso aos valores do TimeSeries como em MQL4:


Para as funções iHighest e iLowest, estão disponíveis os identificadores predefinidos de séries MODE_OPEN, MODE_LOW, MODE_HIGH, MODE_CLOSE, MODE_VOLUME, MODE_TIME.

Exemplo de realização da função iClose:

//+------------------------------------------------------------------+
//| iClose function in MQL4 notation                                 |
//+------------------------------------------------------------------+
double   iClose(
                string                    symbol,              // symbol
                ENUM_TIMEFRAMES           timeframe,           // timeframe
                int                       shift                // shift
                )
  {
   double result=0.0;
//---
   double val[1];
   ResetLastError();
   int copied=CopyClose(symbol,timeframe,shift,1,val);
   if(copied>0)
      result=val[0];
   else
      Print(__FUNCTION__,": CopyClose error=",GetLastError());
//---
   return(result);
  }

Obtemos o valor do preço de fechamento da barra shift com ajuda de CopyClose, isto é, a primeira forma da chamada (tratamento segundo a primeira posição e número de elemento requeridos):

int  CopyClose( 
   string           symbol_name,       // nome do símbolo 
   ENUM_TIMEFRAMES  timeframe,         // período 
   int              start_pos,         // por onde começamos  
   int              count,             // quanto copiamos 
   double           close_array[]      // matriz para copiar preços de fechamento 
   );


Fim do artigo

Como podemos ver, MQL5 permite os adeptos da MQL4 receberem valores dos indicadores e timeseries em seu estilo favorito. Eles dizem que um código desse tipo é mais curto e mais fácil de ler. Os desenvolvedores da plataforma, por sua vez, exigem um trabalho mais cuidadoso com o código e o máximo de verificações ao chamar as funções (e eu concordo plenamente com eles). Enumeremos brevemente os prós e contras das funções discutidas neste artigo.


Contras:
  • restrição no processamento do erro de retorno ao acessar o indicador;
  • queda da velocidade de teste ao acessar simultaneamente mais de um indicador;
  • necessidade de especificar corretamente a linha de indicadores dependendo da conexão "IndicatorsMQL5.mqh" ou "IndicatorsMQL4.mqh".
Prós
  • simplicidade na escrita do código, isto é, uma linha em vez de várias;
  • clareza e concisão, ou seja, quanto menos código, é mais fácil de entendê-lo.
No entanto, eu continuo um defensor da abordagem MQL5 clássica no acesso aos indicadores, e, neste artigo, só foi testada a opção alternativa.

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

Arquivos anexados |
MQL5.zip (26.02 KB)
O padrão Rompimento de Canal O padrão Rompimento de Canal
As tendências de preços formam canais de preços que podem ser observados nos gráficos dos instrumentos financeiros. O rompimento do canal atual é um forte sinal de reversão de tendência. Neste artigo, eu sugiro uma maneira de automatizar o processo de encontrar esses sinais e ver se o padrão de rompimento de canal pode ser usado para criar uma estratégia de negociação.
Geração automática de linhas de suporte e resistência Geração automática de linhas de suporte e resistência
O artigo fala sobre a construção automática de linhas de suporte e resistência atravessando máximos e mínimos locais, nos gráficos de preços. Para determinar estes extremos, é aplicado o popularmente conhecido ZigZag.
Gerenciamento de capital de Vince. Realização como módulo de Assistente MQL5 Gerenciamento de capital de Vince. Realização como módulo de Assistente MQL5
O artigo foi escrito com base no livro de Ralph Vince, “The Mathematics of Money Management”. Nele, são discutidos os métodos empíricos e paramétricos, a fim de encontrar o tamanho ideal de lotes de negociação, em cuja base estão escritos os módulos de gerenciamento de capital para o assistente MLQ5.
Testador de estratégia personalizada com base em cálculos matemáticos rápido Testador de estratégia personalizada com base em cálculos matemáticos rápido
O artigo descreve como criar um testador de estratégias personalizado e um analisador de corridas de otimização próprio. Depois de lê-lo, você vai entender como funciona o modo de cálculos matemáticos e o mecanismo de quadros, como preparar e fazer upload de seus próprios dados para cálculos e usar algoritmos eficientes para comprimi-los. Além disso, este artigo será de interesse para quem deseje saber maneiras de armazenar informações personalizadas num EA.