Сбой в работе функции iTime()

 

Доброго времени суток!

[Вопрос по MetaTrader 4.]


Давно (больше года) заметил сбой в функции:

datetime  iTime(
   string           symbol,          // символ
   int              timeframe,       // период
   int              shift            // сдвиг
   );

когда shift==0, т.е. с текущим/настоящим временем.

Возвращаемое значение оказывается равным предыдущему вызову.

-----

Есть скрипт, бросаю его на график с периодом H1. Параметр  timeframe равен PERIOD_M15. Добавил Print() с выводом значений переменных. 

Из окна "Навигатор" мышкой перетаскиваю скрипт на график. Сделал так в 0 часов 10 минут, возвращаемое значение 00:00, правильное.

Следующий раз использовал скрипт в 11 часов 28 минут, возвращаемое значение 00:00, как при прошлом вызове! 

Тут же, через 3-5 секунд, повторно бросаю скрипт, и он отрабатывает без сбоев возвращаемое значение 11:15.

(Бывает через раз. Около часа ночи запускаю >> работает. В 14 часов запускаю >> работает. В 18 часов запускаю >> возвращает время соответствующее первому часу ночи.)

 
прикрепите код скрипта или его кусок с вызовом iTime()
 
FxPro7009:

Есть скрипт, бросаю его на график с периодом H1. Параметр  timeframe равен PERIOD_M15. 

Очевидно же, что сначала нет данных для М15.
 
Vladislav Boyko #:
прикрепите код скрипта или его кусок с вызовом iTime()
input int PERIOD_Vivoda=PERIOD_M15;  //Выводимый период.
input datetime Nachalo=D'2022.08.15 00:00:00';
input datetime Final=0; //От Nachalo и до конца.

void OnStart(void)
{
   if(VremyaFailaPoiska==Nachalo)ZapisStroki(Visota-VisotaLiteri-3,OtstupLinii,"СТАРТ"); //СТАРТ
 

   tx=TimeCurrent();
   if(Final==0)ty=NTB(tx,PERIOD_Vivoda,nazad,"#673:");//Округлить до PERIOD_Vivoda, вызов NTB()
     else ty=Final;

...
}



//-----------------------------------------------------------------------------//
// Нормализует время до действительно существующего бара.                      //
// (При отсутствии нескольких баров подряд.)                                   //
// Т.е. половина неполного периода определится как существующий бар. Если бара //
// не существует (дыра, выходной) то см. 'kudaNT'                              //
// Пример: PERIOD_NTB равен PERIOD_D1                                          //
//        t1NTB=середина субботы или середина_воскресения, тогда:              //
//      'nazad'=начало_пятницы,                                                //
//     'vpered'=начало_понедельника,                                           //
//       'bliz'=начало_пятницы или начало_понедельника по абсолютной           //
//                 разнице в секундах после вычета времени выходных.           //
//-----------------------------------------------------------------------------//

datetime NTB(datetime t1NTB,int PERIOD_NTB=0,int kudaNT=vpered,string strNTB="")

{
int j,k;
datetime told,tper,tnew;

j=iBarShift(NULL,PERIOD_NTB,t1NTB,false);
told=iTime(NULL,PERIOD_NTB,j);

if(j==0)//Если нулевой(крайний) бар.
   {
      //Расчет времени окончания нулевого бара(бар есть, значит не выходной)
   tper=told+PERIOD_NTB*60; if(told<=t1NTB && t1NTB<tper)return(told);
   
      //Расчет следующего возможного бара
   tnew=tper+PERIOD_NTB*60; k=TimeDayOfWeek(tnew);
   if(k==0 || k==6)tnew+=172800;//Не считая первого часа в субботу
   if(tper<=t1NTB && t1NTB<tnew)return(tper);//Конец нулевого бара(как начало следующего бара)
      else{//Если t1NTB далеко в будующем
            Print(">>>>>Предупреждение:<<<<<");
            Print(VivodTime("told",told),VivodTime("t1NTB",t1NTB),VivodTime("tper",tper),VivodTime("tnew",tnew)
                   ," ",PeriodToStr(PERIOD_NTB)," j=",j
                  );
   }       }

...

}

 
FxPro7009 #:

