既刊のスプレッド記録インジケーターは、

実測スプレッドをCSVに記録して、

その日の最小・最大・平均を表示するだけでした。

記録はできても、

実際にスプレッドが広がっているときに、

エントリーを止める機能はありませんでした。

この記事では、直近の実測スプレッドの平均を基準にして、

今のスプレッドが通常より広がっている間はエントリーを見送る、

フィルターEAの作り方を解説します。

この記事で分かること
・既刊のスプレッド記録インジケーターとの役割の違い
・固定ポイントではなく移動平均基準でスプレッドを判定する設計
・既存ポジションの決済は妨げず新規エントリーだけを見送る設計
・実際に動くコード全体

設計思想: なぜ移動平均基準のスプレッド判定が必要か

まず、このEAがどんな問題を解決するのかを整理します。

既刊のスプレッド記録インジケーターとの関係

既刊のスプレッド記録インジケーターのFAQでは、

「記録したCSVをEAの発注条件に組み込める」と予告していました。

本記事はその回答として、

CSVを読み込む代わりにEA自身が直近のスプレッドを配列で保持し、

直接判定する設計にしました。

既刊のATRボラティリティフィルターEAとの違い

既刊のATRボラティリティフィルターEAは、

値動きの大きさ(ボラティリティ)を判定基準にしていました。

記事 判定対象 意味
本記事 スプレッド 今発注すると、どれだけ余分なコストがかかるか
ATRボラティリティフィルターEA(既刊) ATR(価格変動幅) 今は値動きが大きい相場か

両者は似て見えますが、

値動きが小さくてもスプレッドだけが広がる場面(閑散時間帯・指標発表直前)もあるため、

別の判定軸として使い分けられます。

スプレッドの取得と移動平均の計算

固定ポイントではなく、

直近の実測値を基準にする設計を組み立てます。

MarketInfo(MODE_SPREAD)で現在のスプレッドを取得する

int currentSpread = (int)MarketInfo(_Symbol, MODE_SPREAD);

既刊のスプレッド記録インジケーターと同じ関数です。

固定ポイントで判定しない理由

固定ポイント判定の弱点
・通貨ペアごとに「普段のスプレッド」が大きく異なる
・同じ20ポイントでも、USDJPYでは広い・ゴールド系では普通ということがある
・固定値だと通貨ペアを変えるたびに調整が必要になる

直近N分のスプレッドを配列で保持する

g_spreadHistory[g_historyIndex] = currentSpread;
g_historyIndex = (g_historyIndex + 1) % InpHistorySize;
if(g_historyCount < InpHistorySize)
   g_historyCount++;

1分ごとに1件、

直近InpHistorySize分のスプレッドを循環配列に保存します。

エントリー条件とスプレッドフィルターの組み合わせ

既存のシンプルなエントリー条件に、

スプレッドフィルターを追加します。

移動平均の何倍かで判定する

bool IsSpreadOK()
  {
   int currentSpread = (int)MarketInfo(_Symbol, MODE_SPREAD);

   if(g_historyCount < InpHistorySize)
      return(currentSpread <= InpFallbackMaxSpread);

   double sum = 0;
   for(int i = 0; i < g_historyCount; i++)
      sum += g_spreadHistory[i];
   double avgSpread = sum / g_historyCount;

   return(currentSpread <= avgSpread * InpSpreadMultiplier);
  }

データがまだInpHistorySize分たまっていない起動直後は、

平均値が信頼できないため、

InpFallbackMaxSpreadという固定の上限値で代用します。

決済は妨げず新規エントリーだけを見送る

注意
・このフィルターはOrderSend(新規発注)の直前だけに適用する
・OrderClose(決済)の前にはこのフィルターを使わない
・スプレッドが広い状態でも、損切り・決済は必ず実行できるようにする

スプレッドが広がっている間に決済まで止めてしまうと、

損切りが遅れて被害が大きくなるため、

このフィルターは新規発注の判定だけに使います。

実際に動くコード全体

移動平均基準のスプレッドフィルターと、

シンプルな移動平均クロスのエントリー条件を組み合わせた、

実際にMetaEditorでコンパイルできるEAです。

先に注意
・このコードは買い方向・売り方向とも実装済みです
・エントリー条件はMAクロスで固定(本質はスプレッドフィルターの実装)
・デモ口座で十分に検証してから使ってください
//+------------------------------------------------------------------+
//|                                        SpreadFilterEA.mq4         |
//|                     Copyright 2026, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link      "https://creator.fx-ea-system-project.com/"
#property version   "1.00"
#property strict

input int    InpFastMAPeriod     = 5;     //短期移動平均の期間
input int    InpSlowMAPeriod     = 20;    //長期移動平均の期間
input int    InpHistorySize      = 30;    //移動平均に使う直近サンプル数(分)
input double InpSpreadMultiplier = 1.5;   //平均スプレッドの何倍までエントリーを許可するか
input int    InpFallbackMaxSpread= 30;    //サンプルが揃うまでの固定上限(ポイント)
input double InpLots             = 0.01;
input int    InpStopLossPoints   = 300;
input int    InpMagicNumber      = 20261029;

double   g_spreadHistory[30];
int      g_historyCount  = 0;
int      g_historyIndex  = 0;
datetime g_lastRecordedMinute = 0;

void RecordSpread()
  {
   int currentSpread = (int)MarketInfo(_Symbol, MODE_SPREAD);
   g_spreadHistory[g_historyIndex] = currentSpread;
   g_historyIndex = (g_historyIndex + 1) % InpHistorySize;
   if(g_historyCount < InpHistorySize)
      g_historyCount++;
  }

