English Русский 中文 Español Deutsch
preview
MQL5経済指標カレンダーを使った取引(第4回):ダッシュボードでのリアルタイムニュース更新の実装

MQL5経済指標カレンダーを使った取引(第4回):ダッシュボードでのリアルタイムニュース更新の実装

MetaTrader 5トレーディング |
95 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[];

ここでは、ダッシュボードへのライブ更新のために経済イベントデータを管理および比較するための2つのstring配列「current_eventNames_data」と「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関数を使用して配列のサイズを1つ増やし、新しいエントリ用のスペースを確保します。サイズ変更後、「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);
}

ここでは、2つの文字列配列「arr1」と「arr2」を比較して、それらの間の変更を検出するブール関数「isChangeInStringArrays」を定義します。まず、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を返して変更が検出されたことを示す場合、「CHANGES IN EVENT NAMES DETECTED..UPDATE THE DASHBOARD VALUES」というメッセージを出力します。これに続いて、オブジェクトプレフィックスとして識別子「DATA_HOLDERS」と「ARRAY_NEWS」を指定して、ObjectsDeleteAll関数を使用してチャート上のデータホルダーとニュース配列に関連するすべてのオブジェクトを削除します。これは、ダッシュボードを最新のイベントデータで更新する前に、古い情報を消去するためにおこないます。最後に、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
ユーザーはおそらく、同じトピックがalgotrading bookで徹底的にカバーされていることを知ることに興味を持つだろう(そして、サイトは自動サジェストで何らかの形でこれを表示しない)、イベントの変更の追跡と保存、フィールドの拡張セットに対する複数の条件(異なる論理演算子)によるイベントのフィルタリング、チャート上のパネルでカスタマイズ可能なオンザフライカレンダーのサブセットの表示、およびテスターへのカレンダーの完全なアーカイブの転送を サポートするEAへの取引のための埋め込みを含む
Allan Munene Mutiiria
Allan Munene Mutiiria | 5 12月 2024 において 00:08
Stanislav Korotky #:
ユーザーはおそらく、同じトピックがalgotrading bookで徹底的にカバーされていることを知ることに興味を持つだろう(そして、サイトは自動サジェストでなぜかこれを表示しない)。これには、イベント変更の追跡と保存、フィールドの拡張セットに対する複数の条件(異なる論理演算子)によるイベントのフィルタリング、チャート上のパネルでのカスタマイズ可能なオンザフライ・カレンダー・サブセットの表示、カレンダーの完全なアーカイブをテスターに転送する サポートとEAへの取引のための埋め込みが含まれる。
わかりました。ありがとう。
MQL5で取引管理者パネルを作成する(第8回):分析パネル MQL5で取引管理者パネルを作成する(第8回):分析パネル
今日は、管理パネルEAに統合された専用ウィンドウ内に、便利な取引メトリクスを組み込む方法について掘り下げていきます。本稿では、MQL5を活用して分析パネル(Analytics Panel)を開発する方法に焦点を当て、そのパネルが取引管理者にもたらすデータの価値について解説します。この開発プロセスは教育的意義が大きく、初心者・経験者を問わず開発者にとって有益な学びを提供します。この機能は、高度なソフトウェアツールを通じて取引マネージャーを支援する本連載の可能性を示す好例です。さらに、取引管理パネル(Trading Administrator Panel)の機能拡張の一環として、PieChartクラスとChartCanvasクラスの実装についても取り上げます。
知っておくべきMQL5ウィザードのテクニック(第50回):Awesome Oscillator 知っておくべきMQL5ウィザードのテクニック(第50回):Awesome Oscillator
Awesome Oscillatorは、モメンタム(勢い)を測定するために使用されるビル・ウィリアムズのインジケーターの一つです。複数のシグナルを生成できるため、以前の記事と同様に、MQL5ウィザードクラスとアセンブリを活用して、パターンベースでこれらを確認します。
MQL5とデータ処理パッケージの統合(第4回):ビッグデータの取り扱い MQL5とデータ処理パッケージの統合(第4回):ビッグデータの取り扱い
今回は、MQL5と強力なデータ処理ツールを統合する高度なテクニックに焦点を当て、取引分析および意思決定を強化するためのビッグデータの効率的な活用方法を探ります。
知っておくべきMQL5ウィザードのテクニック(第49回):近接方策最適化による強化学習 知っておくべきMQL5ウィザードのテクニック(第49回):近接方策最適化による強化学習
近接方策最適化は、強化学習におけるアルゴリズムの一つで、モデルの安定性を確保するために、しばしばネットワーク形式で非常に小さな増分で方策を更新します。前回の記事と同様に、ウィザードで作成したエキスパートアドバイザー(EA)において、これがどのように役立つかを探ります。