23,666 trades, −₹23.7 lakh after costs. 0 of 8 variants profitable.
The opening range breakout (ORB) is probably the most taught intraday strategy. One widely shared guide adds three filters to the basic idea, each meant to cut out false breakouts, and quotes win rates of 42–65%. We followed it exactly on every NIFTY 50 stock from January 2016 to August 2026.
The rules, as written:
- Range: the high and low of the first 30 minutes (09:15–09:45).
- Trend: long only when the 50 EMA is above the 200 EMA and the candle closes above both (15-minute chart); short only in the mirror case.
- Entry: a 15-minute candle closes beyond the range, with a full body (at least 60% of its range) in the breakout direction and volume at least 1.5× the average of the previous 20 candles.
- Exit: stop at the far end of the breakout candle, target 2× the risk, anything open is closed at 15:15.
One trade per stock per day, ₹5 lakh per trade, entered at the next bar’s open after the candle closes.
Weekly cumulative net P&L. Hover or tap for values.
It isn’t one bad year
Ten of the eleven years lost money, mostly ₹2–4 lakh each. The one exception was 2020, a year of unusually large daily moves, which made ₹3.5 lakh (about ₹140 per trade). Every year since has lost money again.
Show as table
| Year | Trades | Net | Win % |
|---|---|---|---|
| 2016 | 2,212 | −₹4.2 L | 42.1 |
| 2017 | 2,295 | −₹4.3 L | 40.8 |
| 2018 | 2,369 | −₹3.9 L | 40.7 |
| 2019 | 2,353 | −₹40,599 | 41.3 |
| 2020 | 2,568 | ₹3.5 L | 41.9 |
| 2021 | 2,441 | −₹3.4 L | 41.1 |
| 2022 | 1,947 | −₹1.2 L | 42.3 |
| 2023 | 2,164 | −₹3.4 L | 38.9 |
| 2024 | 2,205 | −₹8,471 | 42.7 |
| 2025 | 1,876 | −₹2.7 L | 40.1 |
| 2026 | 1,236 | −₹3.7 L | 40.4 |
The filters help, just not enough
The guide’s filters do what they claim: each one improves the average trade. Remove all three and the gross edge falls from 4.5 to 2.3 bps per trade while the number of trades grows 4.6 times, and the loss grows almost tenfold, to −₹2.27 crore. The breakouts it picks are also better than a random NIFTY 50 stock traded at the same minute in the same direction (+4.5 vs +3.0 bps).
The problem is size. A ₹5 lakh round trip costs about 6.5 bps (brokerage, STT, exchange fees, stamp duty, GST and 1 bp of slippage on each fill; see how we count costs). An edge of 4.5 bps before costs is a loss of about 2 bps after them, on every trade.
No variant earns more before costs than a round trip costs (~6.5 bps).
Eight variants, none profitable
We changed one thing at a time: dropped each filter, used a 15-minute range, moved the target to 1R or 3R, and used 30-minute candles. These were decided before running anything. Here is every result:
| Variant | Trades | Gross / trade | Win % | Net after costs | +3 bps slippage | 2016-20 | 2021-23 | 2024-26 |
|---|---|---|---|---|---|---|---|---|
| As written: 30-min range, 15-min candle, trend + full body + volume, 2R | 23,666 | 4.5 bps | 41.2% | −₹23.7 L | −₹94.5 L | −₹9.2 L | −₹8.0 L | −₹6.4 L |
| No trend filter | 39,627 | 4.0 bps | 40.9% | −₹50.0 L | −₹1.69 Cr | −₹16.4 L | −₹19.6 L | −₹14.0 L |
| No volume filter | 55,436 | 2.7 bps | 38.8% | −₹1.06 Cr | −₹2.72 Cr | −₹44.4 L | −₹32.5 L | −₹28.8 L |
| No trend, no volume, no body filter | 1,08,182 | 2.3 bps | 38.0% | −₹2.27 Cr | −₹5.51 Cr | −₹86.8 L | −₹74.0 L | −₹65.8 L |
| 15-min range | 27,940 | 3.1 bps | 40.6% | −₹47.3 L | −₹1.31 Cr | −₹20.0 L | −₹15.3 L | −₹12.0 L |
| Target 1R | 23,666 | 2.0 bps | 49.6% | −₹53.0 L | −₹1.24 Cr | −₹25.6 L | −₹16.5 L | −₹10.9 L |
| Target 3R | 23,666 | 5.9 bps | 39.5% | −₹7.4 L | −₹78.3 L | ₹22,345 | −₹2.2 L | −₹5.5 L |
| 30-min candles | 13,961 | 6.4 bps | 44.3% | −₹73,727 | −₹42.6 L | −₹73,779 | ₹79,841 | −₹79,788 |
₹5 lakh per trade, 5-minute bars, v2026.08 snapshot (Jan 2016 – Aug 2026), NIFTY 50 point-in-time members, discount-broker (₹20/order) and statutory charges + 1 bp slippage per fill. Generated 2026-10-07.
The closest to break-even was the 30-minute candle version: −₹74,000 over 13,961 trades, about −₹5 per trade. It made ₹0.8 lakh in 2021–23 and lost in 2016–20 and 2024–26. It’s tempting to call that “the version that works”. It isn’t. We’d be choosing it after seeing eight results, it didn’t make money over the full period, and three basis points of extra slippage per fill turn it into a ₹43 lakh loss.
What to take from this
- A filter that improves win rate or edge is not the same as a filter that makes money. Check whether the edge per trade clears the cost of a round trip.
- Quoted win rates mean little on their own. At 2R targets a 41% win rate loses money after costs here. What matters is the average gain per trade compared with the average cost.
- Breakouts on NIFTY 50 stocks are close to fairly priced at this horizon. The gross edge of a well-filtered ORB is a few basis points: real, but smaller than costs.
The exact code we ranorb_pdf() in dpx_bt · 8 variants · snapshot v2026.08
def orb_pdf(or_min=30, chart=15, trend=True, full_body=True, vol=1.5, r=2.0):
"""Range = first `or_min` minutes. Signal = a `chart`-minute candle CLOSES beyond the range; optional EMA50/200
trend filter (on chart candles), full-body candle (body >= 60% of range), volume >= vol x 20-candle average.
Stop at the far end of the breakout candle, target r x risk, one trade per stock per day."""
def strategy(p):
c = p.candles(chart)
cl = p.candle_series(c, 'close')
cv = p.candle_series(c, 'volume')
o, h, l, cc, v = c['open'], c['high'], c['low'], c['close'], c['volume']
orh, orl = p.opening_range(or_min)
long, short = cc > orh, cc < orl
if full_body:
body, rng = (cc - o).abs(), (h - l)
long &= (cc > o) & (body >= 0.6 * rng)
short &= (cc < o) & (body >= 0.6 * rng)
if vol:
vavg = cv.rolling(20).mean().shift(1).reindex(p.index)
long &= v >= vol * vavg
short &= v >= vol * vavg
if trend:
e50 = cl.ewm(span=50, adjust=False).mean().reindex(p.index)
e200 = cl.ewm(span=200, adjust=False).mean().reindex(p.index)
long &= (e50 > e200) & (cc > e50) & (cc > e200)
short &= (e50 < e200) & (cc < e50) & (cc < e200)
first = p.once_per_day(long | short)
risk = ((cc - l) / cc).where(long, (h - cc) / cc).clip(lower=0.001)
return bt.Signals(long=long & first, short=short & first, stop=risk, target=r * risk)
return strategyEvery variant in this post is this function with different arguments, run through dpx_bt over Jan 2016 – Aug 2026.
Have a variation in mind?
Ask runs your own idea on the same data and rules, with the same report: net after costs, +3 bps stress test, three periods.
Backtest on historical data (Jan 2016 – Aug 2026), net of discount-broker (₹20 per order) and statutory charges and 1 bp slippage per fill. Education and research only, not investment advice. Past results do not predict future returns. Disclaimer.