Способы вызова индикаторов в MQL5

KlimMalgin | 9 марта, 2010

Введение

В MQL5 существует несколько вариантов вызова индикаторов, и осуществляются они в основном при помощи функций IndicatorCreate() и iCustom(). Причем эти функции лишь возвращают хендл индикатора, и дальнейшая работа с индикаторами ведется именно через него. Так что же такое хендл? Как работать с функциями IndicatorCreate() и iCustom()? И как получить данные индикатора в вашем эксперте? Обо всем этом рассказывается в данной статье.

Создание исходного файла

Для того чтобы начать работу над экспертом, создадим его исходный файл. Делается это также как и в четвертой версии редактора: вызываем мастера создания экспертов MQL5 через меню Файл -> Создать. На первом шаге из списка выбираем Советник и переходим Далее.


На втором шаге необходимо ввести имя советника, контактные данные автора и входные параметры для будущего советника. Введем только Имя советника (обязательное поле) и Автора. Входные параметры при необходимости можно будет добавить позже, прямо в коде программы.


После нажатия кнопки Готово мастер создаст вот такой исходник:

//+------------------------------------------------------------------+
//|                                                   SampleMQL5.mq5 |
//|                                             Copyright KlimMalgin |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "KlimMalgin"
#property link      ""
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Структура исходного файла

Кратко рассмотрим структуру исходного файла эксперта и пойдем дальше.

