English Deutsch 日本語
preview
Трейдинг с экономическим календарем MQL5 (Часть 4): Обновление новостей в панели управления в реальном времени

Трейдинг с экономическим календарем MQL5 (Часть 4): Обновление новостей в панели управления в реальном времени

MetaTrader 5Трейдинг |
99 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[];

Здесь мы определим строковые массивы 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();
   
}

Здесь мы просто вызываем пользовательскую функцию на каждом тике, чтобы выполнить обновления. После компиляции и запуска программы имеем следующие результаты:

Данные массивов инициализации

На изображении видно, что мы собираем все новости в "текущий" (current) массив, который затем копируем в новый "предыдущий" (previous) массив для хранения, что идеально соответствует нашим потребностям. Теперь мы можем приступить к использованию этих скопированных данных для дальнейшего анализа. В функции мы просто получаем текущие события следующим образом:

//--- 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));

Это всего лишь предыдущий фрагмент кода, который мы использовали в разделе инициализации, но он нам нужен на каждом тике для получения обновлений. Пройдемся по нему вкратце. Мы уделяем особое внимание динамическому обновлению панели мониторинга последними экономическими новостями на каждом этапе, и для этого мы оптимизируем логику, отказываясь от создания графических объектов, и вместо этого концентрируемся на эффективном управлении данными о событиях. Начнем с определения переменных для отслеживания общего количества новостей и критериев сортировки, таких как валюта, важность и временные диапазоны. Эти фильтры гарантируют, что для дальнейшей обработки будут рассматриваться только соответствующие события.

Мы проходим по извелченным данным календаря, применяя фильтры для определения событий, соответствующих указанным условиям. Для каждого совпадающего события мы извлекаем ключевые данные, такие как название события, время, валюта и уровень важности. Эти данные хранятся в массиве current_eventNames_data, размер которого динамически изменяется для размещения новых записей. Этот массив имеет решающее значение для отслеживания событий и позволяет нам выявлять изменения между тиками, сравнивая их с предыдущими данными. Наконец, мы обновляем метку панели, чтобы она отражала общее количество отфильтрованных событий и текущее время сервера, гарантируя, что панель мониторинга всегда будет отображать последние данные о событиях без создания ненужных объектов. Такой подход позволяет эффективно собирать и обновлять экономические новости в реальном времени.

Далее нам необходимо отслеживать, произошли ли изменения в массивах хранения, используя вновь полученные данные. Для этого мы используем пользовательскую функцию.

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

Здесь мы определяем булеву функцию isChangeInStringArrays, которая сравнивает два строковых массива, arr1 и arr2, для обнаружения любых изменений между ними. Начнем с определения размеров обоих массивов с помощью функции ArraySize и сохраним эти размеры в size1 и size2. Если размеры массивов различаются, мы выводим соответствующие размеры, устанавливаем флаг isChange в значение true и возвращаем true, что указывает на изменение. Если размеры одинаковы, то переходим к сравнению элементов массивов с помощью цикла for. Для каждого индекса мы используем функцию StringCompare для проверки идентичности строк в обоих массивах. Если какие-либо строки различаются, мы выводим сведения об изменении и устанавливаем isChange в 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);

   //---

}

Здесь мы проверяем, произошли ли какие-либо изменения между массивами previous_eventNames_data и current_eventNames_data, вызывая функцию isChangeInStringArrays. Если функция возвращает true, что указывает на то, что изменения обнаружены, мы выводим сообщение "CHANGES IN EVENT NAMES DETECTED. UPDATE THE DASHBOARD VALUES" (Обнаружены изменения в именах событий. Обновите значения панели). После этого мы удаляем все объекты, связанные с держателями данных и массивами новостей на графике с помощью функции ObjectsDeleteAll, указав идентификаторы DATA_HOLDERS и ARRAY_NEWS в качестве префиксов объектов. Мы делаем это для того, чтобы удалить всю устаревшую информацию перед обновлением панели последними данными о событиях. Наконец, мы освобождаем память, используемую массивом current_eventNames_data с помощью функции ArrayFree, обеспечивая очистку массива при подготовке к следующему обновлению. После этого мы обновляем данные о событиях как обычно, но на этот раз создаем держатели данных и обновляем новостные события на новой панели мониторинга. Логика представлена ниже.

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);

Здесь мы начинаем с вывода текущего размера массива current_eventNames_data с помощью функции ArraySize и отображения ее содержимого с помощью функции ArrayPrint. Это поможет нам проверить текущий набор названий событий, которые мы отслеживаем. Далее мы выводим размер массива previous_eventNames_data до его обновления, а затем - его содержимое.

Затем мы освобождаем память, используемую previous_eventNames_data с помощью функции ArrayFree, гарантирующей очистку всех предыдущих данных, сохраненных в массиве, во избежание проблем с памятью. После освобождения памяти мы используем функцию 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 дек. 2024 в 20:57
Пользователям, вероятно, будет интересно узнать, что те же темы подробно рассмотрены в книге по алготрейдингу (а на сайте это почему-то не отображается в автоматических предложениях), включая отслеживание и сохранение изменений событий, фильтрацию событий по нескольким условиям (с различными логическими операторами) для расширенного набора полей, отображение настраиваемого на лету подмножества календаря в панели на графике и встраивание для торговли в советники с поддержкой переноса полного архива календаря в тестер.
Allan Munene Mutiiria
Allan Munene Mutiiria | 5 дек. 2024 в 00:08
Stanislav Korotky #:
Пользователям, вероятно, будет интересно узнать, что те же темы подробно рассмотрены в книге по алготрейдингу (а на сайте это почему-то не отображается в автоматических предложениях), включая отслеживание и сохранение изменений событий, фильтрацию событий по нескольким условиям (с различными логическими операторами) для расширенного набора полей, отображение настраиваемого на лету подмножества календаря в панели на графике и встраивание для торговли в советники с поддержкой переноса полного архива календаря в тестер.
Хорошо. Спасибо.
WebSocket для MetaTrader 5 — Асинхронные клиентские соединения с помощью Windows API WebSocket для MetaTrader 5 — Асинхронные клиентские соединения с помощью Windows API
В данной статье подробно описывается разработка пользовательской динамически подключаемой библиотеки, предназначенной для упрощения асинхронных клиентских соединений по протоколу WebSocket для программ MetaTrader.
Гауссовcкие процессы в машинном обучении (Часть 1): Модель классификации в MQL5 Гауссовcкие процессы в машинном обучении (Часть 1): Модель классификации в MQL5
В данной статье мы рассмотрим модель классификации гауссовских процессов. Мы начнём с изучения её теоретических принципов, а затем перейдём к практической разработке библиотеки ГП на MQL5.
От новичка до эксперта: Индикатор силы уровней поддержки и сопротивления (SRSI) От новичка до эксперта: Индикатор силы уровней поддержки и сопротивления (SRSI)
В настоящей статье мы поделимся информацией о том, как использовать программирование на MQL5 для точного определения уровней рынка, различая более слабые и самые сильные уровни цен. Мы в полном объеме разработаем действующий Индикатор силы уровней поддержки и сопротивления (SRSI).
Нейросети в трейдинге: Сквозная многомерная модель прогнозирования временных рядов (Основные компоненты) Нейросети в трейдинге: Сквозная многомерная модель прогнозирования временных рядов (Основные компоненты)
Предлагаем познакомиться с новой реализацией ключевых компонентов Фреймворка GinAR — адаптивного алгоритма для работы с графовыми временными рядами. В статье шаг за шагом разобраны архитектура, алгоритмы прямого прохода и обратного распространения ошибки.