前日の高値・安値を表示する`hl-line.md`のインジケーターを使っていて、

「もっと直近の、細かい反転ポイントも水平線で見たい」と感じたことはないでしょうか。

`iFractals()`を使う`ifractals.md`のインジケーターもありますが、

こちらは前後2本固定の反転点にしか矢印をつけられず、水平線として後まで残ってはくれません。

この記事では、判定範囲を自分で調整できる「直近のスイング高値・安値」

を自動検出し、水平線として引き続けるインジケーターの作り方を、

既刊`ihighest-ilowest.md`で解説した、

`iHighest()`・`iLowest()`関数を応用する形で解説します。

この記事で分かること
・hl-line.md/ifractals.mdとの設計方針の違い
・「窓の中で自分が一番高い(安い)か」でスイングを判定する考え方
・iHighest/iLowestを使った判定範囲の指定方法
・水平線(OBJ_HLINE)を自動で引き、古い線を整理する実装コード
  1. このインジケーターで分かること・hl-line.md・ifractals.mdとの違い
    1. hl-line.mdは「前日」という固定単位の高値・安値
    2. ifractals.mdは「前後2本」固定の反転点を矢印表示
    3. 本記事は「判定範囲を自分で指定できる」水平線
  2. スイング高値・安値をどう判定するか
    1. 「窓の中で自分が一番高い(安い)か」で判定する
    2. 確定するまで最低でも「InpSwingRange + 1本」のラグが生じる
    3. InpSwingRangeを大きくするほど「効いている」ポイントに絞られる
  3. iHighest・iLowest関数で窓の中の最大値・最小値を調べる
    1. iHighest・iLowestのシグネチャ(既刊ihighest-ilowest.mdからの再掲)
    2. startとcountで「窓」の範囲を指定する
    3. 戻り値がshiftと一致するかどうかで判定する
  4. 水平線(OBJ_HLINE)の描画と古い線の整理
    1. ObjectCreateでOBJ_HLINEを作る
    2. 同じバーで二重に線を作らないよう、バーの時刻を名前に使う
    3. 線の本数の上限を決め、古い線から削除する
  5. 実際に動くサンプルコード
  6. MetaEditorでのコンパイル・表示手順
  7. よくあるエラーと注意点
    1. 表示直後は線が少ない(初回スキャンの範囲外)
    2. InpSwingRangeを大きくしすぎるとスイングが全く検出されない
    3. 同じ価格帯に複数の線が重なって見えることがある
  8. よくある質問
    1. なぜiFractalsを使わず、iHighest/iLowestで自作するのですか?
    2. このラインをEAのエントリー条件に組み込めますか?
    3. 線が多すぎて見づらいときはどうすればいいですか?
  9. 【MQL4】直近のスイング高値・安値を自動検出して水平線を引くインジケーターの作り方(hl-line.md・iFractals.mdとの違い):まとめ
    1. 関連記事

このインジケーターで分かること・hl-line.md・ifractals.mdとの違い

まず、既刊の2記事と本記事の設計方針の違いを整理します。

hl-line.mdは「前日」という固定単位の高値・安値

`hl-line.md`のインジケーターは、日足(D1)の1本前、

つまり前日の高値・安値を2本の水平線で表示する設計です。

1日単位の目安としては使いやすい一方、「今日の値動きの中での反転ポイント」

までは表示できません。

ifractals.mdは「前後2本」固定の反転点を矢印表示

`ifractals.md`のインジケーターは、`iFractals()`関数を使い、

対象の足の前後2本ずつ(計5本)の中で一番高い(安い)点に矢印を表示します。

ただし、この「前後2本」という範囲はiFractals関数の仕様で固定されており、

自分で広げたり狭めたりすることはできません。

本記事は「判定範囲を自分で指定できる」水平線

本記事のインジケーターは、前後何本を比較するかを入力パラメーターで指定できるようにし、

見つけたスイング高値・安値を、矢印ではなく水平線として引き続けます。

記事 判定範囲 表示方法 向いている使い方
hl-line.md 固定(前日1日分) 水平線2本 1日単位の目安をざっくり知りたい
ifractals.md 固定(前後2本) 矢印 反転の「発生」だけを知りたい
本記事 可変(入力パラメーターで調整) 水平線(複数残る) サポート・レジスタンスの目安を後からも確認したい

スイング高値・安値をどう判定するか

次に、「スイング高値・安値」をどう定義し、判定するかを考えます。

「窓の中で自分が一番高い(安い)か」で判定する

対象の足を中心に、前後`InpSwingRange`本ずつ、

