return (false);
}
// – - заполняем часть массива DI_plusBuffer значениями из индикаторного буфера под индексом 2
if (CopyBuffer (ind_handle,2,0,amount, DIminus_values) <0)
{
// – - если копирование не удалось, сообщим код ошибки
PrintFormat («Не удалось скопировать данные из индикатора iADX, код ошибки %d», GetLastError ());
// – - завершим с нулевым результатом – это означает, что индикатор будет считаться нерассчитанным
return (false);
}
Здесь ind_handle – это хэндл индикатора ADX, второй параметр – индекс буфера используемого индикатора, из которого производится копирование, третий параметр – стартовая позиция, откуда начинается копирование. Здесь мы помним, что копирование идет от конца к началу, и поэтому нулевая стартовая позиция – это самые свежие данные. Четвертый параметр – это наш размер данных, которые необходимо рассчитать в вызове функции OnCalculate (), и последний параметр – это обычно динамический массив, привязанный к буферу индикатора, куда производится копирование.
Тут есть вопрос, как связать второй параметр функции CopyBuffer с индексом буфера используемого индикатора.
Это определяется вызовом функции SetIndexBuffer в используемом индикаторе. Например, для индикатора ADX:
SetIndexBuffer (0,ExtADXBuffer);
SetIndexBuffer (1,ExtPDIBuffer);
SetIndexBuffer (2,ExtNDIBuffer);
Отсюда нулевой индекс связан с буфером самого индикатора ADX, 1 индекс связан с буфером индикатора направленности +DI, 2 индекс связан с буфером индикатора направленности —DI.
Таким образом, для связывания второго параметра функции CopyBuffer с индексом буфера используемого индикатора, нужно знать код используемого индикатора.
Также для заполнения буфера индикатора значениями, может использоваться цикл, например:
for (int i=start; i <rates_total &&!IsStopped ();i++)
{
}
Здесь start – это стартовая позиция, с которой начинается заполнение буфера индикатора.
При значении prev_calculated=0, значение start это, как правило, 0, при значении prev_calculated= rates_total, зачение start=prev_calculated-1.
Если же перед реализацией цикла с помощью функции ArraySetAsSeries поменять порядок доступа к массивам буферов индикатора и цен, тогда цикл примет вид:
for (int i=start; i> =0;i – ) {
}
Где start=rates_total-1, если prev_calculated=0, и start=0, если prev_calculated= rates_total.
Пример создания индикатора
В качестве примера рассмотрим создание индикатора, который будет реализовывать форекс стратегию «Impulse keeper» (Ловец импульсов) и показывать на графике сигналы на покупку и продажу.
В данной стратегии применяются четыре индикатора:
Экспоненциальная скользящая средняя с периодом 34 для цены High.
Экспоненциальная скользящая средняя с периодом 34 для цены Low.
Экспоненциальная скользящая средняя с периодом 125 для цены Close.
Parabolic SAR.
Сигналы на покупку и продажу в данной стратегии описываются следующим образом.
Сигнал на покупку: зеленая свеча закрывается выше EMA34 High и EMA34 Low, зеленая свеча выше EMA125 и Parabolic SAR.
Сигнал на продажу: красная свеча закрывается ниже EMA34 Low и EMA34 High, красная свеча ниже EMA125 и Parabolic SAR.
Давайте, реализуем эту стратегию в коде, который будет отображать на графике стрелки вверх и вниз сигналов на покупку и продажу.
Откроем MQL5 редактор и в меню File выберем New. В диалоговом окне MQL Wizard выберем Custom Indicator и нажмем кнопку Далее. Введем имя индикатора Impulse keeper, имя автора и ссылку и нажмем два раза Далее, а затем Готово.
В результате мы получим код индикатора с пустыми функциями OnInit и OnCalculate.
Создание индикатора начнем с определения его свойств.
Количество буферов индикатора определим 8.
2 буфера – данные и цвет, для сигналов на покупку. 2 буфера – данные и цвет, для сигналов на продажу. И 4 буфера промежуточных вычислений для скопированных данных из индикаторов EMA34 Low, EMA34 High, EMA125 и Parabolic SAR:
#property indicator_buffers 8
Определим число графических построений – 2, одно построение для сигналов на покупку и другое построение для сигналов на продажу:
#property indicator_plots 2
Определим цвет и тип для обоих графических построений:
#property indicator_color1 clrGreen, clrBlack
#property indicator_type1 DRAW_COLOR_ARROW
#property indicator_color2 clrRed, clrBlack
#property indicator_type2 DRAW_COLOR_ARROW
Далее определим массивы буферов индикатора и хэндлы используемых индикаторов:
double IKBuyBuffer [];
double ColorIKBuyBuffer [];
double IKSellBuffer [];
double ColorIKSellBuffer [];
double EMA34HBuffer [];
double EMA34LBuffer [];
double EMA125Buffer [];
double PSARBuffer [];
int EMA34HHandle;
int EMA34LHandle;
int EMA125Handle;
int PSARHandle;
В функции OnInit () для первого графического построения определим тип стрелки – стрелка вверх, пустое значение и сдвиг:
int OnInit ()
{
PlotIndexSetInteger (0,PLOT_ARROW,233);
PlotIndexSetDouble (0,PLOT_EMPTY_VALUE,0);
PlotIndexSetInteger (0,PLOT_ARROW_SHIFT, -10);
Для второго графического построения определим тип стрелки – стрелка вниз, пустое значение и сдвиг:
PlotIndexSetInteger (1,PLOT_ARROW,234);
PlotIndexSetDouble (1,PLOT_EMPTY_VALUE,0);
PlotIndexSetInteger (1,PLOT_ARROW_SHIFT,10);
Свяжем массивы с буферами индикатора:
SetIndexBuffer (0,IKBuyBuffer, INDICATOR_DATA);
SetIndexBuffer (1,ColorIKBuyBuffer, INDICATOR_COLOR_INDEX);
SetIndexBuffer (2,IKSellBuffer, INDICATOR_DATA);
SetIndexBuffer (3,ColorIKSellBuffer, INDICATOR_COLOR_INDEX);
SetIndexBuffer (4,EMA34HBuffer, INDICATOR_CALCULATIONS);
SetIndexBuffer (5,EMA34LBuffer, INDICATOR_CALCULATIONS);
SetIndexBuffer (6,EMA125Buffer, INDICATOR_CALCULATIONS);
SetIndexBuffer (7,PSARBuffer, INDICATOR_CALCULATIONS);
Получим хэндлы используемых индикаторов:
EMA34HHandle=iMA (NULL,0,34,0,MODE_EMA, PRICE_HIGH);
EMA34LHandle=iMA (NULL,0,34,0,MODE_EMA, PRICE_LOW);
EMA125Handle=iMA (NULL,0,125,0,MODE_EMA, PRICE_CLOSE);
PSARHandle=iSAR (NULL,0,0.02, 0.2);
В функции OnCalculate () произведем проверку размера доступной истории для расчета используемых индикаторов, определим количество копируемых значений используемых индикаторов и определим стартовую позицию расчета индикатора:
int values_to_copy;
int start;
int calculated=BarsCalculated (EMA34HHandle);
if (calculated <=0)
{
return (0);
}
if (prev_calculated==0 || calculated!=bars_calculated)
{
start=1;
if (calculated> rates_total) values_to_copy=rates_total;
else values_to_copy=calculated;
}
else
{
start=rates_total-1;
values_to_copy=1;
}
Переменную bars_calculated определим как глобальную int bars_calculated=0; в свойствах индикатора.
Далее произведем копирование из буферов используемых индикаторов в массивы буферов нашего индикатора:
if (!FillArrayFromMABuffer (EMA34HBuffer,0,EMA34HHandle, values_to_copy)) return (0); if (!FillArrayFromMABuffer (EMA34LBuffer,0,EMA34LHandle, values_to_copy)) return (0); if (!FillArrayFromMABuffer (EMA125Buffer,0,EMA125Handle, values_to_copy)) return (0);
if (!FillArrayFromPSARBuffer (PSARBuffer, PSARHandle, values_to_copy)) return (0);
Здесь FillArrayFromMABuffer и FillArrayFromPSARBuffer – пользовательские функции, определенные вне функции OnCalculate ():
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
bool FillArrayFromPSARBuffer (
double &sar_buffer [], // индикаторный буфер значений Parabolic SAR
int ind_handle, // хэндл индикатора iSAR
int amount // количество копируемых значений
)
{
ResetLastError ();
if (CopyBuffer (ind_handle,0,0,amount, sar_buffer) <0)
{
return (false);
}
return (true);
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
bool FillArrayFromMABuffer (
double &values [], // индикаторный буфер значений Moving Average
int shift, // смещение
int ind_handle, // хэндл индикатора iMA
int amount // количество копируемых значений
)
{
ResetLastError ();
if (CopyBuffer (ind_handle,0, -shift, amount, values) <0)
{
return (false);
}
return (true);
}
Далее в функции OnCalculate () заполним буфера индикатора данными и цветом:
for (int i=start; i <rates_total &&!IsStopped ();i++)
{
IKBuyBuffer [i-1] =0;
ColorIKBuyBuffer [i-1] =1;
IKSellBuffer [i-1] =0;
ColorIKSellBuffer [i-1] =1;
if (close [i-1]> open [i-1] &&close [i-1]> EMA34HBuffer [i-1] &&close [i-1]> EMA34LBuffer [i-1] &&low [i-1]> EMA125Buffer [i-1] &&low [i-1]> PSARBuffer [i-1] &&EMA125Buffer [i-1] <EMA34LBuffer [i-1] &&EMA125Buffer [i-1] <EMA34HBuffer [i-1]) {
IKBuyBuffer [i-1] =high [i-1];
ColorIKBuyBuffer [i-1] =0;
}
if (close [i-1] <open [i-1] &&close [i-1] <EMA34HBuffer [i-1] &&close [i-1] <EMA34LBuffer [i-1] &&high [i-1] <EMA125Buffer [i-1] &&high [i-1] <PSARBuffer [i-1] &&EMA125Buffer [i-1]> EMA34LBuffer [i-1] &&EMA125Buffer [i-1]> EMA34HBuffer [i-1]) {
IKSellBuffer [i-1] =low [i-1];
ColorIKSellBuffer [i-1] =0;
}
}
bars_calculated=calculated;
// – - return value of prev_calculated for next call
return (rates_total);
}
Здесь мы рассчитываем индикатор на предыдущем баре, так как на текущем баре цена close – это текущая цена тика.
Откомпилируем код и присоединим индикатор к графику:
Мы увидим, что, в общем и целом, индикатор дает верные сигналы на продажу и покупку, хотя в некоторых случаях он запаздывает и дает ложные сигналы:
Как мы видим, происходит это из-за трендовой линии EMA125.
Попробуем отвязать ее от текущего периода и попробуем определять тренд, скажем по дневному графику:
EMA125Handle=iMA (NULL, PERIOD_D1,125,0,MODE_EMA, PRICE_CLOSE);
При этом запаздывание, конечно, сократится, но количество ложных сигналов увеличится:
Видимо для улучшения данной стратегии, нужно привлекать дополнительные индикаторы.
Попробуем сделать этот же самый индикатор, но не с помощью графических построений, а помещая графические объекты на график символа.