English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Исследование быстродействия скользящих средних в MQL5

Исследование быстродействия скользящих средних в MQL5

MetaTrader 5Примеры | 16 июля 2010, 10:49
4 354 0
Sergey Pavlov
Sergey Pavlov


Введение

В исследованиях временных рядов, построении индикаторов и в разработке советников, редко кто обходится без применения в анализе  рынка скользящих средних (MovingAverage). Это самый популярный метод сглаживания ценовых данных. В новой версии языка, вариантов использования алгоритма MovingAverage (MA) насчитывается около десятка.

Возникает вопрос: а все ли йогурты одинаково полезны? Действительно, на сколько снижается или увеличивается быстродействие расчётов при участии скользящих средних? Какой вариант выбрать в той или иной ситуации?

Насколько расчёт MovingAverage в MetaTrader 5 быстрее чем в MetaTrader 4? И подобных вопросов возникает очень много. Вот и давайте разберёмся с большинством из них.

Конечно, быстродействие новой платформы впечатляет, но проверить экспериментально истинное положение вещей не помешает.


1. Условия проведения экспериментов

Быстродействие расчётов относительно и зависит от многих факторов. Поэтому, данные, которые были получены в рамках проведённых исследований, в других условиях будут отличаться. Иначе говоря, абсолютные значения быстродействия изменятся, а относительные (один вариант относительно другого в пределах одной платформы) нет.

Поскольку индикатор скользящих средних iMA  в MQL5 результаты расчёта данных непосредственно не возвращает (возвращается хэндл индикатора), то исследовать быстродействие надо для связки iMA-CopyBuffer, т.к. нас интересует быстродействие не только самого расчёта, но и получения результатов.

Исходные условия:

  • Процессор: Core i7 965
  • Символ: "EURUSD"
  • Размер массива ценовых данных: 10000 элементов
  • Режим работы терминала: автономный, максимальное количество баров в окне 10000
  • Модели расчёта скользящих средних: MODE_SMA, MODE_EMA, MODE_SMMA, MODE_LWMA
  • Точность расчёта быстродействия: ограничена 2 значащими цифрами
  • Количество вариантов вызова скользящих средних: 7

2. Программа испытаний

Для того чтобы измерить время расчёта скользящих средних, в нашем распоряжении есть функция GetTickCount(), которая оперирует миллисекундами. Этой точности не достаточно, поэтому необходимо организовать какие-то циклы для повышения качества измерений.

Однако, если организовать цикл, который будет многократно повторять один и тот же расчёт с одними и теми же входными данными, то результаты будут искажены. Дело в том, что функция iMA из MQL5 создаёт в глобальном кеше клиентского терминала копию соответствующего технического индикатора. А если копия индикатора с этими параметрами уже существует, то новая копия в глобальном кеше не создаётся(!), а увеличивается счётчик ссылок на данную копию. Другими словами, весь индикаторный буфер рассчитывается только один раз при первом обращении, а при всех последующих - берёт готовые значения и пересчёт происходит только новых данных.

Поэтому, цикличность расчётов надо организовать так, чтобы на каждом цикле входные параметры скользящих средних изменялись. Таких параметров выбрано три: период усреднения; таймфрейм и используемая цена.

Параметр
 Диапазон значений
 Период усреднения
 от 1 до 100
 Таймфрейм
 М1, М5, М15, М30
 Используемая цена
 PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED

Таблица 1. Диапазоны изменения входных параметров

Расчёт значений МА будем проводить на массиве из 10 000 элементов и для семи вариантов вызова скользящих средних (подробно см. раздел 4).

3. Результаты исследований

Результаты исследований сведены в следующую таблицу, в которой быстродействие расчёта скользящих средних оцениваем по времени исполнения всей программы испытаний (см. табл.1) в секундах. По этой программе получается 100х4х7=2800 шагов (циклов), т.е. определяем за какое время происходит расчёт всех шагов (циклов) на массиве ценовых данных из 10 000 элементов. Время исполнения одного шага (цикла) приблизительно будет равно отношению полученного быстродействия к 2800. Например, для варианта 1 и модели расчёта SMA оно равняется ~ 0,0028/2800.

