Comparamos la velocidad de los indicadores de almacenamiento automático en la caché

15 marzo 2018, 15:19
Vladimir Karputov
0
1 633

Introducción

Supongamos que un buen día nos aburre el acceso MQL5 clásico a los indicadores y queremos comparar la velocidad de acceso con la de las variantes alternativas. Por ejemplo, lo compararemos con el acceso a los indicadores en el estilo MQL4 sin almacenamiento en la caché y con él. Las ideas para el acceso en el estilo MQL4 se han tomado del artículo LifeHack para tráders: preparando comida rápida a partir de indicadores y sus añadidos.


Vamos a investigar la numeración MQL5 de los manejadores de los indicadores

Existe la suposición de que la numeración de los manejadores de los indicadores en el terminal es continua y comienza por cero. Para comprobar esta hipótesis, crearemos un pequeño asesor "iMACD and IndicatorRelease.mq5", que crea varios manejadores de indicadores, los imprime y recurre a estos indicadores periódicamente en OnTick():

//+------------------------------------------------------------------+
//|                                   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

Datos originales: en el terminal están abiertos los gráficos AUDJPY M15, USDJPY M15 y EURUSD M15, en los que no hay ni indicadores, ni asesores. El parámetro Count MACD indicators del asesor iMACD and IndicatorRelease.mq5 es igual a 6.

Justo después de reiniciar el terminal, fijamos el asesor iMACD and IndicatorRelease.mq5 al primer 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)

Como podemos ver, la numeración de los manejadores comienza a partir de 10, no a partir de 0.

Experimento 2

Datos originales: en el primer gráfico (AUDJPY M15) fijamos el asesor iMACD and IndicatorRelease.mq5, el parámetro Count MACD indicators es igual 6.

Fijamos el asesor iMACD and IndicatorRelease.mq5 al 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)

Por lo que se ve, la numeración de los manejadores en el gráfico (USDJPY M15) también comienza, no a partir de 0, sino a partir de 10.

Conclusión: la numeración de los manejadores de los indicadores en el terminal (la que se da al usuario) no es continua y NO comienza por cero.

Experimento 3

Dos gráficos idénticos AUDJPY, M15 (ChartID 131571247244850509) y AUDJPY, M15 (ChartID 131571247244850510). A cada uno de ellos se ha fijado un asesor iMACD and IndicatorRelease.mq5 con un parámetro Count MACD indicators igual a 6.

La numeración no continua de los manejadores de los indicadores creados confirma que MQL5 lleva para ellos su propio registro interno (un contador para cada manejador único). Para asegurarnos de ello con seguridad, comentaremos el crecimiento del periodo:

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 

De esta forma, intentaremos crear varios manejadores del indicador MACD con ajustes exactamente idénticos.

Eliminamos de los gráficos los asesores que han quedado tras los experimentos № 1 y № 2, y fijamos el asesor iMACD and IndicatorRelease.mq5 al primer 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, como respuesta a la creación de indicadores absolutamente idénticos, se ha retornado el mismo manejador.

Fijamos el asesor iMACD and IndicatorRelease.mq5 (también con el comentado incremento de periodo) al 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)

