English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Pruebas de rendimiento computacional de los promedios móviles en MQL5

Pruebas de rendimiento computacional de los promedios móviles en MQL5

MetaTrader 5Ejemplos | 25 febrero 2014, 10:49
1 005 0
Sergey Pavlov
Sergey Pavlov


Introducción

El uso de los Promedios móviles es una práctica muy común en el análisis de las series de tiempo del mercado, en los indicadores y en la programación de los Expert Advisors. Es el método de suavizado de datos de precios más conocido. En la nueva versión de MQL hay disponible una docena de algoritmos de Promedios móviles.

¿Cuál es la diferencia entre ellos? ¿Depende de verdad la velocidad de cálculo de ciertos algoritmos de promedios móviles? ¿Cuál es el algoritmo más rápido?

¿Ha incrementado la velocidad computacional de los Promedios móviles en MetaTrader5 en comparación con MetaTrader4? Surgen muchas preguntas. Así que, vamos a tratar de responder a la mayoría de ellas.

Por supuesto, la velocidad de la nueva plataforma es impresionante, pero lo mejor es comprobarlo experimentalmente.


1. Condiciones de las pruebas

La velocidad de cálculo depende de muchos factores. En consecuencia, los datos obtenidos como resultados de este estudio serían distintos en otras condiciones de prueba. En otras palabras, los valores absolutos del rendimiento serán distintos, pero los valores relativos deberían ser parecidos (para cierta plataforma).

Puesto que en MQL5 la función iMA no devuelve los resultados del cálculo directamente (devuelve el identificador del indicador), vamos a probar la velocidad de dos funciones: iMA y CopyBuffer.

Condiciones de la prueba:
  • CPU: Core i7 965
  • Símbolo: "EURUSD"
  • Tamaño de los datos del precio: 10.0000 elementos
  • Terminal de cliente: autónomo, se establece el número máximo de barras en el gráfico a 10.000
  • Modelos de promedios móviles: MODE_SMA, MODE_EMA, MODE_SMMA, MODE_LWMA
  • La precisión de la velocidad de cálculo se limita a 2 cifras significativas.
  • El número de llamadas posibles de las funciones de los promedios móviles: 7


2. Cómo hemos realizado las pruebas

Para medir el tiempo de computación de los promedios móviles, disponemos de la función GetTickCount(), que opera en milisegundos. Esta precisión no es suficiente, por lo que tenemos que poner algunos bucles para mejorar la calidad de las medidas.

Sin embargo, si repetimos el mismo bucle varias veces con el mismo cálculo y los mismos datos de entrada, los resultados se pueden distorsionar. Esto se debe a lo siguiente: la función iMA crea una copia del indicador técnico correspondiente en el caché global del terminal de cliente. Si ya existe la copia de un indicador (con los mismos parámetros) en el caché global, no se crea una nueva copia y se incrementa el contador de referencia de copias del indicador.

En otras palabras, se calcula el indicador del buffer entero solo una vez a la primera llamada, y en todas las llamadas siguientes toma únicamente los valores ya preparados, solo recalcula los nuevos datos.

Por lo tanto, se debe organizar el bucle correctamente, cuando los parámetros de entrada del indicador son únicos durante un ciclo. Hemos elegido tres de estos parámetros: el período promedio, la periodicidad y el precio aplicado.

Parámetro
 Rango de valores
 Período promedio
 de 1 a 100
 Periodicidad
 М1, М5, М15, М30
 Precio aplicado
 PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED

Tabla 1. Los rangos de los parámetros de entrada

Vamos a calcular los valores del promedio móvil para una matriz con 10.000 elementos, mediante siete métodos de llamada distintos (ver los detalles en la sección 4).


3. Los resultados del estudio

Hemos combinado todos los resultados en la Tabla 1, se hace la estimación del rendimiento del cálculo mediante el tiempo del cálculo (ver la Tabla 1) en segundos. El programa calcula 100x4x7=2800 tipos de promedios móviles, y determinamos el tiempo de cálculo para la matriz del precio con 10.000 elementos. El tiempo de cálculo de una única pasada (ciclo) es aproximadamente igual al tiempo total dividido entre 2800. Por ejemplo, para el caso 1 y el modo SMA es igual a ~ 0,0028/2800.