После обращения к таймсериям проверяйте результат. Так если получено значение 0, то вызовите GetLastError() и узнайте причину, по которой не получилось принять данные.

А еще лучше - проверять готовность данных на ТФ загодя, вызвав до начала расчетов функцию:

bool IsTFDataReady(const ENUM_TIMEFRAMES eTF)
{
   ResetLastError();
   iTime(NULL, eTF, 1);
   return GetLastError() == ERR_NO_ERROR;
}
 
Sergey Gridnev #:
Очевидно же, что сначала нет данных для М15.

Иногда нормально работает, в другой раз -- нет. Компьютер работает круглые сутки. Никак не пойму в чем различие... ://

 
Ihor Herasko #:

После обращения к таймсериям проверяйте результат. Так если получено значение 0, то вызовите GetLastError() и узнайте причину, по которой не получилось принять данные.

А еще лучше - проверять готовность данных на ТФ загодя, вызвав до начала расчетов функцию:

Игорь, хочу выразить вам благодарность. Опираясь на ваши подсказки с форума сделал себе такую штуку для работы с котировками других таймфреймов/инструментов. Пользуюсь до сих пор.

#property strict

#define RATES_WAITING_SEC         30
#define RATES_WAITING_TICKS       10

MqlRates             ratesD1[];
MqlRates             ratesH1[];
datetime             ratesWaitingTimeTo;
int                  ratesWaitingTicksLeft;

int OnInit()
  {
   ratesWaitingTimeTo = 0;
   ratesWaitingTicksLeft = 0;
   return(INIT_SUCCEEDED);
  }

void OnTick()
  {
   bool isRatesActual = updateRates();
   calculateSomethingUsingRates(isRatesActual);
  }

bool calculateSomethingUsingRates(bool isRatesActual)
  {
   if(!isRatesActual)
      return(false);
   // Можно использовать ratesD1[] и ratesH1[]
   return(true);
  }

bool updateRates()
  {
   datetime timeCurrent = TimeCurrent();
   // Waiting for rates to be loaded
   if(timeCurrent <= ratesWaitingTimeTo || ratesWaitingTicksLeft > 0)
     {
      ArrayCopyRates(ratesD1, _Symbol, PERIOD_D1);
      ArrayCopyRates(ratesH1, _Symbol, PERIOD_H1);
      PrintFormat("Waiting for rates to be loaded. Remaining: %i seconds, %i ticks", ratesWaitingTimeTo - timeCurrent, ratesWaitingTicksLeft);
      if(ratesWaitingTicksLeft > 0)
         ratesWaitingTicksLeft--;
      return(false);
     }
   // Timeframes copying
   bool allRatesCopied = true;
   updateRates_CopyTo(ratesD1, PERIOD_D1, allRatesCopied);
   updateRates_CopyTo(ratesH1, PERIOD_H1, allRatesCopied);
   // -------
   if(!allRatesCopied)
     {
      ratesWaitingTimeTo = timeCurrent + RATES_WAITING_SEC;
      ratesWaitingTicksLeft = RATES_WAITING_TICKS;
      return(false);
     }
   return(true);
  }

void updateRates_CopyTo(MqlRates &ratesArray[], ENUM_TIMEFRAMES timeframe, bool &allRatesCopied)
  {
   ResetLastError();
   int copied = ArrayCopyRates(ratesArray, _Symbol, timeframe);
   int error = GetLastError();
   if(copied < 1 || error != ERR_NO_ERROR)
     {
      PrintFormat("CopyRates error %i, rates copied: %i, timeframe %s", error, copied, EnumToString(timeframe));
      allRatesCopied = false;
     }
  }