Podemos ver de nuevo que se retorna el mismo manejador. Pero surge la pregunta: ¿los manejadores con el número "10" en el primer y segundo gráfico son el mismo manejador o dos distintos? Para comprobar esto, eliminamos el asesor de los gráficos (recordemos que el asesor en OnDeinit() rodea la matriz de manejadores y elimina cada uno con la ayuda de 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

Hemos obtenido el resultado esperado, si tenemos en cuenta el apartado de la documentación Ejecución de programas:

El experto se ejecuta en un flujo propio, el número de flujos para los EAs coincide con el número de EAs

Es decir, si dos expertos en gráficos idénticos (símbolo y marco temporal idénticos) crean indicadores con parámetros de entrada idénticos, MQL5 en su registro interno los identificará como dos manejadores distintos.

Conclusión general con respecto a la creación de indicadores en los expertos

La numeración de los manejadores de los indicadores en el terminal (aquella que se da al usuario) NO es continua y NO comienza por cero, además, en este caso, MQL5 en el registro interno de los manejadores tiene en cuenta:

  • la función del indicador técnico (iMA, iAC, iMACD, iIchimoku, etcétera);
  • los parámetros de entrada del indicador;
  • el símbolo en el que se crea el indicador;
  • el marco temporal en el que se crea el indicador;
  • la ChartID del gráfico en el que trabaja el experto.

¿Tiene sentido el almacenamiento de manejadores en la caché?

Los datos originales (marco temporal, símbolo, intervalo temporal simulado y tipo de generación de ticks) serán así:

Cache test Settings

Fig. 1 Ajustes

Los tests de acceso a los indicadores en el estilo MQL4 (con almacenamiento de manejadores en la caché y sin él) se ejecutan con la ayuda del asesor Cache test.mq5, y los tests de acceso en el estilo MQL5, con la ayuda del asesor 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 del asesor MQL5 test.mq5:

MQL5 test 1

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

Parámetros del asesor Cache test.mq5:

  • Use Timer ("0" -> off timer) — usar el temporizador (valor 0 — el temporizador no se usa).
  • Use indicator ("false" -> 9 indicators, "true" - 1 indicator) — número de indicadores inquiridos (1 o 9).

Cache test 1

Fig. 3. "Cache test.mq5". Sin temporizador, con nueve indicadores

Para medir "el estilo MQL4 sin almacenamiento de manejadores en la caché" se usan los archivos IndicatorsMQL4.mq, incluidos con la ayuda de "SimpleCallMQL4.mqh (ver el artículo LifeHack para tráders: cocinamos ForEach usando #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 medir "el estilo MQL4 sin almacenamiento de manejadores en la caché", al archivo IndicatorsMQL4.mqh se ha añadido el código de almacenamiento de manejadores en la caché del post #113 (solo para MACD, el resto de las funciones ha sido eliminado). El archivo se ha almacenado con el nuevo nombre IndicatorsMQL4Caching.mqh, este se incluye con la ayuda del archivo 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 de la comparación de los estilos de acceso a los nuevos indicadores (los ajustes se dan en la fig. 1):


Fig. 4. Gasto de tiempo de acceso a los nueve indicadores

Al comparar los resultados, le pedimos que tenga en cuenta que el asesor de prueba ha complicado la tarea considerablemente:

  • los datos los recibimos simultánemante de los NUEVE indicadores;
  • recurrimos a los indicadores EN CADA tick;
  • marco temporal M1, en este caso, además, se han generado 26169180 ticks y 370355 barras.

Ahora vamos a realizar la simulación con un solo indicador (en ambos asesores: MQL5 test.mq5 y Cache test.mq5 valor del parámetro Use indicator...  "true", Cache test.mq5 tiene un valor de parámetro Use Timer "0")


Fig. 5. Gasto de tiempo de acceso a uno de los indicadores

Conclusión

El estilo MQL4 con almacenamiento de manejadores en la caché gana en comparación con el estilo MQL4 sin almacenamiento en la caché, pero en este caso, además, el estilo MQL4 a secas pierde con respecto al estilo MQL5. 

Ausencia de control de validez del manejador

Ahora debemos hablar sobre el tremendo peligro que supone la aplicación del almacenamiento de manejadores en la caché: en ninguna parte encontramos control sobre la existencia del manejador en la caché personalizada. Es decir, no se procesa de ninguna forma la situación en la que manejador del indicador ha sido eliminado. 

Imaginemos la situación siguiente: estamos trabajando con indicadores en el estilo MQL4 y almacenamos los manejadores en la caché. Después de la primera invocación desde el asesor:

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

el manejador se ha almacenado en la caché personalizada (puede ser una matriz o una matriz de líneas). A continuación, todas las invocaciones posteriores desde el asesor

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

no se dejarán pasar al núcleo de MQL5, sino que se retornarán los valores del indicador según el manejador tomado de la caché. Y ahora, eliminamos el manejador en OnTimer(): supongamos que sabemos que es igual a "10". Para la simulación, usamos el archivo Cache test.mq5, al que se puede conectar el archivo 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

Mostramos necesariamente el temporizador (en este ejemplo, el temporizador se muestra por seis segundos, obtenemos acceso a un indicador)

Cache test 2

Fig. 6. Ajustes del test con el manejador eliminado

Después de la primera entrada de OnTimer()

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

obtenemos el error 4807:

 ERR_INDICATOR_WRONG_HANDLE  4807  El manejador del indicador es erróneo

Esto significa que no tenemos control de validez del indicador.


Almacenamos los manejadores de los indicadores en la caché. Cómo se organiza este proceso

El principio general de funcionamiento del almacenamiento de manejadores de los indicadores en la caché es como sigue:

  • se crea el almacenamiento personalizado de manejadores en la caché;
  • al solicitar los datos del indicador, comprobamos si se ha creado ya anteriormente un manejador así con los ajustes solicitados (símbolo, marco temporal, periodo de promediación, etcétera):
    • si ya existe en la caché personalizada, entonces retornamos los datos desde el indicador conforme a esta;
    • si ese manejador aún no se ha creado, lo creamos, lo introducimos en la caché y retornamos los datos desde el indicador conforme esta.


Variante 1: matriz de estructuras

La implementación tiene lugar en el archivo IndicatorsMQL4Caching.mqh (se incluye en el experto Cache test.mq5 con la ayuda de SimpleCallMQL4Caching.mqh).

En el experto Cache test.mq5 incluimos el archivo 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

Primero mostraremos un gran bloque de código, que se pega al archivo y a la función 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)
...

