English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Testando o desempenho do cálculo das médias móveis no MQL5

Testando o desempenho do cálculo das médias móveis no MQL5

MetaTrader 5Exemplos | 23 janeiro 2014, 14:46
3 473 0
Sergey Pavlov
Sergey Pavlov

Introdução

O uso de médias móveis é uma prática comum na análise de séries de tempo do mercado, em programação de Expert Advisors e indicadores. é o método de suavização dos dados de preço mais popular. Na nova versão da linguagem MQL há uma dúzia de algoritmos de média móvel disponíveis.

Qual é a diferença entre eles? O cálculo é dependente de velocidade em certos algoritmos das médias móveis? Qual algoritmo é o mais rápido?

A velocidade do cálculo das médias móveis aumentou no MetaTrader 5 em comparação com o MetaTrader 4? Parecem haver muitas dessas questões surgindo. Então vamos considerar a maioria delas.

Claro, a velocidade de uma nova plataforma é impressionante, mas é melhor verificar experimentalmente.


1. Condições de teste

A velocidade do cálculo é dependente de muitos fatores. Portanto, os dados que foram combinados como resultados, em outras condições de teste seriam diferentes. Em outras palavras, os valores absolutos de desempenho serão diferentes, mas valores relativos devem ser semelhantes (para uma determinada plataforma).

Devido ao fato de que a função iMA no MQL5 não retorna os resultados de cálculo por si (ela retorna um handle do indicador), testaremos a velocidade de duas funções: iMA e CopyBuffer.

Condições de teste:
  • CPU: Core i7 965
  • Símbolo: "EURUSD"
  • Tamanho dos dados do preço: 10000 elementos
  • Terminal do cliente: autônomo, o número máximo de barras no gráfico é configurado para 10000
  • Modelos de médias móveis: MODE_SMA, MODE_EMA, MODE_SMMA, MODE_LWMA
  • A precisão da velocidade de cálculo é limitada a dois dígitos significantes
  • O número possível de chamadas das funções das médias móveis: 7


2. Como nós testamos

Para medir o tempo do cálculo das médias móveis nós possuímos a função GetTickCount(), que opera em milissegundos. Essa precisão não é o suficiente, então nós precisamos organizar alguns ciclos para melhorar a qualidade das medições.

No entanto, se nós repetirmos o ciclo muitas vezes com o mesmo cálculo e as mesmas entradas de dados, os resultados serão distorcidos. A razão para esse fato é a seguinte: a função iMA cria uma cópia do indicador técnico correspondente no cache global do terminal do cliente. Se a cópia de um indicador (com os mesmos parâmetros) já está presente no cache global, a nova cópia não é criada, o contador de referência da cópia do indicador é aumentado.

Em outras palavras, todo o indicador de memória de armazenamento (buffer) é calculado apenas uma vez na primeira chamada, e em todas as chamadas subsequentes ele só toma os valores prontos, calculando novamente apenas os dados novos.

Então, o loop deve ser organizado no caminho, quando os parâmetros de entrada do indicador forem únicos durante o ciclo. Selecionamos três destes parâmetros: período de média; cronograma e preço aplicado.

Parâmetro
Gama de valores
Período de média
de 1 a 100
Cronograma
М1, М5, М15, М30
Preço aplicado
PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED

Tabela 1. Tabela 1.

Calcularemos os valores da média móvel para o arranjo com 10000 elementos utilizando sete diferentes métodos de chamada (ver detalhes na seção 4).


3. Os resultados do estudo

Combinamos todos os resultados na tabela 1, o desempenho calculado é estimado utilizando o tempo de cálculo (ver tabela 1) em segundos. O programa calcula 100х4х7=2800 tipos de médias móveis, e nós determinamos o tempo de cálculo para o arranjo de preço com 10 000 elementos. O tempo de cálculo do passo único (ciclo) é aproximadamente igual ao tempo total dividido por 2800. Por exemplo, para o caso 1 e modo SMA ele é igual a ~ 0,0028/2800.

