「押し目待ちに押し目なし」という相場格言があります。

強いトレンドほど、下がってくるのを待っているうちに、

結局一度も買えないまま値段が上がってしまう、

という状況です。

この記事では、「どれくらい押し目が来ていないか」を感覚ではなく数値で把握できるように、

直近の高値からの下落をATRで判定し、

十分な押し目が何本連続で来ていないかを数えて、

警告するインジケーターの作り方を解説します。

この記事で分かること
・「押し目待ちに押し目なし」を数値化する考え方
・ATRを使った「本物の押し目」の判定基準
・連続本数(ストリーク)をバッファの漸化式で数える実装
・実際に動くインジケーターのコード全体

「押し目待ちに押し目なし」をどう数値化するか

まず、この相場格言をどういう計算に落とし込むかを整理します。

「押し目待ちに押し目なし」とは

上昇トレンドが続いているときに、多くのトレーダーが「下がってきたら買おう」

と待ち構えても、

十分な下落(押し目)が一度も来ないまま値段がさらに上がっていく状態を指します。

数値化の方針(ピークからの下落をATRで判定)

補足
・直近の最高値(ピーク)を常に記録しておく
・ピークから一定以上(ATRの倍数)下落したら「本物の押し目」と判定する
・本物の押し目が来ずに新高値を更新し続けた本数を「連続本数」として数える

この記事で作るインジケーターの概要

連続本数をサブウィンドウに線グラフで表示しつつ、

一定本数を超えたらチャート上のラベルを赤くしてアラートを鳴らします。

判定に使う関数と考え方

発注ではなく判定だけのインジケーターですが、使う材料は同じです。

iATR関数のシグネチャ

double iATR(
   string symbol,
   int    timeframe,
   int    period,
   int    shift
);

「本物の押し目」の基準となる値幅を、ATRで通貨ペア・時間足ごとに自動調整します。

「本物の押し目」の基準

ピーク(直近の最高値)から終値がどれだけ下にあるかを計算し、

`InpPullbackATRMulti`(ATRの倍率)以上下落していれば、

本物の押し目が発生したと判定してピークをリセットします。

連続本数(ストリーク)をバッファで数える仕組み

if(high[i] >= prevPeak)
   StreakBuffer[i] = StreakBuffer[i - 1] + 1;
else if(pullback >= InpPullbackATRMulti * atr)
   StreakBuffer[i] = 0;
else
   StreakBuffer[i] = StreakBuffer[i - 1] + 1;

1本前のバッファの値に1を足すだけの漸化式のため、

毎回ゼロから計算し直さずに連続本数を数えられます。

実際に動くコード全体

ここまでの考え方をまとめた、実際にMetaEditorでコンパイルできるコードです。

//+------------------------------------------------------------------+
//|                         PullbackExhaustionWarning.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
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1 DodgerBlue

input int    InpATRPeriod        = 14;   //ATRの期間
input double InpPullbackATRMulti = 1.0;  //押し目と認める下落(ATR倍率)
input int    InpWarningBars      = 20;   //警告を出す連続本数

double StreakBuffer[];
double PeakBuffer[];

int OnInit()
  {
   SetIndexBuffer(0, StreakBuffer);
   SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2);
   SetIndexLabel(0, "押し目なし継続本数");

   SetIndexBuffer(1, PeakBuffer);
   SetIndexStyle(1, DRAW_NONE);

   ObjectCreate(0, "PullbackWarningLabel", OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "PullbackWarningLabel", OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "PullbackWarningLabel", OBJPROP_XDISTANCE, 10);
   ObjectSetInteger(0, "PullbackWarningLabel", OBJPROP_YDISTANCE, 10);

   IndicatorShortName("Pullback Exhaustion Warning");
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   ObjectDelete(0, "PullbackWarningLabel");
  }

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   int first = InpATRPeriod + 1;
   if(rates_total < first + 1)
      return(0);

   int start = (prev_calculated > 1) ? prev_calculated - 1 : first;

   for(int i = start; i < rates_total; i++)
     {
      if(i == first)
        {
         PeakBuffer[i] = high[i];
         StreakBuffer[i] = 0;
         continue;
        }

      double atr = iATR(NULL, 0, InpATRPeriod, rates_total - 1 - i);
      double prevPeak = PeakBuffer[i - 1];

      if(high[i] >= prevPeak)
        {
         PeakBuffer[i] = high[i];
         StreakBuffer[i] = StreakBuffer[i - 1] + 1;
        }
      else
        {
         double pullback = prevPeak - close[i];
         if(pullback >= InpPullbackATRMulti * atr)
           {
            PeakBuffer[i] = high[i];
            StreakBuffer[i] = 0;
           }
         else
           {
            PeakBuffer[i] = prevPeak;
            StreakBuffer[i] = StreakBuffer[i - 1] + 1;
           }
        }
     }

   int last = rates_total - 1;
   string text = StringFormat("押し目なし継続: %d本", (int)StreakBuffer[last]);
   if(StreakBuffer[last] >= InpWarningBars)
     {
      text = text + "(警告)";
      ObjectSetInteger(0, "PullbackWarningLabel", OBJPROP_COLOR, clrRed);
     }
   else
     {
      ObjectSetInteger(0, "PullbackWarningLabel", OBJPROP_COLOR, clrDodgerBlue);
     }
   ObjectSetString(0, "PullbackWarningLabel", OBJPROP_TEXT, text);

   static double lastAlertedStreak = -1;
   if(StreakBuffer[last] >= InpWarningBars && StreakBuffer[last] != lastAlertedStreak)
     {
      Alert(_Symbol, ":押し目なしが", InpWarningBars, "本以上継続しています");
      lastAlertedStreak = StreakBuffer[last];
     }
   if(StreakBuffer[last] < InpWarningBars)
      lastAlertedStreak = -1;

   return(rates_total);
  }

