Comparamos a velocidade de indicadores de armazenamento automático em cache

24 abril 2018, 10:13
Vladimir Karputov
0
688

Introdução

Vamos supor que nos cansamos do acesso MQL5 clássico a indicadores e queremos comparar a velocidade de acesso em relação com certas alternativas. Por exemplo, queremos compará-lo com o acesso - em estilo MQL4 - a indicadores, sem armazenamento em cache e com armazenamento em cache. As ideias sobre o acesso em estilo MQL4 foram retiradas do artigo LifeHack para traders: preparemos "fast-food" de indicadores e complementadas.


Exploremos a numeração MQL5 de identificadores de indicadores

Existe a suposição de que a numeração dos identificadores do indicador no terminal é de ponta a ponta e começa do zero. Para testar esta hipótese, criaremos um pequeno EA - "iMACD and IndicatorRelease.mq5" - que criará alguns identificadores de indicadores e, em seguida, irá imprimi-los, enquanto, na função OnTick(), acessará estes indicadores:

//+------------------------------------------------------------------+
//|                                   iMACD and IndicatorRelease.mq5 |
//|                              Copyright © 2018, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2018, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.003"
//--- input parameter
input int   count=6;   // Count MACD indicators

int    handles_array[]; // array for storing the handles of the iMACD indicators
//+------------------------------------------------------------------+
//| Expert initialization function                                     |
//+------------------------------------------------------------------+
int OnInit()
  {
   int array_resize=ArrayResize(handles_array,count);
   if(array_resize==-1)
     {
      Print("ArrayResize error# ",GetLastError());
      return(INIT_FAILED);
     }
   if(array_resize!=count)
     {
      Print("ArrayResize != \"Count MACD indicators\"");
      return(INIT_FAILED);
     }
   ArrayInitialize(handles_array,0);
   for(int i=0;i<count;i++)
     {
      handles_array[i]=CreateHandleMACD(12+i);
      //--- if the handle is not created 
      if(handles_array[i]==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);
        }
      Print("ChartID: ",ChartID(),": ",Symbol(),",",StringSubstr(EnumToString(Period()),7),
            ", create handle iMACD (",handles_array[i],")");
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   Comment("");
   for(int i=0;i<count;i++)
     {
      Print("ChartID: ",ChartID(),": ",Symbol(),",",StringSubstr(EnumToString(Period()),7),
            ", remove handle iMACD (",handles_array[i],"): ",IndicatorRelease(handles_array[i]));
     }
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   string text="";
   for(int i=0;i<count;i++)
     {
      double macd_main_1=iMACDGet(handles_array[i],MAIN_LINE,1);
      if(i<15)
        {
         text+="\n"+"ChartID: "+IntegerToString(ChartID())+": "+Symbol()+
               ", MACD#"+IntegerToString(i)+" "+DoubleToString(macd_main_1,Digits()+1);
         Comment(text);
        }
      else if(i==15)
        {
         text+="\n"+"only the first 15 indicators are displayed ...";
         Comment(text);
        }
     }
  }
//+------------------------------------------------------------------+
//| Get value of buffers for the iMACD                               |
//|  the buffer numbers are the following:                           |
//|   0 - MAIN_LINE, 1 - SIGNAL_LINE                                 |
//+------------------------------------------------------------------+
double iMACDGet(const int handle_iMACD,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]);
  }
//+------------------------------------------------------------------+
//| Create handle MACD                                               |
//+------------------------------------------------------------------+
int CreateHandleMACD(const int fast_ema_period)
  {
//--- create handle of the indicator iMACD
   return(iMACD(Symbol(),Period(),fast_ema_period,52,9,PRICE_CLOSE));
  }
//+------------------------------------------------------------------+

Experimento 1

Dados de origem: no terminal, são abertos os gráficos AUDJPY M15, USDJPY M15 e EURUSD M15 - neles não há indicadores ou EAs. Parâmetro Count MACD indicators do EA iMACD and IndicatorRelease.mq5 igual a 6.

Imediatamente após reiniciar o terminal, anexamos o EA iMACD and IndicatorRelease.mq5 ao primeiro gráfico AUDJPY, M15 (ChartID 131571247244850509):

2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (11)
2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (12)
2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (13)
2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (14)
2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (15)

Nós vemos que a numeração dos identificadores não começa com 0, mas sim com 10.

Experimento 2

Dados de origem: ao primeiro gráfico (AUDJPY M15) é anexado o EA iMACD and IndicatorRelease.mq5, parâmetro Count MACD indicatorsigual a 6.