Modo
MODE_SMAMODE_EMAMODE_SMMAMODE_LWMAPlataforma
0 (ver seção 4.1)
0,0041 0,0040 0,0043 0,0041 MetaTrader 4
1 (ver seção 4.2) 0,0028 0,00023 0,00027 0,0045 MetaTrader 5
2 (ver seção 4.3) 0,0029 0,0029 0,0029 0,0029 MetaTrader 5
3 (ver seção 4.4) 0,0998 0,0997 0,0998 0,0998 MetaTrader 5
4 (ver seção 4.5) 0,0996 0,0996 0,0996 0,0996 MetaTrader 5
5 (ver seção 4.6) 0,0030 0,0029 0,0029 0,0029 MetaTrader 5
6 (ver seção 4.7) 0,000140 0,000121 0,000117 0,0035 MetaTrader 5

Tabela 2. Os resultados

O significado de casos de teste serão considerados adiante (seções 4.1-4.7). Vamos estimar o quadro completo de cálculo do desempenho da média móvel.

Por conveniência, os resultados são apresentados em gráficos (ver figuras 1-5). O tipo de chamada da média móvel é apresentado nos eixos X (ver tabela 2), os valores nos eixos Y são apresentados em escala logarítmica multiplicados por -1 de forma que os valores maiores significassem desempenho mais rápido. Cada modelo de cálculo (SMA, EMA, SMMA, LWMA) corresponde a uma coluna do gráfico.

Figura 1 Os resultados do teste de desempenho para diferentes algoritmos de média móvel

Figura 1. Os resultados do teste de desempenho para diferentes algoritmos de média móvel

Pode-se ver a diferença significante no cálculo da velocidade para diferentes casos de cálculo de média móvel. O que isso quer dizer? Os diversos algoritmos para cálculo da média móvel, fornecidos pelos desenvolvedores do MQL5 possuem diferentes desempenhos de cálculo: há um algoritmo mais rápido (caso 6) e métodos mais lentos (casos 3 e 4). Então, é necessário escolher os algorítimos corretos ao escrever programas no MQL5, que usa média móveis.

O cálculo de tempo de cada um dos modelos de médias móveis (0-6) é apresentado em detalhes nas figuras seguintes, ver tabela 2.

Figura 2. O desempenho de cálculo de MA do modo MODE_SMA

Figura 2. O desempenho de cálculo de MA do modo MODE_SMA

Figura 3. O desempenho de cálculo de MA do modo MODE_EMA

Figura 3. O desempenho de cálculo de MA do modo MODE_EMA

Figura 4. O desempenho de cálculo de MA do modo MODE_SMMA

Figura 4. O desempenho de cálculo de MA do modo MODE_SMMA

Figura 5. O desempenho de cálculo de MA do modo MODE_LWMA

Figura 5. O desempenho de cálculo de MA do modo MODE_LWMA

é interessante comprar o cálculo de desempenho das duas plataformas: MetaTrader 4 e MetaTrader 5. Os resultados são apresentados na tabela 2 caso nº. 0 (MQL4) e caso nº. 2 (MQL5).

Por conveniência, vamos combinar os resultados do cálculo do indicador padrão IMA em um gráfico e tabela separados (ver figura 6). O cálculo do tempo do teste é apresentado nos eixos Y.

Figura 6. Gráfico comparativo do desempenho de cálculo do MetaTrader 4 и MetaTrader 5

Figura 6. Gráfico comparativo do desempenho de cálculo do MetaTrader 4 и MetaTrader 5

Conclusões:

  1. A nova plataforma do MetaTrader 5 é 40% mais rápida do que a do MetaTrader 4 anterior.
  2. O desempenho mais rápido foi alcançado para os modelos SMA, EMA, e SMMA (caso nº. 6), para LWMA (casos nº. 2 e nº. 5).
  3. Para os casos de teste, quando o indicador padrão iMA é utilizado, o desempenho de cálculo para diferentes modelos é praticamente o mesmo. Não é a verdade para as funções da biblioteca MovingAverages.mqh. Para modelos diferentes o desempenho difere por quase uma ordem de (0,00023~0,0045).
  4. Os resultados apresentados correspondem a um "início frio", não há nenhum dado pré-calculado no cache global do terminal do cliente.


4. Estudos de casos

