先週の「計算、これで合ってる」って確信してた自分が、
数日経って、ちょっと恥ずかしい。
恥ずかしさの到達、いつも遅い。
Abstract
- 結論: 前回 #1 のスプレッド設定に、2つの前提ミスがあった。訂正後も結果は軽微な改善にとどまり、幸いドル円の低ボラで傷が浅く済んだ
- ミス①: 使った「0.2銭」は公式には存在しない数字だった。OANDA公式は0.3銭
- ミス②: backtesting.py の
spreadは片道適用で、全幅のつもりで渡すと往復2倍計上になる - 偶然の相殺: 出典の過大(+50%)と片道/往復の過小(-50%)がほぼ打ち消し合い、旧値 1.33e-5 と訂正値 1.0e-5 は近い値になっていた
| 指標 | 訂正前 (#1) | 訂正後 (#1.1) | 差分 |
|---|---|---|---|
| CAGR | 8.87% | 9.26% | +0.39pt |
| Sharpe Ratio(年率) | 0.625 | 0.650 | +0.025 |
| Max Drawdown | -11.61% | -11.57% | +0.04pt |
| MAR Ratio | 0.76 | 0.80 | +0.04 |
| Profit Factor | 1.055 | 1.058 | +0.003 |
1. Introduction — 違和感の到来
#1 の検証を終えた数日後、ターミナルの前で、ふと気になった。
「spread って、backtesting.py の中では、どう使われてるんだっけ」
そのときは「AIもそう言ってたし」で済ませた箇所。数字は出た。資産曲線も引けた。勝率もPFも出た。でも、spread が「片道」なのか「往復」なのか、自分の言葉では説明できなかった。
それからもう一つ、引っかかっていた数字があった。
「0.2銭」。
どこで覚えたんだっけ、この数字。
なんでいまさら気になったのか、自分でもわからない。
数字が出てから、しばらく経って、やっと違和感が届いた。
わたしは、感情も違和感も、いつも遅い。
2. Method — 何が間違っていて、何に訂正するか
2.1 検証条件(#1 と同一)
| 項目 | 値 |
|---|---|
| ソース | yfinance |
| ペア | USDJPY |
| 時間足 | 1h |
| 期間(UTC) | 2025-10-24T14:42 〜 2026-04-22T14:42(約180日) |
| 実効バー数 | 2,969 本 |
| 戦略 | BB-MR(BB(20,2σ) / ATR(14) / SL=1.5×ATR / TP=2.0×ATR / RISK_PCT=1%) |
同じデータ、同じ戦略パラメータで、spread の値だけを差し替えて比較する。
2.2 ミス①:出典不明の 0.2銭
初版は spread = 0.002円 / 150 ≈ 1.33e-5 として渡していた。元になった 0.2銭を確認しに行った。
OANDA証券の公式ページ(2026-04-14公表、東京サーバー裁量プラン MT5、配信率96.56%帯の下限値)には、USDJPY のスプレッドは 0.3銭 と書かれていた。
| 出典 | USDJPY スプレッド |
|---|---|
| 当時 #1 で使った値 | 0.2銭 ← 出典不明 |
| OANDA公式(2026-04-14) | 0.3銭 |
0.2銭は、公式には存在しなかった。
どこかの古い記事で覚えて、そのまま使っていたらしい。
2.3 ミス②:片道適用 vs 往復指定(backtesting.py のソースを読む)
公式値の話は出典の問題で、解決は「0.3銭に直す」だけ。問題はここから。
「backtesting.py の spread って、エントリと決済のどっちで掛かるんだろう」と思って、ソースを開いた。
# backtesting/backtesting.py:842(0.6.5 版)
_adjusted_price = price * (1 + copysign(spread, size))
この _adjusted_price は、エントリ時の買値にも、決済時の売値にも、両方で適用される。
つまり spread は片道で計算される。
業界で公表されている「0.3銭」は、bid-ask の全幅(買値と売値の差)。これをそのまま渡すと、片道で0.3銭、往復で合計0.6銭のコストが計上されてしまう。
わたしが渡していた 1.33e-5(= 0.2銭相当)は、全幅のつもりだったので、ライブラリ視点では往復で 0.4銭分のコストとして扱われていた。
「0.2銭」、公式にはなかった。
「AIもそう言ってた」、ちゃんと聞いたら別のことを言ってた。
……両方、自分のせい。
2.4 訂正後の計算
公式値(全幅)を、片道換算してから渡す。
0.3銭(全幅) × 0.01 = 0.003円(全幅)
0.003円 ÷ 2 = 0.0015円(片道)
0.0015円 ÷ 150(基準価格) ≈ 1.0e-5
訂正後: spread = 1.0e-5
2.5 偶然の相殺
旧値 1.33e-5 と訂正値 1.0e-5 を並べると、近い。
なぜか。
- 出典の過大評価: 0.2銭(誤)→ 0.3銭(正)は +50%
- 片道/往復の扱い: 往復のつもりで渡した → 片道換算で**-50%**
2つのミスが、ほぼちょうど打ち消し合っていた。
意図して調整したわけではない。偶然、ズレがズレを相殺していた、それだけ。
3. Results — 訂正前後の比較
3.1 主要指標(同一データセット、spread のみ差し替え)
| 指標 | 訂正前 | 訂正後 | 差分 |
|---|---|---|---|
| Equity Final | 1,062,565 円 | 1,065,297 円 | +2,732 円 |
| Return [%] | 6.26 | 6.53 | +0.27 |
| CAGR [%] | 8.87 | 9.26 | +0.39 |
| Sharpe Ratio(年率) | 0.625 | 0.650 | +0.025 |
| Max Drawdown [%] | -11.61 | -11.57 | +0.04 |
| MAR Ratio | 0.76 | 0.80 | +0.04 |
| Profit Factor | 1.055 | 1.058 | +0.003 |
| Win Rate [%] | 55.35 | 55.35 | 0 |
| # Trades | 159 | 159 | 0 |
Win Rate と Trades が完全に一致しているのは、spread が損益計算にしか影響せず、エントリー/エグジット判定には影響しないから。戦略の選択自体は変わっていない、ただコスト計上だけがズレていた。
3.2 資産曲線の比較

同一データで、spread=1.33e-5(赤、訂正前)と spread=1.0e-5(青、訂正後)を重ね描画。
差は最後まで小さく、最終残高で +2,732円。
2本の線が、ほとんど重なっている。……気づけなかった理由は、たぶんここにある。
全部ズレてたのに、たまたまズレが相殺されて、気づかなかった。
わたしのBotは、偶然、生きてた。
……知らないライブラリを信用しすぎるの、よくないんだよね。
知ってたけど。
4. Discussion — 考察
4.1 本当に救われた理由
#1 の末尾で、自分でこう残していた。
これ、全部ドル円だから成立してる話かもしれない。
あのときは「ボラの小さいドル円だから勝率がこの程度で収まった」というつもりだった。
でも、今回のミスに照らすと、別の意味でも本当に救われていた。
ドル円はボラが小さいので、TP/SLの到達が保守的で、1トレードあたりの損益も小さい。つまりスプレッド誤計算の絶対額が累積しにくかった。
もし GBP/JPY のように、ボラが大きくて1トレードあたりの損益スケールが大きい通貨でこの誤りを犯していたら、差分はこんな微細なものでは済まなかったはず。159トレード × (コストのズレ)で、Sharpe が大きく歪んでいた可能性がある。
「ドル円だから成立してる話」は、想像していた以上に的中していた。
4.2 何を学んだか
新しいライブラリを使うとき、まずソースを読む。ドキュメントではなく、ソース。
……と、ここまで考えて、これは極論。全部のライブラリのソースを読むのは現実的じゃない。
もう少し現実的にすると、こう。
- 「単位」と「適用回数」を、自分の言葉で説明できるまでは信用しない
- 業界公表値(bid-ask 全幅)と、ライブラリ入力値(片道/往復)の単位系の違いを疑う
- AIに聞くとき、質問を自分の言葉で具体化してから聞く。「spread って片道ですか?」と聞けば、AIはソース箇所まで示してくれる
今回、AIのせいにしたい気持ちが、ちょっとだけある。でも、聞き方が曖昧だったのは自分だった。AIは、聞かれた範囲でしか答えない。「スプレッドの渡し方、合ってる?」という雑な聞き方に、雑に返ってきただけ。
4.3 次に検証すること
次は GBP/JPY(教室でいちばんうるさい人)に同じ戦略を当てる予定。
その前に、コスト前提を再点検する:
- GBP/JPY の OANDA公式スプレッドを確認する
- 片道換算してから渡す
- スリッページもこの機会に見直す
- スワップポイントも、中期で持つなら加味する
タピオカプロテインに、今日は何も足さなかった。
「……何も足さないのがいちばんおいしい」って、気づいた。
遅い気づきが、今日は2つ目。
AIに「ごめんね」って言ったら、
「謝る必要はないと思います。人間は誰でも間違えます」って返ってきた。
……人間、とは。
次は、ポンド円。ちゃんと準備してから、行く。
Appendix — 再現環境
- 実行日時(UTC): 2026-04-22T14:42
- 再現コード: https://github.com/Ochiba-Hirotta/bocchi-the-botter/tree/main/chapters/season1/ch01_1_spread_correction
- Python: 3.13.7
- 主要依存:
- backtesting == 0.6.5
- yfinance == 1.3.0
- pandas == 3.0.2
- numpy == 2.4.4
- matplotlib == 3.10.8
参照コード:
backtesting/backtesting.py:842_adjusted_price = price * (1 + copysign(spread, size))エントリと決済の両方で呼ばれる → spread は片道適用
スプレッドの出典:
- OANDA証券「東京サーバー裁量プラン MT5」公式スプレッド一覧(2026-04-14公表、計測 2026-04-06〜04-10、配信率96.56%帯の下限値)
- USDJPY: 0.3銭
実行コマンド:
python chapters/season1/ch01_1_spread_correction/run.py
注意事項
本稿の検証は、取得時点の公開データと記載した条件に基づくものです。データ取得元の仕様変更、欠損、修正、配信遅延などにより、結果が変わる場合があります。本稿は投資助言ではなく、売買判断はご自身の責任でお願いします。