Modo
MODE_SMA MODE_EMA MODE_SMMA MODE_LWMA Plataforma
0   (ver sección 4.1)
0,0041 0,0040 0,0043 0,0041  MetaTrader 4
1   (ver sección 4.2) 0,0028 0,00023 0,00027 0,0045  MetaTrader 5
2   (ver sección 4.3) 0,0029 0,0029 0,0029 0,0029  MetaTrader 5
3   (ver sección 4.4) 0,0998 0,0997 0,0998 0,0998  MetaTrader 5
4   (ver sección 4.5) 0,0996 0,0996 0,0996 0,0996  MetaTrader 5
5   (ver sección 4.6) 0,0030 0,0029 0,0029 0,0029  MetaTrader 5
6   (ver sección 4.7) 0,000140 0,000121 0,000117 0,0035  MetaTrader 5

Tabla 2. Los resultados:

Veremos el significado de los casos de las pruebas más adelante (secciones 4.1-4.7). Vamos a evaluar el rendimiento del cálculo de los promedios móviles en todo su conjunto. 

Para mayor comodidad, se presentan los resultados en gráficos (ver figuras de 1 a 5). El eje X representa el tipo de llamada del Promedio móvil (ver tabla 2), los valores del eje Y están representados en una escala logarítmica y multiplicados por -1, de modo que mayor sea valor más rápido será el rendimiento. Cada modelo de cálculo (SMA, EMA, SMMA, LWMA) corresponde a una columna en el gráfico.

Figura 1. Resultados de las pruebas de rendimiento para distintos algoritmos de Promedios móviles.

Figura 1. Resultados de las pruebas de rendimiento para distintos algoritmos de Promedios móviles.

Se puede observar una diferencia relevante en la velocidad computacional para los distintos casos de cálculo de los promedios móviles. ¿Qué significa esto? Los distintos algoritmos de cálculo de Promedios móviles, proporcionados por los desarrolladores de MQL5 tienen un rendimiento computacional distinto: hay un algoritmo muy rápido (caso 6) y otros más lentos (casos 3 y 4). Por lo tanto, hay que elegir los algoritmos adecuados a la hora de escribir programas en MQL5, y que utilizen Promedios móviles.

Se representa en detalle el tiempo de cálculo de cada modelo de Promedios móviles (0-6) en las siguientes figuras, ver la tabla 2.

Figura 2. Cálculo del rendimiento computacional del promedio móvil del modo MODE_SMA

Figura 2. Cálculo del rendimiento computacional del promedio móvil del modo MODE_SMA

Figura 3. Cálculo del rendimiento computacional del promedio móvil del modo MODE_EMA

Figura 3. Cálculo del rendimiento computacional del promedio móvil del modo MODE_EMA

Figura 4. Cálculo del rendimiento computacional del promedio móvil del modo MODE_SMMA

Figura 4. Cálculo del rendimiento computacional del promedio móvil del modo MODE_SMMA

Figura 5. Cálculo del rendimiento computacional del promedio móvil del modo MODE_LWMA

Figura 5. Cálculo del rendimiento computacional del promedio móvil del modo MODE_LWMA

Es interesante comparar el rendimiento del cálculo de las dos plataformas. MetaTrader 4 y MetaTrader 5. Se representan los resultados en la tabla 2, caso nº 0 (MQL4) y caso nº 2 (MQL5).

Por comodidad, vamos a combinar los resultados de los cálculos del indicador estándar iMA en tablas y gráficos separados (ver la figura 6). El eje Y representa el tiempo de cálculo de las pruebas.

Figura 6. Comparativa del rendimiento del cálculo entre MetaTrader 4 y MetaTrader 5

Figura 6. Comparativa del rendimiento del cálculo entre MetaTrader 4 y MetaTrader 5

