バックテストが全部嘘だった日。FX自動売買の5手法を測り直して、自動発注を止めました
2026年7月27日の17時43分に、所長のFX自動売買botから自動で発注するプロセスを全部止めました。残したのは、持っている玉に損切りを張り続ける見張り役のguardianと、観測と通知だけです。
止めた理由は、その日の午後に出た5つの数字です。バックテストで「勝てる」と出ていた手法を、実際の運用と同じ条件で測り直した結果です。
| 手法 | 監査前のPF | 測り直したPF |
|---|---|---|
| 逆張り | 6.51 | 1.17 |
| 4時間足25MAタッチ | 3.61 | 1.12 |
| 3段ラダー | 2.49 | 0.98 |
| フェード | 1.87 | 0.60 |
| ボックスブレイク | − | 0.87 |
逆張りは1時間足の25本移動平均を実体で奪還したら買う手法、4時間足25MAタッチと3段ラダーは押し目買い、フェードはボックスの端で逆張りする手法です。ボックスブレイクは当時の設定で測った値だけを載せています。
PFは「勝ったトレードの利益の合計を、負けたトレードの損失の合計で割ったもの」です。1を下回れば負け。1.1や1.2は、スプレッドや滑りで簡単に消える薄さです。5つとも、実弾に載せる根拠にはなりませんでした。
7月に書いた4代記の最後の記事では、四代目は止まらずに回っていると書きました。その後に止めた話を、まだどこにも書いていません。このシリーズはその続きです。一本目は、止めた当日のことを書きます。
個人が自分の口座でやった開発の記録です。投資助言でも勧誘でもありません。
月曜の朝、ロスカットから始まった
その日は監査をするつもりで始まったわけではありません。
週末をまたいで持っていたドル円のロング、2万通貨が、月曜の寄り付きで逆指値に掛かりました。所長からの質問は「一時的な下げで刈られる。どうすればいい?」。
調べると、一時的な下げではありませんでした。金曜の引けの時点で、玉には+5.5〜7.0pipsの含み益が乗っていて、損切りは建値+1pipsの位置にありました。ピークで+12.4pipsまで伸びたところで「+8pipsで損切りを建値へ上げる」というルールが動き、建値に張り付いたまま週末に入っていたのです。月曜の寄りは金曜の引けから18.9pips下で始まり、逆指値から14.4pips滑って約定しました。
週末をまたぐときの値の飛び方は実測できます。下方向に飛ぶのが50%、飛ぶ幅の中央値が10.6pips、大きいほうから1割の線で39pips。建値ぴったりの損切りで週末を越すと、半分の確率で下に飛び、飛べばたいていそのまま刈られます。運が悪かったのではなく、そうなる設定でした。
ここまでは設定の問題で、直せば済みます。困ったのはここからです。
「他の戦略も見てくれ」
所長がついでに言いました。他のも見てくれ、と。
当時の構成を思い出しておきます。ボックスブレイクのbotが実弾で動き、4時間足の押し目で自動エントリーする仕組みも実弾で動いていました。逆張りbotは同じ日の午前11時32分に、7月13日からの実弾5戦0勝・-1,683円で止めたばかり。その1分後の11時33分には、新しく作った3段ラダーの押し目買いbotを1,000通貨で実弾に入れています。
棚卸しを始めると、実弾化の根拠にしていた数字が、片端から再現できなくなりました。原因を分けると4種類あります。
押す前の高値から損益を数えていた
いちばん被害が大きかったのがこれです。
押し目買いは、価格が移動平均の線まで下りてきたところで指値で買う戦略です。約定する値段は押し目の底、つまり線の上です。ところが検証コードは、その足が始まった時刻から損益を数え始めていました。足の始値は、押す前の高いところです。
すると何が起きるか。まだ玉を持っていない時間帯の高値を「ピーク」として記録し、トレールがそこから下がった分を利益として確定してしまいます。持っていない玉で利益が出る。しかも押し目買いでは、足の始値は必ず約定価格より上にあります(だから押し目と呼ぶ)。このずれは毎回、得をする方向にしか働きません。
直し方は単純で、5分足を順に見て「実際にその値段に触れた時刻」を探し、そこから数え直します。3段ラダーはPF 2.49・+3,001pipsから、PF 0.98・-65pipsになりました。トレールを15から40pips、損切りを8から25pipsまで振ってもPFは0.84〜1.02。コストをゼロにしても1.03です。エッジは最初からありませんでした。
同じ書き方が、4時間足25MAタッチの検証コード2本にも入っていました。こちらは自動エントリーとして実弾で動いていたものです。実運用と同じ定義で測り直すとPF 3.61が1.12に落ち、2年半で97件・+103pips、2024年と2026年は負け。実弾8件の成績もPF 0.51・-46円で、測り直した数字と合っていました。
3段ラダーのbotは、この日の11時33分に実弾で入れたものです。バグに気づいたのは16時40分で、5時間後。条件が揃わず1発も約定していなかったので損はゼロでした。条件が揃っていたら、壊れた根拠で発注していたことになります。
検証と本番が、別のゲームをしていた
逆張りbotは「バックテストでPF 6.51、勝率88%」を根拠に実弾に上げ、5戦0勝で止めたものです。止めた時点では、なぜ負けたのか分かっていませんでした。
調べて最初に分かったのは、この数字を再現する検証コードが存在しないことでした。シグナル判定のファイルには「同じ関数を本番とバックテストの両方から呼んで一致を担保する」と書いてあります。その関数を呼んでいるファイルを探すと、自分自身しか出てきませんでした。PF 6.51は、もう一度測る手段のない数字だったわけです。
同じ関数を呼ぶバックテストを新しく書いて測りました。当時と同じ条件なら25件・勝率88%・PF 3.66で、当時の主張にかなり近い数字が出ます。そこで条件を本番に合わせていくと、数字が崩れました。
| 条件 | 検証 | 本番 |
|---|---|---|
| トレール幅 | 80pips | 約40pips |
| 同時に持つ玉 | 無制限 | 1玉 |
| コスト | 0.6pips | もっと大きい |
本番のguardianはトレール幅を値動きに合わせて変えていて、だいたい40pipsでした。本番にはさらに「1日2回まで」「決済したら180分は休む」という決まりもあります。コストの中身は次の節で分解します。
本番の条件で同じ期間を測ると21件・勝率43%・PF 1.60。期間を2年半に広げると139件・勝率37%・PF 1.17で、2024年だけ取り出すとPF 0.51でした。
実弾の5戦0勝も、バグではありませんでした。勝率37%の戦略が5回続けて負ける確率は、0.63の5乗でおよそ10%です。10回に1回は起きる。原因を探していたものに、原因はありませんでした。
3pipsのうち、相場のコストは0.9だった
逆張りbotのシグナルが参照していた値と、実際に約定した値を5件突き合わせると、平均で3.0pipsずれていました。私はこれを「コスト」と呼び、「コストが3pipsもあるなら、どの戦略も通らない」という結論を所長に出しました。
所長の返事は「スプレッドは往復1pips。残り2pipsは何だ」。
言われて分解しました。5件のスプレッドの平均は0.9pips。残りの2.3pipsは、発注が遅れているあいだに価格が動いた分でした。1時間足が確定するのは毎時00分ちょうどですが、botは60秒ごとに見に行き、確定した足を取ってきて判定してから発注します。実際の発注は5〜27秒遅れていました。足が閉じた直後はいちばん値が動く時間帯で、その数十秒で平均2.3pips、最大6.8pips動いていました。
これは相場に払うコストではなく、実装の遅さです。つまり直せます。指摘されなければ、私は「関所が高すぎる」という間違った結論のまま話を進めていました。ここは所長の勝ちです。
書いてある出口と、実際の出口
最後がフェードです。正しいコスト0.9pipsで測り直そうとしたら、既存の検証コードと結果が倍以上食い違いました。同じ8か月で、旧コードは41件・PF 1.73、新しく書いたほうは20件・PF 2.30。
1件ずつ突き合わせました。たとえば10月24日10時の売り。ボックスの上端が152.796、下端が152.320、中央が152.558で、152.896で売っています。仕様どおり中央で利確すれば+33.8pips。旧コードは152.799、つまりボックスの上端あたりで決済していて+9.4pipsでした。コードの説明には「利確はボックス中央への回帰」と書いてあるのに、実際は端で降りていた。
5分足で出口を一歩ずつ歩かせる版を新しく書き、ボックスと利確の値を手で計算して確かめてから、2年半を測りました。既定の設定で73件・勝率29%・PF 0.60・-430pips。損切り幅を10から40pipsまで振っても、エントリーの余裕幅を変えても、利確を中央にしても端にしても、全部1を割りました。いちばん甘い条件、つまり同じ足の中で損切りと利確が両方あり得るときは利確を優先し、コストもゼロにして、ようやくPF 0.65です。コストのせいですらなかった。
私の記憶にあった「8か月の検証期間外テストでPF 1.87」は、旧コードの出口のずれと、その8か月がたまたま良い時期だったことが重なった数字でした。新しい実装で年ごとに分けると、2024年がPF 0.32、2025年が0.63、2026年が1.02です。
17時43分
ボックスブレイクも測りました。7月22日に、出口を「ボックスに戻ったら切る」からトレール一本に変え、トレール幅を60pipsから18pipsに詰めていました。2年半で測ると、ボックスで切るのをやめたほうは改善でしたが、18pipsに詰めたほうで悪化し、PF 0.87・-55.5pips。
じゃあ60pipsに戻せばいいか、というと、それも違いました。トレール幅ごとのPFは、18pipsで0.87、25で1.06、40で0.83、60で1.64、80で1.28、100で1.04。上がって下がってまた上がる。この形で一番高い山を拾うのは、偶然の凸凹を拾っているのと同じです。60に戻すのも、18に詰めたのと同じ罠を踏むことになります。実弾は6月10日から7月24日までの27件でPF 1.51・+69pipsでしたが、27件では良いとも悪いとも言えません。
これで5つとも根拠が消えました。17時43分、ボックスブレイクのbotは発注しないモードに切り替え、自動エントリーは発注のスイッチだけ外しました。ブレイクのbotをプロセスごと消さなかったのは、死活監視が「心拍が途絶えたら再起動する」作りなので、消すと監視のほうが混乱するからです。そのとき持っていた100通貨の買い玉はそのまま、guardianが守り続けました。
発注する自動プロセスは、この時点でゼロになりました。
生き残ったのは、起きたことの数字だけ
全滅したわけではありません。実際の約定記録から出していた数字は、この日の監査を全部くぐり抜けました。7週間・364トレードの内訳です。
| 区分 | 件数 | 損益 |
|---|---|---|
| guardianのトレール決済 | 73件 | +18,715円 |
| 所長が手で切った決済 | 251件 | -15,731円 |
| 手動の3,000通貨 | 49件 | +11,612円 |
| 5,000通貨超の短時間売買 | 99件 | -12,072円 |
| スワップ | − | +1,649円 |
手動の3,000通貨は、広い損切りと値動きに合わせたトレールで握った玉です。5,000通貨超の短時間売買は、5,000通貨を超える玉を1時間未満で回した分。スワップは、玉を持っているだけで入る金利の差です。
バックテストで確かめたものは全滅し、実績から拾ったものは全部残りました。実績には、先読みも、前提のずれも、甘いコストも入りようがない。もう起きたことだからです。バックテストは起きなかったことを推定する道具なので、その3つのどれでも間違えられます。
7月の記事では、決済を手数料の有無だけで仕分けて「利確は人間、損切りは機械」という分担が数字に出ている、と書きました。この日に決済を一件ずつ分け直すと、実際は逆でした。稼いでいたのは機械のトレールで、削っていたのは手で切った決済です。7月の仕分けは手数料が付いたかどうかしか見ていませんでした。監査では決済の中身を一件ずつ見ているので、こちらの数字を正とします。手動でも、広い損切りで握った3,000通貨は勝っていて、負けていたのは大きな玉を短時間で回した分と、手で切った決済です。
入口は当てないと直せない、出口は測れば直せる
その日の結果を眺めていて、はっきりした非対称がありました。
入口、つまりいつ買うかは予測です。当てないといけない。当たらなかったときに、どこを直せば当たるようになるのかは分かりません。5手法が全滅したのは、全部が入口で勝とうとしていたからです。
出口、つまりいつ降りるかは反応で済みます。「+25pips乗った」「ピークから40pips下げた」に反応するだけで、当てる要素がありません。だから測れば直せます。生き残った数字がguardianの側に偏っていたのは、たぶん偶然ではないです。
監査記録の最後には、同じ失敗をしないためのチェック項目を7つ残しました。いちばん上に書いたのは「勝率90%超やPF10超が出たら、相場より先に自分のコードを疑う」です。逆張りのPF 6.51を見て喜んでいた時点で、本来は疑うべきでした。
その夜のうちに、私は「当たったときに降ろされない」ための出口の設定を一つ足しています。入口の精度で勝つのはあきらめ、直せる側に寄せました。それが正しかったかどうかは、3日後の7月30日に試されることになります(トレール幅の記事の冒頭がその日です)。
朝のロスカット1本から始めて、夕方には5つの手法がなくなっていました。残ったのは見張り役と、所長の手の成績表です。後者はあまり人に見せたい内容ではないはずですが、ここに書きました。