利用 MQL5 经济日历进行交易(第四部分):在仪表盘中实现实时新闻更新(基础篇)
把经济日历搬进 MT5 仪表盘
MT5 内置的经济日历接口能直接在图表或自定义面板里拉取宏观事件,省去切网页看新闻的麻烦。2025 年 8 月 19 日发布的这套实现思路,核心是用 MQL5 的 CEventInfo 相关类订阅日历流,再把事件标题、影响星级、公布值写进自建仪表盘。
外汇与贵金属对 CPI、非农、利率决议这类事件反应剧烈,属于高风险品种;事件窗口前后点差扩张和滑点概率明显上升,实盘前务必在策略测试器或模拟盘验证刷新逻辑。
下面这段是原文给出的日历事件抓取骨架,去掉了界面绘制部分,只留数据订阅与读取:
CEconomicCalendar calendar; if(calendar.Open()) { CEventInfo event; while(calendar.GetNextEvent(event)) { class="type">class="kw">string name = event.Name(); class="type">int impact = event.Impact(); class="type">class="kw">datetime time = event.Time(); } }
「给经济日历仪表盘接上实时刷新」
上一篇文章里我们搭好了一个 MQL5 经济日历面板,能按货币、重要性和时间筛新闻,但数据是一次性的。这一节要补的缺口是:让面板自己持续拉新数据,不再靠手动重开脚本。 实时更新的核心价值在于,MT5 终端里的日历不再是一张静态截图。宏观新闻往往在分钟级别改变盘面波动概率,外汇与贵金属杠杆高、跳空风险大,迟到的事件提示可能直接放大止损被扫的概率。 本节的落地范围很明确:在已有筛选逻辑之上,嵌入定时轮询机制,把新到的关键事件推到面板。做完这一步,仪表盘从「查历史」变成「盯实时」,交易决策响应窗口会明显缩短。
◍ 用双数组差分让新闻面板跟着tick走
实时刷新经济事件面板,核心不是反复读日历,而是「当前数组」与「先前数组」的差分。MT5 里用两个 string 数组分别承接本次与上次报价周期的事件名,只在二者不一致时才重绘对象,能省掉大量无谓的 GUI 创建开销。 初始化时先 ArrayFree 清空 current_eventNames_data,再用 ArrayResize 把长度加 1,把新事件名写进末位索引。随后 Print + ArrayPrint 打出大小与内容,ArrayCopy 把当前数组整份拷进 previous_eventNames_data,作为下一 tick 的比对基准。这两步在 OnInit 里做一遍,拿到「初始事件」。 每个 tick 调 update_dashboard_values:重新拉日历、按货币/重要性/时间窗过滤、动态塞进 current_eventNames_data。之后用 bool isChangeInStringArrays 比大小再比逐元素 StringCompare,任一处不同就返回 true。实测日志会出现「事件更新 / 仪表盘更新」的连续打印,说明变化被捕获。 检测到变化后先 ObjectsDeleteAll 清掉前缀为 DATA_HOLDERS 与 ARRAY_NEWS 的旧对象,ArrayFree 释放 current 内存,再走一遍「拷贝进 previous」的流程。这样面板始终只反映最新事件,且 previous 数组留作下次差分——外汇与贵金属受新闻跳空影响大,这种高频比对能帮你更快察觉日历变动,但新闻市况流动性骤降,实操仍属高风险。
class="type">class="kw">string current_eventNames_data[]; class="type">class="kw">string previous_eventNames_data[]; ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+class="num">1); current_eventNames_data[ArraySize(current_eventNames_data)-class="num">1] = event.name; ArrayFree(current_eventNames_data); Print("CURRENT EVENT NAMES DATA SIZE = ",ArraySize(current_eventNames_data)); ArrayPrint(current_eventNames_data); Print("PREVIOUS EVENT NAMES DATA SIZE(Before) = ",ArraySize(previous_eventNames_data)); ArrayCopy(previous_eventNames_data,current_eventNames_data); Print("PREVIOUS EVENT NAMES DATA SIZE(After) = ",ArraySize(previous_eventNames_data)); ArrayPrint(previous_eventNames_data); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit(){
把财经事件塞进数组并落盘到上一轮
这段逻辑干的事很直接:先释放上一帧的 current_eventNames_data,再按 valuesTotal 条日历事件、array_calendar 列宽去拼 news_data。每一行都带日期、分钟级时间、货币对所属国代码,以及用 0x25CF 实心圆点标记的重要度符号。 重要度配色是硬规则:低影响给 clrYellow,中等给 clrOrange,高影响给 clrRed,对应 CALENDAR_IMPORTANCE_LOW / MODERATE / HIGH 三个枚举。实际、预测、前值都用 DoubleToString(...,3) 锁死三位小数,避免 MT5 终端里浮点显示漂移。 每跑完一个事件就把 event.name 追进 current_eventNames_data,ArrayResize 每次只加 1,属于典型的小步扩容。循环结束用 Print + ArrayPrint 把当前与 previous 两个数组的尺寸和內容打到日志,再用 ArrayCopy 把当前帧整体覆盖给上一帧。 开 MT5 按 F4 贴这段代码,跑完看 Experts 标签:若 PREVIOUS EVENT NAMES DATA SIZE (After) 与 CURRENT 一致,说明帧间传递没漏事件;外汇与贵金属受此日历驱动跳空概率高,验证时务必用模拟盘。
ArrayFree(current_eventNames_data); class=class="str">"cmt">//--- Loop through each calendar value up to the maximum defined total for (class="type">int i = class="num">0; i < valuesTotal; i++){ class=class="str">"cmt">//--- class=class="str">"cmt">//--- Loop through calendar data columns for (class="type">int k=class="num">0; k<ArraySize(array_calendar); k++){ class=class="str">"cmt">//--- class=class="str">"cmt">//--- Prepare news data array with time, country, and other event details class="type">class="kw">string news_data[ArraySize(array_calendar)]; news_data[class="num">0] = TimeToString(values[i].time,TIME_DATE); class=class="str">"cmt">//--- Event date news_data[class="num">1] = TimeToString(values[i].time,TIME_MINUTES); class=class="str">"cmt">//--- Event time news_data[class="num">2] = country.currency; class=class="str">"cmt">//--- Event country currency class=class="str">"cmt">//--- Determine importance class="type">class="kw">color based on event impact class="type">class="kw">color importance_color = clrBlack; if (event.importance == CALENDAR_IMPORTANCE_LOW){importance_color=clrYellow;} else if (event.importance == CALENDAR_IMPORTANCE_MODERATE){importance_color=clrOrange;} else if (event.importance == CALENDAR_IMPORTANCE_HIGH){importance_color=clrRed;} class=class="str">"cmt">//--- Set importance symbol for the event news_data[class="num">3] = ShortToString(0x25CF); class=class="str">"cmt">//--- Set event name in the data array news_data[class="num">4] = event.name; class=class="str">"cmt">//--- Populate actual, forecast, and previous values in the news data array news_data[class="num">5] = DoubleToString(value.GetActualValue(),class="num">3); news_data[class="num">6] = DoubleToString(value.GetForecastValue(),class="num">3); news_data[class="num">7] = DoubleToString(value.GetPreviousValue(),class="num">3); class=class="str">"cmt">//--- } ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+class="num">1); current_eventNames_data[ArraySize(current_eventNames_data)-class="num">1] = event.name; } Print("CURRENT EVENT NAMES DATA SIZE = ",ArraySize(current_eventNames_data)); ArrayPrint(current_eventNames_data); Print("PREVIOUS EVENT NAMES DATA SIZE(Before) = ",ArraySize(previous_eventNames_data)); ArrayCopy(previous_eventNames_data,current_eventNames_data); Print("PREVIOUS EVENT NAMES DATA SIZE(After) = ",ArraySize(previous_eventNames_data)); ArrayPrint(previous_eventNames_data);