Anexamos o EA iMACD and IndicatorRelease.mq5 ao segundo gráfico USDJPY, M15 (ChartID 131571247244850510):

2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (10)
2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (11)
2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (12)
2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (13)
2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (14)
2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (15)

Nós vemos que a numeração dos identificadores no gráfico (USDJPY M15) também não começa com 0, mas sim com 10.

Conclusão: a numeração - dada ao usuário - dos identificadores de indicadores no terminal NÃO é de ponta a ponta e NÃO começa do zero.

Experimento 3

Dois gráficos idênticos AUDJPY, M15 (ChartID 131571247244850509) e AUDJPY, M15 (ChartID 131571247244850510). Para cada EA iMACD and IndicatorRelease.mq5 em anexo, com parâmetro Count MACD indicators igual a 6.

A numeração de ponta a ponta dos identificadores de indicadores criados confirma que a MQL5 mantém seu registro interno para eles (um contador para cada identificador). Para ter certeza disso, comentamos o incremento de período:

int OnInit()
  {
***
   ArrayInitialize(handles_array,0);
   for(int i=0;i<count;i++)
     {
      handles_array[i]=CreateHandleMACD(12/*+i*/);
      //--- if the handle is not created 

Desse modo, tentamos criar vários identificadores do indicador MACD, com exatamente as mesmas configurações.

Removemos dos gráficos os EAs que ficaram após os experimentos № 1 e 2 e, em seguida, anexamos o EA iMACD and IndicatorRelease.mq5 ao primeiro gráfico AUDJPY, M15 (ChartID 131571247244850509):

2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)

Vemos que em resposta à criação de indicadores totalmente idênticos, foi retornado o mesmo identificador.

Anexamos o EA iMACD and IndicatorRelease.mq5 (também com comentário do incremento de período) ao segundo gráfico AUDJPY,M15 (ChartID 131571247244850510):

2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)

Mais uma vez, vemos que é retornado o mesmo identificador. No entanto, surge a questão: os identificadores "10" no primeiro e segundo gráficos são o mesmo ID ou dois diferentes? Para verificar isso, removemos o EA dos gráficos (lembre que o EA em OnDeinit() percorre a matriz de identificadores e remove cada um usando IndicatorRelease).

2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): true
2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): false

2018.02.18 07:53:36.116 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): true
2018.02.18 07:53:36.117 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:36.117 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:36.117 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:36.117 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:36.117 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): false

O resultado era esperado, se considerarmos a Execução de programas:

O EA é executado em seu próprio "thread", além disso, a quantidade de EAs é igual ao número de "threads" de execução para eles.

Quer dizer, se dois EAs nos mesmos gráficos (símbolo e timeframe idênticos) criarem indicadores com os mesmos parâmetros de entrada, a MQL5 em seu registro interno os identificará como dois identificadores diferentes.

Conclusão geral em relação à criação de indicadores em EAs

A numeração - dada ao usuário - dos identificadores de indicadores no terminal NÃO é de ponta a ponta e NÃO começa do zero, enquanto a MQL5 em seu registro interno de identificadores leva em consideração:

  • a função do indicador técnico (iMA, iAC, iMACD, iIchimoku, etc.);
  • os parâmetros de entrada do indicador;
  • o símbolo em que é criado o indicador;
  • o timeframe em que é criado o indicador;
  • o ChartID do gráfico em que funciona o EA.

Faz sentido a cache de identificadores?

Os dados iniciais (timeframe, símbolo, período de teste e tipo de geração de ticks) serão os seguintes:

Cache test Settings

Fig. 1. Configurações

Testes com acesso a indicadores em estilo MQL4 (com cache de identificadores e sem ele) são realizados com a ajuda do EA Cache test.mq5, enquanto os testes com acesso em estilo MQL5 - com a ajuda do EA MQL5 test.mq5:

//+------------------------------------------------------------------+
//|                                                    MQL5 test.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"
//--- input parameters
input bool     UseOneIndicator=false;  // Use indicator: "false" -> 9 indicators, "true" - 1 indicator
//---
int            arr_handle_iMACD[];     // array for storing the handles of the iMACD indicators
//+------------------------------------------------------------------+
//| Expert initialization function                                     |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(UseOneIndicator)
      ArrayResize(arr_handle_iMACD,1);
   else
      ArrayResize(arr_handle_iMACD,9);
   if(!CreateHandle(arr_handle_iMACD))
      return(INIT_FAILED);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   int arr_size=ArraySize(arr_handle_iMACD);
   for(int i=0;i<arr_size;i++)
     {
      double macd_main_30=iMACDGet(arr_handle_iMACD[i],MAIN_LINE,0);
     }
  }
