チャート上に水平線を引いてサポート/レジスタンスを意識して、
トレードしている人は多いと思いますが、
その水平線を毎回自分の目で引き直すのは地味に手間がかかります。
既刊のスイング高安値を自動検出して水平線を引くインジケーターの記事では、
iHighest/iLowestを使ってこの作業を自動化しましたが、
この記事ではそのロジックを「表示するだけ」から一歩進めて、
検出したレベルを価格がブレイクした瞬間に自動でエントリーするEAに発展させます。
この記事で分かること
・既刊インジケーターのスイング判定ロジックをEAの発注条件に転用する方法
・直近確定したスイング高値・安値を保持し続ける変数設計
・同じブレイクで何度もエントリーしないためのガード条件
・損切り必須・ポジション重複防止を備えた実際に動くコード全文
設計思想: なぜ「自動検出したスイング高安値」をブレイク条件に使うのか
まず、手動で引いた水平線ではなく自動検出したレベルを使う理由を整理します。
既刊インジケーターとの関係
既刊記事では、直近InpSwingRange本の範囲で自分が一番高い(安い)バーを、
IsSwingHigh()・IsSwingLow()で判定し、
水平線として描画しました。
この記事では同じ判定関数を、
線を引くためではなく「ブレイクしたかどうか」を判定するために使います。
手動で引いた水平線との違い
手動の水平線は、相場が動いても自分で引き直すまで同じ位置に残り続けます。
自動検出のレベルは、新しいスイング高値・安値が確定するたびに自動で更新されるため、
EAを起動している間、常に「直近の」サポート/レジスタンスを基準にできます。
ブレイクアウト手法のメリット・弱点
メリット
・トレンドが出始めた初動を捉えやすい
・判定ロジックが既刊インジケーターで実績のあるシンプルな関数で済む
弱点
・レベルを一瞬だけ超えてすぐ戻る「フェイクブレイク」に弱い
・レンジ相場では何度もダマシに遭いやすい
スイング高安値の判定ロジック
既刊インジケーターのIsSwingHigh/IsSwingLowを、
そのままEAの内部ロジックとして使います。
iHighest/iLowestを使った判定の仕組み
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);
}
shiftを中心に前後range本の範囲で、
自分が一番高い(安い)バーであればスイング高値(安値)と判定します。
判定に生じるラグ
補足
・shiftバーがスイング高値かどうかは、
右側にrange本分のバーが確定するまで分からない
・つまり、スイングが確定した時点で、
そのバーからInpSwingRange本分だけ時間が経っている
・このラグは検出方式そのものの限界であり、既刊インジケーターと同じ制約を引き継ぐ
最新のスイング高値・安値を保持する変数設計
double LastSwingHigh = 0;
double LastSwingLow = 0;
void UpdateSwingLevels()
{
int shift = InpSwingRange; //右側がInpSwingRange本分確定した最新のバー
if(IsSwingHigh(shift, InpSwingRange))
LastSwingHigh = High[shift];
if(IsSwingLow(shift, InpSwingRange))
LastSwingLow = Low[shift];
}
毎ティックUpdateSwingLevels()を呼ぶことで、
新しいスイング高値・安値が確定するたびに、基準となるレベルが自動で更新されます。
ブレイク判定とエントリー条件
更新され続けるLastSwingHigh/LastSwingLowを使って、
実際の発注条件を組み立てます。
直近確定したスイング高値を超えたら買い
AskがLastSwingHighを、
余白(InpBreakBuffer)の分だけ超えたら買いエントリーします。
直近確定したスイング安値を割ったら売り
同じ考え方で、BidがLastSwingLowを余白分だけ下回ったら売りエントリーします。
同じレベルで何度もエントリーしないためのガード
ブレイクした後も、価格がそのレベルの上(下)に留まっている間は、
条件がティックごとに満たされ続けてしまいます。
LastTradedHigh・LastTradedLowという、
「最後にエントリーしたレベル」を記録し、
同じレベルでは1回しかエントリーしないようにガードします。
損切り・ポジション重複防止
発注条件が整っても、損切りとポジション管理の設計を省略しないようにします。
損切りは反対側のスイングレベルを使う
買いエントリーの損切りには直近のLastSwingLowを、
売りエントリーの損切りには直近のLastSwingHighを、そのまま使います。
ブレイクの根拠にしたレベルの反対側が崩れたら、そもそも前提が崩れたとみなす設計です。
最小ストップ距離のチェック
反対側のスイングレベルが現在値に近すぎる場合、
ブローカーの最小ストップ距離(MODE_STOPLEVEL)を満たせず、
OrderSendが失敗することがあります。
既刊atr-trailing-stop-ea-mt4.mdと同じ考え方で、
発注前にこの距離をチェックします。
OrdersTotalループでの重複エントリー防止
int GetMyTicket()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagic)
return(OrderTicket());
}
return(-1);
}
同じ通貨ペア・同じマジックナンバーのポジションがすでにある場合は、
新規エントリーをスキップします。
実際に動くコード全体
スイング高安値の自動検出からブレイク判定・発注まで、
実際にMetaEditorでコンパイルできるEAです。
//+------------------------------------------------------------------+
//| SupportResistanceBreakoutEA.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 InpSwingRange = 5; //前後何本を比較してスイングと判定するか
input double InpBreakBuffer = 3; //ブレイク判定に追加する余白(pips)
input double InpLots = 0.1; //発注ロット数
input int InpMagic = 20261025; //マジックナンバー
double LastSwingHigh = 0;
double LastSwingLow = 0;
double LastTradedHigh = 0;
double LastTradedLow = 0;
double PipSize()
{
if(Digits == 3 || Digits == 5)
return(Point * 10);
return(Point);
}
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 UpdateSwingLevels()
{
int shift = InpSwingRange;
if(IsSwingHigh(shift, InpSwingRange))
LastSwingHigh = High[shift];
if(IsSwingLow(shift, InpSwingRange))
LastSwingLow = Low[shift];
}
int GetMyTicket()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagic)
return(OrderTicket());
}
return(-1);
}
void CheckBreakoutEntry()
{
if(LastSwingHigh == 0 || LastSwingLow == 0)
return; //まだスイングが1度も確定していない
if(GetMyTicket() >= 0)
return; //すでにポジションがある
double buffer = InpBreakBuffer * PipSize();
double minDistance = MarketInfo(_Symbol, MODE_STOPLEVEL) * Point;
if(Ask > LastSwingHigh + buffer && LastSwingHigh != LastTradedHigh)
{
double sl = LastSwingLow;
if(Ask - sl < minDistance)
{
Print("損切り(直近スイング安値)が近すぎるため買いエントリーを見送りました");
LastTradedHigh = LastSwingHigh;
return;
}
int result = OrderSend(_Symbol, OP_BUY, InpLots, Ask, 3, sl, 0,
"SR Breakout Buy", InpMagic, 0, clrBlue);
if(result < 0)
Print("買い注文に失敗しました。エラーコード=", GetLastError());
else
LastTradedHigh = LastSwingHigh;
return;
}
if(Bid < LastSwingLow - buffer && LastSwingLow != LastTradedLow)
{
double sl = LastSwingHigh;
if(sl - Bid < minDistance)
{
Print("損切り(直近スイング高値)が近すぎるため売りエントリーを見送りました");
LastTradedLow = LastSwingLow;
return;
}
int result = OrderSend(_Symbol, OP_SELL, InpLots, Bid, 3, sl, 0,
"SR Breakout Sell", InpMagic, 0, clrRed);
if(result < 0)
Print("売り注文に失敗しました。エラーコード=", GetLastError());
else
LastTradedLow = LastSwingLow;
return;
}
}
void OnTick()
{
UpdateSwingLevels();
CheckBreakoutEntry();
}
PipSize()で4桁/5桁ブローカーの差を吸収する
InpBreakBufferは「pips」で指定したいので、
5桁(3桁)ブローカーではPointの10倍、
4桁(2桁)ブローカーではPointそのものを使うよう分岐しています。
MetaEditorでのコンパイル・実行手順
- MetaEditorで新規Expert Advisor「SupportResistanceBreakoutEA」を作成し、上のコードを貼り付ける
- F7キーでコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
- デモ口座のチャートにドラッグ&ドロップで適用する
- ストラテジーテスターで、スイング高値・安値が更新されるたびにLastSwingHigh/LastSwingLowが変わり、ブレイク時にだけエントリーする様子を確認する
よくあるエラーと注意点
実際に運用する前に確認しておきたい点をまとめます。
InpSwingRangeを大きくしすぎるとブレイク検出が遅れる
注意
・InpSwingRangeを大きくするほど、スイングの確定に必要な本数が増える
・確定が遅れるほど、ブレイクしてからエントリーするまでの「出遅れ」が大きくなる
・まずは既定値5前後から試し、時間足に合わせて調整する
OrderSendがエラー130で失敗する
反対側のスイングレベルが現在値に近すぎると、
損切り距離がブローカーの最小ストップ距離(MODE_STOPLEVEL)を満たせず、
エラー130(Invalid stops)でOrderSendが失敗します。
コード内のminDistanceガードはこれを防ぐためのものです。
同じブレイクで何度もエントリーしてしまう
LastTradedHigh/LastTradedLowによるガードを削除したり、
変数をグローバルではなく関数内のローカル変数にしてしまうと、
毎ティック同じレベルでエントリーを試み続けてしまうので注意してください。
MQL4とMQL5の違い
このブレイクアウト判定の考え方をMQL5に移植する場合の違いを確認します。
iHighest/iLowestはMQL5に存在しない
MQL5には同名の関数がなく、CopyHigh/CopyLowで価格配列を取得し、
ArrayMaximum/ArrayMinimumと組み合わせて、
自分で最大値・最小値のインデックスを探す設計になります。
発注はCTradeクラスに変わる
OrderSendの代わりに、
CTradeクラスのBuy()/Sell()メソッドを使う設計になります。
ガード条件(最小ストップ距離・重複エントリー防止)の考え方自体はそのまま応用できます。
よくある質問
サポレジ自動検出EAについて、よくある疑問に回答します。
実践アドバイス
・InpBreakBufferを0に近づけるほど早くエントリーできるが、ダマシも増える
・既刊インジケーターと併用する場合は、
チャート上の線とEAの判定基準が同じロジックであることを確認する
・レンジ相場ではダマシが増えやすいため、他の環境認識系の記事と組み合わせる案も検討する
既刊のスイング高安値インジケーターと同時に使えますか?
使えます。
このEAは水平線を描画せず、
内部でIsSwingHigh/IsSwingLowの判定だけを行っているため、
既刊インジケーターをチャートに表示しながら、同時にEAを動かすことができます。
ブレイクアウトはダマシ(フェイクブレイク)が多いと聞きますが対策はありますか?
InpBreakBufferで余白を設けることで、
ごく小さいブレイクには反応しないようにできますが、
ダマシを完全にゼロにする設定値はありません。
既刊のトレンド/レンジ判定インジケーター等と組み合わせ、
レンジ相場ではエントリーを控えるという使い方も検討してください。
InpSwingRangeの推奨値はどれくらいですか?
既刊のスイング高安値インジケーターと同じく、まずは5前後から試すことを推奨します。
短い時間足ではやや小さく、長い時間足ではやや大きくするなど、
通貨ペアや時間足にあわせて調整してください。
【MQL4】自動検出したスイング高安値をブレイクしたらエントリーするEAの作り方(サポレジ自動検出版):まとめ
手動で引いた水平線は相場が動いても自分で更新するまで変わりませんが、
既刊インジケーターと同じ、
IsSwingHigh/IsSwingLowの判定ロジックを使えば、
直近のサポート/レジスタンスを自動で更新し続けながら、
そのレベルをブレイクした瞬間にエントリーするEAを作ることができます。
損切りには反対側のスイングレベルを使い、
最小ストップ距離・重複エントリー防止のガードを組み合わせることで、
実際に運用できる設計にまで落とし込みました。
コメント