最終更新: 2026年6月
ATRを損切りに使う考え方自体は広まっているが、「具体的にどう実装してバックテストで何を見るか」まで踏み込んだ解説は少ない。本稿ではMQL5での実装コードから、バックテストでのATR倍率最適化、さらに過剰適合を避けるための検証設計まで体系的に解説する。
損切りをATRに連動させる本質的な意義は「マーケットのノイズの外側に損切りを置く」という点にある。ATRの統計的な意味は「この期間内でこれくらいの値動きは普通」という参照水準だ。損切りをATRの1.0倍以内に設定すると、純粋なランダムウォークの範囲内で損切りされてしまうリスクが高くなる。
Claudeと会話しながらインジケータが作れるhedgrow-fxはこちら
ATR損切り設定の基本構造
基本公式
買いエントリーの損切り価格 = エントリー価格 − (ATR × 倍率)
売りエントリーの損切り価格 = エントリー価格 + (ATR × 倍率)
倍率(Multiplier)の目安:
| 手法 | 倍率の目安 | 理由 | |------|-----------|------| | スキャルピング | 0.5〜1.0倍 | 小さい値幅で利益を積み上げるため損切りも小さくする | | デイトレード | 1.0〜1.5倍 | 標準的なノイズをカバーする | | スイングトレード | 1.5〜3.0倍 | 日をまたぐボラティリティに対応 |
「ATR with values」系インジケーターでは、デフォルトで利食いをATRの1.0倍、損切りを1.5倍に設定しているものが多い。これは1:1.5のリスクリワード比を初期値として設定していることを意味する。
MQL5での実装コード
input int ATRPeriod = 14;
input double SLMultiplier = 1.5; // 損切り倍率
input double TPMultiplier = 2.5; // 利確倍率
input double RiskPercent = 1.0; // 資金に対するリスク割合(%)
int ATRHandle;
double ATRBuffer[];
int OnInit()
{
ATRHandle = iATR(_Symbol, _Period, ATRPeriod);
if(ATRHandle == INVALID_HANDLE) return INIT_FAILED;
ArraySetAsSeries(ATRBuffer, true);
return INIT_SUCCEEDED;
}
// ===== ATR取得 =====
double GetATR()
{
if(CopyBuffer(ATRHandle, 0, 0, 3, ATRBuffer) < 3) return 0.0;
return ATRBuffer[0];
}
// ===== 損切り価格の計算 =====
double CalcStopLoss(bool isBuy)
{
double atr = GetATR();
double slDistance = atr * SLMultiplier;
double entryPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
return isBuy ? entryPrice - slDistance : entryPrice + slDistance;
}
// ===== 利確価格の計算 =====
double CalcTakeProfit(bool isBuy)
{
double atr = GetATR();
double tpDistance = atr * TPMultiplier;
double entryPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
return isBuy ? entryPrice + tpDistance : entryPrice - tpDistance;
}
// ===== ATRベースのポジションサイジング =====
double CalcLotSize(bool isBuy)
{
double atr = GetATR();
double slDistance = atr * SLMultiplier;
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = accountBalance * RiskPercent / 100.0;
// ピップ値を取得してロット計算
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double pipValue = tickValue / tickSize;
double slPips = slDistance / _Point;
double lotSize = riskAmount / (slPips * pipValue);
// ロットサイズの正規化
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lotSize = MathRound(lotSize / lotStep) * lotStep;
lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
return lotSize;
}
void OnDeinit(const int reason)
{
IndicatorRelease(ATRHandle);
}
ポジションサイジングまで実装することで、ボラティリティが高い局面では自動的にロットが縮小し、リスク一定の取引が実現できる。これがATRベース損切りの真価だ。
バックテストでのATR倍率最適化手順
Step 1: 最適化パラメーターの設定
MT5ストラテジーテスターの「最適化」タブで、SLMultiplierとTPMultiplierを以下の範囲で設定する:
| パラメーター | 開始 | ステップ | 終了 | |------------|------|--------|------| | SLMultiplier | 0.5 | 0.25 | 3.0 | | TPMultiplier | 1.0 | 0.25 | 5.0 |
これで組み合わせ数は (11 × 17) = 187通りになる。
Step 2: 最適化実行とデータ分割
バックテスト期間を3つに分割する:
| 期間 | 用途 | |------|------| | 直近3年 | 最適化用インサンプル | | 3〜5年前 | 第1バリデーション | | 5〜7年前 | 第2バリデーション(アウトオブサンプル) |
インサンプルで最良の倍率を見つけ、バリデーション期間で同じパラメーターがどれくらい機能するかを確認する。
Step 3: 評価指標の確認
バックテスト結果を見る際に確認すべき指標:
| 指標 | 合格基準の目安 | 意味 | |------|-------------|------| | プロフィットファクター | 1.5以上 | 総利益÷総損失 | | 勝率 | 40〜65% | ただし単独では意味をなさない | | 最大ドローダウン | 20%以下 | 最大下落幅 | | リカバリーファクター | 2.0以上 | 純利益÷最大ドローダウン | | シャープレシオ | 0.5以上 | リスク調整後リターン |
プロフィットファクターだけに注目すると過剰最適化の罠にはまりやすい。複数指標を並べて「総合的にバランスが良い」倍率を選ぶことが重要だ。
過剰適合(カーブフィッティング)を避けるための設計原則
ATR倍率の最適化で最も注意すべきは過剰適合だ。
過剰適合の典型パターン: インサンプル期間ではPF=2.5と優秀だが、アウトオブサンプルではPF=0.9という結果になる。
回避策1: ロバスト性の確認
最適値の前後±0.25のパラメーターでもバックテスト結果が大きく劣化しないことを確認する。例えばSL=1.5倍が最適なら、1.25倍・1.75倍でも安定して機能しているかを見る。
SL=1.25倍: PF=1.4
SL=1.50倍: PF=1.8(最適値)
SL=1.75倍: PF=1.6
このようにパラメーター周辺でなだらかな分布になっていれば、過剰最適化のリスクが低い。
回避策2: 複数通貨ペアでの検証
特定の通貨ペアだけで最適化したATR倍率は、その通貨ペアの過去の値動きに特化している可能性が高い。同じパラメーターを複数の通貨ペアで試し、安定して機能するかを確認する。
回避策3: 時間足を変えて検証
日足で最適化した倍率をH4に適用してみる。大きく劣化するようなら、倍率が時間足特有のノイズに過剰適合している可能性がある。
ATR損切りのよくある実装ミス
ミス1: エントリー時のATRではなく現在のATRを使う
エントリーから時間が経つとATRが変化する。損切り価格をリアルタイムのATRで更新し続けると、損切りが予期せず動いてしまう。損切り価格はエントリー時に確定させるのが原則だ。
ミス2: ATRが0の場合の処理をしていない
double atr = GetATR();
if(atr <= 0.0)
{
Print("ATR取得エラー: エントリーをスキップ");
return;
}
このガード処理がないとdivision by zeroや異常なロット計算が発生する可能性がある。
ミス3: スプレッドをATR損切り幅に含めていない
実際の損切りトリガー価格にはスプレッドが加算される。スプレッドを無視すると、計算上の損切り幅より実際の損失が大きくなる場合がある。
double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) -
SymbolInfoDouble(_Symbol, SYMBOL_BID);
double slDistance = atr * SLMultiplier + spread; // スプレッドを加算
Claudeと会話しながらインジケータが作れるhedgrow-fxはこちら
よくある質問(FAQ)
Q: ATR損切りとトレーリングストップは組み合わせられますか? A: 組み合わせ可能です。エントリー時の損切りはATR×1.5倍で確定し、含み益が一定以上になったらATR×1.0倍のトレーリングストップに切り替えるという実装が一般的です。ただし複雑化するほど最適化パラメーターが増え、過剰適合のリスクが高まります。
Q: バックテストでの最適倍率とフォワードテストで乖離した場合はどうすべきですか? A: まず市場環境の変化を確認してください(バックテスト期間とフォワード期間のボラティリティ水準の違いなど)。乖離が大きい場合は、インサンプル期間を見直して再最適化するか、倍率を固定せずATRの相対的な水準(ATRの移動平均との比率)を使う適応的な設計に変更することを検討します。
Q: 損切りをATRだけで決めるのは危険ですか? A: ATR単体は最低限の損切り幅の目安を与えますが、サポート・レジスタンスラインや重要な価格水準を無視することになります。理想的には「ATR×倍率で計算した幅」と「直近の高値・安値から少し外側」の広い方を損切りに採用するハイブリッドアプローチが有効です。
Q: 高ボラティリティの指標発表時はATR損切りをどう扱いますか? A: 経済指標発表の直前にATRが急上昇し、損切り幅が一時的に過大になることがあります。指標発表前後の一定時間はエントリーを停止する「ニュースフィルター」をEAに組み込むことを推奨します。
著者情報
本記事は金融工学・アルゴリズム取引の実務経験を持つライターが執筆しています。
免責事項: 本記事はFXの教育・情報提供を目的としており、投資勧誘を行うものではありません。FX取引には元本割れリスクがあります。バックテスト結果は将来の運用成果を保証しません。実際の取引は自己責任で行ってください。