//+------------------------------------------------------------------+
//| CreateHandle                                                     |
//+------------------------------------------------------------------+
bool CreateHandle(int &arr_handles[])
  {
   int arr_size=ArraySize(arr_handles);
   for(int i=0;i<arr_size;i++)
     {
      int fast_ema_repiod=30+10*i;
      //--- create handle of the indicator iMACD
      arr_handles[i]=iMACD(NULL,0,fast_ema_repiod,26,9,PRICE_CLOSE);
      //--- if the handle is not created 
      if(arr_handles[i]==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(false);
        }
     }
   return(true);
  }
//+------------------------------------------------------------------+
//| Get value of buffers for the iMACD                               |
//|  the buffer numbers are the following:                           |
//|   0 - MAIN_LINE, 1 - SIGNAL_LINE                                 |
//+------------------------------------------------------------------+
double iMACDGet(const int handle_iMACD,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]);
  }
//+------------------------------------------------------------------+

Parâmetro do EA MQL5 test.mq5:

MQL5 test 1

Fig. 2. "MQL5 test.mq5". Nove indicadores

Parâmetros do EA Cache test.mq5:

  • Use Timer ("0" -> off timer) — utilizar o temporizador (0 indica que não se usa o temporizador).
  • Use indicator ("false" -> 9 indicators, "true" - 1 indicator) — número de indicadores a enviar (1 ou 9).

Cache test 1

Fig. 3. "Cache test.mq5". Sem temporizador, com nove indicadores

Para medição do "estilo MQL4 sem cache de identificadores" são usados os arquivos IndicatorsMQL4.mq conectados com a ajuda de "SimpleCallMQL4.mqh (veja o artigo LifeHack para traders: "amassando" ForEach com os define (#define) ).

#include <SimpleCall\SimpleCallMQL4.mqh> // for tests without caching of the handles
//#include <SimpleCall\SimpleCallMQL4Caching.mqh> // for tests with caching of the handles
//#include <SimpleCall\SimpleCallString.mqh> // for tests with string

Para medição do "estilo MQL4 com cache de identificadores" no arquivo IndicatorsMQL4.mqh, foi adicionado o código de cache de código na publicação #113 (apenas para o MACD, as outras funções são excluídas). O arquivo é salvo com o novo nome IndicatorsMQL4Caching.mqh - ele é conectado usando o arquivo SimpleCallCaching.mqh:

//#include <SimpleCall\SimpleCallMQL4.mqh> // for tests without caching of the handles
#include <SimpleCall\SimpleCallMQL4Caching.mqh> // for tests with caching of the handles
//#include <SimpleCall\SimpleCallString.mqh> // for tests with string

Resultados da comparação dos estilos de acesso a nove indicadores (as configurações são dadas na Fig. 1):


Fig. 4. Tempo gasto no acesso a nove indicadores

Ao comparar os resultados, observe que o EA de teste tornou mais difícil a tarefa:

  • obtemos os dados simultaneamente a partir de NOVE indicadores;
  • acessamos os indicadores EM CADA tick;
  • timeframe M1 - neste caso, foram gerados 26 169 180 ticks e 370 355 barras.

É hora de realizar o teste em que é chamado apenas um indicador (em dois EAs: MQL5 test.mq5 e Cache test.mq5, valor do parâmetro Use indicator...  "true", o valor do parâmetro para o Cache test.mq5 é Use Timer "0")


Fig. 5. Tempo gasto no acesso a um indicador

Conclusão

Em comparação com o estilo MQL4 sem armazenamento em cache, o estilo MQL4 com cache de identificadores dá chance de ganhar - o estilo MQL4 perde para o estilo MQL5 de maneira contundente. 

Falta de controle da validade do identificador

Agora é preciso mencionar o enorme perigo de implementar o armazenamento em cache de identificadores, isto é, em nenhum lugar existe controle sobre a existência do identificador no cache personalizado, quer dizer, a remoção do identificador do indicador não é processada de forma nenhuma. 

Imagine que estamos trabalhando com indicadores em estilo MQL4 e armazenamos em cache os identificadores. Após a primeiro acesso ao EA:

   double macd_main_30=iMACD(NULL,0,30,26,9,PRICE_CLOSE,MODE_MAIN,0);