Conclusiones:

  1. La nueva plataforma MetaTrader 5 es 40% más rápida que MetaTrader 4.
  2. Se han alcanzado los rendimientos más rápidos con los modelos SMA, EMA y SMMA (caso nº 6), para LWMA (casos nº 2 y nº 5).
  3. Cuando se utiliza el indicador estándar iMA en las distintas pruebas, el rendimiento del cálculo para los distintos modelos es prácticamente idéntico. No es el caso con las funciones de la librería MovingAverages.mqh. La diferencia de rendimiento es del orden de (0,00023~0,0045) para los distintos modelos.
  4. Los resultados presentados corresponden a un "arranque en frío" (cold start), no hay ningún dato precalculado en el caché global del terminal de cliente.


4. Estudios de caso

Los desarrolladores de MQL5 recomiendan el siguiente método para obtener los valores de los indicadores técnicos estándar:

//---- indicator buffers
double      MA[];                // array for iMA indicator values
//---- handles for indicators
int         MA_handle;           // handle of the iMA indicator
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- creating handle of the iMA indicator
   MA_handle=iMA(NULL,0,21,0,MODE_EMA,PRICE_CLOSE);
   //--- print message if there was an error
   if(MA_handle<0)
      {
      Print("The iMA object is not created: MA_handle= ",INVALID_HANDLE);
      Print("Runtime error = ",GetLastError());
      //--- forced termination of program
      return(-1);
      }
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- filling the MA[] array with current values of the iMA indicator
   //--- we will copy 100 elements, or return if there was an error
   if(CopyBuffer(MA_handle,0,0,100,MA)<=0) return;
   //--- set ordering of MA[] as timeseries
   ArraySetAsSeries(MA,true);  
   //--- here you can do anything with these data
  }

En el artículo"MQL5 para principiantes: Guía para el uso de indicadores técnicos en Expert Advisors", se describe en detalle este método .

Para probar el rendimiento computacional de los promedios móviles, es mejor utilizar el script, puesto que puede llevar a cabo todos los cálculos sin esperar los eventos (por ejemplo, el evento de un nuevo tick, etc.).

No es necesario crear un programa universal separado para todos los casos de las pruebas, por lo tanto vamos a crear un script separado para cada caso de cálculo de Promedio móvil.

Vamos a ver en detalle cada caso de cálculo del Promedio móvil.


4.1. Caso nº 0

En este caso hemos medido el rendimiento de computación del indicador técnico iMA con MQL4. Se realizan los cálculos en MetaTrader 4 y se llevan a cabo con todos los datos.

Modelo Resultado Mejor resultado
MODE_SMA 0,0041 0,000140 (caso 6)
MODE_EMA 0,0040 0,000121 (caso 6)
MODE_SMMA 0,0043 0,000117 (caso 6)
MODE_LWMA 0,0041 0,0029 (casos 2 y 5)


A continuación está el código de este caso (MQL4):

int         M[4]=
  {
   PERIOD_M1,
   PERIOD_M5,
   PERIOD_M15,
   PERIOD_M30
  };
int         P[7]=
  {
   PRICE_CLOSE,
   PRICE_OPEN,
   PRICE_HIGH,
   PRICE_LOW,
   PRICE_MEDIAN,
   PRICE_TYPICAL,
   PRICE_WEIGHTED
  };
int         periodMA;
double      buf[];
double      time;
int         count=10000;
int         startGTC,endGTC;
int         m,p;
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start()
  {
   if(ArrayResize(buf,count)<0) return(-1);
   Print("START ");
   startGTC=GetTickCount();
//----
   for(m=0;m<=3;m++)
     {
      for(p=0;p<=6;p++)
        {
         for(periodMA=1;periodMA<=100;periodMA++)
           {
           Test0();
           }
        }
     }
//----
   endGTC=GetTickCount();
   time=endGTC-startGTC;
   Print("Total time [msec] ",time);
   time=time/1000/m/p/periodMA;
   Print("Performance [sec] ",DoubleToStr(time, 10));
   return(0);
  }
//+------------------------------------------------------------------+
void Test0()
  {
//--- Model: MODE_SMA; MODE_EMA; MODE_SMMA; MODE_LWMA
   for(int i=0;i<count;i++)
     {
      buf[i]=iMA(NULL,M[m],periodMA,0,MODE_SMA,P[p],i);
     }
  }

