ゴールデンクロスやRSIの条件が揃っても、
値動きがほとんど無い相場でエントリーして、
スプレッド分だけ損をした経験はありませんか。
ATRの「今の値動きの大きさ」を、
過去の平均と比べた比率で判定すれば、
動きにくい相場でのエントリーだけを、
機械的に見送るフィルターが作れます。
この記事では、既刊のATR比率インジケーターの考え方を、
実際にエントリーをブロックするEAのフィルター関数として、
実装する方法を解説します。
・固定pipsでなく比率でボラティリティを判定する理由
・iATRとiMAOnArrayを使ったフィルター関数の作り方
・エントリーロジックへの組み込み方
・しきい値を決める際の注意点
ボラティリティが低い相場でエントリーすると何が起きるか
まず、なぜこのフィルターが必要なのかを確認します。
スプレッド負けしやすい
値動きが小さい相場では、
利益の幅そのものがスプレッド分と大差なくなり、
勝ってもコストでほとんど相殺されてしまいます。
だましのレンジブレイクに引っかかりやすい
値幅が小さい相場では、
わずかな値動きでも「ブレイクした」と、
誤判定しやすく、すぐに逆行する傾向があります。
既刊のATR比率インジケーターとの関係
既刊記事では、ATR比率をチャート上に、
ヒストグラムとして表示するところまで扱いました。
本記事では、同じ判定ロジックを使って、
実際にエントリーを止める関数まで発展させます。
ATRの比率で「今は動きやすい相場か」を判定する考え方
判定ロジックの設計方針を確認します。
固定pips閾値ではなく相対値で判定する理由
・固定pipsの閾値は通貨ペアや時間帯ごとに調整が必要
・比率(現在÷平均)なら同じ設定を使い回しやすい
直近ATRと平均ATRの比率という設計
直近のATRを、
長期平均のATRで割った値を「ATR比率」とします。
比率が1.0を超えていれば、
平均より値動きが大きい相場と判断できます。
比率のしきい値をどう決めるか
本記事ではVolatilityRatioMinという、
入力パラメーターとしてしきい値を外出しします。
通貨ペアや時間足によって適切な値は変わるため、
バックテストで調整することを前提にした設計です。
iATRとiMAOnArrayを使ったフィルター関数の実装
実際にフィルター関数を組み立てます。
iATRでATRの値を配列に保存する
for(int i = 0; i < ATRAvgPeriod; i++) atrArray[i] = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod, i);
shift(第4引数)を0からATRAvgPeriod-1まで変えながら、
過去分のATR値を配列に保存していきます。
ATR配列の平均をiMAOnArrayで計算する
double avgATR = iMAOnArray(atrArray, 0, ATRAvgPeriod, 0, MODE_SMA, 0);
第2引数の0は「配列全体を対象にする」という意味です。
ArraySetAsSeriesで配列を時系列順にしておくことで、
shift0が「直近の平均」を指すようになります。
IsVolatilityOK関数として切り出す設計
判定ロジックをIsVolatilityOK()という、
1つの関数にまとめておくことで、
OnTick側のコードが条件だけで読みやすくなります。
エントリーロジックへのフィルター組み込み
フィルター関数を実際のエントリー条件に組み込みます。
移動平均クロスの基本エントリー条件
本記事では、短期MAと長期MAの、
ゴールデンクロス・デッドクロスを、
シンプルなエントリー条件として採用します。
フィルター関数をOnTick内で呼び出す
if(!IsVolatilityOK()) return;
OnTickの冒頭でこの判定を行い、
条件を満たさない場合はエントリー処理に進まず、
そのままreturnする設計にします。
フィルターでブロックした回数をログに残す
blockedCountという変数を用意し、
フィルターで見送った回数を記録しておくと、
バックテスト後にOnDeinitで件数を確認でき、
しきい値が厳しすぎないかの判断材料になります。
実際に動くコード全体
ここまでの要素をまとめた、実際に動くEAです。
//+------------------------------------------------------------------+
//| ATRVolatilityFilterEA.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 FastMAPeriod = 5;
input int SlowMAPeriod = 20;
input int ATRPeriod = 14;
input int ATRAvgPeriod = 50;
input double VolatilityRatioMin = 1.0;
input double FixedLots = 0.1;
input int MagicNumber = 20261005;
int blockedCount = 0;
bool IsVolatilityOK()
{
double atrArray[];
ArrayResize(atrArray, ATRAvgPeriod);
ArraySetAsSeries(atrArray, true);
for(int i = 0; i < ATRAvgPeriod; i++)
atrArray[i] = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod, i);
double currentATR = atrArray[0];
double avgATR = iMAOnArray(atrArray, 0, ATRAvgPeriod, 0, MODE_SMA, 0);
if(avgATR == 0)
return(false);
double ratio = currentATR / avgATR;
if(ratio < VolatilityRatioMin)
{
blockedCount++;
return(false);
}
return(true);
}
bool HasOpenPosition()
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == _Symbol && OrderMagicNumber() == MagicNumber)
return(true);
}
}
return(false);
}
void OnTick()
{
if(HasOpenPosition())
return;
if(!IsVolatilityOK())
return;
double fastMA = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double slowMA = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double fastMAPrev = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double slowMAPrev = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
if(fastMAPrev <= slowMAPrev && fastMA > slowMA)
{
OrderSend(_Symbol, OP_BUY, FixedLots, Ask, 3, 0, 0, "ATRFilterEA", MagicNumber, 0, clrBlue);
}
else if(fastMAPrev >= slowMAPrev && fastMA < slowMA)
{
OrderSend(_Symbol, OP_SELL, FixedLots, Bid, 3, 0, 0, "ATRFilterEA", MagicNumber, 0, clrRed);
}
}
void OnDeinit(const int reason)
{
Print("フィルターでブロックした回数: ", blockedCount);
}
MetaEditorでのコンパイル・実行とバックテスト手順
実際にMT4で動かすまでの手順を確認します。
- MetaEditorで新規EA「ATRVolatilityFilterEA」を作成し、上のコードを貼り付けてコンパイルする
- ストラテジーテスターでVolatilityRatioMinを0(フィルター無効)にして一度バックテストする
- 次にVolatilityRatioMinを1.0程度に設定し、同じ期間で再度バックテストする
- 両者の取引回数・損益・最大ドローダウンを比較し、フィルターの効果を確認する
- OnDeinitのPrint出力でブロック回数が極端に多すぎないか確認する
よくあるエラーと注意点
実装でつまずきやすいポイントをまとめます。
ATR配列の添字(shift)のずれ
ArraySetAsSeriesを呼び忘れると、
配列のindex0が「直近」ではなく「最古」になり、
比率の計算結果が実態と逆転してしまいます。
しきい値を固定しすぎて全くエントリーしない
・VolatilityRatioMinを高く設定しすぎると取引機会が激減する
・まずは0に近い値から少しずつ上げて様子を見る
MQL4とMQL5の違い
このコードはMQL4のiATR・iMAOnArrayを使った書き方です。
MQL5ではiATRはハンドルを返す方式に変わり、
CopyBufferで値を取得する設計に変更する必要があります。
よくある質問
ATRボラティリティフィルターについて、よくある疑問に回答します。
ATRPeriodとATRAvgPeriodは同じ値でよいですか?
別の値を推奨します。
ATRPeriod(例: 14)は「今の値動き」を、
ATRAvgPeriod(例: 50)は「普段の水準」を測るためのもので、
目的が異なるため期間を分けるのが基本です。
既存のEAに後からこのフィルターを追加できますか?
できます。
IsVolatilityOK()関数をそのまま移植し、
既存のエントリー処理の先頭にif(!IsVolatilityOK()) return;
という1行を追加するだけで組み込めます。
インジケーター版とEA版、どちらを先に使うべきですか?
まずは既刊のインジケーター版をチャートに表示し、
目視でATR比率の動きに慣れてから、
本記事のEA版でしきい値を調整することをおすすめします。
【MQL】ATRの比率でボラティリティが低い相場のエントリーを見送るフィルターEAの作り方について解説してみた:まとめ
ATRの比率でボラティリティを判定すれば、
固定pipsの閾値を通貨ペアごとに調整しなくても、
動きにくい相場でのエントリーを機械的に見送れます。
既存のエントリーロジックにも、
IsVolatilityOK()関数を1行追加するだけで、
組み込める設計にしています。
コメント