使用MQL5经济日历进行交易(第一部分):精通MQL5经济日历的功能·进阶篇
(2/3)· 从日历结构到MQL5取数逻辑,打通新闻事件与自动交易的最后一公里
◍ 正文
我们在 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">//| &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 <Calendar.mqh> 就能看到同名定义。
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 = N | Array size = M”,若 N 与 M 不一致,说明有事件被过滤或数组未完全利用,值得在日志里核对。 |
|---|
外汇与贵金属受这些数字冲击的概率偏高,事件窗内点差扩张和滑点是常态,用历史回看验证时别把 nan 当 0 处理。
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;外汇与贵金属受数据行情瞬时冲击,滑点扩大属高频风险,参数没调通前别挂实盘。
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";