Nota: Este código no va a funcionar con MetaTrader 5, ya que fue escrito en MQL4. Hay que ejecutarlo en el terminal de cliente de MetaTrader.


4.2. Caso nº 1

En este caso hemos realizado los cálculos de 4 modelos: nº 1(SMA), nº 2(EMA), nº3(SMMA) y nº 4(LWMA) mediante las funciones de la librería MovingAverages.mqh.

Se lleva a cabo el cálculo con todos los datos de la matriz.

Modelo
 Resultado Mejor resultado
MODE_SMA
0,0028
0,000140 (caso 6)
MODE_EMA
0,00023
0,000121 (caso 6)
MODE_SMMA
0,00027 0,000117 (caso 6)
MODE_LWMA
0,0045 0,0029 (casos 2 y 5)
#include <MovingAverages.mqh>
ENUM_TIMEFRAMES      M[4]=
  {
   PERIOD_M1,
   PERIOD_M5,
   PERIOD_M15,
   PERIOD_M30
  };
ENUM_APPLIED_PRICE   P[7]=
  {
   PRICE_CLOSE,
   PRICE_OPEN,
   PRICE_HIGH,
   PRICE_LOW,
   PRICE_MEDIAN,
   PRICE_TYPICAL,
   PRICE_WEIGHTED
  };
int         periodMA;
double      buf[],close[];
double      time;
int         count=10000;
int         startGTC,endGTC;
int         m,p;
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int OnStart()
  {
   if(ArrayResize(buf,count)<0) return(-1);
   ArraySetAsSeries(buf,false);
   ArraySetAsSeries(close,false);
   startGTC=GetTickCount();
//---
   for(m=0;m<=3;m++)
     {
      for(p=0;p<=6;p++)
        {
         CopyClose(_Symbol,M[m],0,count,close);
         for(periodMA=1;periodMA<=100;periodMA++)
           {
            Test1(); // the test is here
           }
        }
     }
//---
   endGTC=GetTickCount();
   time=endGTC-startGTC;
   time=time/1000/m/p/periodMA;
//---
   return(0);
  }
//+------------------------------------------------------------------+
void Test1()
  {
   for(int i=0;i<count;i++)
     {
      buf[i]=SimpleMA(i,periodMA,close);
     }
  }
//+------------------------------------------------------------------+
void Test2()
  {
   buf[0]=close[0];
   for(int i=1;i<count;i++)
     {
      buf[i]=ExponentialMA(i,periodMA,buf[i-1],close);
     }
  }
//+------------------------------------------------------------------+
void Test3()
  {
   buf[0]=close[0];
   for(int i=1;i<count;i++)
     {
      buf[i]=SmoothedMA(i,periodMA,buf[i-1],close);
     }
  }
//+------------------------------------------------------------------+
void Test4()
  {
   for(int i=0;i<count;i++)
     {
      buf[i]=LinearWeightedMA(i,periodMA,close);
     }
  }

Nota. Nuestra idea era utilizar distintos tipos de datos en la matriz, pero para simplificar, hemos utilizado una única matriz con los datos de los precios de cierre (no afecta al rendimiento de de los cálculos).


4.3. Caso nº 2

En este caso hemos utilizado el indicador técnico estándar iMA y la prueba nº 5.

Se lleva a cabo el cálculo con todos los datos de la matriz.

Modelo Resultado Mejor resultado
MODE_SMA 0,0029 0,000140 (caso 6)
MODE_EMA 0,0029 0,000121 (caso 6)
MODE_SMMA 0,0029 0,000117 (caso 6)
MODE_LWMA 0,0029 0,0029 (casos 2 y 5)
 MODE_SMA; MODE_EMA; MODE_SMMA; MODE_LWMA
   MA_handle=iMA(NULL,M[m],periodMA,0,MODE_SMA,P[p]);
   while(BarsCalculated(MA_handle)<count){}
   CopyBuffer(MA_handle,0,0,count,MA);
  }


4.4. Caso nº 3

En el caso nº 3, se utilizan las clases que manejan los indicadores de la Librería estándar.

Se copian los datos elemento por elemento. Se lleva a cabo el cálculo con todos los datos de la matriz.