合計`InpSwingRange × 2 + 1`本の「窓」

を作り、その中で対象の足の高値が最大(安値が最小)であれば、

スイング高値(安値)と判定します。

確定するまで最低でも「InpSwingRange + 1本」のラグが生じる

注意
・窓の右側(新しい側)にInpSwingRange本の確定済みバーが必要
・スイング高値・安値の確定にはInpSwingRange+1本のラグが生じる
・リアルタイムの反転検出ではなく「あとから見て効いているポイントの可視化」が目的の指標

InpSwingRangeを大きくするほど「効いている」ポイントに絞られる

`InpSwingRange`を小さくすると、些細な上下動にも反応して線が増えすぎます。

大きくすると、より大きな反転だけに絞られますが、

確定までのラグも大きくなるトレードオフがあります。

iHighest・iLowest関数で窓の中の最大値・最小値を調べる

判定の中心になる、iHighest・iLowest関数の使い方を確認します。

iHighest・iLowestのシグネチャ(既刊ihighest-ilowest.mdからの再掲)

int iHighest(
   string symbol,    //通貨ペア(NULLで現在の通貨ペア)
   int    timeframe, //時間足(0で現在の時間足)
   int    type,      //対象の時系列(MODE_HIGH等)
   int    count,     //調べる本数
   int    start      //調べ始めるシフト(新しい側の起点)
   );

int iLowest(
   string symbol,
   int    timeframe,
   int    type,      //MODE_LOW等
   int    count,
   int    start
   );

戻り値は、窓の中で最大(最小)だったバーのシフト(何本前か)です。

startとcountで「窓」の範囲を指定する

対象のバーのシフトを`shift`とすると、

窓の新しい側の端は`shift – InpSwingRange`、

窓の本数は`InpSwingRange * 2 + 1`になります。

これを`start`と`count`にそのまま渡します。

戻り値がshiftと一致するかどうかで判定する

`iHighest()`の戻り値が、対象のバーの`shift`と一致していれば、

「窓の中で対象のバーが一番高い」ことが確定します。

一致しなければ、窓の中に対象より高いバーが別にある、ということです。

水平線(OBJ_HLINE)の描画と古い線の整理

判定結果を水平線として表示し、増えすぎないように整理する方法を確認します。

ObjectCreateでOBJ_HLINEを作る

`OBJ_HLINE`は、時間軸に関係なく、

価格だけを指定して引ける水平線のオブジェクトタイプです。

同じバーで二重に線を作らないよう、バーの時刻を名前に使う

補足
・オブジェクト名に対象バーのTime[]を埋め込んでおく
・ObjectFindで同名のオブジェクトが既にあれば作成をスキップする
・OnCalculateは毎ティック呼ばれるため、重複防止が無いと同じ線が増殖する

線の本数の上限を決め、古い線から削除する

水平線を無制限に残すとチャートが線だらけになるため、

`InpMaxLines`で本数の上限を決め、

上限を超えたら古い線(ObjectsTotal側から見て末尾側)を削除します。

実際に動くサンプルコード

直近のスイング高値・安値を検出し、水平線として引き続けるインジケーターです。

//+------------------------------------------------------------------+
//|                                SwingHighLowAutoLine.mq4           |
//|                     Copyright 2026, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property strict
#property indicator_chart_window

input int   InpSwingRange = 5;          //前後何本を比較してスイングと判定するか
input int   InpMaxLines   = 6;          //チャートに残す水平線の最大本数(高値・安値合計)
input color InpHighColor  = clrOrangeRed;
input color InpLowColor   = clrLimeGreen;

string LinePrefix = "SwingLine_";

int OnInit()
  {
   if(InpSwingRange < 1)
     {
      Alert("InpSwingRangeは1以上を指定してください");
      return(INIT_PARAMETERS_INCORRECT);
     }
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   for(int i = ObjectsTotal() - 1; i >= 0; i--)
     {
      string name = ObjectName(i);
      if(StringFind(name, LinePrefix) == 0)
         ObjectDelete(0, name);
     }
  }

bool IsSwingHigh(int shift, int range)
  {
   int windowStart = shift - range;
   if(windowStart < 0)
      return(false);

   int idx = iHighest(NULL, 0, MODE_HIGH, range * 2 + 1, windowStart);
   return(idx == shift);
  }

bool IsSwingLow(int shift, int range)
  {
   int windowStart = shift - range;
   if(windowStart < 0)
      return(false);

   int idx = iLowest(NULL, 0, MODE_LOW, range * 2 + 1, windowStart);
   return(idx == shift);
  }

