利用 MQL5 经济日历进行交易(第四部分):在仪表盘中实现实时新闻更新·进阶篇
📡

利用 MQL5 经济日历进行交易(第四部分):在仪表盘中实现实时新闻更新·进阶篇

(2/3)· 明明挂了日历面板却总比行情慢半拍?本篇让仪表盘随报价自动追新

新手友好 第 2/3 篇
很多交易者把经济日历面板当静态表格用,新闻过了十分钟才发现。给仪表盘接上实时更新,事件变动随报价周期自动追平,决策窗口才不会白白漏掉。

◍ 用财经日历数组给盘面打时间标签

在 MT5 面板里实时显示服务器时间与新闻计数,核心是把 CalendarValueHistory 抓到的结构体数组先截断再呈现。下面这段初始化收尾代码把总时间、已过滤新闻数拼进标签,并直接返回 INIT_SUCCEEDED。 代码里 valuesTotal 被硬性限制在 11 条以内:当 allValues 超过 11 时只取前 11,避免面板被刷屏。配合 curr_filter 里写死的 8 个币种(AUD/CAD/CHF/EUR/GBP/JPY/NZD/USD),news_filter_count 就是当前币种过滤后的命中数。 时间窗口用 TimeTradeServer 加减 PeriodSeconds(PERIOD_H12) 取前后各 12 小时,再叠加 PERIOD_D1 做日级回看。外汇与贵金属受此窗口内数据冲击的概率偏高,实盘使用前应在策略测试器确认时区偏移。 把这段直接塞进 EA 的 OnInit 末尾,开 MT5 挂上就能看到 Total News 字段随日历刷新;若想盯单一币种,改 country_code 或 curr_filter 即可。

MQL5 / C++
  updateLabel(TIME_LABEL,"Server Time: "+TimeToString(TimeCurrent(),
                      TIME_DATE|TIME_SECONDS)+"   |||   Total News: "+
                      IntegerToString(news_filter_count)+"/"+IntegerToString(allValues));
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Function to update dashboard values                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void update_dashboard_values(){
class=class="str">"cmt">//---
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick(){
class=class="str">"cmt">//---

   update_dashboard_values();

}
class=class="str">"cmt">//--- Declare variables for tracking news events and status
class="type">int totalNews = class="num">0;
class="type">bool isNews = false;
MqlCalendarValue values[]; class=class="str">"cmt">//--- Array to store calendar values
class=class="str">"cmt">//--- Define start and end time for calendar event retrieval
class="type">class="kw">datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_H12);
class="type">class="kw">datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_H12);
class=class="str">"cmt">//--- Set a specific country code filter(e.g., "US" for USD)
class="type">class="kw">string country_code = "US";
class="type">class="kw">string currency_base = SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE);
class=class="str">"cmt">//--- Retrieve historical calendar values within the specified time range
class="type">int allValues = CalendarValueHistory(values,startTime,endTime,NULL,NULL);
class=class="str">"cmt">//--- Print the total number of values retrieved and the array size
class=class="str">"cmt">//Print("TOTAL VALUES = ",allValues," || Array size = ",ArraySize(values));
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">//--- Print the furthest time look-back and current server time
class=class="str">"cmt">//Print("FURTHEST TIME LOOK BACK = ",timeBefore," >>> CURRENT = ",TimeTradeServer());
class=class="str">"cmt">//--- Limit the total number of values to display
class="type">int valuesTotal = (allValues <= class="num">11) ? allValues : class="num">11;

class="type">class="kw">string curr_filter[] = {"AUD","CAD","CHF","EUR","GBP","JPY","NZD","USD"};
class="type">int news_filter_count = class="num">0;
ArrayFree(current_eventNames_data);
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

「用三重过滤器筛出要盯的财经事件」

在 MT5 日历事件回调里,先按货币、重要性、时间三个维度做筛选,能大幅降低面板噪点。下面这段逻辑直接声明了国家结构与数值结构,并通过 ID 把事件归属国与具体值拉出来: MqlCalendarCountry country; 声明国家结构变量,用于承载事件所属国信息。 CalendarCountryById(event.country_id,country); 用事件里的 country_id 取回国家详情,比如货币代码就藏在 country.currency。 MqlCalendarValue value; 声明日历数值结构,准备接实际值、预期值、前值。 CalendarValueById(values[i].id,value); 按值 ID 取回该事件的三项核心数据。 货币过滤由 enableCurrencyFilter 开关控制,遍历 curr_filter 数组做字符串比对,命中才放行,否则 continue 跳走。重要性过滤同理,allowed_importance_levels 里存的是事件 importance 枚举,比如只留 2 级和 3 级。 时间过滤用 TimeTradeServer() 取交易服务器时间,已发生的落在 [timeBefore, 当前] 区间,未发生的落在 [当前, timeAfter] 区间,都不在范围内就跳过。三层全过才会 news_filter_count++,并用 news_filter_count%2 给行交替上色(C'213,227,207' 与 clrWhite),肉眼扫起来不累。 外汇与贵金属受这些数据冲击波动可能放大,属高风险场景,参数阈值建议在策略测试器里按自己品种调。

MQL5 / C++
  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;
        class="kw">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;
        class="kw">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)
  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">class="kw">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">//--- 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

把财经日历事件塞进数组并标色