o identificador é armazenado no cache do usuário (pode ser uma matriz de estruturas ou uma matriz de cadeias de caracteres). Depois disso, todas as tentativas subsequentes de acesso a partir do EA

   double macd_main_30=iMACD(NULL,0,30,26,9,PRICE_CLOSE,MODE_MAIN,0);

não poderão passar para o kernel MQL5, em vez disso, serão retornados os valores do indicador, de acordo com o identificador retirado do cache. Agora, em OnTimer(), removemos o identificador - vamos supor que sabemos que é igual a "10". Para o teste, usamos o arquivo Cache test.mq5, ao qual deve ser conectado o arquivo SimpleCallMQL4Caching.mqh:

//#include <SimpleCall\SimpleCallMQL4.mqh> // for tests without caching of the handles
#include <SimpleCall\SimpleCallMQL4Caching.mqh> // for tests with caching of the handles
//#include <SimpleCall\SimpleCallString.mqh> // for tests with string

Temos que colocar o temporizador (neste exemplo, o temporizador é de seis segundos, temos acesso a um indicador)

Cache test 2

Fig. 6 Configuração do teste com remoção do identificador

Após a primeira entrada da OnTimer()

OnTimer, IndicatorRelease(10)=true
iMACD: CopyBuffer error=4807
iMACD: CopyBuffer error=4807
iMACD: CopyBuffer error=4807
iMACD: CopyBuffer error=4807 

obtemos o erro 4807:

 ERR_INDICATOR_WRONG_HANDLE  4807  Identificador de indicador errado

Isso significa que não há controle da validade do identificador do indicador.

Armazenamos em cache os identificadores do indicador. Como funciona

O princípio geral de operação do armazenamento em cache de indicador é o seguinte:

  • cria-se um cache de identificadores personalizado;
  • ao consultar dados a partir do indicador, verificamos se já foi criado esse identificador nas configurações solicitadas (símbolo, timeframe, período de média e assim por diante):
    • se já existir no cache personalizado, retornamos seus dados a partir do indicador;
    • se esse identificador ainda não tiver sido criado, nós o criamos, o colocamos no cache e retornamos seus dados a partir do indicador.

Opção 1: matriz de estruturas

A realização ocorre no arquivo IndicatorsMQL4Caching.mqh (conecta-se ao EA Cache test.mq5 com a ajuda de SimpleCallMQL4Caching.mqh).

No EA Cache test.mq5, conectamos o arquivo SimpleCallMQL4Caching.mqh":

//#include <SimpleCall\SimpleCallMQL4.mqh> // for tests without caching of the handles
#include <SimpleCall\SimpleCallMQL4Caching.mqh> // for tests with caching of the handles
//#include <SimpleCall\SimpleCallString.mqh> // for tests with string

Primeiro, mostrarei um bloco de código grande que é inserido no arquivo e na função iMACD:

...         
//+------------------------------------------------------------------+
//| Struct CHandle                                                   |
//+------------------------------------------------------------------+
template<typename T>
struct SHandle
  {
private:
   int               Handle;
   T                 Inputs;

public:
   //+------------------------------------------------------------------+
   //| A constructor with an initialization list                        |
   //+------------------------------------------------------------------+
                     SHandle() : Handle(INVALID_HANDLE)
     {
     }
   //+------------------------------------------------------------------+
   //| Operation Overloading "=="                                       |
   //+------------------------------------------------------------------+
   bool operator==(const T &Inputs2) const
     {
      return(this.Inputs == Inputs2);
     }
   //+------------------------------------------------------------------+
   //| Operation Overloading "="                                        |
   //+------------------------------------------------------------------+
   void operator=(const T &Inputs2)
     {
      this.Inputs=Inputs2;
     }
   //+------------------------------------------------------------------+
   //| SHandle::GetHandle                                               |
   //+------------------------------------------------------------------+
   int GetHandle()
     {
      return((this.Handle != INVALID_HANDLE) ? this.Handle : (this.Handle = this.Inputs.GetHandle()));
     }
  };
//+------------------------------------------------------------------+
//| Get Handle                                                       |
//+------------------------------------------------------------------+
template<typename T>
int GetHandle(SHandle<T>&Handles[],const T &Inputs)
  {
   const int Size=ArraySize(Handles);

   for(int i=0; i<Size; i++)
      if(Handles[i]==Inputs)
         return(Handles[i].GetHandle());

   ArrayResize(Handles,Size+1);
   Handles[Size]=Inputs;

   return(Handles[Size].GetHandle());
  }