Вариант
MODE_SMAMODE_EMAMODE_SMMAMODE_LWMAПлатформа
0   (см. раздел 4.1)
0,0041 0,0040 0,0043 0,0041  MetaTrader 4
1   (см. раздел 4.2) 0,0028 0,00023 0,00027 0,0045  MetaTrader 5
2   (см. раздел 4.3) 0,0029 0,0029 0,0029 0,0029  MetaTrader 5
3   (см. раздел 4.4) 0,0998 0,0997 0,0998 0,0998  MetaTrader 5
4   (см. раздел 4.5) 0,0996 0,0996 0,0996 0,0996  MetaTrader 5
5   (см. раздел 4.6) 0,0030 0,0029 0,0029 0,0029  MetaTrader 5
6   (см. раздел 4.7) 0,000140 0,000121 0,000117 0,0035  MetaTrader 5

Таблица 2. Сводная таблица результатов исследований

Что означают номера вариантов, будет подробно рассмотрено в статье чуть ниже (разделы 4.1-4.7). А пока давайте оценим общую картину быстродействия MA.

Для наглядности анализа, результаты представлены в графическом виде на рис.1-5. По оси Х номер варианта вызова MA (см. табл.2), а по оси Y логарифмическая шкала времени с обратным порядком значений, т.е. чем выше столбик на диаграмме, тем больше быстродействие. Каждой модели расчёта (SMA, EMA, SMMA, LWMA) соответствует свой столбик диаграммы.

Рис.1 Результаты исследования быстродействия скользящих средних

Рисунок 1. Результаты исследования быстродействия скользящих средних

Трудно не заметить существенную разницу в скорости расчёта скользящих средних для разных вариантов. О чём это может говорить? Самое простое, что напрашивается, не все предлагаемые разработчиками языка способы использования MA одинаково хороши: есть очень скоростные (вариант 6) и есть очень медленные (варианты 3 и 4). Следовательно, разработчикам прикладных программ на языке MQL5 надо быть очень разборчивыми с "машками" (MovingAverag).

На следующих рисунках, для удобства анализа, каждая модель расчёта MovingAverage рассмотрена в отдельности для всех исследованных вариантов (от 0 до 6) см. в табл. 2.

Рис.2 Быстродействие МА для модели MODE_SMA

Рисунок 2. Быстродействие МА для модели MODE_SMA

Рис.3 Быстродействие МА для модели MODE_EMA

Рисунок 3. Быстродействие МА для модели MODE_EMA

Рис.4 Быстродействие МА для модели MODE_SMMA

Рис.4 Быстродействие МА для модели MODE_SMMA.

Рис.5 Быстродействие МА для модели MODE_LWMA

Рисунок 5.  Быстродействие МА для модели MODE_LWMA

Интересно сравнить быстродействие двух платформ: MetaTrader 4 и MetaTrader 5. В таблице 2 это вариант №0 (MQL4) и вариант №2 (MQL5). Для удобства анализа, поместим результаты расчёта стандартного индикатора скользящих средних iMA в отдельную таблицу и диаграмму (см. рис.6). По оси Y шкала времени исполнения теста.

Рис.6 Быстродействие платформ MetaTrader 4 и MetaTrader 5

Рисунок 6. Быстродействие платформ MetaTrader 4 и MetaTrader 5

Выводы:

  1. Новая платформа MetaTrader 5 по быстродействии более чем на 40% опережает предыдущую - MetaTrader 4.
  2. Самое высокое быстродействие получено: для моделей расчёта SMA, EMA, SMMA - вариант вызова MovingAverage №6, а для модели LWMA - варианты №2 и №5.
  3. В тех вариантах, где используется стандартный индикатор iMA, быстродействие разных моделей расчёта практически одинакова. Чего нельзя сказать о библиотеке скользящих средних из файла MovingAverages.mqh. Там разбег точек для разных моделей отличается почти на порядок (0,00023~0,0045).
  4. Результаты соответствуют режиму "холодный запуск", т.е., предварительно рассчитанных значений в глобальном кеше нет.

