使用MQL5经济日历进行交易(第一部分):精通MQL5经济日历的功能·综合运用
(3/3)·从概览到代码再到系统整合,一篇补齐经济日历驱动交易的最后拼图
用财经日历框定品种的新闻窗口
想在 MT5 里避开重大数据行情,第一步是确认当前图表品种跟目标货币有没有关系。下面这段代码先拆出基础货币与报价货币,若两者都不含过滤货币就直接退出,避免对无关品种做无用扫描。 string currency_base = SymbolInfoString(_Symbol, SYMBOL_CURRENCY_BASE); string currency_quote = StringSubstr(_Symbol, 3, 3); if (currency_base != currency_filter && currency_quote != currency_filter){ Print("Currency (", currency_base, " | ", currency_quote, ") is not equal equal to ", currency_filter); return false; } // 第1行:取当前品种基础货币,如 EURUSD 得到 "EUR" // 第2行:从符号第4字符起截3字符,得到报价货币 "USD" // 第3-6行:若基础与报价都不是过滤项(如"USD"),打印并return false终止 时间窗口用交易日服务器时间加减一天来定。PeriodSeconds(PERIOD_D1) 返回 86400 秒,于是往前看一天、往后看一天,覆盖已发布和未发布的中等重要性事件。 datetime timeRange = PeriodSeconds(PERIOD_D1); datetime timeBefore = TimeTradeServer() - timeRange; datetime timeAfter = TimeTradeServer() + timeRange; // timeBefore 约为当前-86400秒,timeAfter 约为当前+86400秒 扫描逻辑只统计 CALENDAR_IMPORTANCE_MODERATE 级以上且落在窗口内的新闻。若 values[i].time 小于等于服务器时间且大于等于 timeBefore,就是已发布;反之在 timeAfter 内就是未发布,两者都让 totalNews 自增。 if (StringFind(_Symbol, country.currency) >= 0){ if (event.importance == CALENDAR_IMPORTANCE_MODERATE){ if (values[i].time <= TimeTradeServer() && values[i].time >= timeBefore){ Print(event.name, " > ", country.currency, " > ", EnumToString(event.importance), " Time= ", values[i].time, " (ALREADY RELEASED)"); totalNews++; } if (values[i].time >= TimeTradeServer() && values[i].time <= timeAfter){ Print(event.name, " > ", country.currency, " > ", EnumToString(event.importance), " Time= ", values[i].time, " (NOT YET RELEASED)"); totalNews++; } } } // 第1行:品种名里能找到该国货币才继续 // 第2行:只处理中等重要性事件 // 第3-6行:已发布分支,打印并计数 // 第7-10行:未发布分支,打印并计数 最终靠 totalNews 是否大于 0 决定 isNews 布尔值。外汇与贵金属受数据冲击跳空概率高,实盘前建议把这段塞进 EA 的 OnTick 开头做开关,验证时可以先把 currency_filter 写死 "USD" 看 XAUUSD 是否被正确拦截。
class="type">class="kw">string currency_base = SymbolInfoString(_Symbol, SYMBOL_CURRENCY_BASE); class="type">class="kw">string currency_quote = StringSubstr(_Symbol, class="num">3, class="num">3); if (currency_base != currency_filter && currency_quote != currency_filter){ Print("Currency(", currency_base, " | ", currency_quote, ") is not equal equal to ", currency_filter); class="kw">return class="kw">false; } if (StringFind(_Symbol, country.currency) >= class="num">0){ if (event.importance == CALENDAR_IMPORTANCE_MODERATE){ if (values[i].time <= TimeTradeServer() && values[i].time >= timeBefore){ Print(event.name, " > ", country.currency, " > ", EnumToString(event.importance), " Time= ", values[i].time, " (ALREADY RELEASED)"); totalNews++; } if (values[i].time >= TimeTradeServer() && values[i].time <= timeAfter){ Print(event.name, " > ", country.currency, " > ", EnumToString(event.importance), " Time= ", values[i].time, " (NOT YET RELEASED)"); totalNews++; } } } class="type">class="kw">datetime timeRange = PeriodSeconds(PERIOD_D1); class="type">class="kw">datetime timeBefore = TimeTradeServer() - timeRange; class="type">class="kw">datetime timeAfter = TimeTradeServer() + timeRange; Print("FURTHEST TIME LOOK BACK = ", timeBefore, " >>> CURRENT = ", TimeTradeServer()); if (totalNews > class="num">0){ isNews = true; Print(">>>>>>> (FOUND NEWS) TOTAL NEWS = ", totalNews, "/", ArraySize(values)); } else if (totalNews <= class="num">0){ isNews = class="kw">false; Print(">>>>>>> (NOT FOUND NEWS) TOTAL NEWS = ", totalNews, "/", ArraySize(values)); } class="type">class="kw">datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_D1); class="type">class="kw">datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_D1); 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; MqlCalendarValue values[]; class="type">class="kw">datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_D1); class="type">class="kw">datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_D1); class=class="str">"cmt">//class="type">class="kw">string currency_filter = "USD"; class=class="str">"cmt">//class="type">class="kw">string currency_base = SymbolInfoString(_Symbol, SYMBOL_CURRENCY_BASE); class=class="str">"cmt">//class="type">class="kw">string currency_quote = StringSubstr(_Symbol, class="num">3, class="num">3); class=class="str">"cmt">//if (currency_base != currency_filter && currency_quote != currency_filter){ }
「用财经日历筛出当前品种的中等影响事件」
这段逻辑干的事很直接:拉取一段区间内的财经日历数据,只挑出和当前图表货币对相关、且重要性为中等(CALENDAR_IMPORTANCE_MODERATE)的事件,再按发布时间落在「往前一天」到「往后一天」范围内做计数。 核心调用是 CalendarValueHistory,把返回条数存进 valuesTotal,同时用 ArraySize(values) 核对实际数组容量;若两者不一致,说明缓冲区可能不够,需要调大 values 数组长度再跑。 timeBefore 与 timeAfter 以 TimeTradeServer() 加减 PERIOD_D1 的秒数得到,也就是以交易服务器时间为锚,前后各扩一天。循环里先用 StringFind(_Symbol, country.currency) 做币种匹配,再用 values[i].time 判断已发布或未发布,命中就给 totalNews 加一。 最后靠 totalNews 是否大于 0 把 isNews 置真或假,并在日志打出「FOUND NEWS / NOT FOUND NEWS」及命中占比。外汇与贵金属受新闻扰动大、滑点风险高,这类过滤只能提示概率性波动窗口,不能当作方向依据;开 MT5 把这段塞进 EA 的 NewsFilter 函数里,改一下 importance 阈值就能看不同级别事件的实际命中数。
class=class="str">"cmt">// Print("Currency(",currency_base," | ",currency_quote, class=class="str">"cmt">// ") is not equal equal to ",currency_filter); class=class="str">"cmt">// class="kw">return class="kw">false; class=class="str">"cmt">//} class="type">int valuesTotal = CalendarValueHistory(values,startTime,endTime,NULL,NULL); Print("TOTAL VALUES = ",valuesTotal," || Array size = ",ArraySize(values)); class=class="str">"cmt">//if (valuesTotal >=class="num">0 ){ class=class="str">"cmt">// Print("Calendar values as they are: "); class=class="str">"cmt">// ArrayPrint(values); class=class="str">"cmt">//} class="type">class="kw">datetime timeRange = PeriodSeconds(PERIOD_D1); class="type">class="kw">datetime timeBefore = TimeTradeServer() - timeRange; class="type">class="kw">datetime timeAfter = TimeTradeServer() + timeRange; Print("Current time = ",TimeTradeServer()); Print("FURTHEST TIME LOOK BACK = ",timeBefore," >>> LOOK FORE = ",timeAfter); for (class="type">int i = class="num">0; i < valuesTotal; i++){ MqlCalendarEvent event; CalendarEventById(values[i].event_id,event); MqlCalendarCountry country; CalendarCountryById(event.country_id,country); if (StringFind(_Symbol,country.currency) >= class="num">0){ if (event.importance == CALENDAR_IMPORTANCE_MODERATE){ if (values[i].time <= TimeTradeServer() && values[i].time >= timeBefore){ Print(event.name," > ", country.currency," > ", EnumToString(event.importance)," Time= ",values[i].time," (ALREADY RELEASED)"); totalNews++; } if (values[i].time >= TimeTradeServer() && values[i].time <= timeAfter){ Print(event.name," > ", country.currency," > ", EnumToString(event.importance)," Time= ",values[i].time," (NOT YET RELEASED)"); totalNews++; } } } } if (totalNews > class="num">0){ isNews = true; Print(">>>>>>> (FOUND NEWS) TOTAL NEWS = ",totalNews,"/",ArraySize(values)); } else if (totalNews <= class="num">0){ isNews = class="kw">false; Print(">>>>>>> (NOT FOUND NEWS) TOTAL NEWS = ",totalNews,"/",ArraySize(values)); } class="kw">return (isNews); }
◍ 把日历信号接进图表和下单逻辑
前面两节把经济日历的抓取、按货币与事件重要性过滤、区分待发生与已发生做成了结构化流程。这套流程的核心价值不在于看新闻,而在于让 EA 能在高影响事件前后自动调整行为,外汇与贵金属市场对此类事件的反应往往伴随瞬时跳空与滑点,属于典型高风险场景。 下一阶段的直接落点是把过滤后的数据画到图表窗口,并让 EA 能依据重大事件主动开仓。也就是说,系统会从「读日历」跨到「用日历下单」,设计上要以实时框架为基准,在事件触发后而非预测前去动作。 想验证这条路径,可先打开 MT5 把本系列附带的 MQL5_NEWS_CALENDAR_PART_1.mq5 跑起来,观察其过滤输出是否与你手动核对的财经日历一致;不一致的地方就是后续要改的事件权重参数。