Os desenvolvedores do MQL5 recomendam o seguinte método para obter os valores dos indicadores técnicos padrão:

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

Esse método é descrito em detalhes no artigo "MQL5 para novatos: Guia para utilizar indicadores técnicos em conselheiros experientes".

Para testar o desempenho de cálculo das médias móveis, é melhor usar o script, porque é capaz de realizar todos os cálculos sem aguardar os eventos (por exemplo, novo evento de tick, etc.).

é necessário criar um programa universal separado para todos os casos de teste, então, criaremos um script separado para cada caso de cálculo MA.

Então vamos considerar em detalhes os casos dos cálculos da média móvel.


4,1. Caso nº. 1

Neste caso nós medimos o cálculo do desempenho do indicador técnico iMA do MQL4. Os cálculos foram realizados no MetaTrader 4 e realizados com todos os dados.

ModeloResultadoMelhor 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, 5)


O código desse caso é o seguinte (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);
     }
  }

Observação: Esse código não funcionará no MetaTrader 5 porque está escrito em MQL4. Ele deve ser executado em um terminal do cliente do MetaTrader 4.


4,2. Caso nº. 1

Nesse caso nós realizamos o cálculo de 4 modelos: nº. 1(SMA), nº. 2(EMA), nº. 3(SMMA) e nº. 4(LWMA) utilizando as funções da biblioteca MovingAverages.mqh.

O cálculo é realizado com todos os arranjos de dados.

Modelo
ResultadoMelhor resultado
MODE_SMA
0,0028
0,000140 (caso 6)
MODE_EMA
0,00023
0,000121 (caso 6)
MODE_SMMA
0,000270,000117 (caso 6)
MODE_LWMA
0,00450,0029 (casos 2 e 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);
     }
  }

Observação. Nós planejamos utilizar os diversos tipos de dados da biblioteca no arranjo, mas para a simplicidade, utilizamos apenas um arranjo com dados de preços próximos (não afeta o desempenho dos cálculos).


4,3. Caso nº. 2

Neste caso, usamos o indicador técnico padrão iMA e teste nº 5.

O cálculo é realizado com todos os arranjos de dados.

ModeloResultadoMelhor 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 e 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
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int OnStart()
  {
   startGTC=GetTickCount();
//---
   for(m=0;m<=3;m++)
     {
      for(p=0;p<=6;p++)
        {
         for(periodMA=1;periodMA<=100;periodMA++)
           {
            Test5();
           }
        }
     }
//---
   endGTC=GetTickCount();
   time=endGTC-startGTC;
   time=time/1000/m/p/periodMA;
//---
   return(0);
  }
//+------------------------------------------------------------------+
void Test5()
  {
//--- Model: 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

No caso nº. 3 as classes trabalhando com indicadores das classes da biblioteca padrão são utilizadas.

A cópia de dados com relação aos elementos é utilizada. O cálculo é realizado com todos os arranjos de dados.

ModeloResultadoMelhor 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 e 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

No caso nº. 4 as classes trabalhando com indicadores das classes da biblioteca padrão são utilizadas.

O arranjo do indicador do buffer é copiado completamente. O cálculo é realizado com todos os arranjos de dados.

ModeloResultadoMelhor 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, 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

O teste nº 8 é usado: o handle do indicador é criado usando a função IndicatorCreate.

O cálculo é realizado com todos os arranjos de dados.
ModeloResultadoMelhor 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 e 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

Nesse caso nós realizamos o cálculo de 4 modelos: №9(SMA), №10(EMA), №11(SMMA) и №12(LWMA) usando as funções da biblioteca MovingAverages.mqh (o buffer funciona como iMAOnArray do MQL4).

O cálculo é realizado com todos os arranjos de dados.

ModeloResultadoMelhor 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 e 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);
  }

Observação. Nós planejamos utilizar os diversos tipos de dados da biblioteca no arranjo, mas para a simplicidade, utilizamos apenas um arranjo com dados de preços próximos (não afeta o desempenho dos cálculos).


5. Saída dos resultados

Para a saída dos resultados e verificação das médias móveis, utilizei a função 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]);
}

It can be called, as follows (the bar position and data array are parameters of the function):

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

