[СЕРВИСДЕСК] Ошибка получения времени старшего ТФ в таймере!

 

Задача: при запуске индикатора получить в таймере актуальные данные: время открытия недельного, дневного и часового бара. Затем записать их в глобальные переменные для дальнейшего использования. Текущий ТФ - М1.

Реализация: Время баров получаем с помощью функции SeriesInfoInteger().

#property version   "1.00"
#property strict
#property indicator_chart_window
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//--- Время открытия текущего часа, дня и недели
datetime _weekOpenTime = 0;
datetime _hourOpenTime = 0;
datetime _dayOpenTime=0;
//--- Вести лог журнала
const bool inpFileLog=true;
//--- Количество секунд в одном дне
const int SEC_PER_DAY=86400;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Запускаем таймер
   if(!EventSetMillisecondTimer(20))
     {
      Print(__FUNCTION__,": ОШИБКА #",GetLastError(),": таймер с частотой 20 ms не установлен!");
      return( INIT_FAILED );
     }
//--- Сбрасываем время открытия текущего часа, дня и недели
   _weekOpenTime= 0;
   _dayOpenTime = 0;
   _hourOpenTime= 0;
//---
   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[])
  {

   return( rates_total );
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Проверяем, записано ли время открытия текущей недели
   if(!CheckCurrentWeekOpenTime())                        // Если время не записано
      return;                                                // Выходим
//--- Проверяем, записано ли время открытия текущего дня
   if(!CheckCurrentDayOpenTime())                        // Если время не записано
      return;                                                // Выходим
//--- Проверяем, записано ли время открытия текущего часа
   if(!CheckCurrentHourOpenTime())                        // Если время не записано
      return;                                                // Выходим
  }
//+------------------------------------------------------------------+
//| Проверяем, записано ли время открытия текущей недели             |
//+------------------------------------------------------------------+
bool CheckCurrentWeekOpenTime()
  {
//--- Проверяем, записано ли время
   if(_weekOpenTime==0) // Если время не записано
     {
      //--- Получаем время открытия недельного бара
      ResetLastError();
      const datetime weekBarOpenTime=(datetime)SeriesInfoInteger(_Symbol,PERIOD_W1,SERIES_LASTBAR_DATE);
      const int err=GetLastError();
      //---
      if(inpFileLog)
        {
         Print(__FILE__,": Время открытия недельного бара = "+TimeToString(weekBarOpenTime)+". Ошибка #",err);
         //Print( __FILE__,": Время открытия текущей недели через iTime = "+TimeToString( weekOpenTime2 )+". Ошибка #",err2 );
        }
      //ResetLastError();
      //const datetime weekBarOpenTime2 = iTime( _Symbol, PERIOD_W1, 0 );
      //const int err2 = GetLastError();
      //--- Проверяем, получено ли время открытия недельного бара
      if(weekBarOpenTime==0 || err!=0) // Если время бара не получено или история обновляется
         return(false);                              // Возвращаем ложь
      //--- Запоминаем время открытия текущей недели (время открытия недельного бара - воскресенье)
      _weekOpenTime=weekBarOpenTime+SEC_PER_DAY;
      //const datetime weekOpenTime2 = weekBarOpenTime2+SEC_PER_DAY;
      //---
      if(inpFileLog)
        {
         Print(__FILE__,": Актуальное время открытия текущей недели = "+TimeToString(_weekOpenTime)+". Ошибка #",err);
         //Print( __FILE__,": Время открытия текущей недели через iTime = "+TimeToString( weekOpenTime2 )+". Ошибка #",err2 );
        }
      //--- Возвращаем истину
      return( true );
     }
//--- Время открытия недели ранее записано. Возвращаем истину
   return( true );
  }
//+------------------------------------------------------------------+
//| Проверяем, записано ли время открытия текущего дня                  |
//+------------------------------------------------------------------+
bool CheckCurrentDayOpenTime()
  {
//--- Проверяем, записано ли время
   if(_dayOpenTime==0) // Если время не записано
     {
      //--- Получаем время открытия дневного бара
      ResetLastError();
      const datetime tempDayOpenTime=(datetime)SeriesInfoInteger(_Symbol,PERIOD_D1,SERIES_LASTBAR_DATE);
      const int err=GetLastError();
      //ResetLastError();
      //const datetime dayOpenTime2 = iTime( _Symbol, PERIOD_D1, 0 );
      //const int err2 = GetLastError();
      //---
      if(inpFileLog)
        {
         Print(__FILE__,": Время открытия текущего дня = "+TimeToString(tempDayOpenTime)+". Ошибка #",err);
         //Print( __FILE__,": Время открытия текущего дня iTime = "+TimeToString( dayOpenTime2 )+". Err2 #",err2 );
        }
      //--- Проверяем, получено ли время открытия дневного бара
      if(tempDayOpenTime==0 || err!=0) // Если время бара не получено
         return(false);                                 // Возвращаем ложь
      //--- Сохраняем в глобальную переменную значение открытия текущего дня
      _dayOpenTime=tempDayOpenTime;
      //---
      if(inpFileLog)
        {
         Print(__FILE__,": Актуальное время открытия текущего дня = "+TimeToString(_dayOpenTime)+". Ошибка #",err);
         //Print( __FILE__,": Время открытия текущей недели через iTime = "+TimeToString( weekOpenTime2 )+". Ошибка #",err2 );
        }
      //--- Возвращаем истину
      return( true );
     }
//--- Время открытия дня ранее записано. Возвращаем истину
   return( true );
  }