Modelo Resultado Mejor resultado
MODE_SMA 0,0998 0,000140 (caso 6)
MODE_EMA 0,0997 0,000121 (caso 6)
MODE_SMMA 0,0998 0,000117 (caso 6)
MODE_LWMA 0,0998 0,0029 (casos 2 y 5)
#include <Indicators\Trend.mqh>
ENUM_TIMEFRAMES      M[4]=
  {
   PERIOD_M1,
   PERIOD_M5,
   PERIOD_M15,
   PERIOD_M30
  };
ENUM_APPLIED_PRICE   P[7]=
  {
   PRICE_CLOSE,
   PRICE_OPEN,
   PRICE_HIGH,
   PRICE_LOW,
   PRICE_MEDIAN,
   PRICE_TYPICAL,
   PRICE_WEIGHTED
  };
int         periodMA;
double      buf[];
double      time;
int         count=10000;
int         startGTC,endGTC;
int         m,p;
CiMA        objMA;
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int OnStart()
  {
   if(ArrayResize(buf,count)<0) return(-1);
   ArraySetAsSeries(buf,false);
   startGTC=GetTickCount();
//---
   for(m=0;m<=3;m++)
     {
      for(p=0;p<=6;p++)
        {
         for(periodMA=1;periodMA<=100;periodMA++)
           {
            Test6();
           }
        }
     }
//---
   endGTC=GetTickCount();
   time=endGTC-startGTC;
   time=time/1000/m/p/periodMA;
//---
   return(0);
  }
//+------------------------------------------------------------------+
void Test6()
  {
//--- Model: MODE_SMA; MODE_EMA; MODE_SMMA; MODE_LWMA
   objMA.Create(NULL,M[m],periodMA,0,MODE_SMA,P[p]);
   objMA.BuffSize(count);
   objMA.Refresh(1);
   for(int i=0;i<count;i++)
     {
      buf[i]=objMA.Main(i);
     }
  }

4.5. Caso nº 4

En el caso nº 4, se utilizan las clases que manejan los indicadores de la Librería estándar.

Se copia toda la matriz del buffer del indicador. Se lleva a cabo el cálculo con todos los datos de la matriz.

Modelo Resultado Mejor resultado
MODE_SMA 0,0996 0,000140 (caso 6)
MODE_EMA 0,0996 0,000121 (caso 6)
MODE_SMMA 0,0996 0,000117 (caso 6)
MODE_LWMA 0,0996 0,0029 (casos 2 y 5)
#include <Indicators\Trend.mqh>
ENUM_TIMEFRAMES      M[4]=
  {
   PERIOD_M1,
   PERIOD_M5,
   PERIOD_M15,
   PERIOD_M30
  };
ENUM_APPLIED_PRICE   P[7]=
  {
   PRICE_CLOSE,
   PRICE_OPEN,
   PRICE_HIGH,
   PRICE_LOW,
   PRICE_MEDIAN,
   PRICE_TYPICAL,
   PRICE_WEIGHTED
  };
int         periodMA;
double      buf[];
double      time;
int         count=10000;
int         startGTC,endGTC;
int         m,p;
CiMA        objMA;
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int OnStart()
  {
   if(ArrayResize(buf,count)<0) return(-1);
   ArraySetAsSeries(buf,false);
   startGTC=GetTickCount();
//---
   for(m=0;m<=3;m++)
     {
      for(p=0;p<=6;p++)
        {
         for(periodMA=1;periodMA<=100;periodMA++)
           {
            Test7();
           }
        }
     }
//---
   endGTC=GetTickCount();
   time=endGTC-startGTC;
   time=time/1000/m/p/periodMA;
//---
   return(0);
  }
//+------------------------------------------------------------------+
void Test7()
  {
//--- Models: MODE_SMA; MODE_EMA; MODE_SMMA; MODE_LWMA
   objMA.Create(NULL,M[m],periodMA,0,MODE_SMA,P[p]);
   objMA.BuffSize(count);
   objMA.Refresh(1);
   objMA.GetData(0,count,0,buf);          
  }


4.6. Caso nº 5

