使用MQL5经济日历进行交易(第一部分):精通MQL5经济日历的功能·进阶篇
🗓️

使用MQL5经济日历进行交易(第一部分):精通MQL5经济日历的功能·进阶篇

(2/3)· 从日历结构到MQL5取数逻辑,打通新闻事件与自动交易的最后一公里

含代码示例偏理论 第 2/3 篇
多数交易者把经济日历当被动看板,事件过了才后悔没提前规避波动。其实MT5里的日历能直接被EA读取,关键是你得先搞懂它的字段结构和影响等级划分。本篇接着把这套取数逻辑拆开讲透。

◍ 正文

我们在 for循环 之前声明这些逻辑,以在当前服务器时间周围建立一个时间范围,从而定义我们希望考虑新闻事件的过去和未来时间跨度。首先,我们声明一个datetime类型的变量“timeRange”,并使用 PeriodSeconds 函数将其赋值为一天的时间长度。这使得我们能够使用一个标准化的24小时时间框架,不过,您可以根据需要将其调整为期望的范围,比如新闻发布前后的15分钟。接下来,我们定义另外两个datetime类型的变量:“timeBefore”和“timeAfter”。 我们通过从当前服务器时间中减去时间范围来计算变量“timeBefore”,这样一来给出了我们希望回溯的最远时间点。类似地,“timeAfter”是通过将“timeRange”加到当前服务器时间上来确定的,这就提供了我们希望在未来考虑的最晚时间点。“timeBefore”和“timeAfter”共同创建了一个以当前时间为中心的24小时窗口,这有助于我们捕捉刚刚发生或即将发生的事件。因此,我们可以通过扩大时间范围或增加事件搜索范围(在此情况下,可将其扩展为一天)来增加搜索力度。图示如下: 由图可见,当我们考虑的是为期一天的扫描时,当前时间(或更准确地说,是当前日期)为25日,那么回溯的日期是24日,前瞻的日期是26日,这正好是当前日期的前一天和后一天。为了清晰起见,我们已经用蓝色将它们高亮显示。最后,我们可以分析从循环中记录的新闻事件数量,并返回相应的布尔标识。我们采用以下逻辑来实现这一点。 这里,我们根据记录的新闻总数来评估是否发现了任何相关的新闻事件。如果新闻总数大于0,这表明至少找到了一个符合我们标准的新闻事件。在此情况下,我们将“isNews”变量设置为 true,并打印一条消息,显示匹配的新闻事件总数和“values”数组的大小。这条标记为“(发现新闻)”的消息还包括了已识别的新闻总数和最初考虑的所有新闻数量,从而给出了在检索到的所有事件中发现的相关新闻事件的总数量。 相反,如果新闻总数等于0或小于0,这意味着没有找到相关的新闻事件。在这种情况下,我们将“isNews”设置为 false,并打印一条标记为“(未发现新闻)”的消息,显示新闻总数为0以及“values”数组的大小。该结构有助于我们跟踪是否找到了符合标准的新闻事件,并提供了结果日志,这对于验证新闻检查过程的成果非常有用。编译后,我们会得到以下输出。 由图可见,目前没有相关的新闻事件。因此,我们可以通过扩大时间范围或增加事件搜索范围(在此情况下,可将其扩展为一天)来增加搜索力度。 如果我们将新闻搜索范围调整回一天,我们会得到以下输出。 由图可见,在81条新闻中,有5条是相关的新闻事件。我们打印出这些相关新闻事件的信息,这些信息可以用来做出交易决策,即决定是进入市场还是退出市场。这部分内容就讲解到这里,以下是负责从MQL5日历中识别和过滤新闻的完整函数代码段: [CODE] <span class="comment">//+------------------------------------------------------------------+</span> <span class="comment">//|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb

「经济日历事件与数值的结构体字段」

在 MT5 的日历接口里,事件元数据由 MqlCalendarEvent 承载,实际发布的数值由 MqlCalendarValue 承载。两个结构体字段直接决定你后续过滤与取数的写法,少看一个都可能取到空值。 MqlCalendarEvent 里的 type、sector、frequency、time_mode 都是枚举型,用来按类别和节奏筛事件;country_id 是 ulong 国家编号,importance 用 ENUM_CALENDAR_EVENT_IMPORTANCE 标星级,digits 告诉你要保留几位小数。 MqlCalendarValue 侧更偏向落地数据:event_id 关联回事件,time 是发布时间,period 是数据所指的统计周期,revision 表示相对报告期的修正次数。外汇与贵金属受这些数字冲击大,实际跳空幅度取决于 importance 与市场预期差,属典型高风险场景。 下面把两个结构体原样列出,逐行对应上面说的用途,开 MT5 新建脚本 include &lt;Calendar.mqh&gt; 就能看到同名定义。

