既刊のiStdDev(標準偏差)インジケーターの記事では、標準偏差が「普段より急に広がった瞬間」

を検知する設計で解説しました。

この記事では同じiStdDevの比率を使い、値動きが「収縮(スクイーズ)」

したあとに広がった瞬間を、ブレイクアウトのエントリー条件にするEAを作ります。

この記事で分かること
・既刊ATRボラティリティフィルターEAとの役割の違い
・標準偏差の「収縮」を検知して値幅を記録するロジック
・収縮後のブレイクアウトを発注条件に変換する方法
・損切り必須・ポジション重複防止の書き方
・実際に動くEAのコード全体

なぜ「収縮」からの拡大を狙うのか

まず、既刊のボラティリティ系EAと本記事の違いを整理します。

既刊iStdDevインジケーターのおさらい

既刊インジケーターは、直近の平均的な標準偏差と比べて、現在値が何倍かという「ratio」

を常時表示する設計でした。

このratioが一定の倍率を超えたら「広がっている」と判定していました。

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

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

ボラティリティが低いときに新規エントリーを「見送る」フィルターでした。

本記事は逆に、ボラティリティが低い状態(収縮)そのものを、エントリーの「準備段階」

として積極的に使います。

EA 低ボラティリティの扱い 役割
既刊ATRボラティリティフィルターEA エントリーを見送る理由 他の判定ロジックへのフィルター
本記事(iStdDevスクイーズEA) エントリーの準備段階として記録する 単独で発注判定まで行う起点

同じ「ボラティリティが低い」という状態でも、使い方の発想が異なります。

スクイーズ→ブレイクアウトという設計思想

補足
・値動きが収縮している間は、エネルギーを溜めている状態とみなす
・収縮中の値幅(高値・安値)を記録する
・そこを抜けた瞬間を「解放」のタイミングとみなす
・いわゆる「スクイーズ」と呼ばれる考え方をiStdDevで実装したもの

スクイーズ判定とブレイクアウト判定のロジック

発注条件の土台になる、2段階の判定ロジックを確認します。

iStdDev関数のシグネチャ(既刊記事で確認済み)

double iStdDev(
   string symbol,        // 通貨ペア名(NULLで現在のチャート)
   int    timeframe,     // 時間足(0で現在の時間足)
   int    ma_period,     // 移動平均の期間
   int    ma_shift,      // 移動平均のシフト
   int    ma_method,     // 移動平均の計算方法(MODE_SMA等)
   int    applied_price, // 適用価格(PRICE_CLOSE等)
   int    shift           // 何本前のバーの値を取得するか
);

引数の構成は既刊istddev-mt4.mdが、

2026-10-30にMQL4公式リファレンスで直接確認済みのものと同じです。

直近N本の平均との比率でスクイーズを判定する関数

bool IsSqueeze(int shift)
  {
   double current = iStdDev(_Symbol, 0, InpStdDevPeriod, 0, MODE_SMA, PRICE_CLOSE, shift);

   double sum = 0;
   for(int j = shift + 1; j <= shift + InpAvgLookback; j++)
      sum += iStdDev(_Symbol, 0, InpStdDevPeriod, 0, MODE_SMA, PRICE_CLOSE, j);
   double avg = sum / InpAvgLookback;

   if(avg <= 0)
      return(false);

   double ratio = current / avg;
   return(ratio < InpSqueezeRatio);
  }

既刊インジケーターと同じ比率の計算式を使い、基準値の何倍かではなく「何倍未満か」

でスクイーズを判定します。

スクイーズ中の高値・安値を記録してブレイクアウトを検知

スクイーズが続いている間、その期間の高値・安値を記録しておきます。

スクイーズが終わった瞬間に、その範囲をどちらに抜けたかで方向を決めます。

発注条件・損切り・ポジション重複の防止

判定だけでは発注にならないため、安全策を組み込みます。

スクイーズ終了とブレイク方向を同時に確認する

static bool   squeezeActive  = false;
static int    squeezeBars    = 0;
static double squeezeHigh    = 0;
static double squeezeLow     = 0;
static int    breakoutSignal = 0;

