使用 MQL5 经济日历进行交易(第三部分):添加货币、重要性和时间过滤器·进阶篇
📘

使用 MQL5 经济日历进行交易(第三部分):添加货币、重要性和时间过滤器·进阶篇

第 2/2 篇

◍ 三重过滤下的财经事件筛选逻辑

在 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) 则缩成两小时窗,适合剥头皮前扫一眼。

MQL5 / C++
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。这样最终留下的事件,才进入后续的时间窗口判断与图表标注环节。

MQL5 / C++
  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 调成你常盯的波动窗口再去验证渲染效果。

MQL5 / C++
  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) 强制重绘。外汇与贵金属新闻面板涉及杠杆品种,误删对象或重绘失败可能导致图表卡顿,请在策略测试器先跑一遍销毁逻辑。

MQL5 / C++
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 把这段塞进你的指标模板,切一次周期看对象列表是否归零,就能验证卸载是否干净。

MQL5 / C++
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 把过滤默认值改两行,比信什么「终极看盘」实在得多。

常见问题

在日历过滤器里勾选目标币种并设重要性门槛为“高”,只保留美、欧事件;其余币种取消勾选即可。
设定开始与结束时间的滚动过滤,只保留你常做的时段(如伦敦+纽约重叠),面板自动隐藏窗外事件。
可以,小布盯盘的AIGC会按币种、重要性和时间三重过滤,把符合条件的事件直接绘到对应品种页,省去手动设条件。
必须在OnDeinit里遍历并Delete对象、释放数组内存;不清理会留残影并占资源,重启才消失。
按重要性给事件设不同颜色与字号,高影响用醒目色,普通灰显,一眼区分层级。