「この一本だけ持っていく」と決めたのが、うれしかったらしい。
気づいたのは、三日あとだった。
Abstract
- 出典: Trading Steady「Is the ORB Strategy Actually Profitable? 6 Month Results」
- 対象: OANDA practice / USDJPY / M5(bid・ask)の固定抽出から集約した、完全な15分足だけ
- やったこと: 1時間足へ翻訳していたORBのレンジを、原文の9:30–9:45 ET(M15一本)へ差し戻し、bid/ask別のOHLCで約定を判定する事前固定ルールで検証
- 結果: 事前に決めた主判定(決済後残高のリターンがプラス、かつ固定5区間中3区間以上で実現損益がプラス)は不成立
- 非対象: 原文(EURUSD・原著者の環境)の再現・評価、以前の1時間足版との成績比較、実注文の約定再現、実運用の可否
| 指標 | 主版 |
|---|---|
| トレード数 | 292 |
| リターン | -21.647934% |
| 最大ドローダウン(決済後残高) | -26.542576% |
| プロフィットファクター | 0.833976 |
| 勝率 | 44.178082% |
| プラスの固定区間 | 1 / 5 |

1. Introduction — 切符
あの夜は、判断を終えただけだと思ってた。
M5の正本から、9:30 ETの15分足を一本、m15_opening_rangeという変数に置いた。それだけ。うれしいとか、そういうのは、なかったはずだった。
三日たって、気づいたら、あの1時間足のコードを開いていた。
一本の意味が、途中で変わってた。置いたときは「持っていく」だった。三日のあいだに、「これがあれば、あの妥協を消せる」になってた。
前にORBを検証したとき、9:30の15分足が手元になかった。yfinanceの15分足は60日ぶんしか遡れない。だから9:00〜10:00の1時間足へ、レンジを翻訳した。レンジの時刻も、ATRの平均も、16:00の線も、最後はわたしが決めた。決めていたと分かって、怖くなった。そこまでが、この前までのこと。
今は、ある。9:30ちょうどに始まる、三本揃いを確認済みの15分足。
エディタで、1時間足のレンジ定義をコメントアウトする。その下に、M15の一行を書く。
これでやっと、本物のORBが走る。
そう思った。切符を手に入れた人みたいに。
2. Method — 戻った一本、残った条件
2.1 レンジだけは、戻る
いきなり全期間は走らせない。まず一日ぶん。
9:30–9:45 ETの完全なM15一本。そのbidのhighとlowを、レンジの上端と下端にする。前に「9:30から始まる足は、どこにもない」と残っていた場所に、今度はその足が入る。
9:30の足が、9:30のまま置けた。
そこだけは、ちゃんと戻った。
2.2 残った条件を、一つずつ見る
次の条件へ進んで、手が止まる。
通貨。原文はEURUSD。わたしのDBには、USD_JPYしかない。EURUSDを新しく取りにいくこともできるけど、それで増えるのは別のデータの別の検証で、この検証が原文になるわけじゃない。USDJPYのまま。……これは、戻らない。
ブレイクの確認。原文で確認できたのは「ブレイク確認後」まで。終値で確認して、次の足の始値で入る、と決めたのは前回のわたしだった。足を15分にしても、この決め方は要る。戻らない。
12:00 ET以降は建てない。残る。
ATR。原文では平滑の取り方を確認できなかった。14本の単純平均は、わたしが置いた数字。足はM15になっても、平均の取り方はわたしの判断のまま。残る。
16:00に閉じる線。原文にはなかった線。ここにも、そのまま残る。
一つ差し戻して、五つ残った。
| 項目 | 1時間足版 | 今回 | 扱い |
|---|---|---|---|
| レンジ | 9:00–10:00 ETの1h一本 | 9:30–9:45 ETのM15一本 | 今回戻したもの |
| 通貨 | USDJPY | USDJPY | 翻訳のまま残る |
| エントリー | 終値確認 → 次足始値 | bid終値確認 → 方向別の次足始値 | 残って、判断が増える |
| ATR | 1hのTR 14本SMA | M15のbid TR 14本SMA | 足は戻るが平滑判断は残る |
| 12:00 / 16:00 | あり | あり | 翻訳のまま残る |
| コスト | 固定スプレッド0.3銭 | bid/askの実データ。固定幅の追加なし | 方式を置き換える |
2.3 細かいデータで、判断が増える
表のいちばん下の行が、思っていたのと逆だった。
貯めてきたデータには、bidとaskのOHLCが別々にある。前回みたいに「midに固定0.3銭」と仮置きしなくていい。仮定が一つ消える、と思ってた。
開いてみると、bidとaskのhigh/lowは同じ時刻とは限らないから、混ぜて作れる「正しいmidの高値・安値」がない。レンジもブレイクもATRも、どの系列で見るのか。買いはどっちの価格で約定して、売りはどっちで出るのか。
決める場所が、増えてた。
bidを判定の系列に固定する。レンジも、ブレイク確認も、ATRも、ぜんぶbid。約定だけ方向で分ける。買いはaskで入ってbidで出る。売りはbidで入ってaskで出る。固定スプレッドは、もう足さない。
細かくなったら、決めなくて済むと思ってた。逆だった。
書いた分岐の、真ん中はここ(orb_m15.py):
if side == "long":
entry_price = float(entry_bar["ask_open"])
stop_loss = range_low
initial_risk = entry_price - stop_loss
take_profit = entry_price + RR * initial_risk
else:
entry_price = float(entry_bar["bid_open"])
stop_loss = float(range_bar["ask_high"])
initial_risk = stop_loss - entry_price
take_profit = entry_price - RR * initial_risk
決済側の価格、同一足の優先順、窓ぎわの隙間の扱いは、後ろに全部書く。
2.4 揃っている日を、自分で決める
もう一つ、先に決めることがあった。
一日を9:30から16:00まで15分足で追うには、開始時刻が27個、一度ずつ要る。レンジの一本だけじゃなくて、その後ろの26本も。
途中まであった一日を見つける。9:30はある。昼のどこかが、ない。
欠けた場所に前の足を写せば、追えたことにはできる。それは、この前一度やめたやり方だった。一本でも欠けた日は、その日ぜんぶを見送る。欠ける前までの取引も、しない。
920暦日のうち、27本揃った日が651日。揃わなかった日が269日。269日のほとんどは、必要な時刻に足が一本もない日だった。途中まであった日は、1日だけだった。全部を「欠損」とは呼ばない。分類は後ろに置く。
651日から、さらに減る。レンジ幅がATRの1.25倍に届かない日が341日。3.0倍を超えた日が8日。通ったのは302日。そこから、12:00に間に合わなかった7日と、ブレイクが出なかった3日が消えて、292回。
| 項目 | 固定条件 |
|---|---|
| 入力 | 固定範囲のM5抽出 → 完全なM15 |
| 窓 | ET [2024-01-06, 2026-07-14)、920暦日 |
| 有効な日 | 9:30〜16:00 ET開始のM15が27本、一度ずつ揃う日 |
| レンジ | 9:30–9:45 ETのbid high / low |
| ATRフィルタ | bidのTR 14本SMA、レンジ幅が1.25〜3.0倍 |
| エントリー | 最初のbid終値ブレイク後、12:00 ET未満の次足始値 |
| 約定 | 買いask→bid / 売りbid→ask |
| 決済 | 1.5R、同一足はSL優先、16:00 ETの始値で強制手仕舞い |
| サイズ | 初期残高100万円、リスク1%、証拠金率4% |
| コスト | 手数料0、通常スリッページ0、スワップ0、固定スプレッド追加なし |
| 調整 | 結果を見て条件を変えない |
3. Results — 調整しない
3.1 数字
走らせる前に、入力のhashを、前に固定した抽出の値と照合した。一致。走らせる。
| 指標 | 結果 |
|---|---|
| トレード数 | 292 |
| 最終残高 | 783,520.656円 |
| リターン | -21.647934% |
| 勝率 | 44.178082% |
| 最大ドローダウン(決済後残高) | -26.542576% |
| プロフィットファクター | 0.833976 |
| 平均実現R | -0.079005 |
| プラスの固定区間 | 1 / 5 |
| 主判定 | 不成立 |
-21.65%。
もう一度見ても、マイナスだった。プラスの区間は、五つのうち一つ。事前に決めた判定は、不成立。
3.2 直す場所を探す
数字より先に、どこを直せば別の結果になるかを考えていた。
実行条件を見る。合ってる。テストを回す。通る。区間の境界。事前に置いた日付のまま。入力のhash。さっき照合した。……間違いは、見つからない。
1.25。3.0。1.5R。12:00。16:00。どれかを動かせば、別の数字が出る。
カーソルが、1.25の上にあった。
変更せず、閉じた。
3.3 内訳
買いが161回、売りが131回。閉じた理由は、16:00の強制手仕舞いが126回、損切りが121回、利確が45回。