void TrimOldLines()
  {
   int count = 0;
   for(int i = ObjectsTotal() - 1; i >= 0; i--)
     {
      string name = ObjectName(i);
      if(StringFind(name, LinePrefix) != 0)
         continue;

      count++;
      if(count > InpMaxLines)
         ObjectDelete(0, name);
     }
  }

void DrawSwingLine(datetime barTime, double price, bool isHigh)
  {
   string name = LinePrefix + (isHigh ? "H_" : "L_") + IntegerToString((int)barTime);

   if(ObjectFind(0, name) >= 0)
      return; //同じバーの線は作成済み(重複防止)

   ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
   ObjectSetInteger(0, name, OBJPROP_COLOR, isHigh ? InpHighColor : InpLowColor);
   ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DASH);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);

   TrimOldLines();
  }

void CheckSwing(int shift)
  {
   if(IsSwingHigh(shift, InpSwingRange))
      DrawSwingLine(Time[shift], High[shift], true);

   if(IsSwingLow(shift, InpSwingRange))
      DrawSwingLine(Time[shift], Low[shift], false);
  }

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 confirmShift = InpSwingRange + 1;

   if(prev_calculated == 0)
     {
      int scanBars = MathMin(rates_total - confirmShift - InpSwingRange, 500);
      for(int i = scanBars; i >= confirmShift; i--)
         CheckSwing(i);
      return(rates_total);
     }

   if(rates_total == prev_calculated)
      return(rates_total); //同一バー内のティックでは何もしない(重複判定防止)

   CheckSwing(confirmShift);

   return(rates_total);
  }

初回表示時は直近500本をまとめてスキャンし、

以降は新しい確定バーができたときだけ判定するため、負荷を抑えています。

MetaEditorでのコンパイル・表示手順

  1. MetaEditorで新規カスタムインジケーター「SwingHighLowAutoLine」を作成し、上のコードを貼り付けてコンパイルする
  2. 任意の通貨ペア・時間足のチャートにドラッグ&ドロップで適用する
  3. 直近の反転ポイントに、オレンジ(高値)・緑(安値)の点線の水平線が引かれることを確認する
  4. `InpSwingRange`の値を変え、線の数・位置が変わる様子を確認する

よくあるエラーと注意点

実際に使う前に確認しておきたい注意点をまとめます。

表示直後は線が少ない(初回スキャンの範囲外)

注意
・初回スキャンは直近500本に限定している
・それより古い反転ポイントには線が引かれない
・古いチャートまで遡るにはscanBarsの上限値を増やす(処理は重くなる)

InpSwingRangeを大きくしすぎるとスイングが全く検出されない

極端に大きい値(例: 100)を指定すると、窓の範囲が広すぎて「一番高い(安い)」

と確定できるバーがほとんど無くなり、線が全く引かれなくなることがあります。

まずは5〜10程度から試すことをおすすめします。

同じ価格帯に複数の線が重なって見えることがある

似た高さのスイング高値・安値が複数回発生すると、水平線が近い価格帯に重なって描かれ、

見分けにくくなることがあります。

`InpMaxLines`を小さくして本数を絞ると改善します。

よくある質問

直近のスイング高値・安値を自動検出する本インジケーターについて、

よく検索される疑問に回答します。

なぜiFractalsを使わず、iHighest/iLowestで自作するのですか?

`iFractals()`の判定範囲(前後2本)が固定で変更できないためです。

`iHighest()`・`iLowest()`を使えば、

`InpSwingRange`で判定範囲を自由に調整できます。

このラインをEAのエントリー条件に組み込めますか?

できます。

`IsSwingHigh()`・`IsSwingLow()`関数をそのままEA側に移植し、

直近のスイング高値・安値の価格を`double`変数に保存しておけば、

その価格に近づいたときだけエントリーする、といったフィルターに使えます。

線が多すぎて見づらいときはどうすればいいですか?

`InpMaxLines`を小さくするか、`InpSwingRange`を大きくして、

より大きな反転だけに絞り込むことをおすすめします。

【MQL4】直近のスイング高値・安値を自動検出して水平線を引くインジケーターの作り方(hl-line.md・iFractals.mdとの違い):まとめ

サポート・レジスタンスの目安は、

前日の高値・安値だけでも、前後2本固定の反転点だけでもありません。

既刊`ihighest-ilowest.md`で解説した、

`iHighest()`・`iLowest()`を、

「窓の中で自分が一番高い(安い)か」という判定に応用することで、

判定範囲を自分で調整できる、直近のスイング高値・安値の水平線を自動で引き続けられます。

関連記事