Язык программирования MQL5: Продвинутое использование торговой платформы MetaTrader 5 - Тимур Сергеевич Машнин 6 стр.


if (prev_calculated==0 || calculated!=bars_calculated || rates_total> prev_calculated+1)

{

//  - если массив больше, чем значений в индикаторе на паре symbol/period, то копируем не все

//  - в противном случае копировать будем меньше, чем размер индикаторных буферов

if (calculated> rates_total) values_to_copy=rates_total;

else values_to_copy=calculated;

}

else

{

//  - значит наш индикатор рассчитывается не в первый раз и с момента последнего вызова OnCalculate ())

//  - для расчета добавилось не более одного бара

values_to_copy= (rates_total-prev_calculated) +1;

}


//  - запомним количество значений в индикаторе

bars_calculated=calculated;

//  - вернем значение prev_calculated для следующего вызова

return (rates_total);

}

Здесь переменная values_to_copy  количество рассчитываемых значений в вызове функции OnCalculate ().

Переменная prev_calculated  сколько было обработано баров функцией OnCalculate () при предыдущем вызове.

Таким образом, при загрузке индикатора prev_calculated=0, а при каждом следующем поступлении нового тика prev_calculated= rates_total.

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

Переменная bars_calculated  предыдущее количество рассчитанных данных для запрашиваемого индикатора, на основе которого рассчитывается данный индикатор.

Таким образом, первая проверка здесь:

prev_calculated==0  индикатор только что загрузился или изменилась ценовая история.

calculated!=bars_calculated  изменилось количество рассчитанных данных для запрашиваемого индикатора.

rates_total> prev_calculated+1  необходимо рассчитать индикатор для двух или более баров (значит, что-то изменилось в истории).

Последнее условие вступает в противоречие с утверждением справочника:

Если с момента последнего вызова функции OnCalculate () ценовые данные были изменены (подкачана более глубокая история или были заполнены пропуски истории), то значение входного параметра prev_calculated будет установлено в нулевое значение самим терминалом.

Если изменилась история, тогда сработает проверка prev_calculated==0 и проверка последнего условия будет излишней.

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

Если же первое или второе условие не срабатывают, тогда количество рассчитываемых значений:

values_to_copy= (rates_total-prev_calculated) +1;

Опять же, тут есть излишний код, так как, судя по справочнику, переменная prev_calculated может принимать значение либо 0, либо rates_total.

Поэтому, values_to_copy=1.

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

Рассмотрим другую реализацию вычисления размера данных, которые необходимо рассчитать в вызове функции OnCalculate ().

Для индикатора MACD это реализовано следующим образом:

//  - we can copy not all data

int to_copy;

if (prev_calculated> rates_total || prev_calculated <0) to_copy=rates_total;

else

{

to_copy=rates_total-prev_calculated;

if (prev_calculated> 0) to_copy++;

}

Опять же, судя по справочнику, здесь будет работать только код:

to_copy=rates_total-prev_calculated;

if (prev_calculated> 0) to_copy++;


Т.е. при загрузке индикатора to_copy=rates_total, а затем to_copy=1.

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

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

Вот как это реализовано для используемого индикатора ADX:

//  - заполняем часть массива ADXBuffer значениями из индикаторного буфера под индексом 0

if (CopyBuffer (ind_handle,0,0,amount, adx_values) <0)

{

//  - если копирование не удалось, сообщим код ошибки

PrintFormat («Не удалось скопировать данные из индикатора iADX, код ошибки %d», GetLastError ());

//  - завершим с нулевым результатом  это означает, что индикатор будет считаться нерассчитанным

return (false);

}


//  - заполняем часть массива DI_plusBuffer значениями из индикаторного буфера под индексом 1

if (CopyBuffer (ind_handle,1,0,amount, DIplus_values) <0)

{

//  - если копирование не удалось, сообщим код ошибки

PrintFormat («Не удалось скопировать данные из индикатора iADX, код ошибки %d», GetLastError ());

//  - завершим с нулевым результатом  это означает, что индикатор будет считаться нерассчитанным

КОНЕЦ ОЗНАКОМИТЕЛЬНОГО ОТРЫВКА

//  - завершим с нулевым результатом  это означает, что индикатор будет считаться нерассчитанным

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

Назад Дальше