Как в таймере MQL4 узнать реальное время?

 

Всем привет.

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

Раньше я писал такую функцию и долгое время с ней работал (есть в CodeBase)

bool _SignalofTime(int HH,int MM,int SS,int TIMEOUT)
  {
   if(HH>23 || HH<0 || MM>59 || MM<0 || SS>59 || SS<0 || TIMEOUT<0)
     {
      Print("Некорректные параметры времени. Работа функции приостановлена.");
      return(false);
     }
   datetime timeX=iTime(Symbol(),PERIOD_D1,0)+HH*3600+MM*60+SS; // время X (к текущей дате прибавляем часы и минуты (переводя все в секунды))
   if(IsTesting())
     {
      static datetime t=0;
      if(TimeCurrent()>=timeX && t<timeX)
        {
         if(t==0) { t=TimeCurrent(); return(false); }
         else     { t=TimeCurrent(); return(true);  }
        }
      return(false);
     }
   if(TimeCurrent()>timeX || TimeCurrent()<timeX-TIMEOUT) return(false);
   while(TimeCurrent()<timeX) // ... входим в цикл, чтобы не пропустить заданное время
      Sleep(500); // засыпаем на 0,5 секунды до тех пор, пока не стукнет ровно заданное время  
   return(true);
  }

Но она тоже не дает 100% точности. Кроме того, она предназначена для OnTick, а в таймере, кроме как дождаться обновления TimeCurrent в цикле while, нет иных способов узнать точное серверное время.

Можно было бы конечно еще один раз в OnInit найти разницу между серверным и локальным временем и работать по локальным часам. Есть ли у такого подхода минусы?

Есть ли еще способы запустить выполнение кода в строго заданную секунду по времени сервера, с учетом того, что котировок вообще может не прийти в это время и за несколько минут до?

 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Market closed

fxsaber, 2017.09.22 09:45

// Время последнего тика символа
long GetSymbolTime( const string Symb )
{
  MqlTick Tick;
  
  return(SymbolInfoTick(Symb, Tick) ? Tick.time_msc : 0);
}

// Время последнего тика Обзора рынка
long GetMarketWatchTime( void )
{
  long Res = 0;
  
  for (int i = SymbolsTotal(true) - 1; i >= 0; i--)
  {
    const long TmpTime = GetSymbolTime(SymbolName(i, true));
    
    if (TmpTime > Res)
      Res = TmpTime;
  }
  
  return(Res);
}

// Текущее время на торговом сервере без учета пинга
long GetCurrenTime( void )
{
  static ulong StartTime = GetMicrosecondCount();
  static long PrevTime = 0;
  
  const long TmpTime = GetMarketWatchTime();
  
  if (TmpTime > PrevTime)
  {
    PrevTime = TmpTime;
    
    StartTime = GetMicrosecondCount();
  }
  
  return(PrevTime + (long)((GetMicrosecondCount() - StartTime) / 1000));
}
 

Сначала читал код, думал, зачем устраивать цикл по всем инструментам и использовать структуру MqlTick, если аналогичное значение в TimeCurrent(). Протестировал код - понял, что разница лишь в том, что идет расчет интервала с того момента как мы НАШЛИ самое свежее время (но это не означает, что оно пришло прямо сейчас - мы его лишь нашли сейчас) до момента выхода из функции. Такой вариант мне не совсем понравился (так как это не гарантирует, что то, что мы нашли, пришло прямо сейчас - оно могло прийти пол-секунды назад. Но навело меня на мысль. Каждые 100 мс запрашиваем TimeCurrent и запоминаем время прихода свежего времени (с учетом периодичности таймера устаревание значения не более, чем на 100 сек.). Таким образом будет обеспечена погрешность не более 100 мс. Если сократить таймер - то можно еще большей точности добиться.

Спасибо!

 
Может отказаться от OnTick в пользу OnTimer? Или использовать обе.
 
Igor Zakharov:
Может отказаться от OnTick в пользу OnTimer? Или использовать обе.

Решил сделать финт ушами - в обычное время работать в режиме онтик, а за 15 минут до времени наступления события - активировать миллитаймер. При наступлении нужного времени - выполнять событие и вызывать киллера (EventKillTimer). Плюс добавить флаг существования таймера на глобальном уровне программы. Получилась вот такая конструкция для теста, работает сносно)

#property copyright "Artem Kuraev"
#property link      "https://vk.com/artyom52"
#property version   "1.00"
#property strict

bool activate_timer=false;
//+------------------------------------------------------------------+
int OnInit()
  {
   activate_timer=false;
      if(Minute()%2==1)
        { EventSetTimer(5); activate_timer=true; Print("таймер активирован"); }
        
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(activate_timer) EventKillTimer();

  }
//+------------------------------------------------------------------+
void OnTick()
  {
   if(activate_timer)
     {
      if(Minute()%2==0)
        { EventKillTimer(); activate_timer=false; Print("таймер деактивирован"); }
     }
   else
     {
      if(Minute()%2==1)
        { EventSetTimer(5); activate_timer=true; Print("таймер активирован"); }
     }
  }
//+------------------------------------------------------------------+
void OnTimer()
  {
   if(Minute()%2==0) { EventKillTimer(); activate_timer=false; Print("таймер деактивирован"); return; }
   Print(activate_timer," ",Minute()," ",TimeCurrent());
  }
//+------------------------------------------------------------------+
Если кто-то захочет использовать, нужно учесть, что для тестера необходимо прописывать свой вариант работы, без таймера.
 
Artyom Kuraev:

Решил сделать финт ушами - в обычное время работать в режиме онтик, а за 15 минут до времени наступления события - активировать миллитаймер. При наступлении нужного времени - выполнять событие и вызывать киллера (EventKillTimer). Плюс добавить флаг существования таймера на глобальном уровне программы. Получилась вот такая конструкция для теста, работает сносно)

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

мелкий фикс - EventSetTimer может завершаться неудачно. В "боевой" обстановке, бывает редко но метко блин....зависит то-ли от нагрузки, то ли от наличия DLL или просто луны сатурна не в той конфигурации

Поэтому признак activate_timer надо ставить в зависимости от получилось/не получилось.

а если не получилось - то "закат солнца вручную", то есть проверять время при каждой возможности (в OnTick , OnChartEvent) и периодически вызывать OnTimer самому и пытаться всё-же запустить EventSetTimer

 
Maxim Kuznetsov:

мелкий фикс - EventSetTimer может завершаться неудачно. В "боевой" обстановке, бывает редко но метко блин....зависит то-ли от нагрузки, то ли от наличия DLL или просто луны сатурна не в той конфигурации

Поэтому признак activate_timer надо ставить в зависимости от получилось/не получилось.

а если не получилось - то "закат солнца вручную", то есть проверять время при каждой возможности (в OnTick , OnChartEvent) и периодически вызывать OnTimer самому и пытаться всё-же запустить EventSetTimer

Спасибо за дополнение, важное!