【FX自動売買開発ログ #11】勝率46%→31%でも、ドローダウンが半分になった話
前回のおさらい
前回、トリガー方式(SMAクロス×ブレイクアウトのOR、クールダウン6本)でバックテストを598回分実行し、min_agree=1と2がconfidence≥70のときだけ完全に一致するという発見がありました。今回は、その中でも一番安定して見えたai(2,≥70)設定の中身をさらに掘り下げ、あわせてトリガー方式にはまだ組み込んでいなかったATR損切りを実装します。
勝ちトレードと負けトレード、中身を分けてみる
ai(2,≥70)設定について、勝ちトレードと負けトレードをそれぞれ分けて、平均リターンと平均保有時間を再集計してもらいました。判定結果はキャッシュ済みのため、これは新規のAPI呼び出し0回で確認できています。
| 期間 | 勝ちトレード(n) | 勝ち平均リターン% | 負けトレード(n) | 負け平均リターン% |
|---|---|---|---|---|
| train | 6 | +0.424% | 7 | -0.528% |
| test | 5 | +0.833% | 6 | -0.537% |
| 期間 | 勝ちトレード平均保有 | 負けトレード平均保有 |
|---|---|---|
| train | 29.7時間(約1.2日) | 51.3時間(約2.1日) |
| test | 70.6時間(約2.9日) | 60.0時間(約2.5日) |
trainだけを見ると、負けトレードの方が長く保有される傾向(勝ち29.7時間 vs 負け51.3時間)が出ています。これは損切りが実装されていないため、逆行したポジションがシグナル反転まで長く塩漬けになっている可能性を示唆する数字です。まさに損切りを入れることで改善が期待できるパターンに見えます。
一方testでは逆に勝ちトレードの方がやや長く(70.6時間 vs 60.0時間)、trainとtestで傾向が一致していません。ただしどちらもn=5〜7件程度なので、この違いを過度に意味づけするのは危険だという注意も添えられていました。

トリガー方式には、実は損切りが入っていなかった
ここで、地味に重要な事実が発覚しました。ATR損切り(×2.0)は、これまでSTEP3で作ったstride方式のバックテスト(run_ai_backtest.py)にだけ実装されていて、今回のトリガー方式のバックテスト(run_trigger_backtest.py)にはまだ組み込まれていなかったのです。つまり、ここまでのトリガー方式の結果は、すべて「反転まで無制限保有」のまま出していたことになります。
stride方式のロジックを流用できるはずだったので、run_trigger_backtest.pyに--atr-stop-mult引数を追加してもらい、ATR×2.0の損切りを実装しました。既存のバックテストエンジン(backtest/engine.py)の損切りロジックは戦略に依存しない汎用実装だったため、大きな変更なく組み込めています。
損切りあり/なしを比較する
実装後、まず損切りなしの状態で結果が前回と完全に一致することを確認(リグレッションチェック)した上で、ATR×2.0の損切りありで実行してもらいました。こちらも判定結果はキャッシュ済みなので、API呼び出しは0回です。
| 設定 | train収益%(なし→あり) | train DD%(なし→あり) | test収益%(なし→あり) | test DD%(なし→あり) |
|---|---|---|---|---|
| baseline_sma_cross | +2.35→+0.22 | -5.24→-4.03 | -1.44→+0.79 | -11.39→-7.03 |
| ai(1,≥50) | +2.20→-0.61 | -4.67→-3.81 | -2.06→+1.27 | -11.58→-7.18 |
| ai(1,≥60) | +2.71→+1.09 | -4.22→-3.22 | -3.09→+1.17 | -11.33→-6.84 |
| ai(2,≥50) | +0.66→+0.82 | -5.07→-3.95 | -0.01→+3.50 | -9.17→-5.48 |
| ai(2,≥60) | +0.25→+0.70 | -5.07→-3.95 | -1.54→+1.93 | -9.40→-5.57 |
| ai(2,≥70)=ai(1,≥70) | -0.24→-0.37 | -2.52→-1.66 | -0.09→+0.99(不変) | -4.12→-2.47 |
ドローダウンは全設定で明確に改善しました。trainは-4〜5%台→-2〜4%台、testに至っては-7〜11%台→-2〜7%台と、大きく縮小しています。stride方式(1d/4h)のときと同じく、損切りの本来の効果がはっきり出た形です。
test期間の収益率もほぼ全設定で改善していて、特にai(2,≥50)は-0.01%→+3.50%と、マイナス圏からしっかりプラスに転換しました。baseline_sma_crossのtestも-1.44%→+0.79%とプラスに転じ、これまで表示されていた「trainプラス→testマイナス」の過学習っぽい警告も、損切りありでは出なくなりました(trainの数字自体が+0.22%と控えめになったことで、警告の条件に該当しなくなったためです)。

一番気になったのが、ai(1,≥70)とai(2,≥70)(結果は今回も完全一致)です。この設定はtrain_n=13のうち9件が損切りで決済されていました。もともと勝率が高め(46%)だったのが損切り導入後は31%まで低下しています。それでもドローダウンは-2.52%→-1.66%と大幅に縮小しており、損切りが「大きな逆行を防ぐ」役割を果たしている一方で、利益が伸びる前に切られてしまうケースもある、というトレードオフが数字にはっきり表れています。

総括すると、stride方式のときと同様、トリガー方式でもATR×2.0の損切りはドローダウン抑制に明確な効果があり、収益率もほとんどのケースで悪化させていません。ただしトレード数は13〜75件とまだ幅があり、特にconfidence≥70系(n=13/11)は依然サンプルが少ないため、傾向はあくまで参考程度に留めるべき、という慎重さは今回も崩していません。
次回予告
損切りの効果が確認できたところで、次はコスト面の棚卸しに入ります。実際に送っているプロンプトの中身を見直したところ、地味な設定ミスが見つかりました。この続きは次回にまとめます。
※本シリーズは個人の技術検証・学習を目的とした開発記録であり、特定の投資手法や金融商品を推奨するものではありません。自動売買には元本割れのリスクがあり、掲載する内容を参考にした取引の結果について一切の責任を負いかねます。投資判断はご自身の責任で行ってください。