//+------------------------------------------------------------------+
//| Struct Macd                                                      |
//+------------------------------------------------------------------+
struct SMacd
  {
   string            symbol;
   ENUM_TIMEFRAMES   period;
   int               fast_ema_period;
   int               slow_ema_period;
   int               signal_period;
   ENUM_APPLIED_PRICE applied_price;
   //+------------------------------------------------------------------+
   //| An empty default constructor                                     |
   //+------------------------------------------------------------------+
                     SMacd(void)
     {
     }
   //+------------------------------------------------------------------+
   //| A constructor with an initialization list                        |
   //+------------------------------------------------------------------+
                     SMacd(const string             &isymbol,
                                             const ENUM_TIMEFRAMES    &iperiod,
                                             const int                &ifast_ema_period,
                                             const int                &islow_ema_period,
                                             const int                &isignal_period,
                                             const ENUM_APPLIED_PRICE &iapplied_price) :
                                             symbol((isymbol== NULL)||(isymbol == "") ? Symbol() : isymbol),
                                             period(iperiod == PERIOD_CURRENT ? Period() : iperiod),
                                             fast_ema_period(ifast_ema_period),
                                             slow_ema_period(islow_ema_period),
                                             signal_period(isignal_period),
                                             applied_price(iapplied_price)
     {
     }
   //+------------------------------------------------------------------+
   //| SMacd::GetHandle                                                 |
   //+------------------------------------------------------------------+
   int GetHandle(void) const
     {
      return(iMACD(this.symbol, this.period, this.fast_ema_period, this.slow_ema_period, this.signal_period, this.applied_price));
     }
   //+------------------------------------------------------------------+
   //| Operation Overloading "=="                                       |
   //+------------------------------------------------------------------+
   bool operator==(const SMacd &Inputs) const
     {
      return((this.symbol == Inputs.symbol) &&
             (this.period == Inputs.period) &&
             (this.fast_ema_period == Inputs.fast_ema_period) &&
             (this.slow_ema_period == Inputs.slow_ema_period) &&
             (this.signal_period == Inputs.signal_period) &&
             (this.applied_price == Inputs.applied_price));
     }
  };
//+------------------------------------------------------------------+
//| iMACD2 function in MQL4 notation                                 |
//|   The buffer numbers are the following:                          |
//|      MQL4 0 - MODE_MAIN, 1 - MODE_SIGNAL                         |
//|      MQL5 0 - MAIN_LINE, 1 - SIGNAL_LINE                         |
//+------------------------------------------------------------------+
int iMACD2(const string             symbol,
           const ENUM_TIMEFRAMES    period,
           const int                fast_ema_period,
           const int                slow_ema_period,
           const int                signal_period,
           const ENUM_APPLIED_PRICE applied_price)
  {
   static SHandle<SMacd>Handles[];
   const SMacd Inputs(symbol,period,fast_ema_period,slow_ema_period,signal_period,applied_price);

   return(GetHandle(Handles, Inputs));
  }