Vamos a describir su funcionamiento. Primero tiene lugar en el asesor la solicitud sobre la obtención de datos desde el indicador MACD:

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

A continuación, entramos en la función iMACD y pasamos a 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));
  }

Aquí se declara la matriz estática Handles[] con el tipo SMacd (se crea en la primera entrada y no se crea de nuevo en las siguientes entradas) y se crea el objeto Inputs con el tipo SMacd, que se inicia de inmediato con los parámetros.

Después de ello, transmitimos por los enlaces la matriz Handles[] y el objeto Inputs a la función GetHandle (preste atención, ni a SHandle::GetHandle, ni a 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());
  }

En esta función, o bien retornamos el manejador del indicador encontrado en la matriz, o bien, si el manejador no ha sido encontrado, lo obtenemos en SHandle::GetHandle.

Pero, puesto que se trata de la primera invocación y no existe un manejador así

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

lo crearemos en 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));
     }


Variante 2: matriz de líneas

La implementación tiene lugar en el archivo IndicatorsMQL4String.mqh (se incluye en el experto Cache test.mq5 con la ayuda SimpleCallString.mqh).

En el experto Cache test.mq5 incluimos 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

Destacamos de inmediato que el trabajo con las líneas resulta muy costoso en cuanto al gasto de tiempo. Un poco más tarde podremos comprobar esto. Así, la idea del almacenamiento de los parámetros en forma de línea tiene el aspecto siguiente:

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

Recurriremos a iMACD desde el experto con los parámetros presentados más arriba, en la 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

La prueba 1 consiste en un test patrón con acceso a los indicadores en el estilo MQL4, sin trabajo con líneas. En la prueba 2 ya está presente el trabajo con líneas, y la línea se forma con "+". En la prueba 3, la línea se forma con la ayuda de StringConcatenate.

Según las mediciones de tiempo, podemos ver que, aunque StringConcatenate da una ganancia de tiempo de un 21% en comparación con la prueba 2, en este caso, sin embargo, el rendimiento general es aun así 2.5 veces menor en comparación con la prueba 1.

Por ello, descartamos la idea del almacenamiento de manejadores en forma de líneas.


La variante 3 es una clase que almacena manejadores en la caché (la clase iIndicators.mqh se incluye en el experto Cache test.mq5 con la ayuda de SimpleCallMQL4CachingCiIndicators.mqh).

En el experto Cache test.mq5 incluimos el archivo 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 de la función correspondiente en el estilo MQL4) se crea un objeto estático de la clase CHandle. Actúa como repositorio de los objetos de la clase CiIndicators, la clase que contiene los parámetros y los ajustes del indicador.

Scheme

Fig. 6. Esquema

La propia clase CiIndicators se construye con las cinco variables 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:

