◆ New — Strategy Validator, Code Fixer & Prop Compliance Checker are live. Try one free →
Home / Indicators / Opening Range Breakout (ORB)
Session & Time indicator

Opening Range Breakout (ORB)

A session-based framework that records the high and low of the first minutes of trading and watches how price behaves against that range for the rest of the day.

Illustrative diagram — not live market data.

What it is

The opening range is the high-low band established during the first portion of a trading session — commonly the first 5, 15, 30, or 60 minutes after the open. An opening range breakout (ORB) framework simply marks that range once the window closes and then observes whether, when, and how price trades beyond it later in the session. The idea dates back decades in floor-trading literature: the open concentrates overnight order flow, so the early range is treated as a reference for the day's initial balance (a term borrowed from Market Profile). As an indicator, ORB is purely descriptive — two horizontal levels derived from completed price data — and a breakout of the range is an observation, not a prediction: ranges break and then reverse routinely, and no statistic about follow-through should be assumed without testing on your own instrument and rules. The genuinely hard part of ORB is not the concept but the clock: correct session handling is where most implementations silently fail, because timezones, daylight-saving transitions, half-day sessions, and instruments that trade nearly 24 hours all conspire to make 'the open' ambiguous in code. This page is educational only, not financial advice, and all trading involves risk of loss.

How it works

Mechanically, an ORB indicator does three things. First, it defines the range window as a session interval — say 09:30 to 10:00 in the exchange's local time. Second, while the current bar's timestamp falls inside that window, it tracks the running maximum high and minimum low, resetting both when a new session's window begins. Third, once the window ends, it freezes those two values and plots them for the remainder of the session; a close (or trade, depending on your rule) above the frozen high or below the frozen low is labeled a breakout. In Pine Script the reliable way to build this is with a session string and the time() function evaluated in the instrument's own timezone (syminfo.timezone), because exchange time automatically follows daylight-saving changes. Hard-coding UTC offsets is the classic silent bug: a script written in winter drifts one hour wrong in summer, and the 'opening range' quietly becomes minutes 60-90 of the session. Other implementation traps: the range must be built only from completed bars of the chart timeframe (a 30-minute opening range on a 15-minute chart uses two bars; on a 1-hour chart it cannot be computed at all), and breakout signals should be gated on confirmed bars so an intrabar poke that closes back inside does not fire and vanish.

How traders read it

Common settings

Range window: 15 or 30 minutes are the most cited for equities (e.g. 09:30-09:45 or 09:30-10:00 New York time); 5-minute ranges are used by faster intraday traders, and 60 minutes matches the Market Profile initial balance convention. For FX and crypto, which lack a single open, the window must be anchored to a chosen session (London 08:00 local, New York 09:30, or the daily rollover) and stated explicitly. Breakout rule: close-based versus touch-based. Always express the session in the exchange's timezone so daylight-saving shifts are handled for you.

Strengths

Pitfalls to watch

Pine v6 example

//@version=6
indicator("Opening Range Example", overlay = true)

// Session string in the EXCHANGE's timezone — DST is then handled correctly.
rangeSession = input.session("0930-1000", "Opening range window (exchange time)")

inWindow  = not na(time(timeframe.period, rangeSession, syminfo.timezone))
newWindow = inWindow and not inWindow[1]

var float orHigh = na
var float orLow  = na
if newWindow
    orHigh := high
    orLow  := low
else if inWindow
    orHigh := math.max(orHigh, high)
    orLow  := math.min(orLow, low)

// Only show the frozen range after the window has closed
rangeSet = not inWindow and not na(orHigh)
plot(rangeSet ? orHigh : na, "Opening range high", color = color.teal,   style = plot.style_linebr)
plot(rangeSet ? orLow  : na, "Opening range low",  color = color.maroon, style = plot.style_linebr)

breakUp   = rangeSet and ta.crossover(close, orHigh) and barstate.isconfirmed
breakDown = rangeSet and ta.crossunder(close, orLow) and barstate.isconfirmed
plotshape(breakUp,   "Close above range high", style = shape.triangleup,   location = location.belowbar, color = color.teal,   size = size.tiny)
plotshape(breakDown, "Close below range low",  style = shape.triangledown, location = location.abovebar, color = color.maroon, size = size.tiny)

Pro tip: Verify the clock before you trust the levels: pull up a date just after a daylight-saving change and confirm your opening-range window still starts exactly at the session open. Scripts using hard-coded UTC offsets fail this check twice a year while looking perfectly normal the rest of the time — which is why the example above anchors the session to syminfo.timezone. Then, if you study breakouts, log failed breaks with the same care as clean ones; the failures are the statistic most write-ups omit. Educational context only: the opening range describes early trade, predicts nothing, and all trading carries risk of loss.

Built an indicator from this? Run it through the Validator to catch look-ahead bias and repainting, or convert a strategy to Pine Script.

Educational only — not financial advice, not a recommendation to trade. No indicator is predictive; trading involves substantial risk of loss.

Catch the bug that compiles.Run auditGet Pro