【FX自動売買開発ログ #4】結果は出た。でも「トレード数2件」で喜んでいいのか?
前回のおさらい
前回、範囲を広げたバックテストを実行する前に、想定API呼び出し回数(40回)を先に見積もってもらいました。今回はその続きから、実際にバックテストを走らせて出てきた結果と、その結果に自分で疑いを持つまでの話です。
152回のはずが140回で済んだ
今回実行したのはstride=3(3営業日ごとに理論を再評価)という設定で、見積もりは152回のAPI呼び出し。実行には10分以上かかる見込みだったので、バックグラウンドで走らせてもらいました。
完了後の実際の呼び出し回数は140回。見積もりよりやや少なく済んだのは、前回stride=5で実行した際の評価日と一部重複していて、約6日分×2理論=12回がキャッシュから再利用されたためです。日付×理論名でキャッシュしておく設計が、ここでもきちんと効いていました。
結果:AIアンサンブルはbaselineの「過学習」を回避していた
76評価ポイント(train・test各134本、stride=3)での比較結果です。
| 戦略 | train収益% | train勝率% | 検証収益% | 検証勝率% |
|---|---|---|---|---|
| baseline_sma_cross | +2.13 | 0.00% | -2.18 | 0.00% |
| ai_ensemble(min_agree=1, conf≥50) | -7.29 | 18.18% | -0.33 | 42.86% |
| ai_ensemble(min_agree=1, conf≥60) | -2.93 | 25.00% | -1.42 | 33.33% |
| ai_ensemble(min_agree=1, conf≥70) | -1.89 | 33.33% | -2.18 | 50.00% |
| ai_ensemble(min_agree=2, conf≥50) | -4.18 | 0.00% | +2.05 | 60.00% |
| ai_ensemble(min_agree=2, conf≥60) | -1.04 | 0.00% | +1.80 | 60.00% |
| ai_ensemble(min_agree=2, conf≥70) | +0.74 | 0.00% | +0.08 | 66.67% |
過学習の警告(訓練期間で好成績、検証期間で崩れるパターン)が出たのは baseline_sma_cross のみでした。訓練+2.13% → 検証-2.18%という、教科書的な過学習の形です。一方でAIアンサンブル側はこの警告条件に該当せず、特にmin_agree=2(2理論とも一致が必要)の設定は、訓練・検証どちらも極端なマイナスが目立ちませんでした。

一見、良い流れに見えます。「1理論だけの判定より、2理論が一致したときの方が安定している」というのは直感的にも納得できる傾向です。
でも、ちょっと待てよ
ここで、結果を鵜呑みにする前に一つ確認したいことがありました。この勝率や収益率は、実際何回のトレードに基づいているのか、です。
聞いてみたところ、判定結果はキャッシュ済みのためAPI呼び出し0回で再集計してもらえました。出てきたのは以下の数字です。
| 設定 | train_n | test_n |
|---|---|---|
| baseline_sma_cross | 2 | 2 |
| ai_ensemble(1, ≥50) | 11 | 7 |
| ai_ensemble(1, ≥60) | 8 | 6 |
| ai_ensemble(1, ≥70) | 3 | 6 |
| ai_ensemble(2, ≥50) | 9 | 5 |
| ai_ensemble(2, ≥60) | 7 | 5 |
| ai_ensemble(2, ≥70) | 2 | 3 |
一番「安定して良い結果」に見えていたai_ensemble(2, ≥70)は、train 2トレード・test 3トレードでした。

train勝率0.00%というのも、実態は「2トレードのうち勝ちが0」というだけの話で、testの勝率66.67%も「3トレード中2勝1敗」でしかありません。他の設定を見渡しても、train_n/test_nはおおむね2〜11件と、判断材料としてはかなり心もとない水準です。
さっき「良い傾向」だと思っていたものが、実はただの「たまたま数回しか取引していないので大きく崩れなかっただけ」かもしれない、という可能性が急浮上しました。これは正直、自分でもハッとした瞬間です。表の数字だけを見て「AIアンサンブルの方が優秀そうだ」と結論づけていたら、完全にミスリードされていたところでした。
保有ロジックまで掘り下げてみる
トレード数が少ない理由を理解するために、さらに4点を確認してもらいました。
- 各設定のtrain/testトレード数(上の表の通り)
- 使用している時間足 → 日足(1d)のみ。1時間足・4時間足のCSVはSTEP1で取得済みだが、まだAI判定には使っていない
- 平均保有期間 → 例えばai_ensemble(2, ≥70)はtrain平均3.0本/5.0日、test平均7.0本/9.7日
- エントリー〜決済のロジック → 「stride本ごとに理論判定を再評価し、判定が変わらない限りポジションを保有し続ける」方式。反転(またはneutral化)して初めて決済
この4点目のロジックを理解して、ようやく腑に落ちました。ai_ensemble(2, ≥70)の平均保有本数がほぼstride値(3本)に張り付いていたのは、「シグナルがこまめに反転している」のではなく、逆に「ほとんど同じポジションを継続していて、たまたま数回しか反転しなかった」状態だったのです。つまり、あの設定が良く見えたのは、判定が優秀だったからではなく、単に取引機会自体が少なかったから、という可能性が高いというわけです。

もう一つ気づいたのは、現状「反転まで無制限保有」という設計なので、逆行したときのドローダウンに歯止めがかからない仕組みになっている点です。損切りは元々STEP5で予定していたリスク管理層の範囲でしたが、思ったより早めに手を付けた方が良さそうな気配が出てきました。
次回予告
次回は、この「サンプル数が少なすぎる」問題への対処として時間足を4時間足に切り替える案と、無制限保有の弱点を埋める損切りの実装、この2つのどちらから着手すべきかを検討します。
※本シリーズは個人の技術検証・学習を目的とした開発記録であり、特定の投資手法や金融商品を推奨するものではありません。自動売買には元本割れのリスクがあり、掲載する内容を参考にした取引の結果について一切の責任を負いかねます。投資判断はご自身の責任で行ってください。