4. Исследованные варианты использования скользящих средних

Разработчики MQL5 рекомендуют примерно вот такой вариант получения значений стандартных индикаторов:

//---- indicator buffers
double      MA[];                // массив для индикатора iMA
//---- handles for indicators
int         MA_handle;           // указатель на индикатор iMA
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- создание указателя на объект - индикатор iMA
   MA_handle=iMA(NULL,0,21,0,MODE_EMA,PRICE_CLOSE);
   //--- если произошла ошибка при создании объекта, то выводим сообщение
   if(MA_handle<0)
      {
      Print("Объект iMA не создан: MA_handle= ",INVALID_HANDLE);
      Print("Ошибка исполнения = ",GetLastError());
      //--- принудительное завершение программы
      return(-1);
      }
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- заполнение массива MA[] текущими значениями индикатора iMA
   //--- в массив будет записано 100 элементов, а если произошла
   //--- ошибка, то прекращаем выполнение дальнейших операций
   if(CopyBuffer(MA_handle,0,0,100,MA)<=0) return;
   //--- задаём порядок индексации массива MA[] как в таймсерии
   ArraySetAsSeries(MA,true);  
   //--- а дальше делайте с этими данными всё что угодно 
  }

Об этом способе получения значений технических индикаторов подробно написано в одноимённой статье.

Для тестирования скользящих средних лучше всего подойдёт скрипт, поскольку он сразу выполняет все необходимые расчёты, не дожидаясь поступления каких либо событий (например, прихода нового тика).

Создание универсальной программы, для расчёта всех запланированных способов расчёта скользящих средних, вряд ли оправдано. Поэтому для каждого варианта вызова МА создадим свой скрипт.

Итак, рассмотрим подробно каждый вариант вызова скользящих средних (MovingAverage).


4.1. Вариант №0

В этом варианте определяется быстродействие функции технического индикатора iMA из MQL4. Испытания проводились в терминале MetaTrader 4. Расчёт на всём массиве данных.

МодельРезультатЛучший результат
MODE_SMA 0,0041 0,000140 (вариант 6)
MODE_EMA 0,0040 0,000121 (вариант 6)
MODE_SMMA 0,0043 0,000117 (вариант 6)
MODE_LWMA 0,0041 0,0029 (вариант 2,5)


Код этого варианта представлен на языке 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("СТАРТ ");
   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("Общее время [мсек] ",time);
   time=time/1000/m/p/periodMA;
   Print("Быстродействие [сек] ",DoubleToStr(time, 10));
   return(0);
  }
//+------------------------------------------------------------------+
void Test0()
  {
//--- Модель расчёта: 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);
     }
  }
Примечание:

В терминале MetaTrader 5 данный код работать не будет, т.к. он написан на языке MQL4. Его следует запускать в терминале MetaTrader 4.


4.2. Вариант №1

В этом варианте участвуют 4 теста: №1(SMA), №2(EMA), №3(SMMA) и №4(LWMA).

В них используется библиотека функций MovingAverages.mqh из стандартной поставки. Расчёт ведется на всём массиве данных.

Модель
 РезультатЛучший результат
MODE_SMA
0,0028
0,000140 (вариант 6)
MODE_EMA
0,00023
0,000121 (вариант 6)
MODE_SMMA
0,000270,000117 (вариант 6)
MODE_LWMA
0,00450,0029 (варианты 2,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(); // заменить на необходимый тест
           }
        }
     }
//---
   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);
     }
  }

Примечание: В расчётах участвовал только один массив цен закрытия баров, хотя по программе должны участвовать разные. Это упрощение, принятое для данного варианта, на быстродействие никакого влияния не оказывает.


4.3. Вариант №2

В этом варианте используется стандартный технический индикатор iMA и один тест №5. Расчёт ведется на всём массиве данных.

