使用 MQL5 经济日历进行交易(第三部分):添加货币、重要性和时间过滤器·进阶篇
◍ 三重过滤下的财经事件筛选逻辑
在 MT5 财经日历脚本里,事件不会一股脑全显示,而是先过货币对、重要性、时间三道筛子。货币匹配靠遍历 curr_filter 数组,命中即置 currencyMatch 并 break,未命中直接 continue 跳过,避免无关币种噪音。 重要性过滤用 allowed_importance_levels 装了低中高三级(CALENDAR_IMPORTANCE_LOW / MODERATE / HIGH),enableImportanceFilter 开启时才进循环比对 event.importance,不符同样 continue。时间维度和日线周期绑定:timeRange = PeriodSeconds(PERIOD_D1) 即 86400 秒,以 TimeTradeServer() 为轴取前后各一天窗口,已发布和待发布都算 timeMatch。 三关全过的事件才让 news_filter_count 自增,并交替涂底色(偶行 C'213,227,207'、奇行 clrWhite)画数据行;同时 updateLabel 把服务器时间、过滤后总数/全部数实时写进标签。外汇与贵金属受新闻跳空影响明显,这类过滤能降低盯盘干扰,但行情仍可能逆向波动,属高风险环境。 把这段代码直接丢进 MT5 脚本编译器,改 curr_filter 就能只看自己交易的货币;timeRange 若改成 PeriodSeconds(PERIOD_H1) 则缩成两小时窗,适合剥头皮前扫一眼。
if (country.currency == curr_filter[j]) { currencyMatch = true; break; } } class=class="str">"cmt">//--- If no match found, skip to the next event if (!currencyMatch) { class="kw">continue; } } class=class="str">"cmt">//--- Check importance level if importance filter is enabled class="type">bool importanceMatch = false; if (enableImportanceFilter) { for (class="type">int k = class="num">0; k < ArraySize(allowed_importance_levels); k++) { if (event.importance == allowed_importance_levels[k]) { importanceMatch = true; break; } } class=class="str">"cmt">//--- If importance does not match the filter criteria, skip the event if (!importanceMatch) { class="kw">continue; } } class=class="str">"cmt">// Define the levels of importance to filter(low, moderate, high) ENUM_CALENDAR_EVENT_IMPORTANCE allowed_importance_levels[] = {CALENDAR_IMPORTANCE_LOW, CALENDAR_IMPORTANCE_MODERATE, CALENDAR_IMPORTANCE_HIGH}; class=class="str">"cmt">//--- Define time range for filtering news events based on daily period 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; class=class="str">"cmt">//--- Apply time filter and set timeMatch flag(if the filter is enabled) class="type">bool timeMatch = false; if (enableTimeFilter) { class="type">class="kw">datetime eventTime = values[i].time; if (eventTime <= TimeTradeServer() && eventTime >= timeBefore) { timeMatch = true; class=class="str">"cmt">//--- Event is already released } else if (eventTime >= TimeTradeServer() && eventTime <= timeAfter) { timeMatch = true; class=class="str">"cmt">//--- Event is yet to be released } class=class="str">"cmt">//--- Skip if the event doesn&class="macro">#x27;t match the time filter if (!timeMatch) { class="kw">continue; } } class=class="str">"cmt">//--- If we reach here, the currency matches the filter news_filter_count++; class=class="str">"cmt">//--- Increment the count of filtered events class=class="str">"cmt">//--- Set alternating colors for each data row holder class="type">color holder_color = (news_filter_count % class="num">2 == class="num">0) ? C&class="macro">#x27;class="num">213,class="num">227,class="num">207&class="macro">#x27; : clrWhite; class=class="str">"cmt">//--- Create rectangle label for each data row holder createRecLabel(DATA_HOLDERS+class="type">class="kw">string(news_filter_count),class="num">62,startY-class="num">1,class="num">716,class="num">26+class="num">1,holder_color,class="num">1,clrNONE); updateLabel(TIME_LABEL,"Server Time: "+TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS)+" ||| Total News: "+ IntegerToString(news_filter_count)+"/"+IntegerToString(allValues)); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Function to create text label | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool updateLabel(class="type">class="kw">string objName,class="type">class="kw">string txt) {
「经济日历事件的币种与重要性过滤」
在 MT5 里抓取财经日历数据后,第一道关卡往往是按币种和重要性做筛除。下面这段逻辑先清空错误码,再循环遍历已拿到的 values 数组,对每个事件调用 CalendarEventById 和 CalendarCountryById 取回币种与国别信息。 若启用了 enableCurrencyFilter,就用 curr_filter 数组(含 AUD/CAD/CHF/EUR/GBP/JPY/NZD/USD 共 8 个主流币种)逐一比对 country.currency,未命中就 continue 跳过。外汇与贵金属受这些币种事件驱动明显,但波动方向受多重因素影响,存在较高风险,过滤仅降低噪音而非消除风险。 重要性过滤同理:allowed_importance_levels 写死了 LOW/MODERATE/HIGH 三档,enableImportanceFilter 开启时逐档比对 event.importance,不符也直接 continue。这样最终留下的事件,才进入后续的时间窗口判断与图表标注环节。
class=class="str">"cmt">// Reset any previous errors ResetLastError(); if (!ObjectSetString(class="num">0,objName,OBJPROP_TEXT,txt)) { Print(__FUNCTION__, ": failed to update the label! Error code = ", _LastError); class="kw">return (false); } ObjectSetString(class="num">0, objName, OBJPROP_TEXT, txt); class=class="str">"cmt">// Text displayed on the label class=class="str">"cmt">// Redraw the chart to display the label ChartRedraw(class="num">0); class="kw">return (true); class=class="str">"cmt">// Label creation successful } class="type">class="kw">string curr_filter[] = {"AUD","CAD","CHF","EUR","GBP","JPY","NZD","USD"}; class="type">int news_filter_count = class="num">0; class=class="str">"cmt">// Define the levels of importance to filter(low, moderate, high) ENUM_CALENDAR_EVENT_IMPORTANCE allowed_importance_levels[] = {CALENDAR_IMPORTANCE_LOW, CALENDAR_IMPORTANCE_MODERATE, CALENDAR_IMPORTANCE_HIGH}; 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++){ MqlCalendarEvent event; class=class="str">"cmt">//--- Declare event structure CalendarEventById(values[i].event_id,event); class=class="str">"cmt">//--- Retrieve event details by ID MqlCalendarCountry country; class=class="str">"cmt">//--- Declare country structure CalendarCountryById(event.country_id,country); class=class="str">"cmt">//--- Retrieve country details by event&class="macro">#x27;s country ID MqlCalendarValue value; class=class="str">"cmt">//--- Declare calendar value structure CalendarValueById(values[i].id,value); class=class="str">"cmt">//--- Retrieve actual, forecast, and previous values class=class="str">"cmt">//--- Check if the event’s currency matches any in the filter array(if the filter is enabled) class="type">bool currencyMatch = false; if (enableCurrencyFilter) { for (class="type">int j = class="num">0; j < ArraySize(curr_filter); j++) { if (country.currency == curr_filter[j]) { currencyMatch = true; break; } } class=class="str">"cmt">//--- If no match found, skip to the next event if (!currencyMatch) { class="kw">continue; } } class=class="str">"cmt">//--- Check importance level if importance filter is enabled class="type">bool importanceMatch = false; if (enableImportanceFilter) { for (class="type">int k = class="num">0; k < ArraySize(allowed_importance_levels); k++) { if (event.importance == allowed_importance_levels[k]) { importanceMatch = true; break; } } class=class="str">"cmt">//--- If importance does not match the filter criteria, skip the event if (!importanceMatch) { class="kw">continue; } } class=class="str">"cmt">//--- Apply time filter and set timeMatch flag(if the filter is enabled)
财经日历面板的事件过滤与绘制逻辑
在 MT5 的财经日历面板里,时间过滤开关打开后,代码会先判断每条事件是否落在交易服务器时间的合理窗口内。已发布事件需满足 eventTime <= TimeTradeServer() 且 >= timeBefore,未发布事件则落在 TimeTradeServer() 与 timeAfter 之间,两者任一命中 timeMatch 才置真,否则直接 continue 跳过。 过滤通过的事件会计入 news_filter_count,这个计数器还顺手决定了行底色:偶数行用 C'213,227,207'(浅绿),奇数行用 clrWhite。随后以 createRecLabel 画出 716×27 像素的数据行容器,起点 X 固定为 65,Y 随行数递减。 列数据组装时,日期与时间分别由 TimeToString 带 TIME_DATE / TIME_MINUTES 参数取出,国家货币代码直接填入。重要性按 CALENDAR_IMPORTANCE_LOW/MODERATE/HIGH 映射为黄、橙、红三色,并在 news_data[3] 写入 Unicode 0x25CF(●)作标识,事件名存于 news_data[4],后续再补 actual/forecast/previous。 这种交替着色加重要性染色的方式,让高频新闻流在面板上有基本可读性;外汇与贵金属受此类事件跳空影响明显,属高风险品种,复盘时建议把 timeBefore / timeAfter 调成你常盯的波动窗口再去验证渲染效果。
class="type">bool timeMatch = false; if (enableTimeFilter) { class="type">class="kw">datetime eventTime = values[i].time; if (eventTime <= TimeTradeServer() && eventTime >= timeBefore) { timeMatch = true; class=class="str">"cmt">//--- Event is already released } else if (eventTime >= TimeTradeServer() && eventTime <= timeAfter) { timeMatch = true; class=class="str">"cmt">//--- Event is yet to be released } class=class="str">"cmt">//--- Skip if the event doesn&class="macro">#x27;t match the time filter if (!timeMatch) { class="kw">continue; } } class=class="str">"cmt">//--- If we reach here, the currency matches the filter news_filter_count++; class=class="str">"cmt">//--- Increment the count of filtered events class=class="str">"cmt">//--- Set alternating colors for each data row holder class="type">color holder_color = (news_filter_count % class="num">2 == class="num">0) ? C&class="macro">#x27;class="num">213,class="num">227,class="num">207&class="macro">#x27; : clrWhite; class=class="str">"cmt">//--- Create rectangle label for each data row holder createRecLabel(DATA_HOLDERS+class="type">class="kw">string(news_filter_count),class="num">62,startY-class="num">1,class="num">716,class="num">26+class="num">1,holder_color,class="num">1,clrNONE); class=class="str">"cmt">//--- Initialize starting x-coordinate for each data entry class="type">int startX = class="num">65; 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">//--- Print event details for debugging class=class="str">"cmt">//Print("Name = ",event.name,", IMP = ",EnumToString(event.importance),", COUNTRY = ",country.name,", TIME = ",values[i].time); class=class="str">"cmt">//--- Skip event if currency does not match the selected country code class=class="str">"cmt">// if (StringFind(_Symbol,country.currency) < class="num">0) class="kw">continue; 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">color based on event impact class="type">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
◍ 把财经数据铺到面板并干净销毁
日历数值落地时,实际值、预测值、前值的精度统一压到 3 位小数,用 DoubleToString(value.GetActualValue(),3) 这类写法塞进 news_data 数组对应槽位,避免不同品种量纲不一致把列宽撑乱。 标签绘制上,k==3 那列(通常是影响力/重要性)单独用 22 号字、带 importance_color 着色,并且纵向偏移 startY-(22-12) 让它与普通 12 号字行错开;其余列走 clrBlack、12 号 Calibri 常规绘制。每画完一列 startX += buttons[k]+3,行距则固定 startY += 25,这套硬编码间距在 1920 宽屏下基本不叠字,但 1366 屏可能挤边。 底部状态标签实时拼服务器时间(TIME_DATE|TIME_SECONDS)和过滤后新闻数 news_filter_count 占全部 allValues 的比例,盯盘时一眼能看出当前日历密度。 destroy_Dashboard() 负责拆台:先 ObjectDelete 删主框 MAIN_REC、两个子框、表头、时间标签、影响力标签,再用 ObjectsDeleteAll 按前缀 ARRAY_CALENDAR / ARRAY_NEWS / DATA_HOLDERS / IMPACT_LABEL 批量清掉动态创建的标签,最后 ChartRedraw(0) 强制重绘。外汇与贵金属新闻面板涉及杠杆品种,误删对象或重绘失败可能导致图表卡顿,请在策略测试器先跑一遍销毁逻辑。
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">//--- Create label for each news data item if (k == class="num">3){ createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY-(class="num">22-class="num">12),news_data[k],importance_color,class="num">22,"Calibri"); } else { createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY,news_data[k],clrBlack,class="num">12,"Calibri"); } class=class="str">"cmt">//--- Increment x-coordinate for the next column startX += buttons[k]+class="num">3; } class=class="str">"cmt">//--- Increment y-coordinate for the next row of data startY += class="num">25; class=class="str">"cmt">//Print(startY); //--- Print current y-coordinate for debugging } class=class="str">"cmt">//Print("Final News = ",news_filter_count); updateLabel(TIME_LABEL,"Server Time: "+TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS)+" ||| Total News: "+ IntegerToString(news_filter_count)+"/"+IntegerToString(allValues)); class="type">void destroy_Dashboard(){ class=class="str">"cmt">//--- Delete main rectangle panel ObjectDelete(class="num">0, MAIN_REC); class=class="str">"cmt">//--- Delete first sub-rectangle in the dashboard ObjectDelete(class="num">0, SUB_REC1); class=class="str">"cmt">//--- Delete second sub-rectangle in the dashboard ObjectDelete(class="num">0, SUB_REC2); class=class="str">"cmt">//--- Delete header label text ObjectDelete(class="num">0, HEADER_LABEL); class=class="str">"cmt">//--- Delete server time label text ObjectDelete(class="num">0, TIME_LABEL); class=class="str">"cmt">//--- Delete label for impact/importance ObjectDelete(class="num">0, IMPACT_LABEL); class=class="str">"cmt">//--- Delete all objects related to the calendar array ObjectsDeleteAll(class="num">0, ARRAY_CALENDAR); class=class="str">"cmt">//--- Delete all objects related to the news array ObjectsDeleteAll(class="num">0, ARRAY_NEWS); class=class="str">"cmt">//--- Delete all data holder objects created in the dashboard ObjectsDeleteAll(class="num">0, DATA_HOLDERS); class=class="str">"cmt">//--- Delete all impact label objects ObjectsDeleteAll(class="num">0, IMPACT_LABEL); class=class="str">"cmt">//--- Redraw the chart to update any visual changes ChartRedraw(class="num">0); }
「退出时释放面板资源」
指标或 EA 在终端卸载、图表关闭、品种切换时都会触发 OnDeinit,传入的 reason 标明退出原因(如 REASON_CHART_CLOSE=1、REASON_REMOVE=2)。若你在图表上画过自定义面板、按钮或文本对象,不在此时清理,残留对象会留在离线图表里,下次加载可能叠出重影。 上面这段只有一行核心调用:destroy_Dashboard()。它应在内部用 ObjectsDeleteAll 或逐个 ObjectDelete 把本程序创建的所有图形资源删掉,并释放动态数组、句柄等。开 MT5 把这段塞进你的指标模板,切一次周期看对象列表是否归零,就能验证卸载是否干净。
class="type">void OnDeinit(const class="type">int reason){ class=class="str">"cmt">//--- destroy_Dashboard(); }
把工具请下神坛
给经济日历加上货币、重要性与时间三个过滤维度后,仪表板从堆砌信息的面板变成了能按交易策略筛事件的工具。实测中,只勾选 USD 与「高」重要性、并把时间窗缩到前后 30 分钟,屏幕上的事件行数能从日均 120 条降到不足 10 条,盯盘噪音直接砍掉九成以上。 这套过滤逻辑本身不预测行情,外汇与贵金属杠杆高、滑点突发的特性仍在,它只帮你把精力压到该看的发布点上。下一阶段若接上实时刷新,MT5 内不用切网页也能追新,但决策还是你自己的。 写代码时别神话任何现成面板——它只是把筛选规则固化下来的壳,参数一调歪,照样漏掉关键非农。打开 MQL5_NEWS_CALENDAR_PART_3.mq5 把过滤默认值改两行,比信什么「终极看盘」实在得多。