
利用 MQL5 经济日历进行交易(第四部分):在仪表盘中实现实时新闻更新
引言
在本文中,我们继续推进在 MetaQuotes Language 5(MQL5)经济日历仪表盘方面的工作,为其添加了实时更新功能,使我们能够维护一个持续刷新的关键经济新闻事件显示界面。在上一篇文章中,我们设计并实现了一个仪表盘面板,用于根据货币、重要性和时间筛选新闻,为我们提供了一个定制化的相关事件视图。现在,我们通过启用实时更新来进一步扩展功能,确保我们的日历显示最新数据,以便及时做出决策。我们将涵盖的主题包括:
此增强功能将把我们的仪表盘转变为一个动态的实时工具,不断更新最新的经济事件。通过实现实时刷新功能,我们将确保日历保持准确和相关性,从而在 MetaTrader 5 或交易终端中支持及时的交易决策。
在 MQL5 中实时更新新闻事件
要为我们的仪表盘实现实时更新,我们需要确保新闻事件被定期存储和比较,以检测任何变化。这需要维护数组来保存当前和先前的事件数据,使我们能够识别更新并将其准确地反映在仪表盘上。这样,我们就可以确保仪表盘动态调整,以实时显示最新的经济事件。下面,我们定义了相关数组:
string current_eventNames_data[]; string previous_eventNames_data[];
在这里,我们定义了两个string数组,“current_eventNames_data” 和 “previous_eventNames_data”,我们将使用它们来管理和比较经济事件数据,以便为仪表盘进行实时更新。“current_eventNames_data” 数组将存储从经济日历中检索到的最新事件,而 “previous_eventNames_data” 将保存上一个更新周期的数据。通过比较这两个数组,我们可以识别出事件中的任何变化或新增内容,从而允许我们动态更新仪表盘。
使用这些数组,我们需要在初始化部分中获取每个选定事件的当前值,并将其存储在当前数据持有者数组中,然后再将其复制到先前的持有者数组中,我们将在下一个价格报价时使用后者进行比较。
ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+1); current_eventNames_data[ArraySize(current_eventNames_data)-1] = event.name;
在这里,我们动态地扩展 “current_eventNames_data” 数组,并向其中添加一个新的事件名称。我们使用 ArrayResize 函数将数组的大小增加一,为新的数组元素分配空间。调整大小后,我们使用表达式 “current_eventNames_data[ArraySize(current_eventNames_data)-1]” 将事件的名称分配给数组的最后一个索引。此过程确保从经济日历检索到的每个新事件名称都存储在数组中,使我们能够维护一个最新的事件列表,以便进行进一步的处理和比较。
然而,在将事件添加到数组之前,我们需要确保从一个全新的状态开始,这意味着我们需要一个空数组。
ArrayFree(current_eventNames_data);
在这里,我们使用 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);
在这里,我们记录并管理数据在 current_eventNames_data 和 previous_eventNames_data 这两个数组之间的传递。首先,我们使用 Print 函数来显示 current_eventNames_data 数组的大小,以便了解当前存储的事件名称数量。接着,我们调用 ArrayPrint 函数输出数组的内容,以进行进一步的验证。然后,在执行复制操作之前,我们记录 previous_eventNames_data 数组的大小,以作为后续比较的基准。
我们使用 ArrayCopy 函数,将 “current_eventNames_data” 数组的内容复制到 “previous_eventNames_data” 数组中,从而有效地转移最新的事件名称,以备将来进行比较。接着,我们在复制操作后打印 “previous_eventNames_data” 数组的大小,以确认更新已成功完成。最后,我们调用 ArrayPrint 函数来输出 “previous_eventNames_data” 更新后的内容,确保数据传输的准确性和完整性。这些就是我们需要在 OnInit 事件处理函数中进行的更改,用以存储初始事件。下面我们将其高亮显示,以便更清晰地说明。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- ArrayFree(current_eventNames_data); //--- Loop through each calendar value up to the maximum defined total for (int i = 0; i < valuesTotal; i++){ //--- //--- Loop through calendar data columns for (int k=0; k<ArraySize(array_calendar); k++){ //--- //--- Prepare news data array with time, country, and other event details string news_data[ArraySize(array_calendar)]; news_data[0] = TimeToString(values[i].time,TIME_DATE); //--- Event date news_data[1] = TimeToString(values[i].time,TIME_MINUTES); //--- Event time news_data[2] = country.currency; //--- Event country currency //--- Determine importance color based on event impact 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;} //--- Set importance symbol for the event news_data[3] = ShortToString(0x25CF); //--- Set event name in the data array news_data[4] = event.name; //--- Populate actual, forecast, and previous values in the news data array news_data[5] = DoubleToString(value.GetActualValue(),3); news_data[6] = DoubleToString(value.GetForecastValue(),3); news_data[7] = DoubleToString(value.GetPreviousValue(),3); //--- } ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+1); current_eventNames_data[ArraySize(current_eventNames_data)-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); //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)); //--- return(INIT_SUCCEEDED); }
在这里,我们刚刚完成了那些有助于我们获取初始事件数据的更改。为了清晰起见,我们已将这些更改用黄色高亮显示。接下来,我们只需要在检测到对比过程中发生变化时,更新仪表盘上的数值。为此,我们将创建一个自定义函数,该函数将分别包含所有用于事件更新和仪表盘重新计算的逻辑。
//+------------------------------------------------------------------+ //| Function to update dashboard values | //+------------------------------------------------------------------+ void update_dashboard_values(){ //--- }
在这里,我们定义了 update_dashboard_values 函数,该函数将用于处理经济日历仪表盘的动态更新。此函数将包含用于比较已存储的新闻数据、识别任何变更,并将必要的更新应用到仪表盘界面的核心逻辑。通过将此功能组织到这个专用函数中,我们将确保代码结构清晰且模块化,从而使未来的修改或优化更易于管理。接下来,我们将按如下方式在 OnTick 事件处理函数中调用该函数。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- update_dashboard_values(); }
在这里,我们在每个 tick(市场报价变动)时调用该自定义函数来执行更新。编译并运行程序后,我们得到以下结果:
从图片中我们可以看到,我们将所有新闻事件收集到“当前”数组中,然后将其复制到全新的“先前”数组中进行存储,这完全符合我们的预期。我们现在可以继续使用这些复制的数据进行进一步的分析。在该函数中,我们只需按如下方式获取当前事件:
//--- Declare variables for tracking news events and status int totalNews = 0; bool isNews = false; MqlCalendarValue values[]; //--- Array to store calendar values //--- Define start and end time for calendar event retrieval datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_H12); datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_H12); //--- Set a specific country code filter (e.g., "US" for USD) string country_code = "US"; string currency_base = SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE); //--- Retrieve historical calendar values within the specified time range int allValues = CalendarValueHistory(values,startTime,endTime,NULL,NULL); //--- Print the total number of values retrieved and the array size //Print("TOTAL VALUES = ",allValues," || Array size = ",ArraySize(values)); //--- Define time range for filtering news events based on daily period datetime timeRange = PeriodSeconds(PERIOD_D1); datetime timeBefore = TimeTradeServer() - timeRange; datetime timeAfter = TimeTradeServer() + timeRange; //--- Print the furthest time look-back and current server time //Print("FURTHEST TIME LOOK BACK = ",timeBefore," >>> CURRENT = ",TimeTradeServer()); //--- Limit the total number of values to display int valuesTotal = (allValues <= 11) ? allValues : 11; string curr_filter[] = {"AUD","CAD","CHF","EUR","GBP","JPY","NZD","USD"}; int news_filter_count = 0; ArrayFree(current_eventNames_data); // 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}; //--- Loop through each calendar value up to the maximum defined total for (int i = 0; i < valuesTotal; i++){ MqlCalendarEvent event; //--- Declare event structure CalendarEventById(values[i].event_id,event); //--- Retrieve event details by ID MqlCalendarCountry country; //--- Declare country structure CalendarCountryById(event.country_id,country); //--- Retrieve country details by event's country ID MqlCalendarValue value; //--- Declare calendar value structure CalendarValueById(values[i].id,value); //--- Retrieve actual, forecast, and previous values //--- Check if the event’s currency matches any in the filter array (if the filter is enabled) bool currencyMatch = false; if (enableCurrencyFilter) { for (int j = 0; j < ArraySize(curr_filter); j++) { if (country.currency == curr_filter[j]) { currencyMatch = true; break; } } //--- If no match found, skip to the next event if (!currencyMatch) { continue; } } //--- Check importance level if importance filter is enabled bool importanceMatch = false; if (enableImportanceFilter) { for (int k = 0; k < ArraySize(allowed_importance_levels); k++) { if (event.importance == allowed_importance_levels[k]) { importanceMatch = true; break; } } //--- If importance does not match the filter criteria, skip the event if (!importanceMatch) { continue; } } //--- Apply time filter and set timeMatch flag (if the filter is enabled) bool timeMatch = false; if (enableTimeFilter) { datetime eventTime = values[i].time; if (eventTime <= TimeTradeServer() && eventTime >= timeBefore) { timeMatch = true; //--- Event is already released } else if (eventTime >= TimeTradeServer() && eventTime <= timeAfter) { timeMatch = true; //--- Event is yet to be released } //--- Skip if the event doesn't match the time filter if (!timeMatch) { continue; } } //--- If we reach here, the currency matches the filter news_filter_count++; //--- Increment the count of filtered events //--- Set alternating colors for each data row holder color holder_color = (news_filter_count % 2 == 0) ? C'213,227,207' : clrWhite; //--- Loop through calendar data columns for (int k=0; k<ArraySize(array_calendar); k++){ //--- Print event details for debugging //Print("Name = ",event.name,", IMP = ",EnumToString(event.importance),", COUNTRY = ",country.name,", TIME = ",values[i].time); //--- Skip event if currency does not match the selected country code // if (StringFind(_Symbol,country.currency) < 0) continue; //--- Prepare news data array with time, country, and other event details string news_data[ArraySize(array_calendar)]; news_data[0] = TimeToString(values[i].time,TIME_DATE); //--- Event date news_data[1] = TimeToString(values[i].time,TIME_MINUTES); //--- Event time news_data[2] = country.currency; //--- Event country currency //--- Determine importance color based on event impact 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;} //--- Set importance symbol for the event news_data[3] = ShortToString(0x25CF); //--- Set event name in the data array news_data[4] = event.name; //--- Populate actual, forecast, and previous values in the news data array news_data[5] = DoubleToString(value.GetActualValue(),3); news_data[6] = DoubleToString(value.GetForecastValue(),3); news_data[7] = DoubleToString(value.GetPreviousValue(),3); } ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+1); current_eventNames_data[ArraySize(current_eventNames_data)-1] = event.name; } //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));
这只是我们在初始化部分使用过的代码片段,但现在我们需要在每个tick到来时都使用它来获取更新。不过,我们还是会简要地回顾一下。我们的重点是利用每个tick来动态更新仪表盘,展示最新的经济新闻事件。为实现这一目标,我们优化了逻辑,移除了创建图形对象的部分,转而专注于高效地管理事件数据。我们首先定义一些变量,用于追踪新闻总数以及筛选条件,例如货币、重要性和时间范围。这些筛选条件确保只有相关的事件才会被纳入后续处理。
我们遍历检索到的日历值,应用筛选条件以识别出符合指定条件的事件。对于每一个匹配的事件,我们提取其关键信息,如事件名称、时间、货币和重要性级别。这些详细信息被存储在 “current_eventNames_data” 数组中,该数组会动态调整大小以容纳新的条目。这个数组对于追踪事件至关重要,它允许我们通过将当前数据与先前数据进行比较,来识别出每个 tick 之间发生的变化。最后,我们更新仪表盘上的标签,以反映筛选后的事件总数和当前服务器时间,从而确保仪表盘始终显示最新的事件数据,而无需创建不必要的对象。这种方法能够高效地捕获并实时更新经济新闻信息。
接下来,我们需要利用新获取的数据来追踪存储数组中是否发生了变化。为此,我们将使用一个自定义函数。
//+------------------------------------------------------------------+ //| Function to compare two string arrays and detect changes | //+------------------------------------------------------------------+ bool isChangeInStringArrays(string &arr1[], string &arr2[]) { bool isChange = false; int size1 = ArraySize(arr1); // Get the size of the first array int size2 = ArraySize(arr2); // Get the size of the second array // Check if sizes are different if (size1 != size2) { Print("Arrays have different sizes. Size of Array 1: ", size1, ", Size of Array 2: ", size2); isChange = true; return (isChange); } // Loop through the arrays and compare corresponding elements for (int i = 0; i < size1; i++) { // Compare the strings at the same index in both arrays if (StringCompare(arr1[i], arr2[i]) != 0) { // If strings are different // Action when strings differ at the same index Print("Change detected at index ", i, ": '", arr1[i], "' vs '", arr2[i], "'"); isChange = true; return (isChange); } } // If no differences are found, you can also log this as no changes detected //Print("No changes detected between arrays."); return (isChange); }
在这里,我们定义了一个boolean函数 isChangeInStringArrays,它用于比较两个字符串数组 arr1 和 arr2,以检测它们之间是否存在任何变化。我们首先使用ArraySize函数确定两个数组的大小,并将这些大小分别存储在 size1 和 size2 变量中。如果两个数组的大小不一致,我们就打印出它们各自的大小,将 isChange 标志设置为 true,然后返回 true,这表示检测到了变化。如果数组大小相同,我们就继续使用一个for 循环来逐个比较数组中的元素。对于每一个索引,我们使用 StringCompare 函数来检查两个数组中对应位置的字符串是否完全相同。如果发现任何字符串不一致,我们就打印出变化的详细信息,并将 isChange 设置为 true,然后返回 true 以表示有更新。如果在循环结束后没有发现任何差异,函数将返回 false,表示数组没有发生变化。这种方法对于检测更新(例如新增或更新的新闻事件)至关重要,我们需要将这些更新反映到仪表盘上。
有了这个函数,我们就可以利用它来更新事件数据了。
if (isChangeInStringArrays(previous_eventNames_data,current_eventNames_data)){ Print("CHANGES IN EVENT NAMES DETECTED. UPDATE THE DASHBOARD VALUES"); ObjectsDeleteAll(0,DATA_HOLDERS); ObjectsDeleteAll(0,ARRAY_NEWS); ArrayFree(current_eventNames_data); //--- }
在这里,我们通过调用 isChangeInStringArrays 函数,来检查 previous_eventNames_data 和 current_eventNames_data 这两个数组之间是否存在任何变化。如果该函数返回 true,意味着检测到了变化,我们就打印一条消息:检测到事件名称发生变化。请更新仪表盘的值。紧接着,我们使用ObjectsDeleteAll函数删除图表上所有与数据持有者(DATA_HOLDERS)和新闻数组(ARRAY_NEWS)相关的对象。我们指定这两个标识符作为对象前缀。这样做是为了在用最新的事件数据更新仪表盘之前,清除所有过时的信息。最后,我们使用 ArrayFree 函数释放 current_eventNames_data 数组所占用的内存,确保该数组被清空,为下一次更新做好准备。在此之后,我们像往常一样更新事件数据,但这一次,我们会在一个“干净”的仪表盘上重新创建数据持有者并更新新闻事件。以下是其代码。
if (isChangeInStringArrays(previous_eventNames_data,current_eventNames_data)){ Print("CHANGES IN EVENT NAMES DETECTED. UPDATE THE DASHBOARD VALUES"); ObjectsDeleteAll(0,DATA_HOLDERS); ObjectsDeleteAll(0,ARRAY_NEWS); ArrayFree(current_eventNames_data); //--- Initialize starting y-coordinate for displaying news data int startY = 162; //--- Loop through each calendar value up to the maximum defined total for (int i = 0; i < valuesTotal; i++){ MqlCalendarEvent event; //--- Declare event structure CalendarEventById(values[i].event_id,event); //--- Retrieve event details by ID MqlCalendarCountry country; //--- Declare country structure CalendarCountryById(event.country_id,country); //--- Retrieve country details by event's country ID MqlCalendarValue value; //--- Declare calendar value structure CalendarValueById(values[i].id,value); //--- Retrieve actual, forecast, and previous values //--- Check if the event’s currency matches any in the filter array (if the filter is enabled) bool currencyMatch = false; if (enableCurrencyFilter) { for (int j = 0; j < ArraySize(curr_filter); j++) { if (country.currency == curr_filter[j]) { currencyMatch = true; break; } } //--- If no match found, skip to the next event if (!currencyMatch) { continue; } } //--- Check importance level if importance filter is enabled bool importanceMatch = false; if (enableImportanceFilter) { for (int k = 0; k < ArraySize(allowed_importance_levels); k++) { if (event.importance == allowed_importance_levels[k]) { importanceMatch = true; break; } } //--- If importance does not match the filter criteria, skip the event if (!importanceMatch) { continue; } } //--- Apply time filter and set timeMatch flag (if the filter is enabled) bool timeMatch = false; if (enableTimeFilter) { datetime eventTime = values[i].time; if (eventTime <= TimeTradeServer() && eventTime >= timeBefore) { timeMatch = true; //--- Event is already released } else if (eventTime >= TimeTradeServer() && eventTime <= timeAfter) { timeMatch = true; //--- Event is yet to be released } //--- Skip if the event doesn't match the time filter if (!timeMatch) { continue; } } //--- If we reach here, the currency matches the filter news_filter_count++; //--- Increment the count of filtered events //--- Set alternating colors for each data row holder color holder_color = (news_filter_count % 2 == 0) ? C'213,227,207' : clrWhite; //--- Create rectangle label for each data row holder createRecLabel(DATA_HOLDERS+string(news_filter_count),62,startY-1,716,26+1,holder_color,1,clrNONE); //--- Initialize starting x-coordinate for each data entry int startX = 65; //--- Loop through calendar data columns for (int k=0; k<ArraySize(array_calendar); k++){ //--- Print event details for debugging //Print("Name = ",event.name,", IMP = ",EnumToString(event.importance),", COUNTRY = ",country.name,", TIME = ",values[i].time); //--- Skip event if currency does not match the selected country code // if (StringFind(_Symbol,country.currency) < 0) continue; //--- Prepare news data array with time, country, and other event details string news_data[ArraySize(array_calendar)]; news_data[0] = TimeToString(values[i].time,TIME_DATE); //--- Event date news_data[1] = TimeToString(values[i].time,TIME_MINUTES); //--- Event time news_data[2] = country.currency; //--- Event country currency //--- Determine importance color based on event impact 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;} //--- Set importance symbol for the event news_data[3] = ShortToString(0x25CF); //--- Set event name in the data array news_data[4] = event.name; //--- Populate actual, forecast, and previous values in the news data array news_data[5] = DoubleToString(value.GetActualValue(),3); news_data[6] = DoubleToString(value.GetForecastValue(),3); news_data[7] = DoubleToString(value.GetPreviousValue(),3); //--- Create label for each news data item if (k == 3){ createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY-(22-12),news_data[k],importance_color,22,"Calibri"); } else { createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY,news_data[k],clrBlack,12,"Calibri"); } //--- Increment x-coordinate for the next column startX += buttons[k]+3; } ArrayResize(current_eventNames_data,ArraySize(current_eventNames_data)+1); current_eventNames_data[ArraySize(current_eventNames_data)-1] = event.name; //--- Increment y-coordinate for the next row of data startY += 25; //Print(startY); //--- Print current y-coordinate for debugging } 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)); ArrayPrint(previous_eventNames_data); ArrayFree(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); }
在这里,我们根据新获取的数据来更新仪表盘,以确保实时更新能够生效。我们将数据记录复制到先前的数组中,以便在下一次检查时可以使用当前的数据。我们已经将负责此操作的代码部分高亮显示,让我们更深入地分析一下。
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)); ArrayPrint(previous_eventNames_data); ArrayFree(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);
在这里,我们首先使用 ArraySize 函数打印 “current_eventNames_data” 数组的当前大小,并使用 ArrayPrint 函数显示其内容。这将帮助我们检查当前正在追踪的事件名称集合。然后,在更新 “previous_eventNames_data” 数组之前,我们打印它的大小,然后再打印其内容。
接下来,我们使用 ArrayFree 函数释放 “previous_eventNames_data” 数组所占用的内存,确保清除数组中存储的任何先前数据,以避免内存问题。释放内存后,我们使用 ArrayCopy 函数将 “current_eventNames_data” 数组的内容复制到 “previous_eventNames_data” 中,从而有效地用最新的事件名称对其进行更新。
最后,我们打印 “previous_eventNames_data” 数组更新后的大小及其内容,以确认该数组现在包含了最新的事件名称。这确保了先前的事件名称被正确更新,以供将来进行比较。运行程序后,我们得到以下结果。
时间更新。
事件更新。
仪表盘更新。
从图片中我们可以看到,新引入的数据已准确地在仪表盘上更新。为了再次确认这一点,我们可以再等待一段时间,看看我们是否能持续追踪这些数据,以及记录更新数据的日志。结果如下。
事件数据更新。
仪表盘更新。
从图片中我们可以看到,一旦与先前存储的数据相比发生变化,这些变化就会被检测到,并根据新注册的数据进行更新,并存储起来以供将来参考。存储的数据随后被用于实时更新仪表盘界面,显示当前的新闻事件,从而证实我们目标的成功实现。
结论
总之,我们实现了一个强大的系统,通过将先前存储的事件数据与新检索的更新进行比较,来监控和检测MQL5 经济新闻事件的变化。这种比较机制确保了事件名称或细节的任何差异都能被及时识别,并触发我们的仪表盘刷新,以保持其准确性和相关性。通过基于货币、重要性和时间对数据进行筛选,我们进一步优化了这一过程,使其专注于有影响力的事件,同时动态地更新界面。
在本系列的后续部分中,我们将以此为基础,将经济新闻事件集成到交易策略中,实现数据的实际应用。此外,我们旨在通过引入移动性和响应性来增强仪表盘的功能,为交易者提供更无缝、更具互动性的体验。敬请期待。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16386
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.



用户可能会有兴趣知道,同样的主题在算法交易书中也有详细介绍(网站在自动建议中并未以某种方式显示),包括跟踪和保存事件变化、通过多个条件(使用不同的逻辑运算符)对扩展字段集进行事件过滤、在图表面板上显示可自定义的即时日历子集,以及嵌入到 EA 中进行交易,并支持将日历的完整存档传输到测试器中。