bool IsSpreadOK()
  {
   int currentSpread = (int)MarketInfo(_Symbol, MODE_SPREAD);

   if(g_historyCount < InpHistorySize)
      return(currentSpread <= InpFallbackMaxSpread);

   double sum = 0;
   for(int i = 0; i < g_historyCount; i++)
      sum += g_spreadHistory[i];
   double avgSpread = sum / g_historyCount;

   return(currentSpread <= avgSpread * InpSpreadMultiplier);
  }

bool HasOpenPosition()
  {
   for(int i = 0; i < OrdersTotal(); i++)
     {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
         if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagicNumber)
            return(true);
        }
     }
   return(false);
  }

int OnInit()
  {
   ArrayInitialize(g_spreadHistory, 0);
   return(INIT_SUCCEEDED);
  }

void OnTick()
  {
   datetime currentMinute = iTime(_Symbol, PERIOD_M1, 0);
   if(currentMinute != g_lastRecordedMinute)
     {
      g_lastRecordedMinute = currentMinute;
      RecordSpread();
     }

   if(HasOpenPosition())
      return;

   if(!IsSpreadOK())
     {
      Comment("スプレッドが広いためエントリーを見送り中: ",
              (int)MarketInfo(_Symbol, MODE_SPREAD), "pt");
      return;
     }

   double fastMA = iMA(_Symbol, PERIOD_CURRENT, InpFastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
   double slowMA = iMA(_Symbol, PERIOD_CURRENT, InpSlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
   double fastMAPrev = iMA(_Symbol, PERIOD_CURRENT, InpFastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
   double slowMAPrev = iMA(_Symbol, PERIOD_CURRENT, InpSlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);

   if(fastMAPrev <= slowMAPrev && fastMA > slowMA)
     {
      double sl = Ask - InpStopLossPoints * Point;
      OrderSend(_Symbol, OP_BUY, InpLots, Ask, 3, sl, 0,
                "SpreadFilterEA", InpMagicNumber, 0, clrBlue);
     }
   else if(fastMAPrev >= slowMAPrev && fastMA < slowMA)
     {
      double sl = Bid + InpStopLossPoints * Point;
      OrderSend(_Symbol, OP_SELL, InpLots, Bid, 3, sl, 0,
                "SpreadFilterEA", InpMagicNumber, 0, clrRed);
     }
  }

MetaEditorでのコンパイル・実行手順

実際にMT4で動かすまでの手順です。

  1. MetaEditorで新規EA「SpreadFilterEA」を作成し、上のコードを貼り付けてコンパイルする
  2. エラー・警告が0件であることを確認する
  3. デモ口座のチャートに適用し、起動直後はInpFallbackMaxSpreadで判定されることを確認する
  4. InpHistorySize分の時間が経過したあと、平均スプレッド基準に切り替わることを確認する
  5. InpSpreadMultiplierを一時的に小さくし、エントリーが見送られやすくなることを確認する

よくあるエラーと注意点

実装・運用でつまずきやすいポイントをまとめます。

起動直後はフォールバック値で判定される

InpHistorySize分のサンプルが揃うまでは、

平均スプレッドではなくInpFallbackMaxSpreadという固定値で判定します。

通貨ペアの普段のスプレッドより低すぎる値を設定すると、

起動直後にエントリーできない状態が続くため、

事前に既刊のスプレッド記録インジケーターで実測値を確認しておくことをおすすめします。

経済指標発表直後は固定ポイントでは対応しきれない

経済指標発表の直後はスプレッドが数倍に広がることがあり、

固定ポイントの基準だとその通貨ペアの「平常時」の設定が難しくなります。

移動平均基準にすることで、

通貨ペアごとに手動調整しなくても、

ある程度は自動的に「今は普段より広い」と判定できます。

端末再起動でスプレッド履歴は失われる

g_spreadHistoryはEAの変数のため、

端末を再起動するとリセットされ、

再びInpHistorySize分のウォームアップが必要になります。

既刊のスプレッド関連記事との使い分け

似た目的の記事が複数あるため、使い分けを整理します。

記事 発注 役割
本記事 MAクロスで自動発注 スプレッドが広いときは新規エントリーを見送る
スプレッド記録インジケーター(既刊) しない 実測スプレッドをCSVに記録・集計する
ATRボラティリティフィルターEA(既刊) 自動発注 価格の変動幅が小さいときはエントリーを見送る

よくある質問

スプレッドフィルターEAについて、よくある疑問に回答します。

自分のエントリーロジックと組み合わせられますか?

組み合わせられます。

IsSpreadOK()関数は単独で呼び出せるため、

OnTickの中のMAクロス判定を、

自分のエントリーロジックに置き換えれば、

スプレッドフィルターだけを流用できます。

InpSpreadMultiplierはいくつに設定すればいいですか?

まずは既定値の1.5倍から始め、

既刊のスプレッド記録インジケーターでログを取りながら、

エントリーが見送られる頻度を見て調整することをおすすめします。

保有中のポジションの決済もこのフィルターで止まりますか?

止まりません。

このコードはOnTickの新規発注の判定にだけIsSpreadOK()を使い、

決済処理には使っていません。

既存ポジションの損切り・利確は、

スプレッドの状態に関係なく通常どおり実行されます。

【MQL4】スプレッドが通常より広がっているときだけエントリーを見送るフィルターEAの作り方:まとめ

既刊のスプレッド記録インジケーターは実測値を記録するだけで、

発注を止める機能はありませんでした。

直近の実測スプレッドの平均を基準に、

今のスプレッドが通常より広い間だけ新規エントリーを見送ることで、

通貨ペアや時間帯が変わっても同じコードで使えるフィルターになります。

決済は妨げず新規エントリーだけを見送る設計にすることで、

損切りが遅れるリスクも避けられます。

関連記事