void UpdateSqueezeState()
  {
   breakoutSignal = 0;

   if(IsSqueeze(1))
     {
      if(!squeezeActive)
        {
         squeezeActive = true;
         squeezeBars   = 0;
         squeezeHigh   = High[1];
         squeezeLow    = Low[1];
        }
      squeezeHigh = MathMax(squeezeHigh, High[1]);
      squeezeLow  = MathMin(squeezeLow, Low[1]);
      squeezeBars++;
      return;
     }

   if(squeezeActive && squeezeBars >= InpMinSqueezeBars)
     {
      if(Close[1] > squeezeHigh)
         breakoutSignal = 1;
      else if(Close[1] < squeezeLow)
         breakoutSignal = -1;
     }

   squeezeActive = false;
   squeezeBars   = 0;
  }

スクイーズが一定本数(InpMinSqueezeBars)以上続いたあと、

確定済みの終値がスクイーズ中の高値・安値を抜けた瞬間だけを、

シグナルとして扱います。

一瞬だけの収縮をノイズとして除外するための条件です。

損切り・利確を必ず指定する

double sl = Ask - InpStopLossPips * point;
double tp = Ask + InpTakeProfitPips * point;
OrderSend(_Symbol, OP_BUY, InpLots, Ask, 3, sl, tp, "StdDevSqueezeBreakout", InpMagic, 0, clrBlue);

損切り(`sl`)・利確(`tp`)を発注時点で必ず指定し、未設定のまま放置しません。

ポジション重複を防ぐチェック

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

同じマジックナンバー・同じ通貨ペアでポジションが既にあるかを確認し、

あれば新規エントリーをスキップします。

実際に動くスクイーズブレイクアウトEAのコード全体

ここまでの要素をまとめた、EAの全体です。

//+------------------------------------------------------------------+
//|                              StdDevSqueezeBreakoutEA.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    InpStdDevPeriod   = 20;        //標準偏差の計算期間
input int    InpAvgLookback    = 100;       //「普段」とみなす平均の参照本数
input double InpSqueezeRatio   = 0.7;       //普段の何倍未満で「収縮」と判定するか
input int    InpMinSqueezeBars = 5;         //収縮と認める最低連続本数
input double InpLots           = 0.1;       //発注ロット数
input int    InpStopLossPips   = 300;       //損切り(pips)
input int    InpTakeProfitPips = 600;       //利確(pips)
input int    InpMagic          = 20261104;  //マジックナンバー

static bool     squeezeActive  = false;
static int      squeezeBars    = 0;
static double   squeezeHigh    = 0;
static double   squeezeLow     = 0;
static int      breakoutSignal = 0;
datetime lastBarTime = 0;

bool IsSqueeze(int shift)
  {
   double current = iStdDev(_Symbol, 0, InpStdDevPeriod, 0, MODE_SMA, PRICE_CLOSE, shift);

   double sum = 0;
   for(int j = shift + 1; j <= shift + InpAvgLookback; j++)
      sum += iStdDev(_Symbol, 0, InpStdDevPeriod, 0, MODE_SMA, PRICE_CLOSE, j);
   double avg = sum / InpAvgLookback;

   if(avg <= 0)
      return(false);

   double ratio = current / avg;
   return(ratio < InpSqueezeRatio);
  }

void UpdateSqueezeState()
  {
   breakoutSignal = 0;

   if(IsSqueeze(1))
     {
      if(!squeezeActive)
        {
         squeezeActive = true;
         squeezeBars   = 0;
         squeezeHigh   = High[1];
         squeezeLow    = Low[1];
        }
      squeezeHigh = MathMax(squeezeHigh, High[1]);
      squeezeLow  = MathMin(squeezeLow, Low[1]);
      squeezeBars++;
      return;
     }

   if(squeezeActive && squeezeBars >= InpMinSqueezeBars)
     {
      if(Close[1] > squeezeHigh)
         breakoutSignal = 1;
      else if(Close[1] < squeezeLow)
         breakoutSignal = -1;
     }

   squeezeActive = false;
   squeezeBars   = 0;
  }

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

void OnTick()
  {
   if(Time[0] == lastBarTime)
      return;
   lastBarTime = Time[0];

   int needed = InpStdDevPeriod + InpAvgLookback + InpMinSqueezeBars + 2;
   if(Bars < needed)
      return;

   UpdateSqueezeState();

   if(HasOpenPosition())
      return;

   double point = _Point;
   if(_Digits == 3 || _Digits == 5)
      point *= 10;

   if(breakoutSignal == 1)
     {
      double sl = Ask - InpStopLossPips * point;
      double tp = Ask + InpTakeProfitPips * point;
      int ticket = OrderSend(_Symbol, OP_BUY, InpLots, Ask, 3, sl, tp,
                             "StdDevSqueezeBreakout", InpMagic, 0, clrBlue);
      if(ticket < 0)
         Print("買い発注に失敗しました。エラーコード: ", GetLastError());
     }
   else if(breakoutSignal == -1)
     {
      double sl = Bid + InpStopLossPips * point;
      double tp = Bid - InpTakeProfitPips * point;
      int ticket = OrderSend(_Symbol, OP_SELL, InpLots, Bid, 3, sl, tp,
                             "StdDevSqueezeBreakout", InpMagic, 0, clrRed);
      if(ticket < 0)
         Print("売り発注に失敗しました。エラーコード: ", GetLastError());
     }
  }

スクイーズが一定本数続いたあと、記録した高値・安値を終値ベースで抜けた瞬間だけ、

新規エントリーが発生します。

MetaEditorでのコンパイル・バックテスト手順

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

  1. MetaEditorで新規Expert Advisor「StdDevSqueezeBreakoutEA」を作成し、上のコードを貼り付ける
  2. F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
  3. ストラテジーテスターでこのEAを選択し、任意の通貨ペア・期間でバックテストを実行する
  4. 「結果」タブで、収縮期間が続いたあとにエントリーが発生していることを確認する
  5. InpSqueezeRatio・InpMinSqueezeBarsを変えて、シグナルの頻度がどう変わるかを確認する

よくあるエラーと注意点

実際に運用する前に、確認しておきたい点です。

InpSqueezeRatioが厳しすぎてシグナルが出ない

注意
・0.7は目安であり、通貨ペアや時間足によって「普段」の感覚が異なる
・シグナルが出ない場合は0.8〜0.9側に緩め、出すぎる場合は0.5〜0.6側に厳しくする
・InpMinSqueezeBarsを増やすほど収縮の確からしさは上がる
・ただしシグナル自体は減る

ブレイク直後の戻り(フェイク)で損切りに引っかかる

収縮後のブレイクアウトは、抜けた直後に戻ってくる「フェイクブレイク」

が発生することがあります。

このEA単体では戻りを予測できないため、

InpStopLossPipsを極端に狭くしすぎないことをおすすめします。

コンパイルエラーになる

`iStdDev()`の引数は7個すべて指定してください。

`ma_shift`や`applied_price`だけを省略することはできません。

よくある質問

標準偏差スクイーズブレイクアウトEAについて、よく検索される疑問に回答します。

実践アドバイス
・既刊iStdDevインジケーターを併用すれば、収縮の推移を目視でも確認できる
・InpStdDevPeriod・InpAvgLookbackは、
・通貨ペア・時間足でバックテストして調整する
・スクイーズ中の値幅が極端に狭い場合、ブレイク後の値幅も小さくなりやすい

既刊のATRボラティリティフィルターEAと一緒に使えますか?

はい。

既刊のATRボラティリティフィルターEAは「他の判定ロジック用のフィルター」

なので、本記事のEAとは役割が重なりません。

両方を別のマジックナンバーで同時運用することもできますが、

発注する戦略の総数は事前に把握しておいてください。

ボリンジャーバンドのスクイーズと何が違いますか?

考え方は近いですが、判定対象が異なります。

ボリンジャーバンドのスクイーズは「バンドの幅」で収縮を判定するのに対し、

本記事は「標準偏差の値そのもの」を直近平均と比較して収縮を判定します。

いずれも最終的には同じ標準偏差を参照していますが、見ている数値の形が違います。

既刊のバンドウォークEAと同時運用するとどうなりますか?

既刊のバンドウォークEAは「トレンドが続いている間」を狙う設計で、

本記事は「収縮から抜けた瞬間」だけを狙う設計です。

タイミングが重なることもありますが、マジックナンバーを分けて運用すれば、

どちらのエントリーかを区別できます。

【MQL4】iStdDevの標準偏差で「スクイーズ(収縮)」を検知し、ブレイクアウトでエントリーするEAの作り方(ATRフィルターEAとの違いも解説):まとめ

この記事では、既刊iStdDevインジケーターの比率計算を使い、

値動きの「収縮」を記録してブレイクアウトのエントリー条件に変換する方法を解説しました。

既刊ATRボラティリティフィルターEAが「見送るためのフィルター」

だったのに対し、本記事は「収縮そのものを起点にする」設計にすることで役割を分けています。

損切り必須・ポジション重複防止という最低限の安全策を備えた上で、

InpSqueezeRatio・InpMinSqueezeBarsの調整で、

感度を変えられる構成にしています。

関連記事