
利用 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.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。



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