Код программы начинается с заголовка. Это комментарий с названием файла и именем автора. Сразу после заголовка идет область описания свойств(#property) эксперта. Здесь автоматически указывается версия программы, а так же имя автора и ссылка на вашу страницу, если вы их ввели. После описания свойств задаются входные параметры, которых у нас пока нет, и объявляются глобальные переменные.

Хочу заметить, что свойства необходимо задавать в главном mql5-файле. Все свойства, которые будут находиться во включаемых файлах - будут игнорироваться! 

Далее идут три функции обработки событий: 

Подключение индикаторов

Методы работы с индикаторами в MQL5 значительно изменились! Если в четвертой версии языка для получения значения индикатора на конкретном баре, функции iCustom передавался в качестве параметра индекс этого бара, то сейчас мы не получаем значение буфера индикатора, а получаем его хендл. И, уже используя этот хендл, можем получить значения индюка на заданном промежутке при помощи специальной функции CopyBuffer(). Итак, что такое хендл?

Хендл - это уникальный идентификатор, созданный для работы с нужным нам индикатором. Представляется в виде целого числа и хранится в переменной типа int. Для получения хендла индикатора в MQL5 существует 2 способа! Вот они:

Оба способа равноправны, и какой из них в какой ситуации использовать - решать программисту. Рассмотрим оба способа подробнее.

Получение хендла индикатора с помощью IndicatorCreate()

Итак, в результате успешного выполнения IndicatorCreate() мы получим хендл индикатора, а в случае неудачи функция вернет значение INVALID_HANDLE равное -1. Если вызываемый индикатор имеет параметры, то перед вызовом функции необходимо заполнить массив параметров типа MqlParam. В этом массиве каждый элемент будет представлять собой структуру типа MqlParam, содержащую 4 поля. Первое поле этой структуры указывает на то, какое из оставшихся трех полей использовать в качестве параметра для индикатора.

Напишем код, который будет создавать хендлы индикаторов Moving Average и ParabolicSAR. Начнем с объявления глобальных переменных, в которых будут храниться хендлы, а также объявим массив типа MqlParam для задания параметров индикаторов:

int MA1 = 0,            // Объявляем переменную для хранения хендла быстрой MA
    MA2 = 0,            // Объявляем переменную для хранения хендла медленной MA
    SAR = 0;            // Объявляем переменную для хранения хендла SAR
    
MqlParam params[];      // Массив для хранения параметров индикаторов

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

Теперь, когда переменные объявлены, можно заполнять массив параметров и вызывать IndicatorCreate(). Делать это лучше всего в функции OnInit(), чтобы хендл создавался один раз в начале программы, а не на каждом тике!

Заполним массив параметров и вызовем первый индикатор:

int OnInit()
{
//---
   // устанавливаем размер params равный количеству параметров
   // вызываемого индикатора
   ArrayResize(params,4);

   // Задаем период быстрой MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=5;
   // Смещение
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Метод расчета: простое усреднение
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Тип цен для рассчета: цены закрытия
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA1 = IndicatorCreate(Symbol(), // Символ, используемый для расчета индикатора
                         0,        // Таймфрейм. 0 означает текущий
                         IND_MA,   // Тип индикатора из перечисления ENUM_INDICATOR
                         4,        // Количество параметров, передаваемых в массиве params
                         params    // Массив параметров
                         );   
//---
return(0);
}

Так как массив params был объявлен как динамический, т.е. в скобках не был задан размер массива, то перед его использованием необходимо задать ему нужный размер. При помощи функции ArrayResize() зададим размер, равный количеству параметров, которое мы будем передавать в массиве. Для скользящей средней требуется четыре параметра.

Первый параметр - это период MA. Задается целым числом. Поэтому полю type первого параметра присваиваем значение TYPE_INT, тем самым даем понять функции IndicatorCreate(), что значение параметра нужно будет считать из поля integer_value. Так как все параметры для скользящей средней имеют тип int, то они будут заданы аналогично.

После того, как массив заполнен, можно вызывать функцию IndicatorCreate(). Результат ее выполнения будет сохранен в переменной MA1. С вызовом функции не должно возникнуть никаких затруднений, в нее нужно передать всего пять параметров:

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

int OnInit()
  {
//---

   // устанавливаем размер params равный количеству параметров
   // вызываемого индикатора
   ArrayResize(params,4);

//*    Вызов индикаторов    *
//***************************
   // Задаем период быстрой MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=5;
   // Смещение
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Метод расчета: простое усреднение
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Тип цен для рассчета: цены закрытия
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA1 = IndicatorCreate(Symbol(), 0, IND_MA, 4, params);
   
   // Задаем период медленной MA
   params[0].type         =TYPE_INT;
   params[0].integer_value=21;
   // Смещение
   params[1].type         =TYPE_INT;
   params[1].integer_value=0;
   // Метод расчета: простое усреднение
   params[2].type         =TYPE_INT;
   params[2].integer_value=MODE_SMA;
   // Тип цен для рассчета: цены закрытия
   params[3].type         =TYPE_INT;
   params[3].integer_value=PRICE_CLOSE;
   
   MA2 = IndicatorCreate(Symbol(), 0, IND_MA, 4, params);
   
   
   // Изменяем размер массива для хранения параметров индикатора SAR
   ArrayResize(params,2);
   // Шаг
   params[0].type         =TYPE_DOUBLE;
   params[0].double_value = 0.02;
   // Максимум
   params[1].type         =TYPE_DOUBLE;
   params[1].double_value = 0.2;
   
   SAR = IndicatorCreate(Symbol(), 0, IND_SAR, 2, params);

   
//---
   return(0);
  }

Для получения хендла медленной MA достаточно лишь сменить период с 5 на 21. А при вызове ParabolicSAR в качестве параметров передаются числа с плавающей точкой(double). При заполнении массива это нужно учесть и в поле type поместить TYPE_DOUBLE, а сам параметр, соответственно в поле double_value. Также для SAR требуется лишь два параметра, поэтому размер массива при помощи ArrayResize() изменяется на 2.

На этом работа с IndicatorCreate() заканчивается. На мой взгляд, этот способ вызова индикаторов наиболее удобен при использовании объектно-ориентированного подхода, когда для работы с индикаторами создается библиотека классов, которая в дальнейшем упростит работу с масштабными проектами. А для написания и проверки скорописных экспертов лучше всего использовать следующий способ, который мы сейчас рассмотрим.

Получение хендла при помощи функций технических индикаторов

Второй способ, который позволяет получить хендл индикатора - это вызов одной из функций технических индикаторов. Каждая из этих функций предназначена для получения хендла одного из стандартных индикаторов входящих в комплект вместе с MetaTrader 5. Все они имеют определенный набор параметров, в зависимости от индикатора для которого предназначены. Например, для индикатора CCI вызывается функция iCCI() и выглядит это следующим образом:

   CCI = iCCI(
              Symbol(),            // имя символа
              PERIOD_CURRENT,      // период
              20,                  // период усреднения
              PRICE_CLOSE          // тип цены или handle
              );

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

Особый интерес среди функций технических индикаторов представляет функция iCustom(), так как с ее помощью можно вызывать любые индикаторы, в том числе и не входящие в стандартный набор! Напишем код, аналогичный приведенному выше, но с использованием iCustom():

int OnInit()
  {
//---

   MA1 = iCustom(NULL,0,"Examples\\Custom Moving Average",
                          5,          // Период
                          0,          // Смещение
                          MODE_SMA,   // Метод рассчета
                          PRICE_CLOSE // считаем по ценам закрытия
                 );

   MA2 = iCustom(NULL,0,"Examples\\Custom Moving Average",
                          21,         // Период
                          0,          // Смещение
                          MODE_SMA,   // Метод рассчета
                          PRICE_CLOSE // считаем по ценам закрытия
                 );

   SAR = iCustom(NULL,0,"Examples\\ParabolicSAR",
                          0.02,        // Шаг
                          0.2          // Максимум
                 );

//---
   return(0);
  }

Напомню, что хендл лучше всего получить один раз во время запуска эксперта, поэтому выполняем вызов в OnInit(). 

Функция iCustom содержит следующие параметры:

  1. Название рабочего инструмента, на данных которого будет вычисляться индикатор. NULL - означает текущий инструмент. Вместо NULL можно использовать Symbol(), особой разницы нет. Только в случае с Symbol() в iCustom() передается название инструмента, а в случае с NULL, iCustom() определяет финансовый инструмент самостоятельно.
  2. Период графика. Для указания периода можно использовать предопределенные периоды графиков, а так же 0 - для обозначения текущего таймфрейма.
  3. Название индикатора. Вызываемый индикатор должен быть скомпилирован и лежать в директории "MQL5/Indicators/", либо в какой-либо из ее поддиректорий. По умолчанию индикаторы должны лежать в "MQL5/Indicators/Examples/". Оттуда мы и будем их вызывать в нашем случае.
  4. Остальные параметры. Эти значения будут использоваться в качестве входных параметров для вызываемого индикатора, поэтому их тип и порядок следования должны соответствовать. Если они не указаны, то будут использоваться значения указанные по умолчанию.

Как уже было сказано - этот способ удобнее использовать для работы с индикаторами, если у вас под рукой нет библиотеки, с которой вы привыкли работать. Теперь, когда хендлы получены, можно приступить к работе со значениями индикаторов!

Получение данных индикатора

Вся прелесть работы с индикаторными данными в MQL5 в том, что нам предоставляется возможность получить данные только на том интервале, с которым нам нужно работать. Задать интервал можно разными способами:

Для считывания данных индикатора, служит функция CopyBuffer(). Каким из указанных способов будут получены данные, зависит от того, какие входные параметры будут переданы этой функции.

Напишем код, копирующий данные индикаторов, хендлы которых мы получили ранее, в массивы:

void OnTick()
  {
//---

// Динамические массивы для хранения значений индикаторов
double _ma1[],
       _ma2[],
       _sar[];

// Устанавливаем индексацию в массивах такую же, как в таймсериях, т.е. элемент массива с нулевым
// индексом будет хранить значения последнего бара, с первым - предпоследнего и т.д.
   ArraySetAsSeries(_ma1, true);
   ArraySetAsSeries(_ma2, true);
   ArraySetAsSeries(_sar, true);

// Используя хендлы индикаторов, копируем значения индикаторных 
// буферов в специально подготовленные для этого массивы
   if (CopyBuffer(MA1,0,0,20,_ma1) < 0){Print("CopyBufferMA1 error =",GetLastError());}
   if (CopyBuffer(MA2,0,0,20,_ma2) < 0){Print("CopyBufferMA2 error =",GetLastError());}
   if (CopyBuffer(SAR,0,0,20,_sar) < 0){Print("CopyBufferSAR error =",GetLastError());}


//---
  }

Теперь вся работа будет происходить в функции OnTick(), т.к. на каждом тике данные индикаторов изменяются и их нужно обновлять.

Для начала объявим динамические массивы для каждого буфера индикаторов. Работать с индикаторными данными, на мой взгляд, будет удобнее, если индексация массивов будет такой же, как в таймсериях, т.е. в элементе массива под номером 0 будут данные последнего бара, под номером 1 - предпоследнего и т.д. Для того чтобы установить способ индексации, используется функция ArraySetAsSeries()

Установили способ индексации, теперь можно приступать к заполнению массивов. Для расчета каждого индикатора вызовем CopyBuffer() со следующим набором параметров:

  1. Хендл индикатора, данные которого нужно получить;
  2. Номер индикаторного буфера. В индикаторах MA и SAR по одному буферу, поэтому указываем их как нулевые. Если буферов больше одного, то для второго будет номер 1, для третьего номер 2 и т.д.
  3. Начальный бар, с которого начинаем отсчет.
  4. Количество баров, которое нужно скопировать.
  5. Массив, в который копируются данные.

При успешном вызове функция CopyBuffer() вернет количество скопированных элементов, при неудачном -1. В случае неудачи ошибку необходимо отследить, поэтому, если возвращаемое значение меньше 0 - запишем ошибку в журнал экспертов, при помощи функции Print().

Заключение

На этом работа с индикаторами не заканчивается. В статье рассмотрены лишь базовые методы доступа к их данным. А для удобства работы лучше всего использовать объектно-ориентированный подход, который в MQL5 реализован на довольно хорошем уровне!