English Deutsch
preview
MQL5経済指標カレンダーを用いたニュースフィルタリング(第1回):MQL5におけるニュース前・ニュース後ウィンドウの実装

MQL5経済指標カレンダーを用いたニュースフィルタリング(第1回):MQL5におけるニュース前・ニュース後ウィンドウの実装

MetaTrader 5トレーディング |
24 8
Solomon Anietie Sunday
Solomon Anietie Sunday

一般的な問題とあまり知られていない問題

多くの自動売買ロボット(EA)に搭載されているニュースフィルタは、単純に「ニュース発表中の新規取引を停止する」だけの機能です。しかし、それだけでは十分ではありません。

重要な経済指標の発表時には、市場で以下のような現象が頻繁に発生します。

  • 突発的な価格急騰・急落
  • スプレッドの拡大
  • スリッページの発生
  • ストップロスを狙ったヒゲ(ストップ狩り)

保有中のポジションはそのままリスクにさらされ続けるため、戦略が持つ統計的優位性が完全に失われる可能性があります。

さらに、以下のような追加の問題も存在します。

  • プロップファームの規則対応:特定のニュースイベント前後に設定された厳格な時間制限を遵守する必要があります。
  • 通貨の関連性:すべてのニュースがすべての銘柄に影響するわけではありません。そのため、フィルタリングは通貨単位で判断する必要があります。
  • ニュース発表時の戦略不安定化:ストップロスが実際の価格変動ではなく、スプレッド拡大によって発動することがあります。
  • EAやVPS再起動後の状態消失:多くの実装では、途中停止後にトレードロジックの状態を正しく保持できません。
  • テストモードの欠如:実際のニュース発表を待たずに動作確認することが困難です。

解決策

解決策は、MQL5標準の経済指標カレンダー機能を利用した、構造化されたニュースフィルタを構築することです。

このシステムでは以下を実行します。

  • 銘柄が使用する通貨を基準にニュースの関連性を判定
  • 重要度レベルによるフィルタリング
  • 設定可能なニュース前後のバッファ時間を適用
  • ニュース有効期間中の新規エントリーを制限
  • 高インパクトニュース前に必要に応じてポジションを決済
  • ニュースウィンドウの開始・終了タイミングを正確に検出

単純なON/OFFスイッチではなく、リスクを考慮した高度な管理メカニズムになります。


本記事の範囲

この記事は、MQL5向けニュース対応型トレードシステムの第1回です。

本記事では以下の内容を扱います。

  • 重要度レベルによるニュース検出
  • 通貨ロジックを基準とした銘柄へのイベント紐付け
  • ニュース前後の監視時間(ウィンドウ)の定義
  • 新規トレードエントリーの制限
  • 高インパクトニュース前のポジション決済

高度なストップ管理や状態復旧メカニズムについては、実装内容を明確かつ実用的に保つため、別の記事で解説します。

以下は、本記事で扱うニュースフィルタの動作フローをまとめたワークフロー図です。

ワークフロー図

MQL5カレンダー:本記事で採用するアプローチと、その必要性

以前、トレーダーはニュースデータを取得するために、外部WebサイトへのWebリクエストに依存する必要がありました。しかし、この方法には、処理速度が遅い、信頼性が低い、正確なバックテストが困難という問題があります。現在のMQL5では、MqlCalendar関数を利用することで、ターミナルから直接経済データを取得できます。

この方法は、HTTPリクエストが存在しないため速度面で優れており、データはローカルキャッシュから取得されます。また、ニュースイベントのバックテストでは手動でトリガーするシミュレーションが可能であり、外部サービスよりも過去データ上でより正確なテストをおこなうことができます。さらに、リンク切れやAPI変更など、外部サービスに伴う問題を心配する必要がないため、信頼性の面でも優れています。


制限事項と設計上の考慮点

