Если это стандартный индикатор, проще всего получить его хэндл можно с помощью стандартной функции для работы с техническими индикаторами.
Стандартная функция для индикатора скользящего среднего это:
int iMA (
string symbol, // имя символа
ENUM_TIMEFRAMES period, // период
int ma_period, // период усреднения
int ma_shift, // смещение индикатора по горизонтали
ENUM_MA_METHOD ma_method, // тип сглаживания
ENUM_APPLIED_PRICE applied_price // тип цены или handle
);
И в индикаторе MACD хэндлы индикатора скользящего среднего получаются с помощью вызова функции iMA в функции OnInit ():
// – - get MA handles
ExtFastMaHandle=iMA (NULL,0,InpFastEMA,0,MODE_EMA, InpAppliedPrice);
ExtSlowMaHandle=iMA (NULL,0,InpSlowEMA,0,MODE_EMA, InpAppliedPrice);
где используются свойства индикатора:
// – - input parameters
input int InpFastEMA=12; // Fast EMA period
input int InpSlowEMA=26; // Slow EMA period
input ENUM_APPLIED_PRICE InpAppliedPrice=PRICE_CLOSE; // Applied price
Предположим, что мы хотим использовать не стандартный, а пользовательский индикатор.
В папке Indicators/Examples редактора MQL5 есть нужный нам индикатор – это файл Custom Moving Average.mq5.
Для вызова того индикатора воспользуемся функцией iCustom:
int iCustom (
string symbol, // имя символа
ENUM_TIMEFRAMES period, // период
string name // папка/имя_пользовательского индикатора
…// список входных параметров индикатора
);
В функции OnInit () индикатора MACD изменим код:
// ExtFastMaHandle=iMA (NULL,0,InpFastEMA,0,MODE_EMA, InpAppliedPrice);
// ExtSlowMaHandle=iMA (NULL,0,InpSlowEMA,0,MODE_EMA, InpAppliedPrice);
ExtFastMaHandle=iCustom (NULL,0,«Examples\Custom Moving Average», InpFastEMA,0,MODE_EMA, InpAppliedPrice);
ExtSlowMaHandle=iCustom (NULL,0,«Examples\Custom Moving Average», InpSlowEMA,0,MODE_EMA, InpAppliedPrice);
После компиляции индикатора мы увидим, что его отображение никак не изменилось:
Еще один способ получить хэндл пользовательского индикатора, это использовать функцию IndicatorCreate:
int IndicatorCreate (
string symbol, // имя символа
ENUM_TIMEFRAMES period, // период
ENUM_INDICATOR indicator_type, // тип индикатора из перечисления ENUM_INDICATOR
int parameters_cnt=0, // количество параметров
const MqlParam& parameters_array [] =NULL, // массив параметров
);
В функции OnInit () индикатора MACD изменим код:
MqlParam params [];
ArrayResize (params,5);
params [0].type =TYPE_STRING;
params[0].string_value=«Examples\Custom Moving Average»;
// – - set ma_period
params [1].type =TYPE_INT;
params[1].integer_value=InpFastEMA;
// – - set ma_shift
params [2].type =TYPE_INT;
params[2].integer_value=0;
// – - set ma_method
params [3].type =TYPE_INT;
params[3].integer_value=MODE_EMA;
// – - set applied_price
params [4].type =TYPE_INT;
params[4].integer_value=InpAppliedPrice;
// – - initialization done
ExtFastMaHandle=IndicatorCreate (NULL, NULL, IND_CUSTOM,4,params);
params[1].integer_value=InpSlowEMA;
ExtSlowMaHandle=IndicatorCreate (NULL, NULL, IND_CUSTOM,4,params);
После компиляции индикатора мы опять увидим, что его отображение никак не изменилось.
После получения хэндла индикатора, если он используется в коде один раз, для экономии памяти неплохо использовать функцию IndicatorRelease:
bool IndicatorRelease (
int indicator_handle // handle индикатора
);
которая удаляет хэндл индикатора и освобождает расчетную часть индикатора.
Хорошо, хэндл индикатора мы получили. Как же теперь извлечь его данные?
Делается это в функции OnCalculate с помощью функции CopyBuffer:
int CopyBuffer (
int indicator_handle, // handle индикатора
int buffer_num, // номер буфера индикатора
int start_pos, // откуда начнем
int count, // сколько копируем
double buffer [] // массив, куда будут скопированы данные
);
При этом функция CopyBuffer () распределяет размер принимающего массива под размер копируемых данных.
Напомним, что это работает, если принимающий массив является просто динамическим массивом.
Если же принимающий массив связан с буфером индикатора, тогда клиентский терминал сам заботится о том, чтобы размер такого массива соответствовал количеству баров, доступных индикатору для расчета.
В индикаторе MACD именно такая ситуация. Промежуточные массивы ExtFastMaBuffer [] и ExtSlowMaBuffer [] привязаны к буферам индикатора:
SetIndexBuffer (2,ExtFastMaBuffer, INDICATOR_CALCULATIONS);
SetIndexBuffer (3,ExtSlowMaBuffer, INDICATOR_CALCULATIONS);
И в эти массивы производится копирование буфера индикатора Moving Average на основе его хэндлов:
if (CopyBuffer (ExtFastMaHandle,0,0,to_copy, ExtFastMaBuffer) <=0)
{
Print («Getting fast EMA is failed! Error», GetLastError ());
return (0);
}
if (CopyBuffer (ExtSlowMaHandle,0,0,to_copy, ExtSlowMaBuffer) <=0)
{
Print («Getting slow SMA is failed! Error», GetLastError ());
return (0);
}
Если убрать привязку массивов ExtFastMaBuffer [] и ExtSlowMaBuffer [] к буферам индикатора, тогда клиентский терминал выдаст ошибку:
Происходит это потому, что при загрузке индикатора значение to_copy равно размеру ценовой истории, а дальше to_copy=1 и производится частичное копирование в массивы ExtFastMaBuffer [] и ExtSlowMaBuffer [], при этом их размеры становятся равны 1.
В этом случае применением функции ArrayResize проблему не решить, так как функция CopyBuffer все равно будет уменьшать размер массива до 1.
Можно конечно использовать еще один массив-посредник, в который копировать один элемент. И уже из этого массива-посредника производить копирование в промежуточный массив, но проще всего, конечно, просто привязать промежуточный массив к буферу индикатора.
Функции OnInit (), OnDeinit (), OnCalculate ()
Как уже говорилось, функции OnInit (), OnDeinit (), OnCalculate () вызываются клиентским терминалом при наступлении определенных событий.
Функция OnInit ()
Функция OnInit () вызывается сразу после загрузки индикатора и соответственно используется для его инициализации.
Инициализация индикатора включает в себя привязку массивов к буферам индикатора, инициализацию глобальных переменных, включая инициализацию хэндлеров используемых индикаторов, а также программную установку свойств индикатора.
Давайте разберем некоторые из этих пунктов более подробно.
Как уже было показано, привязка массивов к буферам индикатора осуществляется с помощью функции SetIndexBuffer:
bool SetIndexBuffer (
int index, // индекс буфера
double buffer [], // массив
ENUM_INDEXBUFFER_TYPE data_type // что будем хранить
);
Где data_type может быть INDICATOR_DATA (данные индикатора для отрисовки, по умолчанию, можно не указывать), INDICATOR_COLOR_INDEX (цвет индикатора), INDICATOR_CALCULATIONS (буфер промежуточных расчетов индикатора).
После применения функции SetIndexBuffer к динамическому массиву, его размер автоматически поддерживается равным количеству баров, доступных индикатору для расчета.
Каждый индекс массива типа INDICATOR_COLOR_INDEX соответствует индексу массива типа INDICATOR_DATA, а значение индекса массива типа INDICATOR_COLOR_INDEX определяет цвет отображения индекса массива типа INDICATOR_DATA.
Значение индекса массива типа INDICATOR_COLOR_INDEX, при его установке, берется из свойства #property indicator_colorN как индекс цвета в строке.
Индекс буфера типа INDICATOR_COLOR_INDEX должен следовать за индексом буфера типа INDICATOR_DATA.
После привязки динамического массива к буферу индикатора можно поменять порядок доступа к массиву от конца к началу, т.е. значение массива с индексом 0 будет соответствовать последнему полученному значению индикатора. Сделать это можно с помощью функции ArraySetAsSeries:
bool ArraySetAsSeries (
const void& array [], // массив по ссылке
bool flag // true означает обратный порядок индексации
);
При применении функции ArraySetAsSeries физическое хранение данных массива не меняется, в памяти массив, как и прежде, хранится в порядке от первого значения до последнего значения.
Функция ArraySetAsSeries меняет лишь программный доступ к элементам массива – от последнего элемента массива к первому элементу массива.
В функции OnInit () также может осуществляться проверка входных параметров на корректность, так как пользователь может ввести все, что угодно.
При этом значение входного параметра переназначается с помощью глобальной переменной, и далее в расчетах используется уже значение глобальной переменной.
Например, для индикатора ADX это выглядит так:
// – - check for input parameters
if (InpPeriodADX> =100 || InpPeriodADX <=0)
{
ExtADXPeriod=14;
printf («Incorrect value for input variable Period_ADX=%d. Indicator will use value=%d for calculations.», InpPeriodADX, ExtADXPeriod);
}
else ExtADXPeriod=InpPeriodADX;
здесь ExtADXPeriod – глобальная переменная, а InpPeriodADX – входной параметр.
При использовании хэндлов индикатора, необходимо указывать символ (финансовый инструмент) для которого индикатор будет создаваться.
При этом такой символ может определяться пользователем.
В функции OnInit () также полезно проверить этот входной параметр на корректность.
Пусть определен входной параметр:
input string symbol=" "; // символ
Объявим глобальную переменную:
string name=symbol;
В функции OnInit () произведем проверку:
// – - удалим пробелы слева и справа
StringTrimRight (name);
StringTrimLeft (name);
// – - если после этого длина строки name нулевая
if (StringLen (name) ==0)
{
// – - возьмем символ с графика, на котором запущен индикатор
name=_Symbol;
}
Программная установка свойств индикатора осуществляется с помощью функций IndicatorSetDouble, IndicatorSetInteger, IndicatorSetString, PlotIndexSetDouble, PlotIndexSetInteger, PlotIndexSetString.
Функция IndicatorSetDouble позволяет программным способом определять такие свойства индикатора как indicator_minimum, indicator_maximum и indicator_levelN, например:
IndicatorSetDouble (INDICATOR_LEVELVALUE, 0, 50)
является аналогом:
property indicator_level1 50