Observe, que a classificação do arranjo é diferente antes e depois dos cálculos.

IMPORTANTE. O sinalizador AsSeries é configurado como falso durante os cálculos e é configurado para verdadeiro quando imprimindo os resultados.


6. Investigações adicionais

Para responder as perguntas sobre o efeito dos parâmetros iniciais no desempenho do cálculo, algumas medições adicionais foram feitas.

Que eu me lembre, o caso nº 6 tem a melhor performance, então nós iremos usá-lo.

Parâmetros de teste:

Modo
Cronograma
Período de média
1
М1
144
2
М5
144
3
М15
144
4
М30
144
5
М121
6
М134
7
М155
8
М189
9
М1233
10
М1377
11
М1610
12
М1987

Tabela 3. Investigações adicionais

Código fonte dos testes:

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

Para os testes adicionais, usaremos o programa autotest, sua interface de usuário gráfica é apresentada na Fig. 7.

Figura 7. O programa autotest para verificação automatizada

Figura 7. O programa autotest para a verificação automatizada

Resultados: (os eixos X possuem escala de tempo logarítmica)

Figura 8. O parâmetro Cronograma (Y) e o desempenho do cálculo das médias móveis (X)

Figura 8. O parâmetro Cronograma (Y) e o desempenho do cálculo das médias móveis (X)

Figura 9. O parâmetro Período (Y) e o desempenho do cálculo das médias móveis (X)

Figura 9. O parâmetro Período (Y) e o desempenho do cálculo das médias móveis (X)

As conclusões dos resultados de investigações adicionais:

  1. O parâmetro cronograma não é importante, ele não afeta o cálculo do desempenho (ver a figura 8).
  2. O período não é um parâmetro importante para o desempenho do cálculo das médias móveis para os modelos SMA, EMA e SMMA. Mas em contraste, ele significativamente (de 0,00373 segundos para ,145 segundos) desacelera os cálculos do modelo LWMA (ver a figura 9).


Conclusão

A escolha incorreta do algoritmo das médias móveis é capaz de reduzir o desempenho de cálculo dos seus programas.

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

Arquivos anexados |
autotest__1.zip (10.86 KB)
Os princípios do cálculo econômico de indicadores Os princípios do cálculo econômico de indicadores
Chamadas para usuário e indicadores técnicos ocupam um espaço muito pequeno no código do programa dos sistemas de negócio automatizado. Geralmente, são apenas algumas linhas de código. Mas, o que geralmente acontece é que essas poucas linhas de código são as que usam a maior parte do tempo, tempo que precisa ser gasto em teste do Expert Advisor. Então, tudo que está relacionado com cálculos de dados dentro de um indicador, precisa ser considerado mais a fundo do que só ser visto de relance. Este artigo falará precisamente sobre isso.
Criando um Consultor Especialista, que negocia em um número de instrumentos Criando um Consultor Especialista, que negocia em um número de instrumentos
O conceito da diversificação de ativos nos mercados financeiros é bastante antigo e sempre atraiu negociantes iniciantes. Neste artigo, o autor propõe uma abordagem maximamente simples para a construção de um Expert Advisor de moeda múltipla, para uma introdução inicial a esta direção das estratégias de negócio.
O uso de ORDER_MAGIC para negociação com diferentes consultores especialistas em um único instrumento O uso de ORDER_MAGIC para negociação com diferentes consultores especialistas em um único instrumento
Este artigo considera as questões de codificação de informação, usando a identificação mágica, assim como a divisão, montagem e sincronização de negociação automática de diferentes Expert Advisors. Este artigo será interessante para iniciantes, assim como para negociantes mais experientes, porque trata da questão das posições virtuais, o que pode ser útil na implementação de sistemas completos de sincronização de Expert Advisors e várias estratégias.
Analisando padrões de velas Analisando padrões de velas
A construção do gráfico de velas japonês e a análise dos padrões de vela constituem uma incrível área da análise técnica. A vantagem das velas é que elas representam dados de uma forma que é possível rastrear a dinâmica dentro dos dados. Neste artigo, analisamos os tipos de velas, a classificação dos padrões de vela e apresentamos um indicador que pode determinar os padrões de vela.