この実装は、軽量で決定論的、かつMQL5標準の経済指標カレンダーと互換性を持つように設計されています。そのため、いくつかの制限事項と前提条件を明確にする必要があります。
  1. ニュースフィルタは、ローリング方式の24時間ウィンドウ内でカレンダーイベントを読み込みます。この設計により、カレンダーへの繰り返しクエリを最小限に抑え、実行時のパフォーマンスを向上させています。しかし、その一方で、次の24時間を超えるイベントは事前に読み込まれません。そのため、正確性を維持するにはカレンダーキャッシュを定期的に更新する必要があります。
  2. 探索的な銘柄と通貨のマッピング。銘柄の関連性は、FX銘柄におけるベース通貨・クォート通貨の抽出や、インデックス、金属、コモディティに対するキーワードベースの検出など、ヒューリスティックなルールを使用して判定されます。これは一般的なブローカーの命名規則では機能しますが、すべてのカスタム銘柄を網羅することは保証されません。そのため、特殊なケースや独自の銘柄形式に対応するために、手動で通貨を上書きできる入力項目が用意されています。
  3. すべての時間比較は、取引サーバーの現在時刻を使用して実行されます。MQL5経済指標カレンダーのイベントもサーバー環境を基準としたタイムスタンプを持っていますが、ブローカーのサーバー時間帯の違い、サマータイムの切り替え、過去データでのテスト環境などにより、時間の不一致が発生する可能性があります。そのため、ニュース前後のバッファウィンドウは固定値ではなく、設定可能になっています。
  4. この実装は、完全にブローカーが提供するMQL5経済指標カレンダーに依存しています。もしブローカーが特定のイベント、国、または重要度レベルを正確に提供していない場合、それらのイベントはフィルタによって検出できません。したがって、このシステムの完全性は、基盤となるブローカー提供のカレンダーデータの品質に依存します。

    時間ウィンドウの理解

    ニュースフィルタは、イベントの正確な発表時刻だけに反応するわけではありません。その代わりに、発表前のボラティリティや発表後の市場の不安定性を考慮するため、イベントの前後に設定可能なバッファウィンドウを定義します。このウィンドウは、ニュース前バッファ(イベント前の分数)、実際のニュースイベント発表時刻、ニュース後バッファ(イベント後の分数)で構成されます。この結合されたウィンドウ内に存在するすべての時間は、ニュースが有効な期間として扱われます。

    以下は、ニュースウィンドウがどのように機能するかを示す画像です。

    ニュースウィンドウ画像の説明



      実装

      ステップ1:コア構造の定義

      経済ニュースを検出したり、それに反応したりする前に、まずニュースデータをどのように保存するか、そしてシステムがニュースウィンドウが有効かどうかをどのように管理するかを決定する必要があります。

      多くの初心者向け実装では、データを適切に整理せずに毎ティックごとにニュースイベントを直接チェックしようとします。その結果、不要なCPU使用量、繰り返される計算、そして保守や拡張が困難になる複雑なロジックにつながります。これらの問題を避けるために、まず2つの基本的な目的を果たす、シンプルで明確に範囲を定めたデータ構造を定義します。
      1. MetaTraderから取得した経済指標カレンダーイベントを保存する
      2. 取引システムが現在ニュース制限期間内にあるかどうかを追跡する
      この段階では、まだ取引動作は変更されません。ここでは、後のロジックが依存する基盤を構築しているだけです。 

      MQL5経済指標カレンダー構造体の使用

      MetaTrader 5には、MqlCalendarValueと呼ばれる組み込み構造体が用意されています。

      この構造体には、経済イベントに関連するすべての重要な情報が含まれています。これには、イベント予定時刻、関連する通貨、影響度レベル(低・中・高)、および実際値、予測値、前回値が含まれます。

      経済イベント用に独自のカスタム構造体を定義する代わりに、この組み込み型を使用します。これにより、MQL5経済指標カレンダーとの互換性を確保し、実装エラーのリスクを低減できます。

      カレンダーデータは配列に保存され、後に現在の取引日に関連するすべてのイベントを表すようになります。

      ニュースイベントの例

      シナリオと想定される動作

      以下では、異なる銘柄と経済イベントに対して、ニュースフィルタがどのように動作することが期待されるかを示します。これは、経済指標カレンダーに一致するイベントが存在し、設定された時間ウィンドウが有効であることを前提としています。 以下の例はすべての可能なシナリオを示すものではなく、フィルタの動作全体を網羅するものではありません。

      銘柄 イベント通貨 イベント重要性 ニュースウィンドウ内 期待される結果
      EURUSD EURまたはUSD 高(赤) はい 新規取引をブロック
      EURUSD EURまたはUSD  いいえ 取引許可 
      EURUSD EURまたはUSD 低(グレー) はい 低重要度が有効でない場合、取引をブロック。有効な場合は取引許可
      EURUSD GBP 高、低、中 はい 取引許可
      GOLD USD はい 新規取引をブロック
      US100 USD 中(黄色) はい ニュース取引をブロック(中重要度が有効な場合)
      GBPJPY GBPまたはJPY いいえ 取引許可
      AUDUSD AUDまたはUSD  ウィンドウ後 取引許可

      各ケースにおいて、判定は通貨の関連性と時間ウィンドウの一致のみに基づいておこなわれます。イベント通貨が銘柄の関連通貨のいずれかと一致し、現在時刻が設定されたバッファウィンドウ内にある場合、ニュースフィルタはアクティブ状態になります。

      ストラテジーテスターで手動検証をおこなう場合、テスト用入力値を使用して特定のイベント時刻をシミュレーションできます。これにより、リアルタイムのカレンダーデータに依存せず、決定論的にニュースウィンドウを発生させることができます。これにより、リアルタイムのカレンダーデータに依存することなく、ニュースウィンドウを確定的にトリガーすることが可能になります。

      銘柄の通貨関連性を理解する

      経済ニュースイベントは、取引可能な銘柄ではなく、通貨に関連付けられています。特定のイベントが関連性を持つかどうかを判断するために、システムは現在取引されている銘柄に関連する通貨セットを取得し、それをイベントで報告されている通貨と比較します。

      標準的なFX銘柄の場合、ベース通貨とクォート通貨は銘柄名から抽出されます(例:EURUSDの場合、EURとUSD)。

      インデックス、金属、コモディティについては、あらかじめ定義されたキーワードベースのヒューリスティックを使用して、その銘柄に最も影響を与える通貨を関連付けます(例:GOLDはUSD、GER40はEUR)。

      銘柄が既知のパターンに一致しない場合は、入力項目で手動通貨オーバーライドを指定し、監視対象とする通貨を明示的に定義できます。経済イベントは、その通貨が銘柄に関連付けられた通貨の少なくとも1つと一致した場合、関連性があると判断されます。

      実践ステップ

      ニュース状態の追跡

      ニュースフィルタは、同じ情報を毎ティックごとに繰り返し評価するべきではありません。

      これを防ぐために、システムが現在ニュース制限ウィンドウ内で動作しているかどうかを記憶できる、単純な状態フラグを導入します。

      さらに、カレンダーデータが最後に読み込まれた時刻を追跡するためにタイムスタンプを使用します。これにより、継続的にクエリを実行するのではなく、必要な場合のみカレンダー情報を更新できます。

      このアプローチにより、CPU使用量を削減し、実行効率を向上させ、ロジックを予測可能で理解しやすい状態に保ち、明確なログ出力とデバッグを可能にします。

      このステップの終了時点では、エキスパートアドバイザー(EA)にはまだ機能するニュースロジックはありません。しかし、今後のステップで構築される、クリーンで拡張可能な構造が完成しています。

      //+------------------------------------------------------------------+
      //|                                      News Filter Integration.mq5 |
      //|                                                    soloharbinger |
      //+------------------------------------------------------------------+
      #property copyright "soloharbinger"
      #property link      ""
      #property version   "1.01"
      
      #include <Trade/Trade.mqh>
      
      //===================================================================
      // GLOBAL VARIABLES                                                 |
      //===================================================================
      MqlCalendarValue TodayEvents[];    // Stores calendar events for the trading day
      
      datetime lastCalendarLoad = 0;     // TimeStamp of the last calendar update into our above value
      
      CTrade trade;                      // Ctrade instance for order management 
      
      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //---
         Print("News Filter Demo initialized");
      //---
         return(INIT_SUCCEEDED);
        }
      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //---
        }
      //+------------------------------------------------------------------+
      //| Expert tick function                                             |
      //+------------------------------------------------------------------+
      void OnTick()
        {
      //---
      // News detection logics will be implemented in the next steps
        }
      //+------------------------------------------------------------------+

      ステップ2:経済指標カレンダーイベントの読み込みとキャッシュ処理

      基本的なデータ構造が整ったら、次の課題は経済ニュースデータをどのように効率的に取得するかです。

      MQL5経済指標カレンダーには、複数年にわたる数千件のイベントが含まれています。毎ティックごとにカレンダー全体を検索することは、非常に非効率であり、必要ありません。このような方法はCPUリソースを無駄に消費し、特に複数のEAを同時に実行している場合、EAの動作速度を大幅に低下させる可能性があります。

      これを避けるために、実際に必要なイベントのみを取得する、制御された読み込みメカニズムを導入します。

      キャッシュが必要な理由

      経済ニュースイベントは毎秒変化するものではありません。事前にスケジュールされており、多くのティックで再利用できます。そのため、価格が更新されるたびにカレンダーデータを繰り返し取得する意味はありません。

      代わりに、初期化時に一度カレンダーイベントを読み込み、その後は定義した時間間隔ごとに定期的に更新します。これにより、市場データの急な変更やイベント内容の変更にも対応できます。

      取得したデータはTodayEvents[]配列に保存され、後続のロジックで追加の処理負荷なしに再利用できます。

      イベント読み込み期間の定義

      この記事では、ローリング方式の24時間期間を定義します。これは、現在のサーバー時刻から24時間先までの範囲です。

      このウィンドウは、セッション開始前のニュース、日中のイベント、そして終盤または夜間に発表される指標を取得するのに十分な広さがあります。正確なウィンドウの長さは調整可能ですが、24時間は明確で初心者にも扱いやすい開始点となります。

      カレンダーイベント読み込み関数

      カレンダー取得のロジックは、専用の関数内にカプセル化します。

      これにより、コードを整理された状態に保ち、EA全体にカレンダーロジックが分散することを防ぎます。

      この関数は、カレンダーが最近読み込まれたかどうかを確認し、キャッシュされたデータがまだ有効である場合は再読み込みをスキップします。その後、定義された時間範囲に対して経済指標カレンダーを検索し、結果をTodayEvents[]配列に保存します。

      現段階では、まだフィルタリングは適用されていません。ここでは、単純に生のイベントデータを収集しているだけです。

      //===================================================================
      // Load calendar (24-hour horizon)
      //===================================================================
      void LoadTodayCalendarEvents()
        {
         datetime now = TimeCurrent();
         datetime fromTime = now;
         datetime toTime = now + 24 * 3600;    // Load next 24 hours
      
      
         ArrayFree(TodayEvents);
         int count = CalendarValueHistory(TodayEvents, fromTime, toTime);
         PrintFormat("Loaded %d calendar records for the next 24 hours.", count);
      
      
         if(count <= 0)
           {
            // Still update timestamp to prevent constant reloading attempts
            lastCalendarLoad = now;
            return;
           }
      
      // Debug: print first few events for verification
         for(int i = 0; i < MathMin(count, 5); i++)
           {
            MqlCalendarEvent ev;
            if(CalendarEventById(TodayEvents[i].event_id, ev))
              {
               PrintFormat("Event %d: %s | Time: %s | Importance: %d",
                           i, ev.name, TimeToString(TodayEvents[i].time), ev.importance);
              }
           }
      
         lastCalendarLoad = now;
        }
      

      EAにおける読み込み済み/キャッシュ済みの今後のニュースイベントの例

      ステップ3:スマート通貨フィルタリング

      基本的なニュースフィルタでよくある不満の1つは、無関係な取引制限です。

      たとえば、EAがEURUSDを取引している場合、オーストラリアドル(AUD)に関連する高インパクトニュースが発表されるだけで取引を停止するべきではありません。しかし、単純な実装の多くは、すべてのニュースを同じように危険なものとして扱ってしまいます。その結果、取引機会の損失や不要な稼働停止が発生します。

      これを解決するために、スマート通貨フィルタリングを導入します。

      このステップの目的は単純です。 実際に取引対象の銘柄に関連するニュースイベントのみに反応することです。

      通貨フィルタリングが重要な理由

      すべての経済ニュースイベントは、特定の国および通貨に関連付けられています。

      同時に、すべての取引銘柄は、1つまたは複数の基礎通貨の影響を受けます。

      この2つを正しく照合しない場合:

      • EAは、本来停止する必要がない場面で取引を停止する可能性があります。または、さらに悪い場合には、実際に銘柄へ影響を与えるニュースを無視してしまいます。

      したがって、ニュースフィルタは以下の2つの質問に答える必要があります。

      • この取引銘柄が依存している通貨はどれか、そしてこのニュースイベントはどの通貨に属しているのか

      両方が一致した場合にのみ、そのイベントは関連性があると判断されるべきです。

      取引銘柄から関連通貨を抽出する

      まず、現在の銘柄に影響を与える通貨を特定します。

      標準的なFXペアの場合、これは単純です。最初の3文字がベース通貨を表し、次の3文字がクォート通貨を表します。

      次は例です。

      • EURUSD → EURとUSD、またはGBPJPY → GBPとJPY。

      かし、すべての金融商品がこの形式に従うわけではありません。

      インデックス、金属、コモディティでは、特別な処理が必要になることがあります。

      システムの柔軟性を維持するため、このロジックをGetRelevantCurrencies()という関数内にカプセル化しています。

      この関数は、FXペアの場合は自動的にベース通貨とクォート通貨を抽出し、インデックスやコモディティについては単純なキーワード検出を適用します。また、自動検出が不十分な場合には手動オーバーライドを使用でき、銘柄に関連する通貨のカンマ区切りリストを返します。

      //===================================================================
      // Helper: Return CSV list of relevant currencies for symbol
      //===================================================================
      string GetRelevantCurrencies(string symbol)
        {
      // 1. Check Manual Override
         if(StringLen(SymbolCurrencyOverride) > 0)
           {
            return SymbolCurrencyOverride;
           }
      
         string upper = symbol;
         StringToUpper(upper);
      
      
      // 2. Standard Forex Pair Logic (e.g. EURUSD -> EUR,USD)
         if(StringLen(symbol) == 6)
           {
            string baseCurr = StringSubstr(upper, 0, 3);
            string quoteCurr = StringSubstr(upper, 3, 3);
            return baseCurr + "," + quoteCurr;
           }
      
      // 3. Heuristics for Indices/Commodities
         if(StringFind(upper, "GER") != -1 || StringFind(upper, "DE40") != -1 || StringFind(upper, "DAX") != -1)
            return "EUR";
         if(StringFind(upper, "UK") != -1 || StringFind(upper, "FTSE") != -1)
            return "GBP";
         if(StringFind(upper, "US30") != -1 || StringFind(upper, "DJ") != -1)
            return "USD";
         if(StringFind(upper, "SPX") != -1 || StringFind(upper, "US500") != -1)
            return "USD";
         if(StringFind(upper, "NAS") != -1 || StringFind(upper, "US100") != -1)
            return "USD";
         if(StringFind(upper, "XAU") != -1 || StringFind(upper, "GOLD") != -1)
            return "USD";
         if(StringFind(upper, "XAG") != -1 || StringFind(upper, "SILVER") != -1)
            return "USD";
         if(StringFind(upper, "OIL") != -1 || StringFind(upper, "WTI") != -1 || StringFind(upper, "BRENT") != -1)
            return "USD";
         if(StringFind(upper, "JPN") != -1 || StringFind(upper, "NIK") != -1)
            return "JPY";
         if(StringFind(upper, "AUD") != -1)
            return "AUD";
         if(StringFind(upper, "CAD") != -1)
            return "CAD";
         if(StringFind(upper, "NZD") != -1)
            return "NZD";
         if(StringFind(upper, "CHF") != -1)
            return "CHF";
         if(StringFind(upper, "EUR") != -1)
            return "EUR";
         if(StringFind(upper, "GBP") != -1)
            return "GBP";
         if(StringFind(upper, "JPY") != -1)
            return "JPY";
      
      // Fallback: if we can't guess, return nothing (safe mode)
         return "";
        }

      この時点で、EAは現在の銘柄にとって重要な通貨を把握しています。しかし、まだニュースイベントがどの通貨に属しているのかは認識していません。

      次に、この問題を解決します。

      ニュースイベントの通貨を判定する

      経済指標カレンダーのエントリーには、「USD」や「EUR」のような通貨コードが直接保存されているわけではありません。その代わりに、各イベントは国IDに関連付けられています。イベントの通貨を取得するには、イベントに関連付けられた国情報を取得し、その国に対応する通貨を読み取る必要があります。

      MQL5ではCalendarCountryById()を使用することで、この処理を直接かつ確実に実行できます。

      このロジックはGetCurrencyFromEventDirect()というヘルパー関数にカプセル化されています。

      //===================================================================
      // Utility: Get currency from calendar event (via Country ID)
      //===================================================================
      string GetCurrencyFromEventDirect(const MqlCalendarEvent &ev)
        {
         MqlCalendarCountry country;
         if(CalendarCountryById(ev.country_id, country))
           {
            return country.currency; // Returns "USD", "EUR", etc.
           }
         return "";
        }
      

      この関数により、カレンダーイベントをUSD、EUR、GBPなどの具体的な通貨コードへ変換できます。

      ここまでで達成したこと

      このステップの終了時点で、EAは現在の銘柄に影響を与える通貨を判断できるようになり、EAはニュースイベントがどの通貨に属するかを判断できるようになります。また、関連性のないニュースイベントを無視するための基盤が整います。

      まだ取引をブロックするロジックは適用されていません。

      この段階では、システムはまだ取引に影響を与える動作はおこないません。単純に関連性を理解しているだけです。これは、インテリジェントなニュースフィルタを構築する上で重要な要素です。 

      次に、キャッシュされたカレンダーイベント(ステップ2)と、銘柄に関連する通貨(ステップ3)を組み合わせ、現在関連するイベントが有効になっているかどうかを検出します。ここで時間ウィンドウと重要度フィルタリングを作成します。

      ステップ4:関連ニュースイベントの検出と回避

      これで、経済指標カレンダーイベントの読み込みとキャッシュ処理(ステップ2)、そして取引銘柄に関連する通貨の識別(ステップ3)が可能になりました。次に、その銘柄に影響を与える関連経済ニュースが現在発生しているかどうかを判断できます。この段階でおこなうのは、EAはニュースの検出と、ニュース発表時刻前後に回避する分数の判定のみです。

      高レベルの検出ルール

      ニュースイベントが関連性を持つと判断されるのは、以下の条件をすべて満たす場合のみです。イベントがキャッシュされたカレンダーデータ内に存在すること、イベントの重要度レベルがフィルタ設定と一致すること、イベントの通貨が銘柄に関連する通貨のいずれかと一致すること、そして現在時刻が設定されたニュース前およびニュース後のウィンドウ内にあること。

      この4つの条件がすべて満たされた場合にのみ、関数はニュースが有効であることを通知します。

      カレンダーデータの安全な再読み込み

      イベントを検索する前に、カレンダーデータがまだ有効であることを確認します。

      カレンダーデータは、まだ一度も読み込まれていない場合、またはキャッシュデータが定義された間隔(例:6時間)より古くなった場合にのみ再読み込みされます。

      これにより、毎ティックごとにカレンダー関数を不必要に呼び出すことを防ぎ、同時にニュースの新しい更新を見逃すことも防ぎます。

      以下に検出関数を示します。

      //===================================================================
      // Core Detection Logic
      // Returns true if 'now' is inside a news window for the specific symbol
      //===================================================================
      bool isUpcomingNews(const string symbol)
        {
         datetime now = TimeCurrent();
      
      // A. Cache Management
         if(lastCalendarLoad == 0 || (now - lastCalendarLoad) > (datetime)CacheReloadHours * 3600)
           {
            LoadTodayCalendarEvents();
           }
      
         if(ArraySize(TodayEvents) == 0)
            return false;
      
      
      // B. Testing Simulation
         if(EnableNewsTesting)
           {
            if(TestNewsNow)
               return true;
            datetime t = StringToTime(TestNewsTime);
            if(t > 0)
              {
               datetime start = t - TestNewsDuration * 60;
               datetime end   = t + TestNewsDuration * 60;
               if(now >= start && now <= end)
                  return true;
              }
           }
      
      // C. Optimization: Prepare Currency List OUTSIDE the loop
         string relevant = GetRelevantCurrencies(symbol);
         if(StringLen(relevant) == 0)
            return false;
      
         string currencyArray[];
         int currencyCount = StringSplit(relevant, ',', currencyArray);
         for(int k = 0; k < currencyCount; k++)
           {
            StringTrimLeft(currencyArray[k]);
            StringTrimRight(currencyArray[k]);
            StringToUpper(currencyArray[k]);
           }
      
      // D. Event Scan
         for(int i = 0; i < ArraySize(TodayEvents); i++)
           {
            MqlCalendarEvent ev;
            // Retrieve event details from ID
            if(!CalendarEventById(TodayEvents[i].event_id, ev))
               continue;
      
            // 1. Importance Filter
            bool okImportance = false;
            switch(NewsImportanceMode)
              {
               case NEWS_HIGH_ONLY:
                  okImportance = (ev.importance == CALENDAR_IMPORTANCE_HIGH);
                  break;
               case NEWS_MODERATE_ONLY:
                  okImportance = (ev.importance == CALENDAR_IMPORTANCE_MODERATE);
                  break;
               case NEWS_HIGH_AND_MODERATE:
                  okImportance = (ev.importance == CALENDAR_IMPORTANCE_HIGH || ev.importance == CALENDAR_IMPORTANCE_MODERATE);
                  break;
              }
            if(!okImportance)
               continue;
      
            // 2. Currency Relevance Filter
            string eventCurrency = GetCurrencyFromEventDirect(ev);
            if(StringLen(eventCurrency) == 0)
               continue;
      
            bool affect = false;
            for(int j = 0; j < currencyCount; j++)
              {
               if(eventCurrency == currencyArray[j])
                 {
                  affect = true;
                  break;
                 }
              }
            if(!affect)
               continue;
      
            // 3. Time Window Check
            datetime eventTime = TodayEvents[i].time;
            datetime windowStart = eventTime - (NewsMinutesBefore * 60);
            datetime windowEnd   = eventTime + (NewsMinutesAfter * 60);
      
            if(now >= windowStart && now <= windowEnd)
              {
               // Optional: Print once per detection to avoid log spam
               static string lastDetectedKey = "";
               string key = ev.name + "|" + TimeToString(eventTime);
               if(key != lastDetectedKey)
                 {
                  PrintFormat("NEWS: %s | Time: %s | Currency: %s", ev.name, TimeToString(eventTime), eventCurrency);
                  lastDetectedKey = key;
                 }
               return true;
              }
           }
      
         return false;
        }

      EA向けの検出処理の簡略化

      EA全体の残りの部分をクリーンで読みやすく保つために、検出ロジックをシンプルなインターフェース関数でラップします。

      これにより、検出ロジックを一元化された状態に保つことができます。

      // Wrapper for external calls
      bool IsNewsTime(string symbol)
        {
         return isUpcomingNews(symbol);
        }
      

      この段階では、ニュースフィルタを必要とするEAの他の部分で、安全に以下のような処理をおこなうことができます。

      if(IsNewsTime(_Symbol))

      {

          // ニュースを検出

      }

      ステップ5:ニュースウィンドウ中のエントリー制限

      この段階では、EAはすでに関連する経済ニュースイベントを正確に検出できる状態になっています。第1回の最後のステップでは、この検出ロジックを使用して、ニュースイベント前後に設定された時間ウィンドウ内で新規トレードエントリーをブロックします。 

      このアプローチは、プロップファームの要件と完全に一致します。プロップファームで最も一般的な制限の1つは、「高インパクトニュース中は新規ポジションを開いてはいけない」というものです。

      重要なのは、既存のポジションは一切変更されないという点です。システムは、新しい条件が有効になっている間、新規エントリーのみを防止します。

      なぜエントリーのみをブロックするのか(第1回)

      取引エントリーの制限は、最もシンプルかつ安全なニュース保護方法です。初心者にも理解しやすく、プロップファームのルールと互換性があり、既存の戦略に影響を与えません。

      本記事では新規エントリー制限のみに範囲を限定することで、意図しない副作用、取引ロジックの破損を防ぎ、後にさらに高度な動作を追加するためのクリーンな基盤を確保します。

      重要:完全なニュースフィルタに必要な入力項目

      ニュースフィルタを設定可能にし、異なる取引スタイルやプロップファーム環境で使用できるようにするため、少数の入力項目を追加します。

      これらの入力項目では、ニュースフィルタを有効にするかどうか、どのニュース重要度レベルを対象とするか、そしてニュース前後何分間トレードをブロックするかを制御します。

      //===================================================================
      // INPUTS
      //===================================================================
      input group "News Filter Configuration"
      input bool   EnableNewsFilter = false;                           // Enable Economic News Filter
      input int    NewsMinutesBefore = 5;                              // Minutes before news to restrict
      input int    NewsMinutesAfter = 5;                               // Minutes after news to restrict
      input bool   RestrictNewTradesDuringNews = true;                 // Block new trades during news window
      input bool   CloseOpenTradesBeforeHighImpactNews = false;        // Close all trades before news
      input string SymbolCurrencyOverride = "";                        // Manual currency override e.g. "USD,JPY"
      enum ENUM_NEWS_IMPORTANCE_MODE
        {
         NEWS_HIGH_ONLY = 0,
         NEWS_MODERATE_ONLY,
         NEWS_HIGH_AND_MODERATE
        };
      input ENUM_NEWS_IMPORTANCE_MODE NewsImportanceMode = NEWS_HIGH_ONLY; // Importance levels
      
      // Cache reload interval
      input int CacheReloadHours = 6;                                  // Reload calendar cache (hours)
      
      
      // News Testing helpers (Simple simulation for Part 1 demonstration)
      input group "Testing Parameters"
      input bool EnableNewsTesting = false;                             // Enable news testing simulation with Current symbol
      input string TestNewsTime = "2025.12.15 14:30:00";                // Simulated news time (yyyy.MM.dd HH:mm:ss)
      input int TestNewsDuration = 10;                                  // Simulated window length (minutes)
      input bool TestNewsNow = false;                                   // Manual immediate trigger
      

      これらの入力項目により、EAは異なるプロップファームのルール、保守的または積極的な取引スタイル、そしてブローカーごとのボラティリティ特性に簡単に適応できます。

      入力パラメータのスクリーンショット

      入力名 用途/目的
      Enable Economic News Filter EA全体のニュースフィルタを有効または無効にする
      Minutes before News to restrict  ニュースイベントの何分前からフィルタを有効にするかを設定する
      Minutes after News to restrict ニュースイベント後、何分間フィルタを有効に維持するかを設定する
      Block new trades during news window ニュースウィンドウが有効な間、新規トレードの開始をブロックする
      Close all trades before news 関連するニュースウィンドウ開始時に既存ポジションを決済する
      Manual currency override 自動検出が不十分な場合に監視する通貨を手動で指定する(自動検出が機能している場合は空欄のままにする)
      Importance level 対象とするニュース重要度レベルを選択する(高、または中、または両方)
      Reload Calendar Cache カレンダーデータを更新する頻度を制御する
      Enable News testing simulation 手動ニューストリガーのみを有効にする
      Simulated News time 過去の任意のyyyy/mm/dd hh/mm/ss形式で、シミュレーションするニュースイベント時刻を指定する
      Simulated news window length [minutes] シミュレーションされたニュースイベントの有効ウィンドウ時間を設定する
      Manual immediate trigger  テスト用にシミュレートされたニュースウィンドウを即座に発生させる

      検出ロジックを使用したトレード制限

      ステップ4ですでに検出処理が実装されているため、トレードの制限は非常に簡単になります。ニュースロジックをトレード関数内に組み込むのではなく、新規ポジションを開く前に単一のゲート条件を使用します。

      これにより、トレードロジックをクリーンで読みやすい状態に保つことができます。

      //===================================================================
      // Check if new trades are allowed (Main Filter Gate)
      //===================================================================
      bool CanOpenNewTrade(string symbol)
        {
         if(!EnableNewsFilter)
            return true;
         if(!RestrictNewTradesDuringNews)
            return true;
      
         if(IsNewsTime(symbol))
           {
            return false; // Block trade
           }
      
         return true;
        }
      

      この関数は、1つの単純な質問に答えます。

      • 今、新しいトレードを開始しても安全か?

      トレードロジックへのゲート適用

      EAが通常新規トレードを開始する場所では、先頭にこのチェックを追加するだけです。たとえば:

      if(!CanOpenNewTrade(_Symbol))
      {
         // News window active — do not open new trades
         return;
      }
      
      // Normal trade entry logic continues here

      このチェックを配置することで、トレードエントリーロジックをクリーンで一元化された状態に保つことができます。

      オプション:ニュースウィンドウ前にすべてのポジションを決済

      以下のスニペットでは、ニュース前の制限ウィンドウが開始された時点で、EAが対象銘柄のすべてのポジションを決済できます。

      このスニペットは、すでに実装されている同じ検出ロジックを使用します。

      この関数の入力項目は、上記で提供した入力項目の中にすでに含まれています。

      //===================================================================
      // CloseAllTradesForSymbol
      // Implements safe reverse iteration and CTrade closing
      //===================================================================
      void CloseAllTradesForSymbol(string symbol)
        {
      // Iterate backward to safely close multiple positions
         for(int i = PositionsTotal() - 1; i >= 0; i--)
           {
            ulong ticket = PositionGetTicket(i);
            if(ticket == 0)
               continue;
      
            if(!PositionSelectByTicket(ticket))
               continue;
      
            // Filter by symbol
            if(PositionGetString(POSITION_SYMBOL) != symbol)
               continue;
      
            // Close Logic
            bool closed = trade.PositionClose(ticket);
      
            if(closed)
              {
               PrintFormat("Closed position #%d on %s due to news.", ticket, symbol);
              }
            else
              {
               PrintFormat("Failed to close #%d. Error: %d", ticket, GetLastError());
              }
           }
        }

      トリガーロジック[onTick()内]

      //===================================================================
      // OnTick
      //===================================================================
      void OnTick()
        {
      // Static latch to ensure we don't try to close trades multiple times per window
         static bool tradesClosedForThisNewsWindow = false;
      
      // 2. Main News Logic
         if(EnableNewsFilter)
           {
            bool isNews = IsNewsTime(_Symbol);
      
            // Feature: Close open trades before news if enabled
            if(CloseOpenTradesBeforeHighImpactNews && isNews)
              {
               if(!tradesClosedForThisNewsWindow)
                 {
                  Print("News window started -> Closing open trades.");
                  CloseAllTradesForSymbol(_Symbol);
                  tradesClosedForThisNewsWindow = true; // Latch
                 }
              }
            else
               if(!isNews)
                 {
                  // Reset latch when we are out of the news window
                  tradesClosedForThisNewsWindow = false;
                 }
           }
        }

      追加される内容

      • ボラティリティが発生する前にトレードを決済します。
      • 既存の検出ロジックを使用します。
      • 各ニュースウィンドウにつき1回だけ実行されます。
      • 完全にオプションです(入力値をtrueまたはfalseに設定)。
      • プロップファーム向けに適しています。
      • 第2回では、制限されたニュースウィンドウの前後で、ストップロスとテイクプロフィットを一時的に解除・再設定する機能を実装します。これにより、戦略の優位性を維持し、早すぎる決済を回避します。

      第1回のしめくくり

      この時点で、効率的で、銘柄を認識し、プロップファームの要件に対応し、初心者にも扱いやすく、新規エントリー制限およびポジション決済に対応した、実用的なニュースフィルタを構築しました。

      第1回は、それ単体で完全に機能します。


      結論

      本記事では、MQL5で完全かつ実用的なニュースフィルタシステムを構築しました。基本原理から始め、実践的なソリューションへと発展させています。

      ハードコードされた時間設定やWebリクエストベースのニュースブロックに依存するのではなく、このシステムはMQL5経済指標カレンダーを中心に設計されています。そして、パフォーマンス、関連性、明確性に細心の注意を払っています。カレンダーイベントは効率的に読み込まれ、論理的にキャッシュされ、重要度によってフィルタリングされ、実際に取引対象銘柄へ影響を与える通貨とのみ照合されます。

      ニュース検出とトレード制御を分離することで、ロジックは理解しやすく、テストしやすく、拡張しやすい構造になっています。最終的な結果は、設定可能なニュース前後のウィンドウ期間中に新規トレードを防止する、クリーンなエントリー制限メカニズムです。また、ニュースによるボラティリティが始まる前に保有ポジションを決済するオプションルールも備えています。

      このアプローチは、特にプロップファームの取引環境に適しています。プロップファームで最も一般的な要件は、制限されたニュース期間中に新規トレードを開始しないことだからです。保有ポジションを変更したり、トレード管理に干渉したりすることなく動作します。そして、これが第2回でニュース制限期間中のみストップを一時的に解除する機能に取り組む理由です。これにより、ニュース発表時にストップがヒットしたり変更されたりすることを防ぎ、ルール違反につながる可能性を回避します。また、早期決済によって戦略の流れが崩れることも防ぎます。全体として、このシステムは戦略に依存しないまま、コンプライアンスと保護機能を提供します。

      さらに重要なのは、この記事で実装したすべての内容は、それ単体で完全に利用可能であるということです。第1回では、どのEAにも組み込むことができ、ニュースに基づく取引制限をすぐに適用できる、完全なニュースフィルタを提供しています。

      次回は、この基盤を発展させ、ニュースイベント中のより高度な動作、例えばトレード管理制御や追加の保護メカニズムについて解説します。

      ここで紹介したシステムは、すでに中核となる問題をクリーンな方法で解決しています。


      MetaQuotes Ltdにより英語から翻訳されました。
      元の記事: https://www.mql5.com/en/articles/21235

      添付されたファイル |
      最後のコメント | ディスカッションに移動 (8)
      Solomon Anietie Sunday
      Solomon Anietie Sunday | 20 2月 2026 において 22:26
      Vladislav Boyko #:
      この記事に関する質問は、プライベートメッセージではなく、こちらで回答してください。このスレッドはまさにそのためのものです。

      もちろん、彼は最初にプライベートメッセージで尋ねてきたので、私もそこで返信しようと思っていました。

      「エキスパート」タブのデバッグレポート

      Jamshidさんは、この画像に印刷・表示されている今後のニュースイベントが、Forex Factoryの現在のニュースイベントと一致していない理由を尋ねました。

      私の回答:

      その出力は、私がこの記事を書いた時点での最新の経済ニュースカレンダーでした。 デバッグ目的で、キャッシュされたカレンダー内のニュースをいくつか表示させましたこれは、私のキャッシュされたカレンダーが正しく機能しているかを確認するためです

      もしあなたがコードをコンパイルし、チャートにエキスパートアドバイザーを配置すれば、エキスパートタブに現在予定されているニュースイベントが約5件表示され、キャッシュされたイベントが正常に機能していることが確認できるはずです

      もちろん、私がこの記事を書いた時点やそれ以降の時点では、表示内容は異なります!

      表示が異なるもう一つの理由は、Forex FactoryとMQL5が完全に同一ではないことです。重要度のレベルに若干の違いがあり、MQL5の方が影響度の低いニュースや中程度のニュースの数が少し多い場合があります。
      Retail Trading Realities LTD
      Philip Kym Sang Nelson | 7 4月 2026 において 11:54

      素晴らしい仕事ですね。

      素晴らしい一連の記事ですね。

      Evalのプロップファームで取引を行うすべてのアルゴリズムトレーダーが、エキスパートアドバイザー にまさに必要としている内容です。

      これを使っている皆さんには、せめてコメントを残してほしいと思います。

      Enrique Dangeroux
      Enrique Dangeroux | 7 4月 2026 において 12:12
      Solomon Anietie Sunday #:
      状況が異なるもう一つの理由は、Forex FactoryとMQL5が完全に同一ではなく、重要度のレベルに若干の違いがあり、MQL5の方が影響度の低いニュースや中程度のニュースの数が少し多い場合があるからです

      取引、自動取引システム、および取引戦略のテストに関するフォーラム

      ライブラリ:カレンダー

      fxsaber、2024年3月21日 09:49

      残念ながら、現在のカレンダーは信頼できません。


      イベントリストから重要な CHF 関連のニュースが省略されており、それに対する市場の反応も記載されていません。


      そして、こちらはそのニュースが掲載されている別のカレンダーです。



      Solomon Anietie Sunday
      Solomon Anietie Sunday | 19 4月 2026 において 21:54
      Philip Kym Sang Nelson エキスパートアドバイザー に必要とするものです。

      これを使っている皆さんには、せめてコメントを残してほしいと思います。

      へへ、いいですよ。彼らのトレードに何らかの影響を与えられるなら、むしろ嬉しいです。
      ニュース発表前後の取引制限、ブロック、あるいは変更機能は、間違いなくプロップトレーダーが欲しがる機能です。
      そもそも、私がこのニュースフィルターを作ったのは、まさにその目的のためでした。
      Solomon Anietie Sunday
      Solomon Anietie Sunday | 19 4月 2026 において 21:55
      Enrique Dangeroux #:

      その通りですね。MQL5の経済カレンダーは完全に頼りにはできませんし、これは他のカレンダーにも言えることです。より確実な方法は、2つ以上のカレンダー情報源を組み合わせて照合することです。しかし、この記事ではMQL5のネイティブカレンダーのみを対象としています。
      MetaQuote社がこの点に注目し、MQL5カレンダーの情報源の改善に取り組んでくれることを期待しています。
      Market Memory Zonesインジケーターの自動化:価格が戻りやすい領域 Market Memory Zonesインジケーターの自動化:価格が戻りやすい領域
      Market Memory Zonesをチャート上の概念から、完全なMQL5エキスパートアドバイザー(EA)へ発展させます。ディスプレイスメントゾーン、市場構造転換 (CHoCH)ゾーン、流動性スイープゾーンの検出をATRおよびローソク足構造フィルタによって自動化し、下位足での確認を適用するとともに、リスクベースのポジションサイズ計算、動的なストップロス(SL)、そして市場構造に基づくテイクプロフィット(TP)を実装します。また、検出、エントリー、トレード管理、可視化のコード構成に加え、バックテスト結果も簡潔に確認します。
      MQL5取引ツール(第18回):向き制御対応の角丸吹き出し MQL5取引ツール(第18回):向き制御対応の角丸吹き出し
      角丸長方形と三角形のポインタを組み合わせることで、MQL5における角丸の吹き出しを作成する方法を紹介します。また、ポインタの向きを上、下、左、右に制御する方法についても解説します。さらに、形状のジオメトリの事前計算、スーパーサンプリングによる塗りつぶし、頂点部の丸みを持たせた円弧処理、および継ぎ目を滑らかに接続するための拡張比を用いた分割枠線の描画について詳しく説明します。サイズ、角丸半径、色、不透明度、線の太さなどを自由に設定できるコードを提供しており、トレーディングインターフェースのアラートやツールチップなどにそのまま利用できます。
      MQL5入門(第40回):MQL5におけるファイル処理入門(II) MQL5入門(第40回):MQL5におけるファイル処理入門(II)
      MQL5で、指定した期間の口座履歴を読み取り、構造化されたレコードをファイルに書き込むことで、CSV形式の取引ジャーナルを作成します。約定数の取得、チケット番号の取得、銘柄や注文タイプの判別、そしてエントリー(ロット、時間、価格、SL/TP)および決済(時間、価格、利益、結果)データの取得方法を、動的配列を使用して解説します。その結果、分析やレポート作成に適した、整理された永続的なログを作成できます。
      流動性狩りの最適化:流動性狩りと市場構造転換の違いを見極める 流動性狩りの最適化:流動性狩りと市場構造転換の違いを見極める
      流動性狩り後のトレードセットアップをどのように活用するかを明確に解説することを目的とした、トレンドフォロー型EAに関する記事です。本記事では、流動性狩りおよび流動性一掃をトレードのエントリー条件として最適に活用したいトレーダー向けに特別に設計されたEAについて詳しく解説します。また、流動性狩りと市場構造転換を正しく区別する方法、それぞれが発生した際にどのように検証し活用すべきかについても取り上げます。これにより、両者を混同することで生じる損失を軽減することを目指します。