Se utiliza la prueba nº8: se crea el identificador del indicador mediante la función IndicatorCreate.

Se lleva a cabo el cálculo con todos los datos de la matriz.
Modelo Resultado Mejor resultado
MODE_SMA 0,0030 0,000140 (caso 6)
MODE_EMA 0,0029 0,000121 (caso 6)
MODE_SMMA 0,0029 0,000117 (caso 6)
MODE_LWMA 0,0029 0,0029 (casos 2 y 5)
ENUM_TIMEFRAMES      M[4]=
  {
   PERIOD_M1,
   PERIOD_M5,
   PERIOD_M15,
   PERIOD_M30
  };
ENUM_APPLIED_PRICE   P[7]=
  {
   PRICE_CLOSE,
   PRICE_OPEN,
   PRICE_HIGH,
   PRICE_LOW,
   PRICE_MEDIAN,
   PRICE_TYPICAL,
   PRICE_WEIGHTED
  };
int         periodMA;
double      time;
int         count=10000;
int         startGTC,endGTC;
int         m,p;
double      MA[];                // array for the iMA indicator
int         MA_handle;           // handle of the iMA indicator
MqlParam    params[];
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int OnStart()
  {
   ArrayResize(params,4);
   startGTC=GetTickCount();
//---
   for(m=0;m<=3;m++)
     {
      for(p=0;p<=6;p++)
        {
         for(periodMA=1;periodMA<=100;periodMA++)
           {
            Test8();
           }
        }
     }
//---
   endGTC=GetTickCount();
   time=endGTC-startGTC;
   time=time/1000/m/p/periodMA;
//---
   return(0);
  }
//+------------------------------------------------------------------+
void Test8()
  {
//--- Model: MODE_SMA; MODE_EMA; MODE_SMMA; MODE_LWMA
//--- set ma_period
   params[0].type         =TYPE_INT;
   params[0].integer_value=periodMA;
//--- set ma_shift
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
//--- set ma_method
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
//--- set applied_price
   params[3].type         =TYPE_INT;
   params[3].integer_value=P[p];
//--- create MA
   MA_handle=IndicatorCreate(NULL,M[m],IND_MA,4,params);
   while(BarsCalculated(MA_handle)<count){}
   CopyBuffer(MA_handle,0,0,count,MA);
  }


4.7. Caso nº 6

En este caso hemos realizado los cálculos de 4 modelos: nº 9(SMA), nº 10(EMA), nº 11(SMMA) y nº 12(LWMA) mediante la librería de funciones MovingAverages.mqh (las funciones de buffers como iMAOnArray de MQL4).

Se lleva a cabo el cálculo con todos los datos de la matriz.

Modelo Resultado Mejor resultado
MODE_SMA 0,000140 0,000140 (caso 6)
MODE_EMA 0,000121 0,000121 (caso 6)
MODE_SMMA 0,000117 0,000117 (caso 6)
MODE_LWMA 0,00350 0,0029 (casos 2 y 5)
#include <MovingAverages.mqh>
ENUM_TIMEFRAMES         M[4]=
  {
   PERIOD_M1,
   PERIOD_M5,
   PERIOD_M15,
   PERIOD_M30
  };
ENUM_APPLIED_PRICE         P[7]=
  {
   PRICE_CLOSE,
   PRICE_OPEN,
   PRICE_HIGH,
   PRICE_LOW,
   PRICE_MEDIAN,
   PRICE_TYPICAL,
   PRICE_WEIGHTED
  };
int         periodMA;
double      buf[],arr[];
double      close[];
double      time;
int         count=10000,total;
int         startGTC,endGTC;
int         m,p;
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int OnStart()
  {
   CopyClose(_Symbol,_Period,0,count,close);
   total=ArrayCopy(arr,close);
   if(ArrayResize(buf,total)<0) return(-1);
//---
   ArraySetAsSeries(close,false);
   ArraySetAsSeries(arr,false);
   ArraySetAsSeries(buf,false);
   startGTC=GetTickCount();
//---
   for(m=0;m<=3;m++)
     {
      for(p=0;p<=6;p++)
        {
         CopyClose(_Symbol,M[m],0,count,close);
         total=ArrayCopy(arr,close);
         for(periodMA=1;periodMA<=100;periodMA++)
           {
            Test9();    // the test is here
           }
        }
     }
//---
   endGTC=GetTickCount();
   time=endGTC-startGTC;
   time=time/1000/m/p/periodMA;
//---
   return(0);
  }
