iTime рандомно роняет индикатор. Как быть?

 

Пишу индикатор с использованием данных от "внешнего" таймфрейма. Ну, то есть, таймфрейм графика открыт маленький (М1-М15), а дополнительные данные беру с D1.

Собственно, мне нужно посчитать MTR от D1 для каждого бара M15.

Данные, которые я забираю с D1, - это номер бара, функция iTime() и технический индикатор iMTR.


И вот, вроде бы индикатор работает себе некоторое время, но на определённом моменте, алгоритмически ни с чем не связанном, Print() печатает, что prev_calculated=0, а iBarShift() по 10 бару D1 выдаёт -1.

Потом эта -1 приходит в скопированный массив iATR и получается выход за пределы массива с падением индикатора :(


Сделал элементарный тестовый индикатор для отлова бага, в нём iBarShift() всегда считает данные для времени открытия 10 бара D1.

Проблема заключается в том, что в определённый момент iTime для 10 бара по D1 выдаёт "1970.01.01 00:00:00".


Может это не баг, а я чё-то не так делаю?


MetaTrader 5

Version: 5 build 3391


Код тестового индикатора:

//+------------------------------------------------------------------+
//|                                               ishiftbar_test.mq5 |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

ENUM_TIMEFRAMES atr_period = PERIOD_D1;
int atr_bars_count = 10;
int atr_ma_count = 10;

int atr_handle;
double atrBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
    SetIndexBuffer(0,atrBuffer,INDICATOR_CALCULATIONS);
    ArraySetAsSeries(atrBuffer, true);
    atr_handle = iATR(NULL, atr_period, atr_ma_count);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//---
    Print("-----------------------");
    //Скопируем данные в буфер ATR.
    int bars_on_atr = iBars(Symbol(), atr_period);
    int new_bars_count = rates_total - prev_calculated;
    int copy_result = CopyBuffer(atr_handle, 0, new_bars_count, bars_on_atr, atrBuffer);

    //Вычислим номер бара для взятия ATR.
    datetime atr_bar_count_time = iTime(NULL, atr_period, atr_bars_count); //<--- Баг вот здесь. Оба аргумента - константы.
    int inner_bars_max = iBarShift(NULL, PERIOD_CURRENT, atr_bar_count_time);
    datetime inner_bars_time = iTime(Symbol(), PERIOD_CURRENT, inner_bars_max);
    int obs_last = iBarShift(Symbol(), atr_period, inner_bars_time);
    Print("obs_last: ", obs_last, " inner_bars_max: ", inner_bars_max, " inner_bars_time: ", inner_bars_time, " atr_bar_count_time: ", atr_bar_count_time, " rates_total: ", rates_total, " prev_calculated: ", prev_calculated);

    //Пусть индикатор упадёт.
    double atr_left = NormalizeDouble(atrBuffer[obs_last], _Digits);
    Print("atr_left: ", atr_left);

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
 
vipermagi:

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


 
Aleksandr Slavskii #:

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


Данная проблема не в этом, а именно в том, что я описал. Даже если я починю копирование буфера, то на строку 51 это ни как не повлияет.

Индикатор у меня работает в 90% запусков и проблема с неправильным копированием всплывает не часто. По этому я начал фикс багов с отрицательного индекса для массива.

В строке 51 вызывается функция iTime, в неё передаётся NULL, константа atr_period=PERIOD_D1 и константа atr_bars_count=10. Даже если я закомментирую копирование буфера, то iTime ни как от этого не зависит и всё равно будет выполняться, и всё равно будет через некоторое время выдавать -1. И эта -1 будет ронять индикатор даже если я правильно скопирую буфер.

С буфером всё понятно, там немножко арифметику поменять и будет он правильно копировать всегда. Этот кусок кода не для вопросов на форуме, это я и сам могу починить.

Кусок кода с буфером я добавил в пример только для того, чтобы он ронял индикатор при попадании туда -1.

 
vipermagi #:

Данная проблема не в этом, а именно в том, что я описал. Даже если я починю копирование буфера, то на строку 51 это ни как не повлияет.

Индикатор у меня работает в 90% запусков и проблема с неправильным копированием всплывает не часто. По этому я начал фикс багов с отрицательного индекса для массива.

В строке 51 вызывается функция iTime, в неё передаётся NULL, константа atr_period=PERIOD_D1 и константа atr_bars_count=10. Даже если я закомментирую копирование буфера, то iTime ни как от этого не зависит и всё равно будет выполняться, и всё равно будет через некоторое время выдавать -1. И эта -1 будет ронять индикатор даже если я правильно скопирую буфер.

С буфером всё понятно, там немножко арифметику поменять и будет он правильно копировать всегда. Этот кусок кода не для вопросов на форуме, это я и сам могу починить.

Кусок кода с буфером я добавил в пример только для того, чтобы он ронял индикатор при попадании туда -1.

если из 100 запусков,1 завершается с ошибкой, то это всё равно ошибка. Хотя и одна из 100

при обращении к таймфреймам отличным от текущего (и не дай бог других инструментов) надо предпринимать шаманские пляски и стучать в бубен

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

Самый простой способ - не запрашивать данные в OnInit (а спрашивать в OnTick/OnTimer) и в случае неполучения просто ждать. Для скрипта, у которого только OnStart - только ждать.

Сложные способы - ожидать синхронизации данных. ( см SeriesInfoInteger )

 

Maxim Kuznetsov #:

Сложные способы - ожидать синхронизации данных. ( см SeriesInfoInteger )

Поставил проверку синхронизации

    bool is_synchronized = (bool)SeriesInfoInteger(Symbol(), atr_period, SERIES_SYNCHRONIZED);
    if(!is_synchronized)
    {
        Print("Синхронизированность данных по символу = ", is_synchronized, "; таймфрейм: ", EnumToString(atr_period));
    }

Но прикол в том, что это условие не срабатывает, то есть кажет, что данные с таймфрейма D1 синхронизированы.

При этом всё равно iTime() по D1 выдаёт "1970.01.01 00:00:00", что приводит к -1 в индексе массива.


Пока думаю просто ловить на самом верху OnCalculate() это время datetime(0) и при срабатывании сразу возвращать return(prev_calculated).

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

Как-то так:

datetime t = iTime(NULL, atr_period, atr_bars_count);
if(t == datetime(0))
{
    Print("Доступ к данным внешнего таймфрейма поломан на этом тике; все текущие вычисления будут отработаны на следующем.");
    return(prev_calculated);
}
Причина обращения: