Trading the opening-range breakout
The rule
The market's first half hour sets a high and a low. When price closes beyond one of them, the reasoning goes, the day has picked a direction and you go with it. It is the most traded intraday pattern there is, and it has an honest story behind it: the opening range is where the overnight disagreement gets settled.

The verdict
No measurable edge — and we did not stop at one test. Five separate versions of this idea, each with the filter someone swears by: volatility regime, order flow, the two of them stacked with a clear-path condition, a trailing runner on high-gamma days, a volume spike with an ADX trend filter. Sixty-three pre-registered parameter combinations. Not one passed.
Hypothetical backtest results. Read the full disclaimer at the bottom of this page.
What we actually tested
We wrote each configuration down, committed it, and only then ran it. That order matters: if you fix the rules after seeing the results, you are no longer testing an idea, you are describing one. Here it matters twice, because five tests give you five chances to fool yourself.
The base setup, identical in all five: the opening range is 09:30–10:00 ET. Entry is the first close beyond the range high or low, long and short, filled on the next bar. The stop is one opening-range width. Flat at the end of the day. Costs — one point round turn plus one tick of adverse slippage — are charged on every fill.
The five variants:
- Volatility regime. Only take the break when the day is expansive: yesterday's ATR above its 14-day average, or VIX above 18, or both. Four reward-to-risk targets each.
- Order flow. Only take the break when cumulative bar delta makes a new extreme at the same moment — the "confirmed breakout". Plus the mirror version: fade the break when confirmation is missing.
- All of it at once. Regime and a clear path to target (no prior-day point of control in the way) and order-flow confirmation. This is the version most traders actually believe in — the one where the edge is supposed to come from the combination.
- Let the winner run. On days when the dealer-gamma proxy says moves extend: take half off at +2R and trail the rest, instead of a fixed target. Three trailing methods, three caps.
- Volume and trend. Break of the session high or low, but only with volume above twice its 20-bar average and ADX above 25.
What ties them together: every one of the 63 cells was written down before the run, and every one is reported — including the ten that came out positive. The instrumented runs we exported these numbers from reproduce the original engines trade for trade, maximum deviation zero, in all five families.
What the numbers say

Hypothetical backtest results. Read the full disclaimer at the bottom of this page.
Ten of the 63 cells made money on average. None of them passed.
That sounds like a contradiction and it is the most useful thing on this page. A cell passes only if it also beats its own control — the same days, the same costs, entries placed at random. The volatility family's headline cell — VIX above 18, a 1.5R target, 959 trades and +0.0135 R — landed at the 63rd percentile of that control distribution. We require the 95th. So the filter did something real: it picked days that moved. It just did not pick entries that were better than chance on those days. The volatility filter is exposure selection, not a trigger edge — it changes when you are in the market, not whether your entry is worth anything.
Three findings we did not expect and are not going to bury:
The confirmation hurt. Waiting for order flow to confirm the break did not improve anything — it made it worse. At a 2R target the confirmed breakouts averaged −0.0975 R against −0.0587 R for taking every break. Roughly four hundredths of an R worse, per trade, for being patient. The confirmation arrives when the move is already paid for.
Stacking made it look better and hold worse. The version with everything switched on — regime and clear path and confirmation — produced the second-best average in the whole study, +0.0119 R. It also produced 345 trades where taking every break in the same family produced 2,288, and it sat at the 18th percentile of its null distribution and the 10th of the random-entry control. That is what stacking does: each filter removes trades, the survivors look handpicked because they were, and the result does not survive contact with a control. We tested the thing everyone means by "confluence", and it is the weakest result we have.

The runner never showed up. The "let your winners run" version takes half off at +2R and trails the rest, which costs a third fill on every trade. Of 1,572 breakouts on those days, 168 — under 11 % — ever reached the +2R scale-out at all, and 55 ran far enough to hit the 3R cap. The remaining 1,404 paid for a runner they never got. All nine runner cells came out below the plain 2R baseline. The fat tail is real; it is just rarer than the cost of fishing for it.
And the one from the volume-and-ADX version, which we put in words rather than in a picture because it needs its window named: on IS 2015–21, a random entry on the same volume-and-ADX days averaged +0.30 R, while the filtered breakout averaged −0.02 R. The filter found the days. The trigger then gave the day back.
This is what a single chart can never show you. One session shows the mechanics. Sixty-three versions across nine years show the outcome.
What this means for your account
Here is the thing worth taking away: the trigger was never the interesting part.
Look at what actually carried information in all five tests. Not the entry — the day. Whether volatility was expanding. Whether the path to your target was clear. Whether the session had any range to give in the first place. Every filter that helped was a statement about the environment, and every attempt to turn that statement into an entry signal failed.
That is a useful split, because the environment is knowable before you trade and the entry is not. Our Regime Lab reads it off the market and shows the distribution of realised daily ranges in the current regime, with the sample size next to it — so you can see how thin the evidence is when it is thin. It is free, it is published on TradingView with the same measurement we use ourselves, and it deliberately does not tell you to enter anything.
Open Regime Lab on TradingView →All 63 cells
Hypothetical backtest results. Read the full disclaimer at the bottom of this page.
N=39 — Volatility regime
Reference window: Test window: in-sample + validation, 2015-2023
Frozen configuration (original, German): OR 09:30-10:00 ET; Entry erster Close jenseits OR-H/L (next bar), long+short; Stop 1,0x OR-Range; RRR-Sweep {1; 1,5; 2; 3}R; EOD-flat; Filter {all, atr_exp, vix_gt18, both}; Kosten 1,0 Pkt Roundturn + 1 Tick adverse; SL zuerst, TP erst bei >=1 Tick Durchgang
In English: Opening range 09:30-10:00 ET. Entry on the first close beyond the range high or low, filled on the next bar, long and short. Stop 1.0x the opening-range width. Reward-to-risk sweep over {1; 1.5; 2; 3}R. Flat at the end of the day. Filters {all, atr_exp, vix_gt18, both}. Costs 1.0 point round turn plus 1 tick of adverse slippage. On the same bar the stop is checked first; the target only fills on a move of at least 1 tick beyond it.
Constant in this family: OR window 09:30-10:00 ET · stop 1.0x OR-Range
| Cell | Setup | Trades | Avg per trade | Share of winning trades | Profit factor | Verdict |
|---|---|---|---|---|---|---|
| all_r10 | filter all · RRR 1 | 2294 | -0.0441 R | 48.6 % | 0.90 | REJECT |
| all_r15 | filter all · RRR 1.5 | 2294 | -0.0260 R | 44.8 % | 0.94 | REJECT |
| all_r20 | filter all · RRR 2 | 2294 | -0.0286 R | 43.4 % | 0.94 | REJECT |
| all_r30 | filter all · RRR 3 | 2294 | -0.0257 R | 42.7 % | 0.95 | REJECT |
| atr_exp_r10 | filter atr_exp · RRR 1 | 1033 | -0.0523 R | 47.8 % | 0.88 | REJECT |
| atr_exp_r15 | filter atr_exp · RRR 1.5 | 1033 | -0.0486 R | 43.3 % | 0.90 | REJECT |
| atr_exp_r20 | filter atr_exp · RRR 2 | 1033 | -0.0512 R | 42.0 % | 0.89 | REJECT |
| atr_exp_r30 | filter atr_exp · RRR 3 | 1033 | -0.0490 R | 41.2 % | 0.90 | REJECT |
| vix_gt18_r10 | filter vix_gt18 · RRR 1 | 959 | -0.0009 R | 51.7 % | 1.00 | REJECT |
| vix_gt18_r15 | filter vix_gt18 · RRR 1.5 | 959 | +0.0135 R | 47.2 % | 1.03 | REJECT |
| vix_gt18_r20 | filter vix_gt18 · RRR 2 | 959 | +0.0125 R | 45.7 % | 1.03 | REJECT |
| vix_gt18_r30 | filter vix_gt18 · RRR 3 | 959 | +0.0136 R | 44.7 % | 1.03 | REJECT |
| both_r10 | filter both · RRR 1 | 494 | +0.0160 R | 51.8 % | 1.04 | REJECT |
| both_r15 | filter both · RRR 1.5 | 494 | +0.0017 R | 46.0 % | 1.00 | REJECT |
| both_r20 | filter both · RRR 2 | 494 | -0.0034 R | 44.7 % | 0.99 | REJECT |
| both_r30 | filter both · RRR 3 | 494 | -0.0044 R | 43.7 % | 0.99 | REJECT |
N=41 — Order flow
Reference window: Test window: in-sample + validation, 2015-2023
Frozen configuration (original, German): OR 09:30-10:00 ET; Entry erster Close-Break; Stop 1,0x OR-Range; RRR-Sweep {1; 1,5; 2; 3}R; Bar-Delta-Proxy v*(2*(c-l)/(h-l)-1), kumuliert ab 09:30; CONFIRM = kum. Delta macht am Break ein neues Hoch (long) bzw. Tief (short), sonst DIVERGENZ; Modi {all, confirm, diverg_fade (Gegenrichtung)}; EOD-flat; Kosten 1,0 Pkt Roundturn + 1 Tick adverse
In English: Opening range 09:30-10:00 ET. Entry on the first close-break. Stop 1.0x the opening-range width. Reward-to-risk sweep over {1; 1.5; 2; 3}R. Bar-delta proxy v*(2*(c-l)/(h-l)-1), accumulated from 09:30. CONFIRM = the cumulative delta makes a new high (long) or a new low (short) at the break, otherwise DIVERGENCE. Modes {all, confirm, diverg_fade (opposite direction)}. Flat at the end of the day. Costs 1.0 point round turn plus 1 tick adverse.
Constant in this family: OR window 09:30-10:00 ET · stop 1.0x OR-Range · delta Bar-Delta-Proxy (kum. ab 09:30)
| Cell | Setup | Trades | Avg per trade | Share of winning trades | Profit factor | Verdict |
|---|---|---|---|---|---|---|
| all_r10 | mode all · RRR 1 | 2294 | -0.0441 R | 48.6 % | 0.90 | REJECT |
| all_r15 | mode all · RRR 1.5 | 2294 | -0.0260 R | 44.8 % | 0.94 | REJECT |
| all_r20 | mode all · RRR 2 | 2294 | -0.0286 R | 43.4 % | 0.94 | REJECT |
| all_r30 | mode all · RRR 3 | 2294 | -0.0257 R | 42.7 % | 0.95 | REJECT |
| confirm_r10 | mode confirm · RRR 1 | 1010 | -0.0553 R | 48.6 % | 0.88 | REJECT |
| confirm_r15 | mode confirm · RRR 1.5 | 1010 | -0.0344 R | 45.0 % | 0.93 | REJECT |
| confirm_r20 | mode confirm · RRR 2 | 1010 | -0.0398 R | 43.8 % | 0.92 | REJECT |
| confirm_r30 | mode confirm · RRR 3 | 1010 | -0.0332 R | 43.4 % | 0.93 | REJECT |
| diverg_fade_r10 | mode diverg_fade · RRR 1 | 1284 | -0.0591 R | 48.1 % | 0.86 | REJECT |
| diverg_fade_r15 | mode diverg_fade · RRR 1.5 | 1284 | -0.0262 R | 44.9 % | 0.94 | REJECT |
| diverg_fade_r20 | mode diverg_fade · RRR 2 | 1284 | -0.0248 R | 43.4 % | 0.95 | REJECT |
| diverg_fade_r30 | mode diverg_fade · RRR 3 | 1284 | -0.0133 R | 42.9 % | 0.97 | REJECT |
N=43 — All of it at once
Reference window: Test window: in-sample + validation, 2015-2023
Frozen configuration (original, German): OR 09:30-10:00 ET; Entry erster Close-Break; Stop 1,0x OR-Range; RRR-Sweep {1; 1,5; 2; 3}R; Masken {user_matrix = Regime UND frei UND confirm; learned(vix&free) = Regime UND frei; free&confirm; all_break = ungefiltert}; Regime = VIX-Vortagsclose > 18; frei = kein gestriger POC zwischen Entry und Ziel; confirm = kum. Bar-Delta-Proxy macht am Break ein neues Extrem; EOD-flat; Kosten 1,0 Pkt Roundturn + 1 Tick adverse
In English: Opening range 09:30-10:00 ET. Entry on the first close-break. Stop 1.0x the opening-range width. Reward-to-risk sweep over {1; 1.5; 2; 3}R. Masks {user_matrix = regime AND clear path AND confirm; learned(vix&free) = regime AND clear path; free&confirm; all_break = unfiltered}. Regime = prior day's VIX close above 18. Clear path = no prior-day point of control between entry and target. Confirm = the cumulative bar-delta proxy makes a new extreme at the break. Flat at the end of the day. Costs 1.0 point round turn plus 1 tick adverse.
Constant in this family: OR window 09:30-10:00 ET · stop 1.0x OR-Range · bedingungen ORB-Break + VIX>18 + freier Weg (kein POC) + CVD-Proxy-Confirm, je nach Maske
| Cell | Setup | Trades | Avg per trade | Share of winning trades | Profit factor | Verdict |
|---|---|---|---|---|---|---|
| user_matrix_r10 | mask user_matrix · RRR 1 | 362 | +0.0097 R | 53.0 % | 1.02 | REJECT |
| user_matrix_r15 | mask user_matrix · RRR 1.5 | 345 | +0.0119 R | 48.4 % | 1.03 | REJECT |
| user_matrix_r20 | mask user_matrix · RRR 2 | 334 | +0.0044 R | 47.6 % | 1.01 | REJECT |
| user_matrix_r30 | mask user_matrix · RRR 3 | 324 | -0.0035 R | 47.5 % | 0.99 | REJECT |
| learned_vix_free_r10 | mask learned(vix&free) · RRR 1 | 785 | -0.0213 R | 50.7 % | 0.95 | REJECT |
| learned_vix_free_r15 | mask learned(vix&free) · RRR 1.5 | 731 | -0.0102 R | 46.4 % | 0.98 | REJECT |
| learned_vix_free_r20 | mask learned(vix&free) · RRR 2 | 703 | -0.0053 R | 45.7 % | 0.99 | REJECT |
| learned_vix_free_r30 | mask learned(vix&free) · RRR 3 | 674 | +0.0004 R | 45.5 % | 1.00 | REJECT |
| free_confirm_r10 | mask free&confirm · RRR 1 | 814 | -0.0502 R | 48.8 % | 0.88 | REJECT |
| free_confirm_r15 | mask free&confirm · RRR 1.5 | 768 | -0.0161 R | 45.4 % | 0.96 | REJECT |
| free_confirm_r20 | mask free&confirm · RRR 2 | 738 | -0.0202 R | 44.3 % | 0.96 | REJECT |
| free_confirm_r30 | mask free&confirm · RRR 3 | 720 | -0.0153 R | 44.4 % | 0.97 | REJECT |
| all_break_r10 | mask all_break · RRR 1 | 2288 | -0.0456 R | 48.5 % | 0.89 | REJECT |
| all_break_r15 | mask all_break · RRR 1.5 | 2288 | -0.0282 R | 44.7 % | 0.94 | REJECT |
| all_break_r20 | mask all_break · RRR 2 | 2288 | -0.0309 R | 43.3 % | 0.93 | REJECT |
| all_break_r30 | mask all_break · RRR 3 | 2288 | -0.0278 R | 42.6 % | 0.94 | REJECT |
N=44 — Let the winner run
Reference window: Test window: in-sample + validation, 2015-2023
Frozen configuration (original, German): Nur Neg-Gamma-Proxy-Tage (ATR_prev > ATR_SMA_prev ODER VIX_prev > 18 UND steigend); 5-min-Bars; OR 09:30-10:00 ET; Entry erster 5-min-Close jenseits OR-H/L nach 10:00; Stop 1,0x OR-Range; Baseline festes Ziel {2R; 3R}; Runner-Sweep Cap {3R; 5R; offen} x Trailing {EMA21; Chandelier 3xATR; 3-Bar}; Scale-Out 50 % bei +2R; EOD-flat; Kosten Baseline 1,0 Pkt Roundturn, Runner ~3 Fills (1,5 x Roundturn)
In English: Negative-gamma-proxy days only (ATR_prev > ATR_SMA_prev, OR VIX_prev > 18 and rising). 5-minute bars. Opening range 09:30-10:00 ET. Entry on the first 5-minute close beyond the range high or low after 10:00. Stop 1.0x the opening-range width. Baseline with a fixed target {2R; 3R}. Runner sweep: cap {3R; 5R; open} x trailing {EMA21; chandelier 3xATR; 3-bar}. 50 % scale-out at +2R. Flat at the end of the day. Costs: baseline 1.0 point round turn; the runner takes about three fills (1.5x round turn).
Constant in this family: tage nur Neg-Gamma-Proxy-Tage (Vol-Expansion ODER VIX>18 steigend) · OR window 09:30-10:00 ET auf 5-min-Bars · stop 1.0x OR-Range
| Cell | Setup | Trades | Avg per trade | Share of winning trades | Profit factor | Verdict |
|---|---|---|---|---|---|---|
| fix_2r | engine simulate (eine Position, festes Ziel) · runner cap — · cap/target 2R · trailing — | 1238 | -0.0314 R | 44.2 % | 0.93 | REJECT |
| fix_3r | engine simulate (eine Position, festes Ziel) · runner cap — · cap/target 3R · trailing — | 1238 | -0.0331 R | 43.4 % | 0.93 | REJECT |
| cap3r_ema21 | engine simulate_runner (Scale-Out 50 % @ +2R, Runner trailt) · runner cap 3R · cap/target 3R · trailing ema21 | 1238 | -0.0417 R | 43.9 % | 0.91 | REJECT |
| cap3r_chand3 | engine simulate_runner (Scale-Out 50 % @ +2R, Runner trailt) · runner cap 3R · cap/target 3R · trailing chand3 | 1238 | -0.0442 R | 43.9 % | 0.91 | REJECT |
| cap3r_bar3 | engine simulate_runner (Scale-Out 50 % @ +2R, Runner trailt) · runner cap 3R · cap/target 3R · trailing bar3 | 1238 | -0.0446 R | 43.9 % | 0.91 | REJECT |
| cap5r_ema21 | engine simulate_runner (Scale-Out 50 % @ +2R, Runner trailt) · runner cap 5R · cap/target 5R · trailing ema21 | 1238 | -0.0418 R | 43.9 % | 0.91 | REJECT |
| cap5r_chand3 | engine simulate_runner (Scale-Out 50 % @ +2R, Runner trailt) · runner cap 5R · cap/target 5R · trailing chand3 | 1238 | -0.0440 R | 43.9 % | 0.91 | REJECT |
| cap5r_bar3 | engine simulate_runner (Scale-Out 50 % @ +2R, Runner trailt) · runner cap 5R · cap/target 5R · trailing bar3 | 1238 | -0.0461 R | 43.9 % | 0.90 | REJECT |
| open_ema21 | engine simulate_runner (Scale-Out 50 % @ +2R, Runner trailt) · runner cap open · cap/target open · trailing ema21 | 1238 | -0.0444 R | 43.9 % | 0.91 | REJECT |
| open_chand3 | engine simulate_runner (Scale-Out 50 % @ +2R, Runner trailt) · runner cap open · cap/target open · trailing chand3 | 1238 | -0.0455 R | 43.9 % | 0.90 | REJECT |
| open_bar3 | engine simulate_runner (Scale-Out 50 % @ +2R, Runner trailt) · runner cap open · cap/target open · trailing bar3 | 1238 | -0.0475 R | 43.9 % | 0.90 | REJECT |
N=47 — Volume and trend
Reference window: Test window: in-sample + validation, 2015-2023
Frozen configuration (original, German): NQ 5-min RTH; Signal = erster 5-min-Close ueber dem bisherigen Session-Hoch (long) bzw. unter dem Session-Tief (short), nur ab Bar 20 und nur wenn Volumen > 2 x SMA20(Volumen) UND ADX(14) > 25; Entry auf der Eroeffnung der FOLGEBAR + 1 Tick adverse; Stop-Sweep {Bar-Extrem des Signalbars; 1 x ATR(14)}; Ziel-Sweep {3R; 4R; 5R; EOD}; EOD-flat; Kosten 1,0 Pkt (run_n47.COST)
In English: NQ 5-minute bars, regular trading hours. Signal = the first 5-minute close above the session high so far (long) or below the session low so far (short), only from bar 20 on and only when volume > 2 x SMA20(volume) AND ADX(14) > 25. Entry at the open of the NEXT bar plus 1 tick adverse. Stop sweep {bar extreme of the signal bar; 1 x ATR(14)}. Target sweep {3R; 4R; 5R; EOD}. Flat at the end of the day. Costs 1.0 point (run_n47.COST).
Constant in this family: signal erster 5-min-Close ueber Session-Hoch / unter Session-Tief · filter Volumen > 2 x SMA20(Volumen) UND ADX(14) > 25 · entry Eroeffnung der FOLGEBAR + 1 Tick adverse
| Cell | Setup | Trades | Avg per trade | Share of winning trades | Profit factor | Verdict |
|---|---|---|---|---|---|---|
| bar_tp30 | stop mode bar · target R 3.0 · stop Bar-Extrem des Signalbars | 464 | -0.1023 R | 35.3 % | 0.85 | REJECT |
| bar_tp40 | stop mode bar · target R 4.0 · stop Bar-Extrem des Signalbars | 464 | -0.1004 R | 34.1 % | 0.85 | REJECT |
| bar_tp50 | stop mode bar · target R 5.0 · stop Bar-Extrem des Signalbars | 464 | -0.0933 R | 33.6 % | 0.87 | REJECT |
| bar_tpeod | stop mode bar · target R EOD · stop Bar-Extrem des Signalbars | 464 | -0.0949 R | 33.2 % | 0.86 | REJECT |
| atr_tp30 | stop mode atr · target R 3.0 · stop 1 x ATR(14) auf 5-min | 464 | -0.0427 R | 33.0 % | 0.94 | REJECT |
| atr_tp40 | stop mode atr · target R 4.0 · stop 1 x ATR(14) auf 5-min | 464 | -0.0129 R | 30.4 % | 0.98 | REJECT |
| atr_tp50 | stop mode atr · target R 5.0 · stop 1 x ATR(14) auf 5-min | 464 | -0.0378 R | 28.9 % | 0.95 | REJECT |
| atr_tpeod | stop mode atr · target R EOD · stop 1 x ATR(14) auf 5-min | 464 | +0.0102 R | 28.2 % | 1.01 | REJECT |
Everything above can be checked right here, cell by cell — no signup. If you want to run the numbers yourself, the trade-by-trade logs of all three studies and their frozen configurations are available as a data bundle on the Free Tools page.
These results are based on simulated or hypothetical performance results that have certain inherent limitations. Unlike the results shown in an actual performance record, these results do not represent actual trading. Also, because these trades have not actually been executed, these results may have under- or over-compensated for the impact, if any, of certain market factors, such as lack of liquidity. Simulated or hypothetical trading programs in general are also subject to the fact that they are designed with the benefit of hindsight. No representation is being made that any account will or is likely to achieve profits or losses similar to these being shown.
Trading futures and other leveraged products involves substantial risk of loss and is not suitable for all investors. Past performance is not indicative of future results. This content is for educational purposes only and is not investment advice.