//+------------------------------------------------------------------+
//| iAC function in MQL4 notation                                    |
...
//+------------------------------------------------------------------+
//| iMACD function in MQL4 notation                                  |
//|   The buffer numbers are the following:                          |
//|      MQL4 0 - MODE_MAIN, 1 - MODE_SIGNAL                         |
//|      MQL5 0 - MAIN_LINE, 1 - SIGNAL_LINE                         |
//+------------------------------------------------------------------+
double   iMACD(
               string                     symbol,              // symbol name 
               ENUM_TIMEFRAMES            timeframe,           // timeframe 
               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=NaN;
//---
   int handle=iMACD2(symbol,timeframe,fast_ema_period,slow_ema_period,signal_period,applied_price);
   if(handle==INVALID_HANDLE)
...

Descrevamos seu funcionamento. Primeiro, no EA ocorre uma solicitação de dados a partir do indicador MACD:

   double macd_main_30=iMACD(NULL,0,30,26,9,PRICE_CLOSE,MODE_MAIN,0);

Em seguida, entramos na função do iMACD e vamos para o iMACD2:

//+------------------------------------------------------------------+
//| iMACD2 function in MQL4 notation                                 |
//|   The buffer numbers are the following:                          |
//|      MQL4 0 - MODE_MAIN, 1 - MODE_SIGNAL                         |
//|      MQL5 0 - MAIN_LINE, 1 - SIGNAL_LINE                         |
//+------------------------------------------------------------------+
int iMACD2(const string             symbol,
           const ENUM_TIMEFRAMES    period,
           const int                fast_ema_period,
           const int                slow_ema_period,
           const int                signal_period,
           const ENUM_APPLIED_PRICE applied_price)
  {
   static SHandle<SMacd>Handles[];
   const SMacd Inputs(symbol,period,fast_ema_period,slow_ema_period,signal_period,applied_price);

   return(GetHandle(Handles, Inputs));
  }

Aqui é declarada a matriz estática Handles[] com o tipo SMacd (ela é criada na primeira entrada e não é criada novamente em entradas ulteriores) e é criado o objeto Inputs com o tipo SMacd, que é inicializado imediatamente pelos parâmetros.

Depois disso, enviamos pelas referências a matriz Handles[] e o objeto Inputs para a função GetHandle (atenção, não é para SHandle::GetHandle e também não para SMacd::GetHandle):

//+------------------------------------------------------------------+
//| Get Handle                                                       |
//+------------------------------------------------------------------+
template<typename T>
int GetHandle(SHandle<T>&Handles[],const T &Inputs)
  {
   const int Size=ArraySize(Handles);

   for(int i=0; i<Size; i++)
      if(Handles[i]==Inputs)
         return(Handles[i].GetHandle());

   ArrayResize(Handles,Size+1);
   Handles[Size]=Inputs;
   return(Handles[Size].GetHandle());
  }

Neste função, quer retornamos o identificador encontrado do indicador para a matriz quer, se o identificador não for encontrado, nós o obtemos em SHandle::GetHandle.

Mas como este é o primeiro acesso e não existe esse identificador

   //+------------------------------------------------------------------+
   //| SHandle::GetHandle                                               |
   //+------------------------------------------------------------------+
   int GetHandle()
     {
      return((this.Handle != INVALID_HANDLE) ? this.Handle : (this.Handle = this.Inputs.GetHandle()));
     }

nós o criamos em SMacd::GetHandle:

   //+------------------------------------------------------------------+
   //| SMacd::GetHandle                                                 |
   //+------------------------------------------------------------------+
   int GetHandle(void) const
     {
      return(iMACD(this.symbol, this.period, this.fast_ema_period, this.slow_ema_period, this.signal_period, this.applied_price));
     }

Opção 2: matriz de cadeias de caracteres

A realização ocorre no arquivo IndicatorsMQL4String.mqh (conecta-se ao EA Cache test.mq5com a ajuda de SimpleCallString.mqh).

No EA Cache test.mq5, conectamos o arquivo SimpleCallString.mqh:

//#include <SimpleCall\SimpleCallMQL4.mqh> // for tests without caching of the handles
//#include <SimpleCall\SimpleCallMQL4Caching.mqh> // for tests with caching of the handles
#include <SimpleCall\SimpleCallString.mqh> // for tests with string

Sabia que trabalhar com strings é terrivelmente caro em termos de velocidade. Um pouco mais tarde, vamos ter certeza disso. Bem, a ideia de armazenar parâmetros na forma de uma string se parece com isto:

   string Hashes[];
   static int Handles[];
   string hash=((symbol==NULL) || (symbol=="") ? Symbol() : symbol)+
               (string)(timeframe==PERIOD_CURRENT ? Period() : timeframe)+
               (string)(fast_ema_period)+
               (string)(slow_ema_period)+
               (string)(signal_period)+
               (string)(applied_price);

Vamos acessar o iMACD a partir do EA com os parâmetros mostrados acima, na Fig. 1.

 NN  Código Hora
  1
//--- NN2
//static string Hashes[];
//static int Handles[];
//string hash=((symbol==NULL) || (symbol=="") ? Symbol() : symbol)+
//            (string)(timeframe==PERIOD_CURRENT ? Period() : timeframe)+
//            (string)(fast_ema_period)+
//            (string)(slow_ema_period)+
//            (string)(signal_period)+
//            (string)(applied_price);
//--- NN3
//static string Hashes[];
//static int Handles[];
//string hash="";
//StringConcatenate(hash,
//                  ((symbol==NULL) || (symbol=="") ? Symbol() : symbol),
//                  (timeframe==PERIOD_CURRENT ? Period() : timeframe),
//                  fast_ema_period,
//                  slow_ema_period,
//                  signal_period,
//                  applied_price);
 0:01:40.953
  2
//--- NN2
   static string Hashes[];
   static int Handles[];
   string hash=((symbol==NULL) || (symbol=="") ? Symbol() : symbol)+
               (string)(timeframe==PERIOD_CURRENT ? Period() : timeframe)+
               (string)(fast_ema_period)+
               (string)(slow_ema_period)+
               (string)(signal_period)+
               (string)(applied_price);
//--- NN3
//static string Hashes[];
//static int Handles[];
//string hash="";
//StringConcatenate(hash,
//                  ((symbol==NULL) || (symbol=="") ? Symbol() : symbol),
//                  (timeframe==PERIOD_CURRENT ? Period() : timeframe),
//                  fast_ema_period,
//                  slow_ema_period,
//                  signal_period,
//                  applied_price);
 0:05:20.953
  3
//--- NN2
//static string Hashes[];
//static int Handles[];
//string hash=((symbol==NULL) || (symbol=="") ? Symbol() : symbol)+
//            (string)(timeframe==PERIOD_CURRENT ? Period() : timeframe)+
//            (string)(fast_ema_period)+
//            (string)(slow_ema_period)+
//            (string)(signal_period)+
//            (string)(applied_price);
//--- NN3
   static string Hashes[];
   static int Handles[];
   string hash="";
   StringConcatenate(hash,
                     ((symbol==NULL) || (symbol=="") ? Symbol() : symbol),
                     (timeframe==PERIOD_CURRENT ? Period() : timeframe),
                     fast_ema_period,
                     slow_ema_period,
                     signal_period,
                     applied_price);
 0:04:12.672

O teste 1 é um teste de referência com acesso a indicadores em estilo MQL4, sem trabalhar com strings. No teste 2, já se trabalha com strings, formadas através do "+". No teste 3, a cadeia de caracteres é formada usando StringConcatenate.

De acordo com as medições do tempo, pode-se observar que, em comparação com o teste 2, StringConcatenate dá um ganho de tempo de 21%, no entanto o desempenho geral ainda é 2,5 vezes menor do que no teste 1.

Daí que descartamos a idéia de salvar os identificadores na forma de cadeias de caracteres.

A opção 3 é uma classe que armazena em cache identificadores  (classe iIndicators.mqh é conectada ao EA Cache test.mq5 com a ajuda de SimpleCallMQL4CachingCiIndicators.mqh).

No EA Cache test.mq5 conectamos o arquivo SimpleCallMQL4CachingCiIndicators.mqh:

//#include <SimpleCall\SimpleCallMQL4.mqh> // for tests without caching of the handles
//#include <SimpleCall\SimpleCallMQL4Caching.mqh> // for tests with caching of the handles
//#include <SimpleCall\SimpleCallString.mqh> // for tests with string
#include <SimpleCall\SimpleCallMQL4CachingCiIndicators.mqh>

Para cada indicador (dentro da função correspondente em estilo MQL4), cria-se um objeto estático de classe CHandle. Ele serve como um repositório dos objetos da classe CiIndicators, isto é, da classe que contém os parâmetros e configurações do indicador.

Scheme

Fig. 6 O esquema

A classe CiIndicators em si é construída sobre cinco variáveis private:

//+------------------------------------------------------------------+
//| Class iIndicators                                                |
//+------------------------------------------------------------------+
class CiIndicators
  {
private:
   string            m_symbol;                        // symbol name 
   ENUM_TIMEFRAMES   m_period;                        // timeframe 
   ENUM_INDICATOR    m_indicator_type;                // indicator type from the enumeration ENUM_INDICATOR 
   int               m_parameters_cnt;                // number of parameters 
   MqlParam          m_parameters_array[];            // array of parameters 

public:

Eles correspondem com exatidão às variáveis da função  IndicatorCreate. Isso não é feito em vão, pois nós obtemos o identificador do indicador, precisamente, através de IndicatorCreate.

A classe CHandle é construída sobre dois matrizes:

//+------------------------------------------------------------------+
//| Class CHandle                                                    |
//+------------------------------------------------------------------+
class CHandle
  {
private:
   int               m_handle[];
   CiIndicators      m_indicators[];

public:

A matriz m_handle contém a os identificadores de indicadores criados, enquanto a matriz m_indicators é um array de classe CiIndicators.

O código de trabalho com as classes CiIndicators e CHandle, usando o exemplo do MACD, se assemelha a isso:

//+------------------------------------------------------------------+
//| iMACD function in MQL4 notation                                  |
//|   The buffer numbers are the following:                          |
//|      MQL4 0 - MODE_MAIN, 1 - MODE_SIGNAL                         |
//|      MQL5 0 - MAIN_LINE, 1 - SIGNAL_LINE                         |
//+------------------------------------------------------------------+
double   iMACD(
               string                     symbol,              // symbol name 
               ENUM_TIMEFRAMES            timeframe,           // timeframe 
               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
               )
  {
//---
   static CHandle Handles_MACD;
//--- fill the structure with parameters of the indicator      
   MqlParam pars[4];
//--- period of fast ma 
   pars[0].type=TYPE_INT;
   pars[0].integer_value=fast_ema_period;
//--- period of slow ma 
   pars[1].type=TYPE_INT;
   pars[1].integer_value=slow_ema_period;
//--- period of averaging of difference between the fast and the slow moving average 
   pars[2].type=TYPE_INT;
   pars[2].integer_value=signal_period;
//--- type of price 
   pars[3].type=TYPE_INT;
   pars[3].integer_value=applied_price;

   CiIndicators MACD_Indicator;
   MACD_Indicator.Init(Symbol(),Period(),IND_MACD,4);
   int handle=Handles_MACD.GetHandle(MACD_Indicator,Symbol(),Period(),IND_MACD,4,pars);
//---
   double result=NaN;
//---
   if(handle==INVALID_HANDLE)
     {
      Print(__FUNCTION__,": INVALID_HANDLE error=",GetLastError());
      return(result);
     }
   double val[1];
   int copied=CopyBuffer(handle,buffer,shift,1,val);
   if(copied>0)
      result=val[0];
   else
      Print(__FUNCTION__,": CopyBuffer error=",GetLastError());
   return(result);
  }
  • É declarada a matriz Handles_MACD de classe CHandle - ela armazenará os identificadores criados e os parâmetros dos indicadores MACD.
  • Cria-se e inicializa-se o objeto MACD_Indicator de classe CiIndicators.
  • O próprio indicador é criado (ou dado, ser já tiver sido criado para esses parâmetros) na função  Handles_MACD::GetHandle.

O trabalho da classe CiIndicators.mqh com acesso em estilo MQL4 e armazenamento em cache de identificadores demorou 2 minutos e 30 segundos.


Gráfico final da velocidade de acesso a nove indicadores

Verificamos o estilo MQL4 sem e com armazenamento em cache, com a ajuda do Expert Advisor "Cache test.mq5", enquanto verificamos os testes do estilo MQL5 padrão, usando o Expert Advisor "MQL5 test.mq5".


Fim do artigo

Realizamos alguns experimentos interessantes que entram em conflito com o paradigma do correto acesso MQL5 a indicadores. Finalmente, aprendemos mais sobre o mecanismo interno de trabalho com os identificadores dentro do kernel MQL5:

  • sobre o contador de identificadores;
  • sobre o armazenamento em cache e controle das identificadores.

No que diz respeito às formas de acesso a indicadores, os resultados do teste mostraram que a velocidade do estilo MQL5 ultrapassou os estilos MQL4 (com e sem armazenamento em cache de identificadores).

Traduzido do russo por MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/4388

Arquivos anexados |
MQL5.zip (11.79 KB)
Como criar uma Especificação de Requisitos para solicitar um indicador Como criar uma Especificação de Requisitos para solicitar um indicador

Na maioria das vezes, a primeira etapa no desenvolvimento de um sistema de negociação é a criação de um indicador técnico, que pode identificar padrões favoráveis ​​de comportamento do mercado. Um indicador desenvolvido de forma profissional pode ser encomendado no serviço Freelance. Neste artigo você aprenderá a criar uma Especificação de Requisitos apropriada, que o ajudará a obter o indicador desejado mais rapidamente.

Otimização Controlada: Recozimento Simulado Otimização Controlada: Recozimento Simulado

O Testador de Estratégia da plataforma de negociação MetaTrader 5 fornece apenas duas opções de otimização: otimização completa dos parâmetros e o algoritmo genético. Este artigo propõe um novo método para otimizar as estratégias de negociação — Recozimento Simulado (Simulated Annealing). Será introduzido o algoritmo do método, sua implementação e integração em qualquer Expert Advisor. O algoritmo desenvolvido é testado no EA Moving Average (Média Móvel).

Sincronização de vários gráficos num instrumento em diferentes timeframes Sincronização de vários gráficos num instrumento em diferentes timeframes

Muitas vezes, para tomar decisões, no processo de negociação, é necessário analisar simultaneamente gráficos em vários timeframes. Ao mesmo tempo, há objetos de análise gráfica nos gráficos. Implementar os mesmos objetos em todos os gráficos é incômodo. Neste artigo, proponho automatizar a clonagem de objetos em gráficos.

Criando EAs multimódulo Criando EAs multimódulo

A linguagem de programação MQL permite concretizar o conceito de design modular de estratégias de negociação. O artigo mostra um exemplo de criação de um Expert Advisor multimodular que consiste em módulos de arquivo compilados separadamente.