//+------------------------------------------------------------------+
//| Проверяем, записано ли время открытия текущего часа              |
//+------------------------------------------------------------------+
bool CheckCurrentHourOpenTime()
  {
//--- Проверяем, записано ли время
   if(_hourOpenTime==0) // Если время не записано
     {
      //--- Получаем время открытия часового бара
      ResetLastError();
      const datetime tempHourOpenTime=(datetime)SeriesInfoInteger(_Symbol,PERIOD_H1,SERIES_LASTBAR_DATE);
      const int err=GetLastError();
      //const datetime hourOpenTime2 = iTime( _Symbol, PERIOD_H1, 0 );
      //---
      if(inpFileLog)
        {
         Print(__FILE__,": Время открытия текущего часа = "+TimeToString(tempHourOpenTime)+". Ошибка #",err);
         //Print( __FILE__,": Время открытия текущего часа iTime = "+TimeToString( hourOpenTime2 ) );
        }
      //--- Проверяем, получено ли время открытия часового бара
      if(tempHourOpenTime==0 || err!=0) // Если время бара не получено
         return(false);                                 // Возвращаем ложь
      //---
      _hourOpenTime=tempHourOpenTime;
      //---
      if(inpFileLog)
        {
         Print(__FILE__,": Актуальное время открытия текущего часа = "+TimeToString(_hourOpenTime)+". Ошибка #",err);
         //Print( __FILE__,": Время открытия текущего часа iTime = "+TimeToString( hourOpenTime2 ) );
        }
      //--- Возвращаем истину
      return( true );
     }
//--- Время открытия часа ранее записано. Возвращаем истину
   return( true );
  }

Результат: В результате, если терминал НЕ БЫЛ запущен несколько часов, например, был выключен на ночь, получаем вот такой вот результат при его первом (за день) запуске:

2018.09.21 11:11:27.306 test_isNewDayInTimer USDCHF.e,M1: test_isNewDayInTimer.mq4: Актуальное время открытия текущего часа = 2018.09.20 16:00. Ошибка #0
2018.09.21 11:11:27.306 test_isNewDayInTimer USDCHF.e,M1: test_isNewDayInTimer.mq4: Время открытия текущего часа = 2018.09.20 16:00. Ошибка #0
2018.09.21 11:11:27.280 test_isNewDayInTimer USDCHF.e,M1: test_isNewDayInTimer.mq4: Время открытия текущего часа = 2018.09.20 16:00. Ошибка #4066
2018.09.21 11:11:27.279 test_isNewDayInTimer USDCHF.e,M1: test_isNewDayInTimer.mq4: Актуальное время открытия текущего дня = 2018.09.20 00:00. Ошибка #0
2018.09.21 11:11:27.279 test_isNewDayInTimer USDCHF.e,M1: test_isNewDayInTimer.mq4: Время открытия текущего дня = 2018.09.20 00:00. Ошибка #0
2018.09.21 11:11:27.265 test_isNewDayInTimer USDCHF.e,M1: test_isNewDayInTimer.mq4: Время открытия текущего дня = 2018.09.20 00:00. Ошибка #4066
2018.09.21 11:11:27.264 test_isNewDayInTimer USDCHF.e,M1: test_isNewDayInTimer.mq4: Актуальное время открытия текущей недели = 2018.09.17 00:00. Ошибка #0
2018.09.21 11:11:27.264 test_isNewDayInTimer USDCHF.e,M1: test_isNewDayInTimer.mq4: Время открытия недельного бара = 2018.09.16 00:00. Ошибка #0
2018.09.21 11:11:27.233 test_isNewDayInTimer USDCHF.e,M1: test_isNewDayInTimer.mq4: Время открытия недельного бара = 2018.09.16 00:00. Ошибка #4066
2018.09.21 11:11:27.066 test_isNewDayInTimer USDCHF.e,M1: initialized
2018.09.21 11:11:27.055 Custom indicator test_isNewDayInTimer USDCHF.e,M1: loaded successfully

