Чтение записи о событии по идентификатору

После того как трейдер изучит расписание событий на ближайшее будущее, он может настраивать соответствующим образом своих роботов. В API календаря нет функций или событий ("событий" — в смысле функций обработки новой финансовой информации вроде OnCalendar, по аналогии с OnTick) для автоматического отслеживания выхода новостей. Алгоритм должен это делать сам с любой выбранной периодичностью. В частности, достаточно загодя выяснить идентификатор интересующего вас события с помощью одной из рассмотренных ранее функций (например, CalendarValueHistoryByEvent, CalendarValueHistory) и затем вызывать CalendarValueById, чтобы получать актуальное состояние полей в структуре MqlCalendarValue.

bool CalendarValueById(ulong id, MqlCalendarValue &value)

Функция заполняет переданную по ссылке структуру текущей информаций о конкретном событии.

Результат функции обозначает признак успеха (true) или ошибки (false).

Создадим простой безбуферный индикатор CalendarRecordById.mq5, который найдет в будущем самое близкое событие с типом "финансового индикатора" (т.е. числового показателя) и будет по таймеру опрашивать его состояние. Когда новость опубликуют, данные изменятся (станет известно "актуальное" значение показателя), и индикатор выведет алерт.

Периодичность опроса календаря задается во входной переменной.

input uint TimerSeconds = 5;

Таймер запустим в OnInit.

void OnInit()
{
   EventSetTimer(TimerSeconds);
}

Для удобного вывода в журнал описания события используется структура MqlCalendarRecord, знакомая нам по примеру скрипта CalendarForDates.mq5.

Для хранения исходного состояния информации о новости опишем структуру track.

MqlCalendarValue track;

Когда структура пуста (и в поле id находится 0), программа должна запросить предстоящие события и найти среди них ближайшее с типом CALENDAR_TYPE_INDICATOR и у которого еще не известно актуальное значение.

void OnTimer()
{
   if(!track.id)
   {
      MqlCalendarValue values[];
      if(PRTF(CalendarValueHistory(valuesTimeCurrent(), TimeCurrent() + DAY_LONG * 3)))
      {
         for(int i = 0i < ArraySize(values); ++i)
         {
            MqlCalendarEvent event;
            CalendarEventById(values[i].event_idevent);
            if(event.type == CALENDAR_TYPE_INDICATOR && !values[i].HasActualValue())
            {
               track = values[i];
               PrintFormat("Started monitoring %lld"track.id);
               StructPrint(MqlCalendarRecord(track), ARRAYPRINT_HEADER);
               return;
            }
         }
      }
   }
   ...

Найденное событие копируется в track и выводится в журнал. После этого каждый вызов OnTimer сводится к получению обновленной информации о событии в структуру update — именно она передается в CalendarValueById, с указанием идентификатора track.id. Далее производится сравнение исходной и новой структур с помощью вспомогательной функции StructCompare (на основе StructToCharArray и ArrayCompare, см. полный исходный код). Любое различие приводит к печати нового состояния (может быть поменялся прогноз), а если появилось актуальное значение, таймер останавливается. Чтобы запустить ожидание следующей новости, этот индикатор нужно переинициализировать: он является демонстрационным, а для контроля обстановки по списку новостей мы позднее разработаем более практичный класс-фильтр.

   else
   {
      MqlCalendarValue update;
      if(CalendarValueById(track.idupdate))
      {
         if(fabs(StructCompare(trackupdate)) == 1)
         {
            Alert(StringFormat("News %lld changed"track.id));
            PrintFormat("New state of %lld"track.id);
            StructPrint(MqlCalendarRecord(update), ARRAYPRINT_HEADER);
            if(update.HasActualValue())
            {
               Print("Timer stopped");
               EventKillTimer();
            }
            else
            {
               track = update;
            }
         }
      }
      
      if(TimeCurrent() <= track.time)
      {
         Comment("Forthcoming event time: "track.time,
            ", remaining: "Timing::stringify((uint)(track.time - TimeCurrent())));
      }
      else
      {
         Comment("Forthcoming event time: "track.time,
            ", late for: "Timing::stringify((uint)(TimeCurrent() - track.time)));
      }
   }
}

Во время ожидания события индикатор выводит в комментарий ожидаемое время выхода новости, и сколько до неё осталось (или каково опоздание).

Комментарий об ожидании или истечении ближайшей новости

Комментарий об ожидании или опоздании ближайшей новости

Важно отметить, что новость может выйти как чуть раньше, так и чуть позже запланированного срока. Это создает некоторые проблемы при тестировании новостных стратегий на истории, так как время обновления записей календаря в терминале и через MQL5 API не предоставляется. Мы попробуем частично решить эту проблему в следующем разделе.

Вот фрагменты вывода в журнал, произведенного индикатором с некоторым промежутком:

CalendarValueHistory(values,TimeCurrent(),TimeCurrent()+(60*60*24)*3)=186 / ok

Started monitoring 156045

  [id] [event_id]              [time]            [period] [revision] »

156045  840020013 2022.06.27 15:30:00 2022.05.01 00:00:00          0 »

»       [actual_value] [prev_value] [revised_prev_value] [forecast_value] [impact_type] »

» -9223372036854775808       400000 -9223372036854775808                0             0 »

» [importance]                     [name] [currency] [code] [actual] [previous] [revised] [forecast]

» "Medium"     "Durable Goods Orders m/m" "USD"      "US"        nan    0.40000       nan    0.00000

...

Alert: News 156045 changed

New state of 156045

  [id] [event_id]              [time]            [period] [revision] »

156045  840020013 2022.06.27 15:30:00 2022.05.01 00:00:00          0 »

» [actual_value] [prev_value] [revised_prev_value] [forecast_value] [impact_type] »

»         700000       400000 -9223372036854775808                0             1 »

» [importance]                     [name] [currency] [code] [actual] [previous] [revised] [forecast]

» "Medium"     "Durable Goods Orders m/m" "USD"      "US"    0.70000    0.40000       nan    0.00000

Timer stopped

 

В обновленной новости появилось значение actual_value.

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

Функция CalendarValueById — не единственная, и, вероятно, не самая гибкая, с помощью которой можно мониторить изменения в календаре. Пару других подходов мы рассмотрим в следующих разделах.