ゴールデンクロスや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で動かすまでの手順を確認します。

  1. MetaEditorで新規EA「ATRVolatilityFilterEA」を作成し、上のコードを貼り付けてコンパイルする
  2. ストラテジーテスターでVolatilityRatioMinを0(フィルター無効)にして一度バックテストする
  3. 次にVolatilityRatioMinを1.0程度に設定し、同じ期間で再度バックテストする
  4. 両者の取引回数・損益・最大ドローダウンを比較し、フィルターの効果を確認する
  5. 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行追加するだけで、

組み込める設計にしています。

関連記事