Как видно, время получения данных 2018.09.21 11:11, и для этого времени мы получаем время открытия дня = 2018.09.20 (хотя должно быть 2018.09.21) и время открытия часа = 2018.09.20 16:00 (хотя должно быть 2018.09.21 11:00). Причем время, возвращенное терминалом, это не что иное как данные, на момент его закрытия в последний раз. Т.е. данные кешируются и возвращаются вне зависимости от того, что терминал был выключен. Причем я понимаю, если бы каждый раз возвращалась ошибка #4066 (запрошенные исторические данные в состоянии обновления) пока данные не будут обновлены, но нет, возвращаются именно ОШИБОЧНЫЕ данные! Эта ошибка возвращается только один раз, а далее - живите как хотите. На лицо явная ошибка кеширования. Прошу разработчиков (@Slava) обратить на нее внимание!

Повторюсь. Ошибочные данные появляются если запрашивать их в таймере!

Версия терминала: x64, 1090.

 
Ну так ошибка 4066 типичная проблема для МТФ индюков, терминал не подкачал данные других ТФ необходима проверка на целостность данных других ТФ.
 
Vitaly Gorbunov:
Ну так ошибка 4066 типичная проблема для МТФ индюков, терминал не подкачал данные других ТФ необходима проверка на целостность данных других ТФ.

Посмотрите внимательнее на код и на то, что я написал прежде, чем давать советы.

В коде есть проверка на наличие ошибок и есть проверка на пустые данные. А когда функция возвращает некорректные данные без ошибки - это ошибка!

 
Alexey Kozitsyn:

Посмотрите внимательнее на код и на то, что я написал прежде, чем давать советы.

В коде есть проверка на наличие ошибок и есть проверка на пустые данные. А когда функция возвращает некорректные данные без ошибки - это ошибка!

Возможно я что то упустил покажите где вы проверяете целостность истории например в процедуре CheckCurrentHourOpenTime()
 
Vitaly Gorbunov:
Возможно я что то упустил покажите где вы проверяете целостность истории например в процедуре CheckCurrentHourOpenTime()

Что Вы понимаете под целостностью истории?

Я говорю о том, что есть проверка на наличие ошибок при получении значений истории. Проверка есть в каждой функции. Вот она:

//--- Проверяем, получено ли время открытия часового бара
      if(tempHourOpenTime==0 || err!=0) // Если время бара не получено
         return(false);                                 // Возвращаем ложь

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

Суть в том, что функция SeriesInfoInteger() сначала возвращает ошибку, а на следующем запуске - НЕТ! Причем корректное значение она также не возвращает!
 

SeriesInfoInteger() возвращает только информацию по определённому запросу, в данном случае мы просим вернуть время открытия последнего известнорго бара в истории по символу и периоду. Тут ошибки нет, что было крайним на данный момент то он и вернул. Сейчас доберусь до большого компа покажу как можно проверить целостность истории.

 
Vitaly Gorbunov:

SeriesInfoInteger() возвращает только информацию по определённому запросу, в данном случае мы просим вернуть время открытия последнего известнорго бара в истории по символу и периоду. Тут ошибки нет, что было крайним на данный момент то он и вернул. Сейчас доберусь до большого компа покажу как можно проверить целостность истории.

В смысле тут нет ошибки!? А зачем она тогда коды ошибок "левые" выдает. Говорит, что история в порядке там, где она не в порядке...

 
Alexey Kozitsyn:

В смысле тут нет ошибки!? А зачем она тогда коды ошибок "левые" выдает. Говорит, что история в порядке там, где она не в порядке...

Ещё раз эта функция не проверяет целостность истории! Она возвращает информацию которую смогла в ней найти. В конкретном случае она нашла часовой бар который был запрошен в момент выключения терминала. Остальная история ещё не подгрузилась. 

 

Для проверки факта полной подкачки истории заданного ТФ достаточно использовать функцию:

bool IsTFDataReady(ENUM_TIMEFRAMES eTF)
{
   ResetLastError();
   iTime(NULL, eTF, 1);
   return GetLastError() == ERR_NO_ERROR;
}

Если функция возвращает false, то данные по запрошенному ТФ неполные. Иначе - полные.

 
Vitaly Gorbunov:

Ещё раз эта функция не проверяет целостность истории! Она возвращает информацию которую смогла в ней найти. В конкретном случае она нашла часовой бар который был запрошен в момент выключения терминала. Остальная история ещё не подгрузилась. 

Еще раз. Об этом нигде не сказано. Это во-первых. Во-вторых, зачем она вводит тогда в заблуждение сначала показывая код ошибки 4066, а потом - нет?

 
Ihor Herasko:

Для проверки факта полной подкачки истории заданного ТФ достаточно использовать функцию:

Если функция возвращает false, то данные по запрошенному ТФ неполные. Иначе - полные.

В таймере проверяли? Видите у меня закомментированные строки? Я проверил эту функцию, она вообще никаких ошибок не показывала, причем также показывала некорректные данные. Проверю еще раз.

Причина обращения: