English Русский Deutsch 日本語
preview
利用 MQL5 经济日历进行交易(第四部分):在仪表盘中实现实时新闻更新

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

MetaTrader 5交易 |
15 2
Allan Munene Mutiiria
Allan Munene Mutiiria

引言

在本文中,我们继续推进在 MetaQuotes Language 5(MQL5)经济日历仪表盘方面的工作,为其添加了实时更新功能,使我们能够维护一个持续刷新的关键经济新闻事件显示界面。在上一篇文章中,我们设计并实现了一个仪表盘面板,用于根据货币、重要性和时间筛选新闻,为我们提供了一个定制化的相关事件视图。现在,我们通过启用实时更新来进一步扩展功能,确保我们的日历显示最新数据,以便及时做出决策。我们将涵盖的主题包括:

  1. 在 MQL5 中实时更新新闻事件
  2. 结论

此增强功能将把我们的仪表盘转变为一个动态的实时工具,不断更新最新的经济事件。通过实现实时刷新功能,我们将确保日历保持准确和相关性,从而在 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” 数组更新后的大小及其内容,以确认该数组现在包含了最新的事件名称。这确保了先前的事件名称被正确更新,以供将来进行比较。运行程序后,我们得到以下结果。

时间更新。

时间更新 GIF

事件更新。

事件更新

仪表盘更新。

仪表盘更新

从图片中我们可以看到,新引入的数据已准确地在仪表盘上更新。为了再次确认这一点,我们可以再等待一段时间,看看我们是否能持续追踪这些数据,以及记录更新数据的日志。结果如下。

事件数据更新。

新事件数据更新

仪表盘更新。

新仪表盘更新

从图片中我们可以看到,一旦与先前存储的数据相比发生变化,这些变化就会被检测到,并根据新注册的数据进行更新,并存储起来以供将来参考。存储的数据随后被用于实时更新仪表盘界面,显示当前的新闻事件,从而证实我们目标的成功实现。


结论

总之,我们实现了一个强大的系统,通过将先前存储的事件数据与新检索的更新进行比较,来监控和检测MQL5 经济新闻事件的变化。这种比较机制确保了事件名称或细节的任何差异都能被及时识别,并触发我们的仪表盘刷新,以保持其准确性和相关性。通过基于货币、重要性和时间对数据进行筛选,我们进一步优化了这一过程,使其专注于有影响力的事件,同时动态地更新界面。

在本系列的后续部分中,我们将以此为基础,将经济新闻事件集成到交易策略中,实现数据的实际应用。此外,我们旨在通过引入移动性和响应性来增强仪表盘的功能,为交易者提供更无缝、更具互动性的体验。敬请期待。

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16386

附加的文件 |
最近评论 | 前往讨论 (2)
Stanislav Korotky
Stanislav Korotky | 3 12月 2024 在 20:57
用户可能会有兴趣知道,同样的主题在算法交易书中也有详尽的介绍(网站并没有在自动建议中以某种方式显示),包括跟踪和保存事件变化通过多个条件(使用不同的逻辑运算符)对扩展字段集进行事件过滤、在图表面板上显示可自定义的即时日历子集,以及嵌入到 EA 中进行交易,并支持将完整的日历存档转移到测试器中。
Allan Munene Mutiiria
Allan Munene Mutiiria | 5 12月 2024 在 00:08
Stanislav Korotky #:
用户可能会有兴趣知道,同样的主题在算法交易书中也有详细介绍(网站在自动建议中并未以某种方式显示),包括跟踪和保存事件变化通过多个条件(使用不同的逻辑运算符)对扩展字段集进行事件过滤、在图表面板上显示可自定义的即时日历子集,以及嵌入到 EA 中进行交易,并支持将日历的完整存档传输到测试器中。
好的,谢谢。谢谢。
基于时间、价格和成交量创建 3D 柱状图引入波动率测量 基于时间、价格和成交量创建 3D 柱状图引入波动率测量
本文探讨了多元三维价格图表及其创建方法。我们还将探讨 3D 柱状图如何预测价格反转,以及 Python 和 MetaTrader 5 如何让我们实时绘制这些成交量柱状图。
利用CatBoost机器学习模型作为趋势跟踪策略的过滤器 利用CatBoost机器学习模型作为趋势跟踪策略的过滤器
CatBoost是一种强大的基于树的机器学习模型,擅长基于静态特征进行决策。其他基于树的模型,如XGBoost和随机森林(Random Forest),在稳健性、处理复杂模式的能力以及可解释性方面具有相似特性。这些模型应用广泛,可用于特征分析、风险管理等多个领域。在本文中,我们将逐步介绍如何将训练好的CatBoost模型用作经典移动平均线交叉趋势跟踪策略的过滤器。
将您自己的 LLM 集成到 EA 中(第 5 部分):使用 LLM 开发和测试交易策略(二)-LoRA-调优 将您自己的 LLM 集成到 EA 中(第 5 部分):使用 LLM 开发和测试交易策略(二)-LoRA-调优
随着当今人工智能的快速发展,语言模型(LLMs)是人工智能的重要组成部分,因此我们应该考虑如何将强大的 LLMs 整合到我们的算法交易中。对于大多数人来说,很难根据他们的需求微调这些强大的模型,在本地部署它们,然后将它们应用于算法交易。本系列文章将采取循序渐进的方法来实现这一目标。
交易中的神经网络:双曲型潜在扩散模型(HypDiff) 交易中的神经网络:双曲型潜在扩散模型(HypDiff)
本文研究经由各向异性扩散过程在双曲型潜在空间中编码初始数据的方法。这有助于更准确地保留当前市场状况的拓扑特征,并提升其分析品质。