МодельРезультатЛучший результат
MODE_SMA 0,0029 0,000140 (вариант 6)
MODE_EMA 0,0029 0,000121 (вариант 6)
MODE_SMMA 0,0029 0,000117 (вариант 6)
MODE_LWMA 0,0029 0,0029 (вариант 2,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[];                // массив для индикатора iMA
int         MA_handle;           // указатель на индикатор iMA
//+------------------------------------------------------------------+
//| 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()
  {
//--- Модель расчёта: 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. Вариант №3

Используется стандартная библиотека: классы для работы с индикаторами.

Копирование поэлементное. Расчёт ведется на всём массиве данных.

МодельРезультатЛучший результат
MODE_SMA 0,0998 0,000140 (вариант 6)
MODE_EMA 0,0997 0,000121 (вариант 6)
MODE_SMMA 0,0998 0,000117 (вариант 6)
MODE_LWMA 0,0998 0,0029 (вариант 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++)
           {
            Test6();
           }
        }
     }
//---
   endGTC=GetTickCount();
   time=endGTC-startGTC;
   time=time/1000/m/p/periodMA;
//---
   return(0);
  }
//+------------------------------------------------------------------+
void Test6()
  {
//--- Модель расчёта: 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. Вариант №4

Используется стандартная библиотека: классы для работы с индикаторами.

Копирование индикаторного буфера целиком. Расчёт производится на всём массиве данных.

МодельРезультатЛучший результат
MODE_SMA 0,0996 0,000140 (вариант 6)
MODE_EMA 0,0996 0,000121 (вариант 6)
MODE_SMMA 0,0996 0,000117 (вариант 6)
MODE_LWMA 0,0996 0,0029 (вариант 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()
  {
//--- Модель расчёта: 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. Вариант №5

Используются тест: №8, в котором применён универсальный способ получения значений технических индикаторов.

Расчёт производится на всём массиве данных.

МодельРезультатЛучший результат
MODE_SMA 0,0030 0,000140 (вариант 6)
MODE_EMA 0,0029 0,000121 (вариант 6)
MODE_SMMA 0,0029 0,000117 (вариант 6)
MODE_LWMA 0,0029 0,0029 (вариант 2,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[];                // массив для индикатора iMA
int         MA_handle;           // указатель на индикатор iMA
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()
  {
//--- Модель расчёта: 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. Вариант №6

В этом варианте участвуют 4 теста: №9(SMA), №10(EMA), №11(SMMA) и №12(LWMA).

В них используется библиотека функций MovingAverages.mqh из стандартной поставки, аналог функции iMAOnArray из MQL4. Расчёт ведется на всём массиве данных.

МодельРезультатЛучший результат
MODE_SMA 0,000140 0,000140 (вариант 6)
MODE_EMA 0,000121 0,000121 (вариант 6)
MODE_SMMA 0,000117 0,000117 (вариант 6)
MODE_LWMA 0,00350 0,0029 (вариант 2,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();    // заменить на необходимый тест
           }
        }
     }
//---
   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);
  }

Примечание: В расчётах участвовал только один массив цен закрытия баров, хотя по программе должны участвовать разные. Это упрощение, принятое для данного варианта, на быстродействие никакого влияния не оказывает.


5. Отображение результатов тестирования

Для получения результатов и проверки правильности расчёта скользящих средних, предлагаю использовать вот такую функцию:

void PrintTest(const int position, const double &price[])
{
   Print("Общее время исполнения [мсек] ",(endGTC-startGTC));
   Print("Быстродействие [сек] ",time);
   Print(position," - элемент массива = ",price[position]);
}

А вызвать её можно, например вот так, при этом указав, на каком баре и какой массив вас интересует:

//---
   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;
//--- Вывод результатов
   ArraySetAsSeries(buf,true);
   ArraySetAsSeries(close,true);
   PrintTest(0,buf);
   PrintTest(0,close);
//---

Обратите внимание на изменение индексации массивов перед расчётом и перед выводом результатов.

ВАЖНО. Перед расчётом скользящих средних флаг индексации = false, а перед выводом результатов флаг индексации = true.


6. Дополнительные исследования

Дополнительные исследования потребовались для того, чтобы ответить на вопрос: как влияют входные параметры на быстродействие расчёта скользящей средней?

Для этого исследуем вариант №6 (см. раздел 4.7). Этот вариант выбран потому, что его быстродействие оказалось самым высоким в рамках проведённых экспериментов.

Программа испытаний:

Вариант
Таймфрейм
 Период усреднения
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

Таблица 3. Программа дополнительных исследований

Код тестов:

//+------------------------------------------------------------------+
//| Test_SMA                              Модель расчёта: 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                              Модель расчёта: 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                             Модель расчёта: 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                             Модель расчёта: 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);
  }