292回の決済理由。時間切れ(close_16)と損切り(sl)で大半を占め、利確(tp)は45回。
3.4 固定した5区間
窓は先に、184日ずつ五つに切ってあった。
| 区間 | 取引数 | 実現損益 |
|---|---|---|
| 1 | 59 | -11,931円 |
| 2 | 68 | +34,729円 |
| 3 | 64 | -46,990円 |
| 4 | 56 | -68,894円 |
| 5 | 45 | -123,393円 |

920暦日窓を開始日から184日ずつ5区間に固定分割。プラスは区間2だけ。区間ごとの標本は45〜68本で、傾きの理由までは、ここからは言えない。
固定した五つの区間のうち、プラスは区間2だけだった。観測は、そこまで。
4. Discussion — 隣
4.1 この数字の場所
この-21.65%が判定しているのは、わたしが走らせる前に固定した条件の集合。原文の手法じゃない。EURUSDでもないし、原著者の環境でもない。
1時間足版とも、比べられない。データの取得元も、期間も、価格の系列も違う。15分足へ戻したから負けた、とも言えない。レンジと一緒に、約定の方式も、データの出どころも変わってる。
数えてみる。原文から確認して運んだ条件。前回から残った翻訳。今回増えた判断。それが一つの場所に集まって、この数字が出た。集めたのは、ぜんぶわたしだった。
原文の場所でもなくて、1時間足版の場所でもない。第三の条件の集合に、この結果は属してる。
4.2 上書き
保存しようとして、ディレクトリ名の入力欄で止まった。
三日前のわたしは、1時間足版を仮の姿だと思ってた。データがなかったせいの、代用品。だったら、15分足版で置き換えれば済む。同じ名前で保存し直せばいい。
orb_1h_translationのディレクトリが、画面の隅にある。82本の明細と、あのときの条件が、ぜんぶそこに残ってる。
手が、止まった。
4.3 名前
15分足版に、orb_m15_retranslationと名前を付けた。
failedとは付けない。負けたことは、名前にしない。原文版とも、完成版とも呼ばない。
1時間足版は、そのまま。292件の明細と、-21.65%の結果も、同じ場所に保存する。
ファイラーの中で、二つのディレクトリが並んだ。
少しのあいだ、それを見てた。
戻る場所はなかった。隣に置いて、先へ進む。
Appendix — 再現条件と技術的な境界
A. 入力と同一性
| 項目 | 値 |
|---|---|
| source / environment | oanda_rest_v20 / practice |
| instrument / granularity / price | USD_JPY / M5 / BA、complete=1のみ |
| 固定抽出範囲 | [2024-01-01T22:00:00Z, 2026-07-14T10:05:00Z) |
| M5行数 | 188,981 |
| M5抽出SHA-256 | f6d0e1cd1bd50ec11f7f3f0bd34e31b61a39970a687f3c5ac83682ae2ea1d512 |
| complete M15 / 不完全bucket | 62,957 / 61 |
M15はchapters/season2/ch02_minute_data_dbの公開実装aggregate_m5_to_m15で集約した派生で、M5 SQLiteを正本のまま使う。完全再計算には同一の非公開SQLiteが必要。行単位データは同梱しない。
B. 記事窓とsession完全性
- 取引窓: ET日付
[2024-01-06, 2026-07-14)の920暦日。ATRのwarm-upだけ窓前データを使用 - 固定5区間(各184暦日、開始日を含み終了日を含まない): 境界は
2024-01-06 / 2024-07-08 / 2025-01-08 / 2025-07-11 / 2026-01-11 / 2026-07-14 - 有効な日: 9:30〜16:00 ET開始のM15が27本、一度ずつ揃う日。一本でも欠ける日は、欠損前までの取引や保有継続をせず日全体を見送り
- 内訳: 有効651日 / 見送り269日(必要時刻に0本: 268日、途中まで: 1日)
- 市場休場かデータ欠損かは、この集計では個別に分類しない
C. ATR・シグナル・エントリー
- ATR: bidのTrue Range(
max(H−L, |H−前C|, |L−前C|))の14本単純平均。現在足を含む直近14本の完全M15。9:30足の確定時点で判定し、未来参照なし - フィルタ:
1.25 <= レンジ幅 / ATR <= 3.0。651日の内訳は、1.25未満341日 / 通過302日 / 3.0超8日 - 通過302日のうち、12:00カットオフ7日 / ブレイクなし3日 / 取引292日
- エントリー: 最初のbid終値ブレイク確認後の次足始値。11:45のエントリーは可、12:00以降は不可。1日最大1回
D. 方向別の約定と決済
- 買い: askで建て、SL・TP・16:00決済はbidで判定。SLは
9:30足のbid low - 売り: bidで建て、SL・TP・16:00決済はaskで判定。SLは
9:30足のask high - TPは建値からの実行側初期リスクに対して1.5R。エントリーした足も判定対象
- 各足の優先順: 不利な窓開き → 有利な窓開き → 同一足はSL優先 → 単独のSL/TP。ただし16:00 ET開始足は、この判定より先に始値で強制手仕舞い。窓開き決済は0件だった
- コスト: 手数料0、通常スリッページ0、スワップ0。固定スプレッドは追加しない(bid/askの幅が価格に含まれる)
- エントリー時の始値quote幅は最大0.096円。結果を見たあとの外れ値除外はしていない
- 履歴のbid/ask OHLCによる約定モデルであり、実注文の約定再現ではない
E. 指標の定義と限界
- リターン:
最終残高 ÷ 1,000,000 − 1。最大DDは初期残高と各取引の決済後残高から算出し、保有中の時価評価を含まない - PF: 総利益 ÷ 総損失の絶対値。実現R: 方向別の単位あたり損益 ÷ 初期リスク
- サイズ: 初期残高100万円、1回の初期リスク1%、証拠金率4%
- M15のOHLCから、足の中の価格経路、SL/TP到達時点のスプレッドは復元できない
- この結果を、原文EURUSD、1時間足版、他の期間、ORB一般へ広げて読まない
F. 再現コードと確認
- 再現コード: GitHub
- 参照結果の再計算:
python chapters/season2/ch03_orb_m15_retranslation/run.py --db "$SOURCE_DB" --private-output-dir ../../code/data/s2_3_orb_m15 --reference-dir results/reference/ch03_orb_m15_retranslation - 独立検証: 同chapterの
verify.py(集計のみのrow-freeモードと、--trades付きの完全モード) - 図の再生成: 同chapterの
figures.py(row-freeの参照JSON/CSVから生成) - Python 3.13.14 / pandas 3.0.2。入力・コード・集約物・図のSHA-256は
results/reference/ch03_orb_m15_retranslation/hashes.json
この固定条件とデータ仕様に基づく検証です。データ仕様の変更・欠損・修正などで結果は変わりえます。投資助言ではなく、売買判断はご自身の責任で。
