Pine Script v6 · Asia, London, New York

TradingView Session High and Low Indicator

A session indicator tracks the highest high and lowest low inside a defined time window. The range develops while that window is open, then stays fixed until the next session. Use separate local timezones for Asian, London, and New York windows, and reset at the session boundary rather than calendar midnight.

The example plots observed prices and reference distances. It does not identify hidden orders, forecast reversals, or submit trades.

Set hours for the market you actually chart

These are editable reference windows, not universal definitions of forex or exchange sessions. Each uses its own timezone. The code applies the window on every day with available bars; it does not include a holiday calendar.

Asian reference

09:00 to 16:00

Asia/Tokyo

A continuous Tokyo-time example. It is not a combined Tokyo/Sydney exchange session and does not model a lunch break.

London reference

08:00 to 16:30

Europe/London

Uses the London local clock. The relative offset to New York can change when the two regions change clocks on different dates.

New York reference

09:30 to 16:00

America/New_York

A New York local-time example. Adjust it to your instrument and handle early closes separately.

For an overnight reference range, enter 1900-0200 and choose its timezone explicitly. This is one continuous window. Split sessions and exchange lunch breaks require different logic.

DST alignment and overnight resets

Use the timezone of the window. An IANA identifier such as Europe/London applies that region's clock rules. Anchoring all three sessions to America/New_York would keep them on the New York clock, not their respective local clocks. Changing the chart display timezone does not change these inputs.

Track the session start date. The example associates each bar with the most recent local start time. Bars after midnight in a 19:00 to 02:00 window still belong to the previous evening's start. The next window gets a new identifier and resets the range.

Handle charts with missing outside-session bars. A simple false-to-true membership test can miss the next session on a regular-hours-only chart, because adjacent available bars may both be in-session. Comparing session identifiers handles that case; it cannot recover missing price data.

References: TradingView time and timezone documentation and session definitions.

Copy the Pine Script v6 example

Open a new indicator in TradingView's Pine Editor, paste the code, and add it to an intraday chart. It includes three independent ranges, boxes, a status table, optional ATR reference bands, and six alert conditions. Start with aligned 5-minute candles and verify the boundaries on your symbol.

Pine Script v6 · Complete indicator example
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// (c) Pineify

//@version=6
indicator(title="Sessions Indicator Highs and Lows [Pineify]", shorttitle="Sessions H/L [Pineify]", overlay=true, max_boxes_count=500)

if not timeframe.isintraday
    runtime.error("Use an intraday, time-based chart with session-aligned bars.")

// ---------------- General Settings ----------------
string grp_gen   = "General Settings"
bool   show_tbl  = input.bool(true, "Show Session Status Dashboard", group=grp_gen)

