2026-05-04(月)。週明けの朝、FX PC の状態確認から始まり、最終的に新戦略 PT007(XAUUSD M1 底ダブルトップ・ブレイクアウト)のデモ投入まで一気に進んだ。途中でバックテストの look-ahead バイアスが発覚して「PF 7.02 → 実態 PF 0.95」に転落する事件があり、戦略をゼロから組み直すことになった。設計の失敗と挽回の過程を時系列で残す。
この記事のポイント
- 朝の状態確認で PT101 が現プロセスに未ロードと判明 → 即時投入(MAGIC=10101/10102)
- XAUUSD M1 でカップ&ハンドルが顕著に出る、という観察から戦略構築を開始
- 放物線フィットでカップ底買いを設計、M1 BT で PF 6.17 の好成績
- look-ahead バイアス発覚 — 過去に遡ってエントリーしていた。live互換に書き直すと PF 0.95に転落
- 「底ダブルトップのブレイクで買い、SL=谷、TP=トレーリング」に再設計 → PF 1.62、OS PF 1.98 でロバスト確認
- SHORT 鏡像版は OS で PF 0.84 に崩壊、上昇相場では機能せず不採用
- PT007 として LONG only で実装・デプロイ完了(MAGIC=70001/70002)
朝の状態確認:PT101 が動いていなかった
月曜の朝、まず FX PC(OpenVPN経由でリモート接続)の稼働状況をチェックした。 pythonw / MetaTrader5 / FXBot_Restart スケジューラ — どれも生きている。ところが、live_trader.log に気になる警告が継続的に出ていた。
[WARNING] whatif: insert failed: CHECK constraint failed: pattern IN ('P1','P2')
調べると、4月末の戦略リネーム(P1/P2 → PT001/PT002)で what_if_signals テーブルの CHECK 制約と整合が崩れていた。ライブトレード本体は try-except で握り潰されているので取引には影響なしだが、4/28 以降の約定行(executed)が約6日間ぶん whatif ログに記録されていない状態。これは別件として 5/9(土)市場閉場後に DB スキーマ修正することにし、ここでは保留。
そして起動バナーを照合して気付いた:
2026-05-02 15:52:44 Live Trader 起動(PT001+PT002+PT003+PT005+PT006)
PT101 がプロセスに乗っていない。 ファイルは 5/3 に FX PC へ転送されていたが、bot を再起動していなかったので IgnoreNew 仕様で旧プロセス(5/2 起動)が走り続けていた。
MAGIC を 70001/70002 → 10101/10102 に変更(オーナー指示)してから taskkill → FXBot_Restart 即時実行。新プロセス PID 8996 が 8:00:15 に起動し、PT101 含む 6戦略のバナーが出た。約 19.5 時間「ファイルは届いたが起動しない」状態が続いていたことになる。
XAUUSD M1 のカップ — 起点となった観察
朝の作業を一段落させたところで、オーナーから「XAUUSD は1分足でカップ&ハンドルが顕著に出る」という観察が来た。実際のチャート(5/1 12:00 〜 5/2 03:00 JST)を見ると、左肩 4625 → 底 4560 → 右肩 4660 という綺麗な U字型反発がはっきりしている。
これを機械化できれば、XAUUSD 1通貨だけでも常時稼働できる戦略になる。早速プロトタイプに入った。
初期設計:放物線フィット
古典的なカップ&ハンドル検出は「軸点を5点(左肩・底・右肩・ハンドル底・ブレイク)抽出」する複雑な処理になりがちだが、「カップは下に凸の放物線」と捉えれば、二次回帰一発で定量化できる。
y = ax² + bx + c で最小二乗フィット
・a > 0 (下に凸 = カップ)
・R² ≥ 0.55 (フィット度)
・頂点が窓中央付近 (底通過済み)
・直近の値幅 ≥ 0.4% (深さ十分)
更に「上から落ちてきた」フィルタとして、窓の左半分で 0.5% 以上の下落を必須化した。XAUUSD M1 の 14ヶ月分(10万本)でスキャンすると、約 200件のセットアップが検出された。
BT結果が異常に良すぎた
パラメータスイープを 36通り走らせた。結果は全パターン黒字、PF 4.65〜7.49。最良条件で:
| 項目 | 値 |
|---|---|
| 件数 | 207 |
| 勝率 | 70.0% |
| 総pips | +38,293 |
| PF | 7.02 |
| 期待値/件 | +185 pips |
| Max DD | -600 pips |
全36パターン黒字、IS/OS 分割でも OS PF 2.86 で安定。一見、過剰最適化の兆候もない。「これは PT007 として live 投入できる」と判断しかけた。
look-ahead バイアスの発覚
BT の find_entries 関数を改めて読んだ:
for j in range(setup['bottom_idx'] + E1_MIN_BARS_PAST, setup_idx + 1):
if closes[j] >= bounce_threshold:
e1_idx = j # ← 過去の bar を約定に使う
break
これは「カップが完成して見える時点(setup_idx)から、過去に遡って最初に反発した bar(e1_idx)でエントリーしたことにする」というロジック。実際のライブトレードでは未来情報なしに過去のbarを買えないので、look-ahead バイアスそのものだった。
本来のライブ実行は「現在 bar まで見えている情報だけで判定し、現在 bar でエントリー」。これに合わせて BT を書き直したところ、結果は一変した。
| BT種別 | 件数 | 勝率 | 総pips | PF |
|---|---|---|---|---|
| Look-ahead版(旧) | 207 | 70.0% | +38,293 | 7.02 |
| Live互換版(実態) | 205 | 46.3% | -993 | 0.95 |
事実上のブレークイーブン。PF 7.02 は幻だった。
原因も明らか:カップが「完成して見える」のはエントリー機会が過ぎた後。底で買うにはカップ未完成、カップが見えた時点では既に右側を上げていて TP(measured move = entry + cup_depth)が遠すぎる。設計上の本質的問題で、フィルタを足しても解決しない。
再設計:底ダブルトップ・ブレイクアウト
放物線アプローチを破棄しかけたところで、オーナーから具体的な代案が来た。
「底値のダブルトップなどのブレイクでエントリー、TP は最大含み益、SL はそのダブルトップの底値で BT を回して」
これは違う発想だった。カップ全体を見るのではなく、底で起きる小さな価格構造(2つの近似ピーク)を捉え、そのブレイクをエントリーtrigger に使う。形状検出ではなく、タイミング検出。SL は谷底(= ダブルトップの底値)、TP はトレーリングで含み益最大値から戻ったらクローズ。
v3 ロジック
1. 底領域確認(直近60本)
・高安幅 ≥ 0.50%
・底bar が直近5本以内ではない(底通過済み)
2. ダブルトップ検出(直近20本)
・PEAK_PIVOT_W=2 で局所max抽出
・上位2ピーク間
- 高さ差 ≤ 0.10%
- 距離 ≥ 3本
- 谷が両ピーク高値より下にある
3. エントリー(LONG)
・close > 2峰の最高値
・直前 bar で未ブレイク(連続シグナル防止)
4. SL = ピーク間の谷底
5. TP = トレーリング: max_high - $0.30(=3pips)
6. 時限: 4時間(M1×240本)
BT結果(live互換、look-ahead無し)
| 期間 | 件数 | 勝率 | PF | 総pips |
|---|---|---|---|---|
| IS(1/20 - 3/31, 2.5ヶ月) | 351 | 76.6% | 1.56 | +2,501 |
| OS(4/1 - 5/1, 1ヶ月) | 114 | 71.1% | 1.98 | +690 |
| FULL | 465 | 75.3% | 1.62 | +3,191 |
OS PF 1.98 が IS PF 1.56 を上回っている。これは過剰最適化していない証拠で、むしろロバスト。トレーリング幅は $0.30〜$5.00 で 28パターンスイープしたが、$0.30〜$0.50 が安定して上位だった(広いトレーリングは負ける)。
PF 1.62 は FX 戦略としては現実的に妥当な数字。先ほどの PF 7.02 と違って、信頼できる数字として受け取れる。
SHORT 鏡像版は不採用
念のため SHORT(売り)も鏡像反転で BT した。「上昇領域で形成されるダブルボトムの下抜けで売り」というロジック。
| 期間 | 件数 | 勝率 | PF | 総pips |
|---|---|---|---|---|
| IS | 332 | 78.3% | 3.02 | +5,864 |
| OS | 97 | 64.9% | 0.84 | -210 |
IS で異常に良く(PF 3.02)、OS で破綻(PF 0.84)。典型的な過剰最適化の症状。原因は明らかで、OS 期間の XAUUSD は強い上昇相場(4500台 → 4660台)であり、SHORT 戦略は機能しない。相場レジーム依存のロジックなのでフィルタ追加が必要だが、本実装では 不採用 として LONG only に絞った。
メモリにも「XAU はボラ駆動で SR 反転に従わない」と記録があり、XAU の特性的に底値からの反発(LONG)は素直、頂点からの反落(SHORT)はフェイクが多いのは整合的かもしれない。
PT007 として実装・デプロイ
戦略仕様が固まったので、既存の scripts/pt006.py や pt101.py と同じ構造で scripts/pt007.py を実装。
| 項目 | 値 |
|---|---|
| MAGIC TP1 | 70001 |
| MAGIC SMA | 70002 |
| 対象通貨 | XAUUSD のみ |
| 方向 | LONG only |
| 時間軸 | M1 |
| SL | ダブルトップの谷底 |
| TP | トレーリング: max_high - $0.30 |
live_trader.py にも check_entry_pt007 と check_pt007_trailing_exit を追加し、メインループに M1 確定検出ループを新設。9:23:41 に新プロセス(PID 7420)が起動し、起動バナーで 7戦略稼働を確認。
Live Trader 起動(PT001+PT002+PT003+PT005+PT006+PT101+PT007)
PT007 MAGIC: TP1=70001 SMA=70002 (M1 底ダブルトップ・ブレイク, XAUUSD only LONG, トレーリング$0.3)
診断のため M1足確定の info ログを一時的に有効化したところ、毎分の M1 ループが回っていることが確認できた。シグナル発生待ちで建玉なし、これは健全な動作。
1日の総括
今日の作業は3段階に分かれた:
| 段階 | 所要 | 結果 |
|---|---|---|
| ① PT101 投入(朝の状態確認から) | 1時間 | 完了 MAGIC=10101/10102 で稼働開始 |
| ② カップ&ハンドル戦略の試行錯誤(look-ahead 発覚含む) | 3時間 | 没 設計の失敗を学習 |
| ③ PT007 再設計・実装・デプロイ | 2時間 | 完了 MAGIC=70001/70002 でデモ並走開始 |
得られた教訓
- BT の数字が異常に良い時は look-ahead を疑う。PF 7 は赤信号
- 形状検出 ≠ タイミング検出。同じ U字パターンでも、エントリー位置(底買い vs ブレイク買い)で全く別の戦略になる
- 過剰最適化チェックは IS/OS 分割が最優先。OS PF が IS PF を上回るのが理想
- SHORT を素朴に鏡像反転で追加してはいけない。相場レジーム依存性が強い
- 運用中の bot に新戦略を「ファイル push しただけ」では反映されない。明示的 restart 必須
PT007 は今後 1ヶ月のデモ並走で実績を確認し、本投入の判断をする予定。期待値は 月+700 pips、月100件前後の発火。BT 通りに行けば XAUUSD 単独で第二の収益柱になる可能性がある。
/data/.company/fx/strategy/parabola_cup/ に保存。PT007 設計書は /data/.company/fx/strategy/PT007.md。実装は scripts/pt007.py + live_trader.py 統合済。