Для проведения дополнительных исследований, воспользуемся программой autotestMA, внешний вид которой показан на рис. 7.

Рис.7 Внешний вид программы автоматического тестирования autotest

Рисунок 7. Внешний вид программы автоматического тестирования autotest

Результаты исследований: (по оси Х логарифмическая шкала времени)

Рис.8 Влияние таймфрейма на быстродействие расчёта скользящих средних

Рисунок 8. Влияние таймфрейма на быстродействие расчёта скользящих средних

Рис.9 Влияние периода усреднения на быстродействие расчёта скользящих средних

Рисунок 9. Влияние периода усреднения на быстродействие расчёта скользящих средних

ВЫВОДЫ по результатам дополнительных исследований:

  1. С какого таймфрейма брать данные для расчёта МА не имеет значения, поскольку на быстродействие этот параметр влияния не оказывает (см. рис.8).
  2. Период усреднения скользящих средних для моделей расчёта SMA, EMA, SMMA на быстродействие практически не влияет. А вот для модели LWMA увеличение периода усреднения замедляет расчёт, причём существенно с 0,00373 сек. до 0,145 сек(см. рис.9).

Заключение

Неудачный выбор функции расчёта скользящих средних может существенно снизить быстродействие ваших программ.


Прикрепленные файлы |
autotest.zip (11.96 KB)
Как написать индикатор на основе другого индикатора Как написать индикатор на основе другого индикатора
На MQL5 можно не только создать новый пользовательский индикатор с чистого листа, но и написать индикатор на базе другого, уже существующего индикатора, встроенного в терминал или пользовательского. И тут существует два способа: первый - доработать индикатор, добавить к нему новые вычисления и графические стили, второй - использовать встроенный в терминал индикатор или существующий пользовательский индикатор при помощи функций iCustom() или IndicatorCreate().
Написание советника в MQL5 с использованием объектно-ориентированного подхода Написание советника в MQL5 с использованием объектно-ориентированного подхода
Эта статья посвящена использованию объектно-ориентированного подхода для создания советника, рассмотренного в статье "Пошаговое руководство по написанию советников для начинающих". Большинство людей думают, что это сложно, но могу вас заверить, что после прочтения этой статьи вы сможете написать свой собственный советник на основе объектно-ориентированного похода.
Перенос индикаторов из MQL4 в MQL5 Перенос индикаторов из MQL4 в MQL5
Статья посвящена особенностям переноса в MQL5 ценовых конструкций, используемых в индикаторах, написанных на MQL4. Для упрощения переноса индикаторных расчетов из MQL4 в MQL5 предложена библиотека функций mql4_2_mql5.mqh, применение которой рассмотрено на примере переноса индикаторов MACD, Stochastic и RSI.
Использование ORDER_MAGIC для торговли разными экспертами на одном инструменте Использование ORDER_MAGIC для торговли разными экспертами на одном инструменте
В статье раскрываются вопросы кодирования информации с помощью магик-идентификатора, а также разделения, совмещения и синхронизации автоторговли разных экспертов. Статья будет интересна не только начинающим, но и уже бывалым, т.к. в ней рассматриваются вопросы виртуальной позиции, что может помочь в реализации сложных систем синхронизации разных советников и разнообразных стратегий.