前日の高値・安値を表示する`hl-line.md`のインジケーターを使っていて、
「もっと直近の、細かい反転ポイントも水平線で見たい」と感じたことはないでしょうか。
`iFractals()`を使う`ifractals.md`のインジケーターもありますが、
こちらは前後2本固定の反転点にしか矢印をつけられず、水平線として後まで残ってはくれません。
この記事では、判定範囲を自分で調整できる「直近のスイング高値・安値」
を自動検出し、水平線として引き続けるインジケーターの作り方を、
既刊`ihighest-ilowest.md`で解説した、
`iHighest()`・`iLowest()`関数を応用する形で解説します。
・hl-line.md/ifractals.mdとの設計方針の違い
・「窓の中で自分が一番高い(安い)か」でスイングを判定する考え方
・iHighest/iLowestを使った判定範囲の指定方法
・水平線(OBJ_HLINE)を自動で引き、古い線を整理する実装コード
このインジケーターで分かること・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でのコンパイル・表示手順
- MetaEditorで新規カスタムインジケーター「SwingHighLowAutoLine」を作成し、上のコードを貼り付けてコンパイルする
- 任意の通貨ペア・時間足のチャートにドラッグ&ドロップで適用する
- 直近の反転ポイントに、オレンジ(高値)・緑(安値)の点線の水平線が引かれることを確認する
- `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()`を、
「窓の中で自分が一番高い(安い)か」という判定に応用することで、
判定範囲を自分で調整できる、直近のスイング高値・安値の水平線を自動で引き続けられます。
コメント