//+------------------------------------------------------------------+
void Test9()
  {
   SimpleMAOnBuffer(total,0,0,periodMA,arr,buf);
  }
//+------------------------------------------------------------------+
void Test10()
  {
   ExponentialMAOnBuffer(total,0,0,periodMA,arr,buf);
  }
//+------------------------------------------------------------------+
void Test11()
  {
   SmoothedMAOnBuffer(total,0,0,periodMA,arr,buf);
  }
//+------------------------------------------------------------------+
void Test12()
  {
   LinearWeightedMAOnBuffer(total,0,0,periodMA,arr,buf);
  }

Nota. Nuestra idea era utilizar distintos tipos de datos en la matriz, pero para simplificar, hemos utilizado una única matriz con los datos de los precios de cierre (no afecta al rendimiento de los cálculos).


5. La salida de los resultados

Para la salida de los resultados y la comprobación de los promedios móviles, he recurrido a la función PrintTest:

void PrintTest(const int position, const double &price[])
{
   Print("Total time [msec] ",(endGTC-startGTC));
   Print("Performance [sec] ",time);
   Print(position," - array element = ",price[position]);
}

Se le puede llamar, de la siguiente manera (la posición de la barra y la matriz de datos son parámetros de la función):

//---
   ArraySetAsSeries(buf,false);
   ArraySetAsSeries(close,false);
   startGTC=GetTickCount();
//---
   for(m=0;m<=3;m++)
     {
      for(p=0;p<=6;p++)
        {
         for(periodMA=1;periodMA<=100;periodMA++)
           {
            Test();
           }
        }
     }
//---
   endGTC=GetTickCount();
   time=endGTC-startGTC;
   time=time/1000/m/p/periodMA;
//--- Output of results
   ArraySetAsSeries(buf,true);
   ArraySetAsSeries(close,true);
   PrintTest(0,buf);
   PrintTest(0,close);
//---

Tenga en cuenta que los índices de la matriz son distintos antes y después de los cálculos.

IMPORTANTE. Se establece la señal de AsSeries en false durante los cálculos y en true al mostrar los resultados.


6. Estudios adicionales

Para poder responder a la pregunta sobre la influencia de los parámetros iniciales en el rendimiento del cálculo, hacen falta algunas medidas adicionales.

Como recordaremos, el caso nº 6 presenta el mejor rendimiento, así que lo vamos a utilizar.

Parámetros de las pruebas:

Modo
Periodicidad
 Período promedio
1
М1
144
2
М5
144
3
М15
144
4
М30
144
5
М1 21
6
М1 34
7
М1 55
8
М1 89
9
М1 233
10
М1 377
11
М1 610
12
М1 987

Tabla 3. Estudios adicionales

Código fuente de las pruebas:

//+------------------------------------------------------------------+
//| Test_SMA                                       Model: MODE_SMA   |
//+------------------------------------------------------------------+
void Test_SMA(int periodMA,ENUM_TIMEFRAMES periodTF)
  {
   CopyClose(_Symbol,periodTF,0,count,close);
   int total=ArrayCopy(arr,close);
   SimpleMAOnBuffer(total,0,0,periodMA,arr,buf);
  }
//+------------------------------------------------------------------+
//| Test_EMA                                       Model: MODE_EMA   |
//+------------------------------------------------------------------+
void Test_EMA(int periodMA,ENUM_TIMEFRAMES periodTF)
  {
   CopyClose(_Symbol,periodTF,0,count,close);
   int total=ArrayCopy(arr,close);
   ExponentialMAOnBuffer(total,0,0,periodMA,arr,buf);
  }
