【FX自動売買開発ログ #16】MTF理論、これまでの検証の中で一番一貫した効果が出た話
前回のおさらい
前回、流動性フィルターを検証しましたが、ADX<20フィルターのときと同じ「該当件数が少なすぎて効果を測れない」という壁にぶつかり、いったん保留にしました。今回は、並行して実装していたもう一つの施策、MTF理論(theory_mtf.py)の検証です。
theory_mtf.pyの中身
MTF(マルチタイムフレーム)理論は、4時間足を使った上位足トレンド判定です。1時間足でのエントリー判断が、より大きな時間軸で見た「大きな流れ」と整合しているかをチェックする役割で、regime_filterと同様の拒否権方式(1h側の合成シグナルが上位足トレンドと逆行する場合はエントリーを見送る)で統合します。
渡す情報はシンプルで、直近window本(既定20本)の4時間足OHLCVをそのまま渡すだけです。ダウ理論・プライスアクションのような細かい個別シグナル判定ではなく、あくまで「4時間足で見た大きな流れ」だけを判定させる設計です。
ルックアヘッド対策も入れています。4時間足バーは開始時刻でラベル付けされる(例:08:00のバーは08:00-12:00をカバーし、12:00まで確定しない)ため、判定時点より前に完全に確定しているバーのみを使うようにしました。
キャッシュ最適化は、期待したほど効かなかった
キャッシュキーには、1時間足側のトリガー時刻ではなく、参照した4時間足バー自身の時刻を使う設計にしました。狙いは、同じ4時間足バー期間内に複数の1時間足トリガーが発生した場合、同じキャッシュエントリを再利用してAPI呼び出しを減らすことです。
ただ、実際に確認してみると、ユニークな4時間足バー数は216件で、トリガー件数(216件)とまったく同じでした。想定していた節約効果が働かなかったということです。原因は、トリガーのクールダウン(6時間)が4時間足バーの幅(4時間)より長いため、連続するトリガーがほぼ必ず別々の4時間足バーを参照してしまうからでした。設計自体は間違っていませんが、今のパラメータ(クールダウン=6)では実質的な節約効果は出ない、という結果です。
見積もり通り実行前にシミュレーション
拒否権の発動頻度を、regime_filterのときと同じ手順でシミュレーションしました。実際のLLM判定の代わりに、SMA20/50クロスを4時間足トレンドの簡易プロキシとして使う方法です。
結果は、4設定とも約50%(51.2%、48.9%、51.1%、49.7%)という高めの数字でした。ただしこの数字には注意が必要でした。プロキシに使ったSMA20/50クロスは常にbuyかsellのどちらかを返す単純な二値判定で、neutralを返すことがありません。regime_filterやtheory_mtfのsystem_promptが明示的に許容している「明確なトレンドがなければneutral」という選択肢がプロキシには存在しないため、この約50%という数字は「プロキシの粗さによる偽陽性」を多く含んでいる可能性が高い、という見立てでした。
API呼び出しの見積もりは216回・概算$0.4〜0.6。これまでの検証と同程度の規模だったので、実行することにしました。
実際の発動率は、プロキシの1/3程度だった
216回のAPI呼び出しを実行(バックグラウンドで約40分)した後、実際の拒否権発動率を確認したところ、15.6〜17.2%でした。プロキシの見積もり(約50%)よりも大幅に低く、事前の見立て通り、実際のLLM判定はneutralを適切に使い分けていたことが確認できました。判定分布はbuy 109件・neutral 70件・sell 37件です。

これまでで一番一貫した効果
拒否権あり/なしでバックテストを比較しました。
| 設定 | 収益%(な→あ) | 勝率%(な→あ) | DD%(な→あ) | n(な→あ) |
|---|---|---|---|---|
| (1,≥50) | 5.29→4.27 | 35.53→42.37 | -6.61→-4.41 | 76→59 |
| (1,≥60) | 1.72→2.37 | 35.06→38.33 | -5.68→-3.99 | 77→60 |
| (1,≥70) | 1.52→0.47 | 38.89→42.86 | -2.07→-1.86 | 18→14 |
| (2,≥50) | 4.32→5.23 | 34.25→42.86 | -5.97→-3.75 | 73→56 |
| (2,≥60) | 2.95→3.29 | 34.72→41.07 | -5.45→-3.75 | 72→56 |
| (2,≥70) | 1.52→0.47 | 38.89→42.86 | -2.07→-1.86 | 18→14 |
勝率は6設定全てで改善、最大DDも6設定全てで改善(縮小)しています。これはregime_filter(v1もv2も、DDに関しては一部悪化していた設定があった)より明確に強く、一貫した効果です。
中でも(2,≥50)は、収益率(+4.32→+5.23)・勝率(+8.61pt)・DD(-5.97→-3.75、2.2pt改善)の3つ全てが同時に改善するという、理想的な結果でした。収益率は4/6で改善、2/6((1,≥70)/(2,≥70))でやや悪化していますが、これらは元々トレード数が18→14件と少なく、勝率・DDは改善している点を踏まえると悪くない結果です。

ここまで試したADX<20フィルター(効果なし)、流動性フィルター(効果測定不能)、regime_filter(トレードオフあり)と比べると、mtf_theory拒否権はもっとも一貫性の高い改善効果を示しました。この結果はCLAUDE.mdに記録しています。

次回予告
regime_filterとmtf_theory、どちらも個別には効果が確認できました。次回は、この2つを組み合わせて使った場合にどうなるか、まずはそれぞれが「同じトレードを除外しているのか、別のトレードを除外しているのか」という重なり具合から確認していきます。
※本シリーズは個人の技術検証・学習を目的とした開発記録であり、特定の投資手法や金融商品を推奨するものではありません。自動売買には元本割れのリスクがあり、掲載する内容を参考にした取引の結果について一切の責任を負いかねます。投資判断はご自身の責任で行ってください。