MQL5 / C++
class="kw">struct MqlCalendarEvent
  {
   ENUM_CALENDAR_EVENT_TYPE type;                class=class="str">"cmt">// 事件类型,取自 ENUM_CALENDAR_EVENT_TYPE 枚举
   ENUM_CALENDAR_EVENT_SECTOR sector;            class=class="str">"cmt">// 事件所属经济板块
   ENUM_CALENDAR_EVENT_FREQUENCY frequency;      class=class="str">"cmt">// 事件发布频率
   ENUM_CALENDAR_EVENT_TIMEMODE time_mode;       class=class="str">"cmt">// 事件时间模式
   class="type">class="kw">ulong country_id;                             class=class="str">"cmt">// 国家 ID
   ENUM_CALENDAR_EVENT_UNIT unit;                class=class="str">"cmt">// 经济指标数值计量单位
   ENUM_CALENDAR_EVENT_IMPORTANCE importance;    class=class="str">"cmt">// 事件重要性等级
   ENUM_CALENDAR_EVENT_MULTIPLIER multiplier;    class=class="str">"cmt">// 经济指标数值乘数
   class="type">uint digits;                                  class=class="str">"cmt">// 小数位数
   class="type">class="kw">string source_url;                            class=class="str">"cmt">// 发布来源 URL
   class="type">class="kw">string event_code;                            class=class="str">"cmt">// 事件代码
   class="type">class="kw">string name;                                  class=class="str">"cmt">// 终端语言下的事件名称
  };
class="kw">struct MqlCalendarValue
  {
   class="type">class="kw">ulong id;                                     class=class="str">"cmt">// 数值 ID
   class="type">class="kw">ulong event_id;                               class=class="str">"cmt">// 关联的事件 ID
   class="type">class="kw">datetime time;                                class=class="str">"cmt">// 事件日期与时间
   class="type">class="kw">datetime period;                              class=class="str">"cmt">// 事件报告周期
   class="type">int revision;                                 class=class="str">"cmt">// 相对报告期的指标修正次数
  };

经济日历数值字段与取值函数

MT5 的 MqlCalendarValue 结构体把每个宏观事件的关键数字都塞进了 long 型成员,并且统一放大 10^6 倍存储。actual_value、prev_value、revised_prev_value、forecast_value 四个字段,若对应数据未发布则置为 LONG_MIN,读之前必须判断有没有值。 结构体还配套了四个 Has* 布尔方法(HasActualValue / HasPreviousValue / HasRevisedValue / HasForecastValue)和四个 Get* 方法。Get* 返回的是还原后的 double,未设置时给 nan 而不是 LONG_MIN,所以直接拿 GetActualValue 做运算前最好先用 Has 系方法拦一道。

下面这段可直接贴进 MT5 脚本跑:取服务器时间前后各一个交易日(PeriodSeconds(PERIOD_D1) 约 86400 秒)的事件,CalendarValueHistory 回填到 values[],返回条数存 valuesTotal。Print 会打出“TOTAL VALUES = NArray size = M”,若 N 与 M 不一致,说明有事件被过滤或数组未完全利用,值得在日志里核对。

外汇与贵金属受这些数字冲击的概率偏高,事件窗内点差扩张和滑点是常态,用历史回看验证时别把 nan 当 0 处理。

