既刊のスプレッド記録インジケーターは、
実測スプレッドを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で動かすまでの手順です。
- MetaEditorで新規EA「SpreadFilterEA」を作成し、上のコードを貼り付けてコンパイルする
- エラー・警告が0件であることを確認する
- デモ口座のチャートに適用し、起動直後はInpFallbackMaxSpreadで判定されることを確認する
- InpHistorySize分の時間が経過したあと、平均スプレッド基準に切り替わることを確認する
- InpSpreadMultiplierを一時的に小さくし、エントリーが見送られやすくなることを確認する
よくあるエラーと注意点
実装・運用でつまずきやすいポイントをまとめます。
起動直後はフォールバック値で判定される
InpHistorySize分のサンプルが揃うまでは、
平均スプレッドではなくInpFallbackMaxSpreadという固定値で判定します。
通貨ペアの普段のスプレッドより低すぎる値を設定すると、
起動直後にエントリーできない状態が続くため、
事前に既刊のスプレッド記録インジケーターで実測値を確認しておくことをおすすめします。
経済指標発表直後は固定ポイントでは対応しきれない
経済指標発表の直後はスプレッドが数倍に広がることがあり、
固定ポイントの基準だとその通貨ペアの「平常時」の設定が難しくなります。
移動平均基準にすることで、
通貨ペアごとに手動調整しなくても、
ある程度は自動的に「今は普段より広い」と判定できます。
端末再起動でスプレッド履歴は失われる
g_spreadHistoryはEAの変数のため、
端末を再起動するとリセットされ、
再びInpHistorySize分のウォームアップが必要になります。
既刊のスプレッド関連記事との使い分け
似た目的の記事が複数あるため、使い分けを整理します。
| 記事 | 発注 | 役割 |
|---|---|---|
| 本記事 | MAクロスで自動発注 | スプレッドが広いときは新規エントリーを見送る |
| スプレッド記録インジケーター(既刊) | しない | 実測スプレッドをCSVに記録・集計する |
| ATRボラティリティフィルターEA(既刊) | 自動発注 | 価格の変動幅が小さいときはエントリーを見送る |
よくある質問
スプレッドフィルターEAについて、よくある疑問に回答します。
自分のエントリーロジックと組み合わせられますか?
組み合わせられます。
IsSpreadOK()関数は単独で呼び出せるため、
OnTickの中のMAクロス判定を、
自分のエントリーロジックに置き換えれば、
スプレッドフィルターだけを流用できます。
InpSpreadMultiplierはいくつに設定すればいいですか?
まずは既定値の1.5倍から始め、
既刊のスプレッド記録インジケーターでログを取りながら、
エントリーが見送られる頻度を見て調整することをおすすめします。
保有中のポジションの決済もこのフィルターで止まりますか?
止まりません。
このコードはOnTickの新規発注の判定にだけIsSpreadOK()を使い、
決済処理には使っていません。
既存ポジションの損切り・利確は、
スプレッドの状態に関係なく通常どおり実行されます。
【MQL4】スプレッドが通常より広がっているときだけエントリーを見送るフィルターEAの作り方:まとめ
既刊のスプレッド記録インジケーターは実測値を記録するだけで、
発注を止める機能はありませんでした。
直近の実測スプレッドの平均を基準に、
今のスプレッドが通常より広い間だけ新規エントリーを見送ることで、
通貨ペアや時間帯が変わっても同じコードで使えるフィルターになります。
決済は妨げず新規エントリーだけを見送る設計にすることで、
損切りが遅れるリスクも避けられます。
コメント