MetaEditorでのコンパイル・適用手順

  1. MetaEditorで新規カスタムインディケータ「PullbackExhaustionWarning」を作成し、上のコードを貼り付ける
  2. F7キーでコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
  3. 強いトレンドが続いている通貨ペアのチャートに適用する
  4. サブウィンドウの線が右肩上がりに伸び続け、一定本数を超えるとラベルが赤くなることを確認する

警告の出し方(OBJ_LABELとAlert)

数値を眺めるだけで終わらないよう、2種類の通知を用意しています。

チャート上に常時表示するラベル

左上に「押し目なし継続: ○本」という文字を常時表示し、

警告本数に達すると文字と色を赤に変えます。

閾値到達時だけ鳴らすアラート

`InpWarningBars`に達した瞬間だけ`Alert()`を鳴らし、

裁量トレード中でも見落とさないようにしています。

重複アラートを防ぐ工夫

静的変数`lastAlertedStreak`で直前に通知した値を記憶し、

同じ連続本数のまま何度もアラートを鳴らさないようにしています。

よくある注意点

実装・運用時につまずきやすい点をまとめます。

注意
・連続本数が多いこと自体は「売りシグナル」ではない
・トレンドが強いほど連続本数が伸びるのは自然な現象
・単独で逆張りの根拠にしない

売買シグナルではないという誤解

このインジケーターは「押し目が来ていない」という事実を知らせるだけで、

「そろそろ下がる」という予測はしていません。

InpPullbackATRMultiの調整

値を小さくすると、浅い下落でもすぐにリセットされるため、連続本数が伸びにくくなります。

通貨ペア・時間足に合わせてバックテストで調整してください。

ストラテジーテスターの最適化中はAlertが鳴らない

公式リファレンスに記載があるとおり、

`Alert()`はストラテジーテスターの最適化中は機能しません。

実運用のデモ・リアルチャートで確認してください。

MQL4とMQL5の違い

iATR関数の扱い方に関する2言語の違いを確認します。

MQL4は値を直接返す方式

MQL4の`iATR()`は、呼び出すたびにATRの値を直接返します。

MQL5はハンドル方式に変わる

MQL5では`iATR()`がハンドルを返す方式になり、

値の取得には`CopyBuffer()`を使う設計に変わります。

漸化式の考え方はMQL5でも応用可能

「1本前のバッファ値に1を足す」という連続本数の数え方自体は、

MQL5にハンドル方式で移植する場合でも、そのまま応用できます。

このインジケーターの使いどころと限界

得意な場面と、単独では対応しきれない場面を整理します。

強いトレンドの”異常さ”に気づける

いつもなら数本に1回は押し目が来る通貨ペアで、連続本数が普段の2〜3倍まで伸びていれば、

「今は普段と違う強さのトレンドだ」と気づく材料になります。

上昇トレンドのみの実装(今後の課題)

本記事は「押し目待ちに押し目なし」という上昇トレンドのケースのみを実装しました。

下降トレンドで戻りが来ない「戻り待ちに戻りなし」も、

高値と安値を入れ替えるだけで対称的に実装できます。

よくある質問

このインジケーターについて、想定される疑問に回答します。

実践アドバイス
・連続本数が伸びている間は、無理に逆張りで待たずに値幅を広げて待つ発想も有効
・InpWarningBarsは通貨ペア
・時間足の平常値を観察して決める
・単独指標ではなく環境認識系の既刊記事と組み合わせて使う

何本くらいでInpWarningBarsを設定すればいいですか?

通貨ペア・時間足によって「普段の連続本数」は異なるため、まずはデフォルトの20本で表示し、

過去のチャートで普段どれくらいの値を推移しているかを観察してから調整してください。

EAの発注条件にそのまま使えますか?

`StreakBuffer`の値を参照する条件式

(例: 連続本数が一定を超えたらエントリーを見送る)として、

既刊のEA記事と同じ構成で転用できます。

レンジ相場ではどう表示されますか?

レンジ相場では高値更新とすぐの押し目が交互に起きやすいため、

連続本数は低い値のまま推移します。

既刊のswing-high-low記事と何が違いますか?

既刊のスイング高値・安値の記事は、直近の高値・安値から水平線を描く記事でした。

本記事は水平線ではなく、押し目が来ない状態の「継続本数」を数値化する点が異なります。

【MQL4】「押し目待ちに押し目なし」を数値化して警告するインジケーターの作り方:まとめ

この記事では、「押し目待ちに押し目なし」という相場格言を、

直近の高値からの下落をATRで判定する仕組みに落とし込み、

連続本数をバッファの漸化式で数える方法を解説しました。

連続本数が伸びていること自体は売買シグナルではなく、「今のトレンドの強さに気づく」

ための材料として使ってください。

関連記事