按 ID 读取事件记录

了解近期事件的时间表后,交易者可以相应地调整他们的机器人。日历 API 中没有用于自动跟踪新闻发布的函数或事件(这里的“事件”是指像 OnCalendar 那样处理新财经信息的功能,类似于 OnTick)。算法必须以任何选定的频率自行执行此操作。特别是,你可以使用先前讨论的函数之一(例如,CalendarValueHistoryByEventCalendarValueHistory)找出所需事件的标识符,然后调用 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 结构体中,该结构体通过 track.id 标识符传递给 CalendarValueById。接下来,使用辅助函数 StructCompare(基于 StructToCharArrayArrayCompare,请参见完整的源代码)比较原始结构体和新结构体。任何差异都会导致打印新状态(预测可能已更改),如果出现当前值,则计时器停止。要开始等待下一条新闻,需要重新初始化此指标:这只是一个演示,为了根据新闻列表控制情况,我们稍后将开发一个更实用的筛选器类。

   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 函数并非唯一,也可能不是最灵活的监控日历变化的方法。我们将在后续章节中探讨其他几种方法。