MQL5 / C++
class="type">long actual_value; class=class="str">"cmt">// actual value multiplied by class="num">10^class="num">6 or LONG_MIN if the value is not set
class="type">long prev_value; class=class="str">"cmt">// previous value multiplied by class="num">10^class="num">6 or LONG_MIN if the value is not set
class="type">long revised_prev_value; class=class="str">"cmt">// revised previous value multiplied by class="num">10^class="num">6 or LONG_MIN if the value is not set
class="type">long forecast_value; class=class="str">"cmt">// forecast value multiplied by class="num">10^class="num">6 or LONG_MIN if the value is not set
ENUM_CALENDAR_EVENT_IMPACT impact_type; class=class="str">"cmt">// potential impact on the currency rate
class=class="str">"cmt">//--- functions checking the values
class="type">bool HasActualValue(class="type">void) class="kw">const; class=class="str">"cmt">// returns true if actual_value is set
class="type">bool HasPreviousValue(class="type">void) class="kw">const; class=class="str">"cmt">// returns true if prev_value is set
class="type">bool HasRevisedValue(class="type">void) class="kw">const; class=class="str">"cmt">// returns true if revised_prev_value is set
class="type">bool HasForecastValue(class="type">void) class="kw">const; class=class="str">"cmt">// returns true if forecast_value is set
class=class="str">"cmt">//--- functions receiving the values
class="type">class="kw">double GetActualValue(class="type">void) class="kw">const; class=class="str">"cmt">// returns actual_value or nan if the value is no set
class="type">class="kw">double GetPreviousValue(class="type">void) class="kw">const; class=class="str">"cmt">// returns prev_value or nan if the value is no set
class="type">class="kw">double GetRevisedValue(class="type">void) class="kw">const; class=class="str">"cmt">// returns revised_prev_value or nan if the value is no set
class="type">class="kw">double GetForecastValue(class="type">void) class="kw">const; class=class="str">"cmt">// returns forecast_value or nan if the value is no set
};
MqlCalendarValue values[];
class="type">class="kw">datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_D1);
class="type">class="kw">datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_D1);
class="type">int valuesTotal = CalendarValueHistory(values, startTime, endTime, NULL, NULL);
Print("TOTAL VALUES = ", valuesTotal, " || Array size = ", ArraySize(values));
if (valuesTotal >=class="num">0 ){
   Print("Calendar values as they are: ");
   ArrayPrint(values);

◍ 把财经日历事件拆给图表看

上面这段脚本把经济事件从日历值数组里逐个取出,再用 CalendarEventById 回填结构体,顺手把前后各 1 小时的服务端交易时间窗算出来——也就是 startTime 到 endTime 跨了 PeriodSeconds(PERIOD_H1)*2 即 7200 秒。 循环体里 Print 与 Comment 双通道输出:前者进日志,后者直接挂图表左下角。事件名、代码、类型、板块、频率、发布模式、重要性枚举串、时间、源链接全部摊开,交易者开 MT5 按 F4 粘进去编译,就能在策略测试器日志里看到具体字段。 国家维度另走 CalendarCountryById,从 event.country_id 反查货币与符号。注意 currency_filter 写死 "USD",只拦美元系事件,改字符串即可切 EUR 或 XAU 相关地区。 isNewsEvent 目前只是空壳,totalNews 与 isNews 都没赋值,OnInit 里必走"DON'T ANY HAVE"分支。真要拦新闻,得在循环内按 country.currency==currency_filter 且 importance>=CALENDAR_IMPORTANCE_MEDIUM 去置 isNews=true;外汇与贵金属受数据行情瞬时冲击,滑点扩大属高频风险,参数没调通前别挂实盘。

MQL5 / C++
for (class="type">int i = class="num">0; i < valuesTotal; i++){
class=class="str">"cmt">//---
}
MqlCalendarEvent event;
CalendarEventById(values[i].event_id,event);

Print("Event ID ",values[i].event_id);

class="type">class="kw">datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_H1);
class="type">class="kw">datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_H1);
Print("Event ID ",values[i].event_id);
Print("Event Name = ",event.name);
Print("Event Code = ",event.event_code);
Print("Event Type = ",event.type);
Print("Event Sector = ",event.sector);
Print("Event Frequency = ",event.frequency);
Print("Event Release Mode = ",event.time_mode);
Print("Event Importance = ",EnumToString(event.importance));
Print("Event Time = ",values[i].time);
Print("Event URL = ",event.source_url);
Comment("Event ID ",values[i].event_id,
        "\nEvent Name = ",event.name,
        "\nEvent Code = ",event.event_code,
        "\nEvent Type = ",event.type,
        "\nEvent Sector = ",event.sector,
        "\nEvent Frequency = ",event.frequency,
        "\nEvent Release Mode = ",event.time_mode,
        "\nEvent Importance = ",EnumToString(event.importance),
        "\nEvent Time = ",values[i].time,
        "\nEvent URL = ",event.source_url);
}
MqlCalendarCountry country;
CalendarCountryById(event.country_id,country);

Print("Country ID ",country.id);
Print("Country Name ",country.name);
Print("Country Code ",country.code);
Print("Country Currency ",country.currency);
Print("Country Currency Symbol ",country.currency_symbol);
Print("Country URL ",country.url_name);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|     FUNCTION TO GET NEWS EVENTS                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool isNewsEvent(){
   class="type">int totalNews = class="num">0;
   class="type">bool isNews = class="kw">false;

   class=class="str">"cmt">//---

   class="kw">return (isNews);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit(){
class=class="str">"cmt">//---  
   if (isNewsEvent()){
      Print("______ ALERT: WE HAVE NEWS EVENT ___");
   }
   else if (isNewsEvent()==class="kw">false){
      Print("______ ALERT: WE DON&class="macro">#x27;T ANY HAVE NEWS EVENT ___");
   }
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
}
class="type">class="kw">string currency_filter = "USD";
让小布替你跑这套
这些经济事件与品种关联的诊断,小布盯盘已内置AIGC看板,打开对应外汇或贵金属品种页就能直接看到高影响事件倒计时,不必自己写解析脚本。

常见问题

标签由事件的历史波动贡献与预期偏差幅度判定,高影响多为利率、GDP、非农类,但具体冲击仍受当时流动性影响,可能不及预期。
不过滤会被全球事件淹没,聚焦与持仓货币对相关的条目才能降低噪音,提升策略对真实风险的反应概率。
可以,小布盯盘的品种页内置了经济日历关联模块,到时自动提示,省去自行对接MQL5接口的步骤。
通常用OBJ_LABEL或CChartObject文本对象按时间轴锚定,本篇后续小节会给出具体绘制结构与坐标换算思路。