Se corresponden al detalle con las variables de la función IndicatorCreate. Esto no se ha hecho porque sí, puesto que el manejador del indicador lo obtenemos precisamente a través de IndicatorCreate.

La clase CHandle se construye con dos matrices:

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

public:

La matriz m_handle contienen los manejadores de indicadores creados, y la matriz m_indicators, que es una matriz de la clase CiIndicators.

El código de trabajo con las clases CiIndicators y CHandle, usando el ejemplo de MACD, tiene el aspecto siguiente:

//+------------------------------------------------------------------+
//| 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);
  }

  • Se declara la matriz estática Handles_MACD de la clase CHandle, en la que se guardarán los manejadores creados y los parámetros de los indicadores MACD.
  • Se crea e inicializa el objeto MACD_Indicator de la clase CiIndicators.
  • El propio manejador del indicador se crea (o se entrega, si ya ha sido creado para estos parámetros) en la función Handles_MACD::GetHandle.

El tiempo de funcionamiento de la clase CiIndicators.mqh con acceso en el estilo MQL4 y almacenamiento de manejadores en la caché ha ocupado 2 minutos y 30 segundos.


Gráfico final de velocidad de acceso a los nueve indicadores

El estilo MQL4 sin almacenamiento en la caché y con almacenamiento en la misma lo comprobaremos con la ayuda del asesor "Cache test.mq5", y las simulaciones del estilo MQL5 estándar las realizaremos usando el asesor "MQL5 test.mq5".



Conclusión

Hemos realizado varios experimentos interesantes, que tienen presentes el paradigma de acceso MQL5 correcto a los indicadores. En suma, hemos conocido con detalle mucha información sobre el mecanismo interno para trabajar con los manejadores dentro del núcleo MQL5:

  • sobre el contador de manejadores;
  • sobre el almacenamiento en la caché y el control de manejadores.

Los resultados de la simulación de los diferentes métodos de acceso a los indicadores han mostrado que el estilo de acceso MQL5 a los indicadores supera claramente en cuanto a velocidad a cualquier estilo MQL4 (tanto sin almacenamiento en la caché, como con él).


Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/4388

Archivos adjuntos |
MQL5.zip (11.79 KB)
Cómo crear una Tarea Técnica al encargar un indicador Cómo crear una Tarea Técnica al encargar un indicador

Los tráders buscan leyes en el comportamiento del mercado que indiquen los momentos adecuados para realizar transacciones comerciales. Muy a menudo, el primer paso en el desarrollo de un sistema comercial es la creación de un indicador técnico que le ayude a ver en el gráfico de precios la información que necesita. Este artículo le ayudará a componer la Tarea Técnica para encargar un indicador.

Visualizando la optimización de una estrategia comercial en MetaTrader 5 Visualizando la optimización de una estrategia comercial en MetaTrader 5

En el artículo se ha implementado una aplicación MQL con interfaz gráfica para la visualización ampliada del proceso de optimización. La interfaz gráfica ha sido creada con la ayuda de la última versión de la biblioteca EasyAndFast. En ocasiones, a muchos usarios les surge la siguiente pregunta: ¿para qué necesitamos las interfaces gráficas en las aplicaciones MQL? En este artículo se muestra uno de los numerosos casos en los que pueden resultar útiles para los tráders.

Optimización controlable: el método del recocido Optimización controlable: el método del recocido

En el simulador de estrategias de la plataforma comercial MetaTrader 5 solo existen dos variantes de optimización: la iteración completa de parámetros y el algoritmo genético. En este artículo se propone una nueva variante de optimización de estrategias comerciales: el método del recocido. Se muestra el algoritmo del método, su implementación y su método de inclusión en cualquier asesor. El algoritmo desarrollado se ha puesto a prueba con el asesor Moving Average.

Simulación de patrones que surgen al comerciar con cestas de parejas de divisas. Parte III Simulación de patrones que surgen al comerciar con cestas de parejas de divisas. Parte III

Terminamos el tema de la simulación de los patrones que surgen al comerciar con cestas de parejas de divisas. En este artículo, presentamos los resultados de la simulación de los patrones que monitorean el movimiento de las divisas de la pareja en relación una a otra.