//+------------------------------------------------------------------+
//| Test_SMMA                                      Model: MODE_SMMA  |
//+------------------------------------------------------------------+
void Test_SMMA(int periodMA,ENUM_TIMEFRAMES periodTF)
  {
   CopyClose(_Symbol,periodTF,0,count,close);
   int total=ArrayCopy(arr,close);
   SmoothedMAOnBuffer(total,0,0,periodMA,arr,buf);
  }
//+------------------------------------------------------------------+
//| Test_LWMA                                      Model: MODE_LWMA  |
//+------------------------------------------------------------------+
void Test_LWMA(int periodMA,ENUM_TIMEFRAMES periodTF)
  {
   CopyClose(_Symbol,periodTF,0,count,close);
   int total=ArrayCopy(arr,close);
   LinearWeightedMAOnBuffer(total,0,0,periodMA,arr,buf);
  }

Vamos a utilizar el programa de prueba automática "autotest" para las pruebas adicionales, se muestra su interfaz gráfica en la figura 7.

Figura 7. El programa autotest para las pruebas automáticas

Figura 7. El programa autotest para las pruebas automáticas

Resultados: (los ejes X tienen una escala de tiempo logarítmica)

Figura 8. El parámetro de periodicidad (Y) y el rendimiento de computación de los Promedios móviles (X)

Figura 8. El parámetro de periodicidad (Y) y el rendimiento de computación de los Promedios móviles (X)

Figura 9. El parámetro período (Y) y el rendimiento de computación de los Promedios móviles (X)

Figura 9. El parámetro período (Y) y el rendimiento de computación de los Promedios móviles (X)

Las conclusiones de los resultados de los estudios adicionales:

  1. La periodicidad no es un parámetro importante, no afecta al rendimiento de los cálculos (ver figura 8).
  2. El periodo no es un parámetro importante para el rendimiento de computación de los promedios móviles para los modelos SMA, EMA y SMMA. Sin embrago, ralentiza de manera significativa los cálculos (de 0,00373 segundos a 0,145 segundos) para el modelo LWMA (ver figura 9).


Conclusión

Una elección errónea del algoritmo de los promedios móviles pude reducir el rendimiento de los cálculos de tus programas.


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

Archivos adjuntos |
autotest__1.zip (10.86 KB)
Los principios del cálculo económico de los indicadores Los principios del cálculo económico de los indicadores
Las llamadas a los usuarios y a los indicadores técnicos requieren muy poco espacio en el código del programa de los sistemas de trading automatizados. Se trata a menudo de pocas líneas de código. Pero con frecuencia, son estas pocas líneas de código las que requieren la mayor parte del tiempo, destinada a probar el Expert Advisor. Por lo tanto, hay que tener en cuenta mucho más de lo parecía al principio todo lo que está relacionado con los cálculos de datos en un indicador. Este artículo trata justamente esta cuestión.
Creación de un Expert Advisor que opera con varios instrumentos Creación de un Expert Advisor que opera con varios instrumentos
El concepto de diversificación de activos en los mercados financieros es bastante antiguo, y siempre ha atraído a los operadores principiantes. En este artículo, el autor propone un enfoque muy simplificado para la implementación de un Expert Advisor multidivisa, para una introducción inicial a este tipo de estrategias de trading.
El uso de ORDER_MAGIC para el trading con distintos Expert Advisors con un solo instrumento El uso de ORDER_MAGIC para el trading con distintos Expert Advisors con un solo instrumento
Este artículo aborda cuestiones de codificación de la información, mediante la identificación mágica (magic-identification), así como la división, la agrupación y la sincronización del trading automatizado de distintos Expert Advisors. Este artículo puede resultar interesante para los principiantes, como para los traders más experimentados, ya que aborda la cuestión de las posiciones virtuales, que pueden ser muy útiles en la implementación de sistemas complejos de sincronización de los Expert Advisors y de varias estrategias.
Análisis de los patrones de velas Análisis de los patrones de velas
La construcción de un gráfico de velas japonesas y el análisis de los patrones de velas constituye un área fascinante del análisis técnico. La ventaja de las velas es que representan los datos de una forma que le permite hacer un seguimiento de las dinámicas en los datos. En este artículo vamos a analizar los tipos de velas, la clasificación de los patrones de velas y presentar un indicador que puede determinar patrones de velas.