Comparamos a velocidade de indicadores de armazenamento automático em cache
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:

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:

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).

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)

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.

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 pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/4388
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Sincronização de vários gráficos num instrumento em diferentes timeframes
Como criar uma Especificação de Requisitos para solicitar um indicador
Criando EAs multimódulo
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
Essa é a interpretação de que estou falando
"Contabilidade interna" interpretada como um contador. Falando francamente, não entendo por que a primeira parte do artigo é sobre alças? Parece que tudo já foi mastigado mais de uma vez e apresentado em formulações mais convenientes. Até as razões para a possibilidade de executar indicadores no Terminal, onde não há gráficos abertos.
Essa é uma conclusão completamente errada. O identificador é o mesmo e isso é confirmado pelo fato de que a identificação corresponde.
O primeiro resultado verdadeiro indica apenas que a contagem de referência do identificador diminuiu.
Sim, é um erro no artigo.
Em geral, você deve parar de inventar e escrever "no estilo MQL4". A MQL5 é mais rápida e mais correta. Foi a compreensão das muletas e das limitações da MQL4 que nos levou a criar uma nova linguagem e a recusar a compatibilidade para não usar um esquema de acesso a dados ruim.
Em geral, é necessário inventar e escrever "no estilo MQL4". A MQL5 é mais rápida e mais correta. Foi a compreensão das muletas e limitações da MQL4 que nos levou a criar uma nova linguagem e a recusar a compatibilidade, para não criar um esquema de acesso a dados ruim.
Acesse!
Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação
Discussão do artigo "Comparação da velocidade dos indicadores de autocache"
fxsaber, 2018.03.07 08:17 pm.
Em geral, o TS chama indicadores com parâmetros de entrada calculados (em vez de codificados). E aqui você não pode ficar sem o estilo MQL4 + cache.
Acho que não é difícil encontrar um consultor MT4 desse nível na KB. Será impossível convertê-lo no que é chamado no artigo de estilo MQL5.
Vamos lá!
Isso é um exagero.
Sem argumentosÉ um exagero.
Não há argumentos.Continue defendendo os TCs primitivos.