// ---------------- Asian Session Inputs ----------------
string grp_asia    = "Asian Reference Window"
bool   show_asia   = input.bool(true, "Show Asian Session", group=grp_asia)
string asia_sess   = input.session("0900-1600", "Local Hours", tooltip="Example Tokyo-time window; customize for your instrument", group=grp_asia)
string asia_tz = input.string("Asia/Tokyo", "IANA Timezone", group=grp_asia)
color  asia_col    = input.color(color.new(#3b82f6, 85), "Box Fill Color", group=grp_asia)
color  asia_border = input.color(#2563eb, "Border & Line Color", group=grp_asia)

// ---------------- London Session Inputs ----------------
string grp_lon     = "London Reference Window"
bool   show_lon    = input.bool(true, "Show London Session", group=grp_lon)
string lon_sess    = input.session("0800-1630", "Local Hours", tooltip="Example London-time window; customize for your instrument", group=grp_lon)
string lon_tz = input.string("Europe/London", "IANA Timezone", group=grp_lon)
color  lon_col     = input.color(color.new(#f59e0b, 85), "Box Fill Color", group=grp_lon)
color  lon_border  = input.color(#d97706, "Border & Line Color", group=grp_lon)

// ---------------- New York Session Inputs ----------------
string grp_ny      = "New York Reference Window"
bool   show_ny     = input.bool(true, "Show New York Session", group=grp_ny)
string ny_sess     = input.session("0930-1600", "Local Hours", tooltip="Example New York-time window; not a holiday or early-close calendar", group=grp_ny)
string ny_tz = input.string("America/New_York", "IANA Timezone", group=grp_ny)
color  ny_col      = input.color(color.new(#10b981, 85), "Box Fill Color", group=grp_ny)
color  ny_border   = input.color(#059669, "Border & Line Color", group=grp_ny)

// ---------------- Volatility Extensions ----------------
string grp_atr     = "ATR Reference Extensions"
bool   show_atr    = input.bool(true, "Plot ATR Extensions", group=grp_atr)
float  atr_mult    = input.float(1.0, "ATR Multiplier", minval=0.1, step=0.1, group=grp_atr)

// ---------------- Session Engine Function ----------------
// Tracks session range, creates dynamic colored boxes, and preserves levels across bars
f_track_session(string sess_spec, string tz_spec, bool is_enabled, color box_bg, color box_border) =>
    in_session   = not na(time(timeframe.period, sess_spec, tz_spec))
    // One continuous HHMM-HHMM window. Identify the local session start date.
    start_hour = int(str.tonumber(str.substring(sess_spec, 0, 2)))
    start_minute = int(str.tonumber(str.substring(sess_spec, 2, 4)))
    before_start = hour(time, tz_spec) * 60 + minute(time, tz_spec) < start_hour * 60 + start_minute
    session_key = timestamp(tz_spec, year(time, tz_spec), month(time, tz_spec), dayofmonth(time, tz_spec) - (before_start ? 1 : 0), start_hour, start_minute)
    var int active_key = na
    session_open = in_session and (na(active_key) or session_key != active_key)
    
    var float s_high = na
    var float s_low  = na
    var box   s_box  = na

    if session_open
        active_key := session_key
        s_high := high
        s_low  := low
        if is_enabled and timeframe.isintraday
            s_box := box.new(left=bar_index, top=high, right=bar_index, bottom=low,
                             border_color=box_border, bgcolor=box_bg, border_width=1,
                             border_style=line.style_solid)
    else if in_session
        s_high := math.max(nz(s_high, high), high)
        s_low  := math.min(nz(s_low, low), low)
        if not na(s_box)
            box.set_top(s_box, s_high)
            box.set_bottom(s_box, s_low)
            box.set_right(s_box, bar_index)

    [s_high, s_low, in_session, session_open]

// ---------------- Execute Tracking ----------------
[asia_h, asia_l, asia_in, asia_start] = f_track_session(asia_sess, asia_tz, show_asia, asia_col, asia_border)
[lon_h, lon_l, lon_in, lon_start]     = f_track_session(lon_sess, lon_tz, show_lon, lon_col, lon_border)
[ny_h, ny_l, ny_in, ny_start]         = f_track_session(ny_sess, ny_tz, show_ny, ny_col, ny_border)

// Previous confirmed daily ATR, not the developing daily value.
daily_atr = request.security(syminfo.tickerid, "1D", ta.atr(14)[1], lookahead=barmerge.lookahead_on)

// ---------------- Plot Session Highs & Lows ----------------
plot(timeframe.isintraday and show_asia ? asia_h : na, "Asian High", color=asia_border, style=plot.style_linebr, linewidth=1)
plot(timeframe.isintraday and show_asia ? asia_l : na, "Asian Low", color=asia_border, style=plot.style_linebr, linewidth=1)

plot(timeframe.isintraday and show_lon ? lon_h : na, "London High", color=lon_border, style=plot.style_linebr, linewidth=1)
plot(timeframe.isintraday and show_lon ? lon_l : na, "London Low", color=lon_border, style=plot.style_linebr, linewidth=1)

plot(timeframe.isintraday and show_ny ? ny_h : na, "NY High", color=ny_border, style=plot.style_linebr, linewidth=1)
plot(timeframe.isintraday and show_ny ? ny_l : na, "NY Low", color=ny_border, style=plot.style_linebr, linewidth=1)

// ATR reference distances, not price targets
plot(timeframe.isintraday and show_atr and not na(ny_h) ? ny_h + daily_atr * atr_mult : na, "NY High + ATR", color=color.gray, style=plot.style_circles)
plot(timeframe.isintraday and show_atr and not na(ny_l) ? ny_l - daily_atr * atr_mult : na, "NY Low - ATR", color=color.gray, style=plot.style_circles)

// ---------------- Real-time Session Dashboard Table ----------------
var table status_tbl = table.new(position.top_right, 4, 4, bgcolor=color.new(color.black, 20), border_color=color.gray, border_width=1)
if show_tbl and barstate.islast and timeframe.isintraday
    table.cell(status_tbl, 0, 0, "Session", text_color=color.white, text_size=size.small, bgcolor=color.navy)
    table.cell(status_tbl, 1, 0, "Status", text_color=color.white, text_size=size.small, bgcolor=color.navy)
    table.cell(status_tbl, 2, 0, "High", text_color=color.white, text_size=size.small, bgcolor=color.navy)
    table.cell(status_tbl, 3, 0, "Low", text_color=color.white, text_size=size.small, bgcolor=color.navy)

    table.cell(status_tbl, 0, 1, "Asia", text_color=color.white, text_size=size.small)
    table.cell(status_tbl, 1, 1, asia_in ? "ACTIVE" : "CLOSED", bgcolor=asia_in ? color.green : color.gray, text_color=color.white, text_size=size.small)
    table.cell(status_tbl, 2, 1, str.tostring(asia_h, format.mintick), text_color=color.white, text_size=size.small)
    table.cell(status_tbl, 3, 1, str.tostring(asia_l, format.mintick), text_color=color.white, text_size=size.small)

    table.cell(status_tbl, 0, 2, "London", text_color=color.white, text_size=size.small)
    table.cell(status_tbl, 1, 2, lon_in ? "ACTIVE" : "CLOSED", bgcolor=lon_in ? color.orange : color.gray, text_color=color.white, text_size=size.small)
    table.cell(status_tbl, 2, 2, str.tostring(lon_h, format.mintick), text_color=color.white, text_size=size.small)
    table.cell(status_tbl, 3, 2, str.tostring(lon_l, format.mintick), text_color=color.white, text_size=size.small)

    table.cell(status_tbl, 0, 3, "New York", text_color=color.white, text_size=size.small)
    table.cell(status_tbl, 1, 3, ny_in ? "ACTIVE" : "CLOSED", bgcolor=ny_in ? color.teal : color.gray, text_color=color.white, text_size=size.small)
    table.cell(status_tbl, 2, 3, str.tostring(ny_h, format.mintick), text_color=color.white, text_size=size.small)
    table.cell(status_tbl, 3, 3, str.tostring(ny_l, format.mintick), text_color=color.white, text_size=size.small)

// ---------------- Breakout & Breakdown Alert Conditions ----------------
// Asian Session Triggers
asia_cross_up = ta.crossover(close, asia_h)
alertcondition(asia_cross_up and not asia_in and barstate.isconfirmed, title="Asian High Breakout", message="Price broke above Asian Session High on {{ticker}} at {{close}}")
asia_cross_down = ta.crossunder(close, asia_l)
alertcondition(asia_cross_down and not asia_in and barstate.isconfirmed, title="Asian Low Breakdown", message="Price broke below Asian Session Low on {{ticker}} at {{close}}")

// London Session Triggers
lon_cross_up = ta.crossover(close, lon_h)
alertcondition(lon_cross_up and not lon_in and barstate.isconfirmed, title="London High Breakout", message="Price broke above London Session High on {{ticker}} at {{close}}")
lon_cross_down = ta.crossunder(close, lon_l)
alertcondition(lon_cross_down and not lon_in and barstate.isconfirmed, title="London Low Breakdown", message="Price broke below London Session Low on {{ticker}} at {{close}}")

// New York Session Triggers
ny_cross_up = ta.crossover(close, ny_h)
alertcondition(ny_cross_up and not ny_in and barstate.isconfirmed, title="New York High Breakout", message="Price broke above New York Session High on {{ticker}} at {{close}}")
ny_cross_down = ta.crossunder(close, ny_l)
alertcondition(ny_cross_down and not ny_in and barstate.isconfirmed, title="New York Low Breakdown", message="Price broke below New York Session Low on {{ticker}} at {{close}}")
Calculation limits: The code uses each included chart bar's full high and low. Align bar boundaries with session times; a candle spanning the session end can include later prices. Use standard time-based candles. The first loaded session may be incomplete, and old boxes are removed once the 500-box limit is reached. ATR bands use the previous confirmed daily ATR(14); they are distances from the New York range, not predicted targets. The active range and its bands still move during the session.

Check these boundaries before using alerts

The following is a reproducible chart checklist, not a claim that these scenarios have been tested on live market data. Syntax validation alone cannot establish that the selected window matches your instrument.

Session indicator boundary verification checklist
ScenarioSetupExpected behavior
Overnight windowSet one window to 19:00 to 02:00 in America/New_York on a chart with overnight data.The 00:00 bar keeps the same session range. The next 19:00 session starts a new range.
No outside-session barsUse a regular-hours chart with a matching 09:30 to 16:00 window and inspect two consecutive trading days.The next day resets even though the previous available bar was also in-session.
DST alignmentCompare London and New York windows before and after each region changes clocks.Each window retains its own local start time; their relative position may change. Compare with a fixed UTC input to see the difference.
Session close and alertOn a chart with later bars, record the completed high and low, then inspect a confirmed close crossing a level.The completed range remains fixed. A crossing can trigger only outside that window, at bar close.
Bar alignment and missing historyCompare aligned 5-minute bars with a timeframe whose bars straddle a boundary; also inspect the first loaded session.Straddling candles can include out-of-window prices. A session loaded halfway through contains only the available bars.

Create an alert with this indicator and choose a high-breakout or low-breakdown condition. The code requires a confirmed close outside that window. Repeated recrossings can trigger again; it is not limited to one alert per session. Changes to the script or inputs require recreating an existing alert. See TradingView's alert behavior.

Session range or opening range?

A full-session range keeps updating until its configured window ends. An opening range uses a shorter initial window, such as the first 15 minutes, and then freezes. Both depend on explicit hours and available candles; neither establishes that a breakout will continue.

The midpoint is simply (high + low) / 2. It is not evidence of fair value or a profitable mean-reversion rule. If you convert these levels into a strategy, define entries, exits, fees, slippage, and evaluation periods before comparing results.

Customize the session rules with Pineify

Give the AI Coding Agent your instrument, local hours, timezone, reset rule, and alert timing. Ask it to adapt the source, then inspect the output and repeat the boundary checks. Use the Visual Pine Editor for supported visual indicator and alert configurations.

Frequently asked questions

Technical behavior references: TradingView repainting documentation and drawing limits. Sources checked October 16, 2026.

Educational information, not investment advice. Session levels and code examples do not guarantee returns. Trading can result in loss.