利用 MQL5 经济日历进行交易(第四部分):在仪表盘中实现实时新闻更新·进阶篇
(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 即可。
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),肉眼扫起来不累。 外汇与贵金属受这些数据冲击波动可能放大,属高风险场景,参数阈值建议在策略测试器里按自己品种调。
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,说明日历有变动需要重绘。
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_HOLDERS 与 ARRAY_NEWS 前缀删 MT5 图表对象,再用 ArrayFree 释放当前数组内存,避免旧引用拖慢终端。
重绘起点写死 startY = 162,这是面板第一行相对图表上边的像素偏移;循环上限用 valuesTotal 而非写死数字,意味着你改 valuesTotal 就能控制最多显示多少条日历。
每条数据先 CalendarEventById 拿事件结构,再 CalendarCountryById 用 event.country_id 取国家,最后 CalendarValueById 取实际/预期/前值。若开了 enableCurrencyFilter,内层循环拿 country.currency 去撞 curr_filter 数组,不匹配就 continue 跳过——外汇与贵金属交易者靠这层把 EUR、XAU 等无关币种挡在面板外,但杠杆品种波动剧烈,过滤不等于风险可控。
重要性过滤紧随货币过滤之后,未贴出的部分就是按 event.importance 对比阈值,只留你关心的级别。整套逻辑跑在 MT5 事件轮询里,开终端按 F4 把这段塞进 EA 的 OnTick 或定时器就能验证刷新行为。
} 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