Читаем Язык программирования MQL5: Продвинутое использование торговой платформы MetaTrader 5 полностью

if (rates_total

return (0);

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

// – - узнаем количество рассчитанных значений в индикаторе

int calculated=BarsCalculated (handle);

if (calculated <=0)

{

PrintFormat («BarsCalculated вернул %d, код ошибки %d», calculated, GetLastError );

return (0);

}

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

В качестве примера, разберем блок кода, который приводится в справочнике, в разделе Технические индикаторы:

int OnCalculate (const int rates_total,

const int prev_calculated,

const datetime &time [],

const double &open [],

const double &high [],

const double &low [],

const double &close [],

const long &tick_volume [],

const long &volume [],

const int &spread [])

{

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

int values_to_copy;

// – - узнаем количество рассчитанных значений в индикаторе

int calculated=BarsCalculated (handle);

if (calculated <=0)

{

PrintFormat («BarsCalculated вернул %d, код ошибки %d», calculated, GetLastError );

return (0);

}

// – - если это первый запуск вычислений нашего индикатора или изменилось количество значений в индикаторе

// – - или если необходимо рассчитать индикатор для двух или более баров (значит что-то изменилось в истории)

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 

{

}

Здесь 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.

Перейти на страницу:

Похожие книги

1С: Управление небольшой фирмой 8.2 с нуля. 100 уроков для начинающих
1С: Управление небольшой фирмой 8.2 с нуля. 100 уроков для начинающих

Книга предоставляет полное описание приемов и методов работы с программой "1С:Управление небольшой фирмой 8.2". Показано, как автоматизировать управленческий учет всех основных операций, а также автоматизировать процессы организационного характера (маркетинг, построение кадровой политики и др.). Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, формировать разнообразные отчеты, выводить данные на печать. Материал подан в виде тематических уроков, в которых рассмотрены все основные аспекты деятельности современного предприятия. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов. Все приведенные в книге примеры и рекомендации основаны на реальных фактах и имеют практическое подтверждение.

Алексей Анатольевич Гладкий

Экономика / Программное обеспечение / Прочая компьютерная литература / Прочая справочная литература / Книги по IT / Словари и Энциклопедии