这段逻辑干的事很直接:遍历日历事件,把每条新闻的日期、时间、币种、重要性色块、名称以及实际/预期/前值写进一个字符串数组 news_data,方便后续在 MT5 图表上渲染。 重要性分级用颜色区分——低影响标黄(clrYellow),中等标橙(clrOrange),高影响标红(clrRed)。外汇和贵金属受高影响事件冲击的概率明显更高,实盘前务必确认自己品种关联国的红字事件时间。 代码里用 ShortToString(0x25CF) 写了一个实心圆符号当作重要性标记,实际/预期/前值统一用 DoubleToString(...,3) 保留三位小数,避免不同事件精度不一致导致显示错位。 循环结束后用 ArrayResize 把当前事件名追加进 current_eventNames_data,最后 updateLabel 刷新右上角标签,显示服务器时间与已过滤新闻数(news_filter_count)占总抓取数(allValues)的比例,例如 12/45 这种直观读数。 isChangeInStringArrays 这个函数用来比对两次抓取的字符串数组是否一致:先取 size1 和 size2,若长度不同直接 Print 差异并返回 true,说明日历有变动需要重绘。

MQL5 / C++
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">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);
 }

 ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+class="num">1);
 current_eventNames_data[ArraySize(current_eventNames_data)-class="num">1] = event.name;

}
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=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|   Function to compare two class="type">class="kw">string arrays and detect changes        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool isChangeInStringArrays(class="type">class="kw">string &arr1[], class="type">class="kw">string &arr2[]) {
  class="type">bool isChange = false;

  class="type">int size1 = ArraySize(arr1);  class=class="str">"cmt">// Get the size of the first array
  class="type">int size2 = ArraySize(arr2);  class=class="str">"cmt">// Get the size of the second array

  class=class="str">"cmt">// Check if sizes are different
  if (size1 != size2) {
    Print("Arrays have different sizes. Size of Array class="num">1: ", size1, ", Size of Array class="num">2: ", size2);
    isChange = true;
    class="kw">return (isChange);

◍ 事件名变动后重绘财经面板

isChangeInStringArrays 判定前后两次事件名数组不一致,说明财经日历数据源有更新,原面板对象必须清掉重画。代码里连续调两次 ObjectsDeleteAll,分别按 DATA_HOLDERSARRAY_NEWS 前缀删 MT5 图表对象,再用 ArrayFree 释放当前数组内存,避免旧引用拖慢终端。 重绘起点写死 startY = 162,这是面板第一行相对图表上边的像素偏移;循环上限用 valuesTotal 而非写死数字,意味着你改 valuesTotal 就能控制最多显示多少条日历。 每条数据先 CalendarEventById 拿事件结构,再 CalendarCountryByIdevent.country_id 取国家,最后 CalendarValueById 取实际/预期/前值。若开了 enableCurrencyFilter,内层循环拿 country.currency 去撞 curr_filter 数组,不匹配就 continue 跳过——外汇与贵金属交易者靠这层把 EUR、XAU 等无关币种挡在面板外,但杠杆品种波动剧烈,过滤不等于风险可控。 重要性过滤紧随货币过滤之后,未贴出的部分就是按 event.importance 对比阈值,只留你关心的级别。整套逻辑跑在 MT5 事件轮询里,开终端按 F4 把这段塞进 EA 的 OnTick 或定时器就能验证刷新行为。

MQL5 / C++
  }
  class=class="str">"cmt">// Loop through the arrays and compare corresponding elements
  for (class="type">int i = class="num">0; i < size1; i++) {
    class=class="str">"cmt">// Compare the strings at the same index in both arrays
    if (StringCompare(arr1[i], arr2[i]) != class="num">0) {  class=class="str">"cmt">// If strings are different
      class=class="str">"cmt">// Action when strings differ at the same index
      Print("Change detected at index ", i, ": &class="macro">#x27;", arr1[i], "&class="macro">#x27; vs &class="macro">#x27;", arr2[i], "&class="macro">#x27;");
      isChange = true;
      class="kw">return (isChange);
    }
  }
  class=class="str">"cmt">// If no differences are found, you can also log this as no changes detected
  class=class="str">"cmt">//Print("No changes detected between arrays.");
  class="kw">return (isChange);
}
if (isChangeInStringArrays(previous_eventNames_data,current_eventNames_data)){
  Print("CHANGES IN EVENT NAMES DETECTED. UPDATE THE DASHBOARD VALUES");

  ObjectsDeleteAll(class="num">0,DATA_HOLDERS);
  ObjectsDeleteAll(class="num">0,ARRAY_NEWS);

  ArrayFree(current_eventNames_data);
  class=class="str">"cmt">//---
}
if (isChangeInStringArrays(previous_eventNames_data,current_eventNames_data)){
  Print("CHANGES IN EVENT NAMES DETECTED. UPDATE THE DASHBOARD VALUES");

  ObjectsDeleteAll(class="num">0,DATA_HOLDERS);
  ObjectsDeleteAll(class="num">0,ARRAY_NEWS);

  ArrayFree(current_eventNames_data);

  class=class="str">"cmt">//--- Initialize starting y-coordinate for displaying news data
  class="type">int startY = class="num">162;
  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;
          class="kw">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
把刷新比对交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到实时新闻与盘面异动的联动,你只管判断影响方向。

常见问题

本篇基于价格报价 tick 触发比对,用当前与上一周期数组差异刷新面板,比固定定时器更贴盘面节奏,资源占用也低。
每次报价先把新取到的事件名写入 current_eventNames_data,再和上一周期 previous 比对,仅差异项进面板,相同项直接跳过。
可以,初始化时按货币和重要性预筛,实时比对只在这部分子集里做,低权重噪音不会冲进仪表盘。
能,小布的品种页已内置经济日历实时模块,不需要自己写 MQL5 数组比对,打开就能看动态事件流。
只要控制比对频率和数组规模,仅在报价到来时做差异检测,对终端负载影响很小,老机器也跑得动。