Просто вызываю updateRates() с каждым тиком.

Этот конкретный кусок кода взят из одного из последних экспертов, где требовались котировки с H1 и D1, не зависимо от текущего таймфрейма.

 
Vladislav Boyko #:

Игорь, хочу выразить вам благодарность. Опираясь на ваши подсказки с форума сделал себе такую штуку для работы с котировками других таймфреймов/инструментов. Пользуюсь до сих пор.

Просто вызываю updateRates() с каждым тиком.

Этот конкретный кусок кода взят из одного из последних экспертов, где требовались котировки с H1 и D1, не зависимо от текущего таймфрейма.

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

...происходит виртуальное копирование таймсерии в массив структур MqlRates. Это означает, что если данные таймсерии обновились, то повторное копирование не требуется - массив rates_array[] по-прежнему будет ссылаться на актуальную версию таймсерии и данные будут корректными всегда.

Возможно и правда достаточно будет один раз вызвать после инициализации. Давненько хочу проверить это, все руки не доходят.

То есть, понятно, что данные будут автоматически обновляться в этих массивах. Вопрос только в том, продолжат ли они обновляться если этот таймфрейм длительное время не будет использоваться (не открыто окно с этим таймфреймом, ни одна mql программа не запрашивает котировки с него). Вот это проверить все руки не доходят. А до тех пор на всякий пожарный дергаю ArrayCopyRates с каждым тиком, запрашивая таким образом постоянно котировки, что бы они оставались актуальными.

С другой стороны, если вызывать  ArrayCopyRates() только раз и случится какая-нибудь херня из-за которой данные вдруг станут не корректными, то мы об этом не узнаем. Может лучше так и оставить, тем более времени на такое дергание тратится не много.

 
Ihor Herasko #:

После обращения к таймсериям проверяйте результат. Так если получено значение 0, то вызовите GetLastError() и узнайте причину, по которой не получилось принять данные.

А еще лучше - проверять готовность данных на ТФ загодя, вызвав до начала расчетов функцию:

Наверное, я не верно выразился. Результат не равен нулю. Возвращаемое значение ноль часов, т.к. на дворе была полночь. Дата при этом соответствовала.

Выдержка из Log файла во вложении.

Видно, что параметр t1NTB изменяется с течением времени. Видно, что j=0 т.е. iBarShift отрабатывает правильно. Вот только told  выдает ноль часов и ноль минут. 

Скрипт запущен:

0 11:28:10.233 Script DiagrammaBMP EURUSD,H1: loaded successfully

Скрипт прошел с ошибками и окончен:

0 11:28:25.661 Script DiagrammaBMP EURUSD,H1: removed

Посмотрел я на это, почесал репу. И запустил повторно (через 10 сек)

Отсюда:

0 11:28:35.520 Script DiagrammaBMP EURUSD,H1: loaded successfully

До окончания:

0 11:28:49.092 Script DiagrammaBMP EURUSD,H1: removed

ни одной ошибки.

Ничего не менял, не перезагружал, не компилировал, вообще ничего не делал. По сути даже мышкой не двигал. Взял файл из навигатора и бросил его на график, переместив на пять сантиметров вправо. И так два раза подряд. Почему один раз прошло с ошибками, а другой раз без них??

Файлы:
2022.08.15.log  55 kb
 

Добавил RefreshRates() в начало OnInit(). Возвращает false.

Перенес из OnInit()  в OnStart(). Так работает.

Поставил снова в OnInit(), но не в начало, а в конец функции. Работает.

-----

RefreshRates() в первых строках OnInit() стабильно возвращает false. GetLastError()=0.

 

рефрешратес нужен только если у вас код очень медленный, иначе какой смысл обновлять каждые 10милесекунд?

если с острова торгуете, разумней будет отложенными ордерами.