Opening Range Breakout (ORB)
ORB is a rules-based day-trading strategy for index futures. Define the opening range as the first 15 or 30 minutes of the regular trading session. When price breaks above the range high, go long. When it breaks below the range low, go short. Exit on an opposite signal, a stop beyond the other side, or at session close. Simple, effective, widely used on ES and NQ.
The intuition
The opening range captures the overnight news, pre-market positioning, and the first burst of cash-session trading. A clean break of that range signals the day's directional bias — traders, market makers, and algos all rebalance around the break.
Most days trade inside or near the opening range. But on trend days — the ones worth trading — price breaks decisively and keeps going. ORB's job is to catch those trend days and skip the chop.
The rules
1. Define the opening range.
- ES: first 15 minutes (9:30–9:45 ET) on a 1-minute or 5-minute chart
- NQ, CL: first 30 minutes often works better due to faster early movement
2. Mark range high (RH) and range low (RL).
3. Entry trigger.
- Long on a close above RH
- Short on a close below RL
- (Variant: use 1-minute close for sensitivity, 5-minute close for less noise)
4. Stop.
- Opposite side of the opening range (long stop at RL, short stop at RH)
- OR: 1× ATR beyond entry bar
5. Target.
- Measured move = height of opening range, projected from breakout
- OR: trail with a 1.5× ATR trailing stop
- OR: flat at session close (4:00 pm ET for ES)
6. One-and-done rule. Take only one trade per side per session. If the first breakout fails and reverses, don't re-enter in the same direction. Many traders add a "one trade total per day" cap.
Realistic expectations
From public research and personal logs (numbers vary by implementation):
- Win rate: 40–55%
- Avg winner: 1.5–2.5× avg loser
- Profit factor: 1.3–1.8
- Frequency: one trade every 1–2 days on average (most days the breakout fails or doesn't trigger)
This is not a scalping strategy. Expect losing weeks that are recouped by a handful of big trend days. A strategy that only produces 2–3 trades per week requires patience.
Common variations
Gap-aware ORB. If the overnight gap is already large, skip the breakout — the move is probably done. Rules: only take ORB if overnight gap is less than X% (e.g., 0.5% on ES).
Higher-timeframe trend filter. Only take longs if daily close > 20-day SMA; only take shorts if < 20-day SMA. Significantly reduces counter-trend failures.
VWAP filter. After break, only continue the trade if price stays on the breakout side of VWAP. A break of RH that immediately goes back under VWAP often fails.
Time cutoff. Don't take breakouts after 11:30am ET. Late breakouts are often thin-market head-fakes.
Full Pine Script (v6) implementation
//@version=6
strategy("ORB Strategy", overlay=true, initial_capital=50000, default_qty_type=strategy.fixed, default_qty_value=1, commission_type=strategy.commission.cash_per_contract, commission_value=2.5)
// ---------- Inputs ----------
sessionStartHour = input.int(9, "RTH Start Hour (ET)")
sessionStartMinute = input.int(30, "RTH Start Minute")
orMinutes = input.int(15, "Opening Range Minutes")
atrLen = input.int(14, "ATR Length for stop sizing")
stopAtrMult = input.float(1.0, "Stop = N × ATR beyond range")
useVwapFilter = input.bool(true, "Require VWAP alignment after break")
closeAtSessionEnd = input.bool(true, "Flatten at session close")
// ---------- Session tracking ----------
inSession = not na(time(syminfo.session.regular)) and dayofweek != dayofweek.saturday and dayofweek != dayofweek.sunday
// Time since session start, in minutes
startMinutes = sessionStartHour * 60 + sessionStartMinute
curMinutes = hour * 60 + minute
sinceStart = curMinutes - startMinutes
inOR = inSession and sinceStart >= 0 and sinceStart < orMinutes
afterOR = inSession and sinceStart >= orMinutes
// ---------- Opening range high/low, reset each day ----------
var float rH = na
var float rL = na
var bool tradedLongToday = false
var bool tradedShortToday = false
if ta.change(time("D")) != 0
rH := na
rL := na
tradedLongToday := false
tradedShortToday := false
if inOR
rH := na(rH) ? high : math.max(rH, high)
rL := na(rL) ? low : math.min(rL, low)
// ---------- Indicators ----------
atrVal = ta.atr(atrLen)
[vwap, _, _] = ta.vwap(hlc3, volume, session.isfirstbar)
// ---------- Entries ----------
longBreak = afterOR and not tradedLongToday and ta.crossover(close, rH) and (not useVwapFilter or close > vwap)
shortBreak = afterOR and not tradedShortToday and ta.crossunder(close, rL) and (not useVwapFilter or close < vwap)
if longBreak
longStop = rL - stopAtrMult * atrVal
longTarget = close + (rH - rL)
strategy.entry("ORB Long", strategy.long)
strategy.exit("Long Exit", "ORB Long", stop=longStop, limit=longTarget)
tradedLongToday := true
if shortBreak
shortStop = rH + stopAtrMult * atrVal
shortTarget = close - (rH - rL)
strategy.entry("ORB Short", strategy.short)
strategy.exit("Short Exit", "ORB Short", stop=shortStop, limit=shortTarget)
tradedShortToday := true
// ---------- Flatten at session close ----------
if closeAtSessionEnd and hour == 15 and minute >= 55
strategy.close_all("EOD flatten")
// ---------- Visuals ----------
plot(afterOR ? rH : na, "Range High", color.green, linewidth=2, style=plot.style_linebr)
plot(afterOR ? rL : na, "Range Low", color.red, linewidth=2, style=plot.style_linebr)
Automating ORB with CrossTrade
Use the standard strategy payload:
key=YOUR-SECRET-KEY;
command=place;
account=Sim101;
instrument=ES 06-26;
action={{strategy.order.action}};
qty={{strategy.order.contracts}};
order_type=market;
tif=day;
sync_strategy=true;
market_position={{strategy.market_position}};
prev_market_position={{strategy.prev_market_position}};
out_of_sync=flatten;
Two alerts:
- One for the
strategy.entry()calls (opens position) - One for
strategy.exit()stops and targets (manages exits)
Or: fire one "any alert() function call" alert and include both entry and exit handling. The bracket orders guide walks through the cleanest setup.
Common mistakes
- Entering before the range is defined. Wait until the full opening range (15 or 30 minutes) has completed.
- Chasing late breaks. A breakout at 11:30am is usually a trap. Use a time cutoff.
- Over-trading the strategy. ORB gives 2–3 signals per week on ES. That's the correct volume. Forcing trades on non-signal days breaks the edge.
- Widening stops mid-trade. If the range low breaks on a long, exit. Don't redefine the stop.
Frequently Asked Questions
What is the opening range?
The high and low of the first 15 or 30 minutes of the regular trading session. For ES, that's 9:30–9:45 (or 9:30–10:00) Eastern Time. The range captures overnight news and early cash-session positioning.
What's the best timeframe for ORB?
The entry timeframe is usually the 1-minute or 5-minute chart. The opening range can be defined on any of those — most traders use 15 or 30 minutes after the open. Going shorter (5-minute range) produces more signals but more false breakouts.
Does ORB work on NQ?
Yes, with two adjustments: (1) use a 30-minute opening range due to NQ's faster early moves, and (2) use wider ATR-based stops because of higher volatility. Many ORB traders run it simultaneously on ES and NQ with instrument-specific tunings.
What's the win rate on ORB?
Typically 40–55%, depending on filters. Without a trend filter, closer to 40%. With a higher-timeframe trend filter and VWAP alignment, closer to 55%. Expect small win rates offset by larger winners — the strategy profits via occasional big trend days.