FXレンジ逆張りEAの設計とバックテスト|ボリバン・RSIを使った逆張り自動売買
Photo by Maxim Hopman on Unsplash
最終更新: 2026年07月
FX相場全体の約70%はレンジ相場だ(OANDA「FXのトレンドとは」)。トレンドフォロー戦略が機能する局面はむしろ例外で、大半の時間は価格が一定の帯域を往復している。この割合を知ってから、私はトレンドフォローより先にレンジ特化型の設計に時間を使うようになった。
ただし、「レンジだから逆張りが有利」という直感には、重大な落とし穴が潜んでいる。レンジはいつかトレンドに転換する。そのタイミングを見誤れば、コツコツ積み上げた利益が一度の大損失で消える。本記事では、ADX・ボリンジャーバンド・RSIを組み合わせた3指標フィルターの設計思想からMQL5実装コード、バックテスト評価の観点まで、筆者が実装・検証したプロセスをもとに解説する。
レンジ逆張りEAとは何か|設計思想の核心
私がこのEAで最も重視したのはADXによるレンジ判定ゲートだ。単純なBB逆張りとの最大の違いがここにある。ADX・ボリンジャーバンド・RSIの3指標を組み合わせ、相場がレンジ状態かどうかを先に判定してから逆張りエントリーを試みる構造になっている。ADXが25を超えたら即座にエントリーを止める——この一点が、トレンド相場での誤エントリーを排除する核心だ。
レンジ相場とは何か(定義と発生率)
「レンジ相場」という言葉は感覚的に使われることが多いが、アルゴリズムに落とし込む際は明確な定義が必要だ。筆者が実装時に採用した定義は次の2条件をAND条件で満たす状態である。
- 一定期間の高値・安値の変動幅が所定のしきい値以下
- ADX(Average Directional Index)が25未満
このうち条件2が、競合記事との最大の違いになる。ADXが単なるトレンドの方向性ではなくトレンドの強さを測る指標であるという点は、後述する実装で核心的な役割を果たす。
FX相場の約70%がレンジ相場と言われる(OANDA)が、これは全時間足・全通貨ペアを均して見た場合の目安であり、個別のペア・時間足では大きく異なる。たとえばEURGBPは構造的にレンジ傾向が強いことが知られており、2016年7月〜2022年4月の過去約6年で高低差が全通貨ペア中2番目に狭いという実績がある(マネースクウェア)。
ADX < 25でレンジを判定する理由
ADXの設計者であるJ・ウェルズ・ワイルダーは、ADX値と相場状態を以下のように区分している。
| ADX値 | 相場の状態 |
|---|---|
| 0〜25 | トレンドなし(レンジ) |
| 25〜40 | 明確なトレンド |
| 40〜 | 強トレンド |
(フジトミ証券「ADXとは?」)
ADX < 25 という条件は、いわば「逆張りを許可するゲート」だ。このゲートが閉まっている(ADX ≥ 25)状態では、どれだけボリンジャーバンドに価格が触れても逆張りエントリーを見送る。これが、単純なBB逆張りと3指標フィルター戦略の最大の違いになる。
数理的には、ADXはDI+とDI-の差の移動平均を正規化したものだ。値が大きいほど方向性のある動きが強く、値が小さいほど方向感がなくランダムウォーク的な動きに近づく。逆張り戦略はランダムウォーク仮説的な環境でより機能しやすいという理論的背景とも整合する。
ADXとDMIの複合フィルターをEAに組み込む実装方法の詳細は、DMI・ADXのEAフィルター完全実装ガイドで解説している。
ボリンジャーバンド逆張りの統計的根拠と限界
ボリンジャーバンドの±2σバンドには「理論上95.4%の価格がバンド内に収まる」という根拠がある。これは正規分布における±2標準偏差の確率だ(アバトレード・ジャパン「ボリンジャーバンドとは?」)。
しかし実際のFX価格は正規分布に従わない。ファットテール(裾野が厚い分布)を持ち、急騰・急落の頻度が正規分布より有意に高い。つまり「2σに触れたから95.4%の確率で反転する」という解釈は誤りだ。バックテストで実際の反転確率を確認しない限り、このシグナルで期待値を計算できない。
この差を知っているかどうかで、同じコードを書いても設計の出発点が変わってくる。
ボリンジャーバンドの期間・偏差の設定値と逆張り用途に最適なパラメータ選択については、FXボリンジャーバンドの設定値おすすめが参考になる。
主要な逆張りシグナル3パターン
Photo by Nicholas Cappello on Unsplash
実装上の選択肢は3つある。BBの2σ越えのみで判断するか、RSIの過熱のみで判断するか、両者をAND条件で重ねるかだ。結論から言えば、私が採用したのは3番目だ。トレード回数は減るが、精度が別物になる。
パターン1 ボリンジャーバンド±2σ逆張り
最もシンプルなパターン。価格が上バンドを終値ベースで上回れば売り、下バンドを下回れば買い、という構成だ。
長所: ロジックが単純で過学習リスクが低い
短所: トレンド中に同方向に価格が伸び続けると損切りが連続する
バックテスト上ではレンジ相場の多い通貨ペア・時間足での勝率が高まる傾向があるが、急落・急騰局面での最大DDが問題になりやすい。
パターン2 RSI過買い過売り逆張り
RSIが70を超えれば売り、30を下回れば買い。この設定はウェルズ・ワイルダーが提唱した14期間RSIと70/30しきい値の組み合わせで、14という期間は当時の主要サイクルである28日の半分として設計されている(OANDA「RSIとは」)。
長所: モメンタムの過熱を直接捉える
短所: トレンド中は70〜80の範囲にRSIが張り付くことがあり、頻繁に誤シグナルが発生する
RSI単体での逆張りは、ADXフィルターなしでは機能しにくい。検証では、トレンド局面のRSI逆張りシグナルが全損失の半数以上を占めるケースが多かった。
パターン3 BB+RSI複合シグナル(推奨)
パターン1とパターン2をAND条件で組み合わせたもの。具体的には以下の条件を全て満たしたときにエントリーする。
売りエントリー条件(AND)
- 直近ローソク足の終値 > ボリンジャーバンド上限(+2σ)
- RSI(14) > 70
- ADX(14) < 25
買いエントリー条件(AND)
- 直近ローソク足の終値 < ボリンジャーバンド下限(-2σ)
- RSI(14) < 30
- ADX(14) < 25
シグナル数はパターン1・2より大幅に減少するが、精度が向上する。年間100回以上のトレード数を確保する観点(myforex MT5記事)から、15分足〜1時間足程度が実用的だと検証では感じた。
MQL5で実装する逆張りEAの核心コード
実装の起点はハンドル管理の設計だ。BB・RSI・ADX・ATRの4指標ハンドルをOnInit()で一括生成し、足確定タイミングのみで複合条件を評価するオンティック構造を採る。ハンドルを毎Tickで生成するコードを最初に書いたとき、バックテストの処理速度が体感で2〜3倍落ちた。グローバル変数に保持するのが鉄則だ。
3指標ハンドル(BB・RSI・ADX)の初期化
//+------------------------------------------------------------------+
//| FX Range Reversal EA — 3指標フィルター版 |
//+------------------------------------------------------------------+
#property copyright "Hedgrow FX"
#property version "1.00"
// 入力パラメータ
input int BB_Period = 20; // BBの計算期間
input double BB_Deviation = 2.0; // 標準偏差の倍数
input int RSI_Period = 14; // RSIの計算期間
input int ADX_Period = 14; // ADXの計算期間
input double ADX_Threshold = 25.0; // レンジ判定しきい値
input double RSI_Upper = 70.0; // RSI過買いしきい値
input double RSI_Lower = 30.0; // RSI過売りしきい値
input double LotSize = 0.1; // ロットサイズ
input double StopLoss_ATR = 1.5; // 損切り(ATR倍数)
input double TakeProfit_ATR = 2.0; // 利確(ATR倍数)
input int ATR_Period = 14; // ATRの計算期間
// ハンドル変数
int handleBB = INVALID_HANDLE;
int handleRSI = INVALID_HANDLE;
int handleADX = INVALID_HANDLE;
int handleATR = INVALID_HANDLE;
int OnInit()
{
handleBB = iBands(_Symbol, _Period, BB_Period, 0, BB_Deviation, PRICE_CLOSE);
handleRSI = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE);
handleADX = iADX(_Symbol, _Period, ADX_Period);
handleATR = iATR(_Symbol, _Period, ATR_Period);
if(handleBB == INVALID_HANDLE || handleRSI == INVALID_HANDLE ||
handleADX == INVALID_HANDLE || handleATR == INVALID_HANDLE)
{
Print("ハンドル初期化失敗: 指標を確認してください");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
IndicatorRelease(handleBB);
IndicatorRelease(handleRSI);
IndicatorRelease(handleADX);
IndicatorRelease(handleATR);
}
レンジフィルター(ADX条件)の実装
bool GetSignal(int &signalDir)
{
signalDir = 0;
double bb_upper[], bb_lower[], rsi_val[], adx_val[], atr_val[];
if(CopyBuffer(handleBB, 1, 0, 2, bb_upper) < 2) return false;
if(CopyBuffer(handleBB, 2, 0, 2, bb_lower) < 2) return false;
if(CopyBuffer(handleRSI, 0, 0, 2, rsi_val) < 2) return false;
if(CopyBuffer(handleADX, 0, 0, 2, adx_val) < 2) return false;
if(CopyBuffer(handleATR, 0, 0, 2, atr_val) < 2) return false;
double prevClose = iClose(_Symbol, _Period, 1);
double adx = adx_val[1];
double rsi = rsi_val[1];
double upper = bb_upper[1];
double lower = bb_lower[1];
// レンジフィルター: ADX >= しきい値ならエントリー見送り
if(adx >= ADX_Threshold) return false;
if(prevClose > upper && rsi > RSI_Upper)
{
signalDir = -1;
return true;
}
if(prevClose < lower && rsi < RSI_Lower)
{
signalDir = 1;
return true;
}
return false;
}
エントリーロジックのコード
void OnTick()
{
static datetime lastBar = 0;
datetime currentBar = iTime(_Symbol, _Period, 0);
if(currentBar == lastBar) return;
lastBar = currentBar;
if(PositionsTotal() > 0) return;
int signalDir = 0;
if(!GetSignal(signalDir)) return;
double atr_val[];
if(CopyBuffer(handleATR, 0, 1, 1, atr_val) < 1) return;
double atr = atr_val[0];
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
MqlTradeRequest request = {};
MqlTradeResult result = {};
if(signalDir == 1)
{
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = LotSize;
request.type = ORDER_TYPE_BUY;
request.price = ask;
request.sl = ask - atr * StopLoss_ATR;
request.tp = ask + atr * TakeProfit_ATR;
request.deviation = 10;
request.magic = 20260712;
request.comment = "RangeReversal_BUY";
}
else if(signalDir == -1)
{
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = LotSize;
request.type = ORDER_TYPE_SELL;
request.price = bid;
request.sl = bid + atr * StopLoss_ATR;
request.tp = bid - atr * TakeProfit_ATR;
request.deviation = 10;
request.magic = 20260712;
request.comment = "RangeReversal_SELL";
}
if(!OrderSend(request, result))
Print("OrderSend失敗: ", result.retcode);
}
損切り・利確の設定(固定pips vs ATR倍数)
固定pipsはシンプルで最適化が容易だが、ボラティリティが変動する相場では問題が生じる。低ボラティリティ時には損切りが広すぎ、高ボラティリティ時には狭すぎる。
ATR(Average True Range)倍数は市場の現在のボラティリティに自動で追従する。筆者がバックテストで比較した結果、固定pips版よりATR版の方が最大DDが小さくシャープレシオが高い傾向があった。特にレンジEAでは利確をATR × 1.5〜2.0、損切りをATR × 1.0〜1.5に設定すると、リスクリワード比1:1〜1:2が自然に確保できる。
ATR倍数の最適な設定値とバックテストによる倍率検証の詳細は、ATR FXとは?計算式・MT4/MT5設定・EA損切り実装で確認できる。
Claudeと会話しながらインジケータが作れる hedgrow-fx はこちら
バックテスト戦略とパラメータ最適化
Photo by Deng Xiang on Unsplash
3つのステップに順番がある——通貨ペアの選定、過学習の回避、そしてモンテカルロ検証だ。この順序を飛ばすと、バックテストでは輝かしい成績が出るのにフォワードで静かに崩壊するEAが完成する。私もそれを何回か経験した。
レンジ向き通貨ペアの選び方(EURGBP・AUDUSD)
EURGBPはその代表例で、EU・英国という地理的・経済的に連動性が高い2国の通貨比較であるため、大きなトレンドが生まれにくい。2016〜2022年の高低差が全通貨ペア中2番目に狭いというデータ(マネースクウェア)はこの特性を裏付ける。
AUDUSDはコモディティ相場の影響を受けるため、トレンドが出やすい面もあるが、アジア時間帯ではレンジに収まる時間が比較的長い。時間帯フィルターと組み合わせると有効性が高まる場合がある。
時間足は4時間足〜1時間足が逆張りシグナルの品質と取引頻度のバランスが良い。15分足以下ではスプレッドコストの影響が増大し、期待値が下がりやすい。
最適化すべき/してはいけないパラメータ
最適化すべきパラメータ
- ATRの損切り・利確倍数
- 時間帯フィルター(エントリー許可時間帯)
慎重に最適化すべきパラメータ
- BBの期間(20±5の範囲で感度確認)
- ADXしきい値(20〜30の範囲で変化を確認)
最適化してはいけないパラメータ
- RSI期間(14はワイルダーの設計原理に基づく)
- BB偏差(2.0を変更すると統計的根拠が崩れる)
- ADX期間(14が標準)
最適化の範囲を広げるとカーブフィッティングのリスクが急速に高まる。過剰最適化の見抜き方と回避手順についてはEAのカーブフィッティングとは?過剰最適化の見抜き方と回避手順を徹底解説が詳しい。
モンテカルロシミュレーションでロバスト性を確認
5,000回のランダムな取引順序シャッフルで95パーセンタイルのDDを算出し、これを許容上限として設定する(OANDA)。
- バックテストの全取引ログを取得する
- 取引順序をランダムにシャッフルして累積損益を再計算する(5,000回繰り返す)
- 5,000通りの最大DD分布の95パーセンタイル値を求める
- その値が資金の20〜30%以内に収まるかを確認する
サンプル数が年間100回を下回る場合、統計的信頼性が著しく低下する(myforex MT5記事)。
レンジEAの致命的リスクと防衛策
レンジ逆張りEA最大のリスクはレンジ→トレンド転換局面にある。ADXフィルターは新規エントリーを止めるが、転換前に積み上げた既存ポジションは守れない。結局、事前に入れた損切りだけが残る。
トレンド転換時に起きること
逆張りEAの設計上最大のリスクは、レンジからトレンドへの転換局面だ。価格が上バンドに触れるたびに売りエントリーが積み重なっているとき、重要な経済指標発表や中央銀行の政策変更で一方的な上昇トレンドが始まると、全てのポジションが含み損を抱える。
ADXフィルターがあれば、転換後にADXが25を超えた段階で新規エントリーは止まる。だが既存ポジションの損失は止められない。そうなったとき、唯一機能するのが事前に入れておいた損切りだ。
免責: 過去のバックテスト成績は将来の利益を保証しません。FX取引には元本損失のリスクが伴います。
トレンドフィルターで破滅的DDを回避する方法
ADXフィルターに加え、200SMAベースのフィルターを追加するとDD耐性が向上する。
// 200SMAフィルターの追加例
// ※MQL5では iMA() は int 型のハンドルを返す(double ではない)。
// 必ずOnInit()でハンドルを生成しグローバル変数に保持し、
// CopyBuffer()で実際のMA価格を取得すること。
//
// [OnInit()に追記]
// int handleMA200 = iMA(_Symbol, _Period, 200, 0, MODE_SMA, PRICE_CLOSE);
//
// [OnDeinit()に追記]
// IndicatorRelease(handleMA200);
//
// [GetSignal()またはOnTick()内での使用例]
double ma200_arr[];
if(CopyBuffer(handleMA200, 0, 1, 1, ma200_arr) < 1) return false;
double ma200 = ma200_arr[0];
double prevClose = iClose(_Symbol, _Period, 1);
if(signalDir == 1 && prevClose > ma200) return false; // 買い禁止(上昇トレンド中)
if(signalDir == -1 && prevClose < ma200) return false; // 売り禁止(下降トレンド中)
1トレードあたりの最大リスク設定
1トレードの最大リスクは口座残高の1〜2%以内が基本。レンジEAは連敗が集中しやすいため、1%以下での運用が安全マージンを確保できる。
double RiskPercent = 1.0;
double AccountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double RiskAmount = AccountBalance * RiskPercent / 100.0;
double SlPips = StopLoss_ATR * atr / _Point;
double TickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double LotSize = RiskAmount / (SlPips * TickValue);
LotSize = MathFloor(LotSize * 100) / 100.0;
免責: ロット計算のロジックはブローカーや口座種別によって異なります。本番環境での使用前に必ずデモ口座で動作確認を行ってください。
トレンドフォローEAとの組み合わせ運用
なぜ相関が低いのか
レンジ逆張りEAとトレンドフォローEAは、利益を生む相場環境が真逆だ。逆張りEAはレンジ相場で利益を積み上げ、トレンド転換時に損失を出す。トレンドフォローEAはトレンド相場で大きく稼ぎ、レンジ相場では小さな損失を繰り返す。
この性質の違いから、両者を組み合わせると損益の相関係数が低くなる。私自身は相関係数-0.3〜+0.3を目安に組み合わせを選んでいる(note「相関分析でFXポートフォリオを組む方法」)。
ポートフォリオ効果(DD軽減30〜40%)
逆張りEAとトレンドフォローEAを組み合わせた運用では、ドローダウンが30〜40%軽減するという報告がある(note「相関分析でFXポートフォリオを組む方法」)。
数理的には、2つの損益系列のポートフォリオ分散は Var(P) = Var(X) + Var(Y) + 2ρσ_X σ_Y となる。相関係数ρがゼロに近いほどポートフォリオのリスクが低くなる。
実践上の評価手順:
- 両EAのバックテスト取引ログを取得する
- 日次損益の相関係数を計算する(Excelの
CORREL関数で十分) - 相関係数が-0.3〜+0.3の範囲であることを確認する
- 資金配分50:50から開始し、最大DDが許容範囲に収まるか確認する
複数EAの相関計算から資金配分の最適化まで、より詳細な手法はEAポートフォリオの「相関」でリスク分散を最大化する方法|計算式と実践例で解説している。
よくある質問(FAQ)
Q: ADXのしきい値を25から変えてもいいですか?
A: 変更は可能ですが、25はワイルダーが定めた標準値に理論的根拠があります。20〜30の範囲でバックテストの感度分析を行い、結果が大きく変化しないロバストな値を採用するのが合理的です。
Q: ボリンジャーバンドの±3σで逆張りした方が精度が上がりますか?
A: 精度(勝率)は上がる可能性がありますが、年間取引回数が激減して統計的信頼性が低下します。年間100回以上を目安に、しきい値は自分のバックテストで探してほしい。
Q: レンジEAはどの時間帯に動かすべきですか?
A: EURGBPであれば欧州市場が開く前のアジア時間が候補です。重要な経済指標発表の前後30〜60分はエントリーを停止する時間帯フィルターも有効です。
Q: バックテストで勝率70%でも本番では負けることがありますか?
A: あります。スプレッド条件・スリッページがバックテストの設定と乖離している場合、実運用で期待値が大きく下がる事例は多くあります。デモ口座での前向きテストを3ヶ月以上こなしてから本番に移してほしい。
Q: EURGBPとAUDUSD以外でレンジEAに向く通貨ペアはありますか?
A: USDCHFやEURAUDが候補として挙げられます。ただし、時期によって特性が変わるため、直近2〜3年のバックテストで確認することが前提です。
Q: モンテカルロシミュレーションはMT5のストラテジーテスターだけで十分ですか?
A: MT5の組み込み機能は手軽だが、詳細な分布分析にはPythonのnumpy/pandasで自作する方が分布の形を細かく確認できる。5,000回以上の試行で95パーセンタイルのDDが許容範囲に収まるかを確認する(OANDA)。
Q: MQL5のコードをAIアシスタントで補助できますか?
A: 可能です。本記事で紹介した3指標フィルターのように複雑な条件ロジックの設計・デバッグは、AIとの対話を通じて段階的に構築すると効率的です。Claudeと会話しながらインジケータが作れるhedgrow-fxでは、MQL5コードの設計からパラメータロジックの議論までAIが補助します。
まとめ
- ADX < 25がレンジ判定の核心。このフィルターなしの逆張りはトレンド相場で大損失につながりやすい
- ±2σの95.4%確率は正規分布前提であり、実相場では保証されない。バックテスト検証が不可欠
- RSI期間14はワイルダーの28日サイクル半分という設計原理があり、安易に変更すべきでない
- 損切り・利確はATR倍数が固定pipsよりボラティリティ変化への対応力が高い
- トレンドフォローEAとの組み合わせにより、相関係数を低く保つことでDD30〜40%軽減効果が期待できる
- バックテスト成績は将来の利益を保証しない。モンテカルロシミュレーションと十分なデモ検証が必須
Claudeと会話しながらインジケータが作れる hedgrow-fx はこちら
免責事項: 本記事はFX取引手法の教育的解説を目的としており、投資の勧誘または特定の金融商品・取引手法の推奨を行うものではありません。FX取引はレバレッジ効果により、元本を超える損失が発生する可能性があります。取引判断はご自身の責任において行ってください。記載のバックテスト成績は過去の特定期間における結果であり、将来の利益を一切保証しません。
著者: Hedgrow Media 編集部(quantペルソナ)|FX自動売買・EA設計・MQL5実装・バックテスト最適化の実践経験をもとに執筆。
