Indicator · Pine Script v6 · On the chart

Buddha Market Levels

by DarthBuddha

A chart-overlay indicator that draws key intraday reference levels as lines and labels.

View on GitHub

Repaints

GitHub stars1on the repository
Pine versionv6current
Lines of code1,600
Settings40inputs
LicenceMIT
Last update3 months ago15 Jul 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

1,600 lines of Pine Script v6 from src/BuddhaMarketLevels.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

BuddhaMarketLevels.pine
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Darth_Buddha

// @version=6
indicator(
      title = 'Buddha Market Levels',
      shorttitle = 'Levels',
      overlay = true,
      max_lines_count = 500,
      max_labels_count = 500,
      dynamic_requests = true)

//----------------------------------------------------------------------------------------------------< CONSTANTS >---//

//----------------------------------------------------< COLORs

// @variable - Todays Hi Color
const color CLR_TD_HI = color.rgb(255, 152, 0, 60)
// @variable - Todays Lo Color
const color CLR_TD_LO = color.rgb(255, 152, 0, 60)
// @variable - Todays PreMarket Hi Color
const color CLR_PM_HI = color.rgb(255, 152, 0, 30)
// @variable - Todays PreMarket Lo Color
const color CLR_PM_LO = color.rgb(255, 152, 0, 30)
// @variable - Yesterday Hi Color
const color CLR_YD_HI = color.rgb(255, 152, 0, 0)
// @variable - Yesterday Lo Color
const color CLR_YD_LO = color.rgb(255, 152, 0, 0)

// @variable - Todays Open Color
const color CLR_TD_OP = color.rgb(33, 150, 243, 0)
// @variable - Yesterday Close Color
const color CLR_YD_CL = color.rgb(255, 82, 82, 50)
// @variable - Yesterday Open Color
const color CLR_YD_OP = color.rgb(12, 50, 153, 0)

// @variable - Opening Range - Globex Color
const color COLOR_GLOBEX = color.rgb(0, 188, 212, 0)
// @variable - New York Opening Range Color
const color COLOR_NEWYORK = color.rgb(224, 64, 251, 50)
// @variable - London Opening Range Color
const color COLOR_LONDON = color.rgb(255, 235, 59, 50)
// @variable - Asia Opening Range Color
const color COLOR_ASIA = color.rgb(0, 230, 118, 50)

// @variable - After Hours Hi Color
const color CLR_AH_HI = color.rgb(156, 39, 176, 0)
// @variable - After Hours Lo Color
const color CLR_AH_LO = color.rgb(156, 39, 176, 0)
// @variable - Over Night Hi Color
const color CLR_OV_HI = color.rgb(136, 14, 79, 0)
// @variable - Over Night Lo Color
const color CLR_OV_LO = color.rgb(136, 14, 79, 0)

// @variable - No Color
const color CLR_NONE = color.new(color.gray, 100)

//----------------------------------------------------< STRINGs

const string O = 'open', H = 'high', L = 'low', C = 'close'
const string HL2 = 'hl2'
const string HLC3 = 'hlc3'
const string OHLC4 = 'ohlc4'
const string HLCC4 = 'hlcc4'
const string RTH = 'RTH'

//----------------------------------------------------< ENUMs

enum State
    on = 'ON'
    off = 'OFF'

// @enum - Line Style
enum Ls
    solid = 'Solid'
    dashed = 'Dashed'
    dotted = 'Dotted'

//-------------------------------------------------------------------------------------------------------< INPUTS >---//

const string g_Pm = 'PreMarket'
i_PmOnOff = input.enum(defval = State.on, title = '',
    group = g_Pm, inline = 'PmHiLo', display = display.none)
i_PmHiClr = input.color(defval = CLR_PM_HI, title = 'High',
    group = g_Pm, inline = 'PmHiLo', display = display.none)
i_PmLoClr = input.color(defval = CLR_PM_LO, title = 'Low',
    group = g_Pm, inline = 'PmHiLo', display = display.none)

const string g_Td = 'Today'
i_TdOnOff = input.enum(defval = State.on, title = '',
    group = g_Td, inline = 'TdHiLo', display = display.none)
i_TdHiClr = input.color(defval = CLR_TD_HI, title = 'High',
    group = g_Td, inline = 'TdHiLo', display = display.none)
i_TdLoClr = input.color(defval = CLR_TD_LO, title = 'Low',
    group = g_Td, inline = 'TdHiLo', display = display.none)

i_TdOpOnOff = input.enum(defval = State.off, title = '',
    group = g_Td, inline = 'TdOp', display = display.none)
i_TdOpClr = input.color(defval = CLR_TD_OP, title = 'Open',
    group = g_Td, inline = 'TdOp', display = display.none)

const string g_Open = 'Opening Range Settings'
i_OrRangeTransparency = input.int(defval = 95, title = 'Range Transparency',
    group = g_Open, display = display.none, minval = 0, maxval = 100, step = 5)

const string g_Globex = 'Globex - Opening Range "Futures Only"'
i_OnOff_GlobexOpen = input.enum(defval = State.on, title = '',
    group = g_Globex, inline = '1', display = display.none)
i_GlobexOpenColorLine = input.color(defval = COLOR_GLOBEX, title = '',
    group = g_Globex, inline = '1', display = display.none)
i_GlobexOpenStart = input.string(defval = '1800', title = 'Start',
    group = g_Globex, inline = '1', display = display.none)
i_GlobexOpenRange = input.int(defval = 15, title = 'Range',
    group = g_Globex, inline = '1', display = display.none,
    minval = 5, maxval = 60, step = 5)

const string g_OpenAsia = 'Asia - Opening Range "Futures Only"'
i_OnOff_AsiaOpen = input.enum(defval = State.on, title = '',
    group = g_OpenAsia, inline = '1', display = display.none)
i_AsiaOpenColor = input.color(defval = COLOR_ASIA, title = '',
    group = g_OpenAsia, inline = '1', display = display.none)
i_AsiaOpenStart = input.string(defval = '2000', title = 'Start',
    group = g_OpenAsia, inline = '1', display = display.none)
i_AsiaOpenRange = input.int(defval = 15, title = 'Range',
    group = g_OpenAsia, inline = '1', display = display.none,
    minval = 5, maxval = 60, step = 5)

const string g_OpenLondon = 'London - Opening Range "Futures Only"'
i_OnOff_LondonOpen = input.enum(defval = State.on, title = '',
    group = g_OpenLondon, inline = '1', display = display.none)
i_LondonOpenColor = input.color(defval = COLOR_LONDON, title = '',
    group = g_OpenLondon, inline = '1', display = display.none)
i_LondonOpenStart = input.string(defval = '0300', title = 'Start',
    group = g_OpenLondon, inline = '1', display = display.none)
i_LondonOpenRange = input.int(defval = 15, title = 'Range',
    group = g_OpenLondon, inline = '1', display = display.none,
    minval = 5, maxval = 60, step = 5)

const string g_OpenNewYork = 'New York - Opening Range'
i_OnOff_NewYorkOpen = input.enum(defval = State.on, title = '',
    group = g_OpenNewYork, inline = '1', display = display.none)
i_NewYorkOpenColor = input.color(defval = COLOR_NEWYORK, title = '',
    group = g_OpenNewYork, inline = '1', display = display.none)
i_NewYorkOpenStart = input.string(defval = '0930', title = 'Start',
    group = g_OpenNewYork, inline = '1', display = display.none)
i_NewYorkOpenRange = input.int(defval = 15, title = 'Range',
    group = g_OpenNewYork, inline = '1', display = display.none,
    minval = 5, maxval = 60, step = 5)

const string g_Ov = 'Over Night "Equity Only"'
i_OvOnOff = input.enum(defval = State.on, title = '',
    group = g_Ov, inline = '1', display = display.none)
i_OvHiClr = input.color(defval = CLR_OV_HI, title = 'High',
    group = g_Ov, inline = '1',display = display.none)
i_OvLoClr = input.color(defval = CLR_OV_LO, title = 'Low',
    group = g_Ov, inline = '1',display = display.none)

const string g_Ah = 'After Hours "Equity Only"'
i_AhOnOff = input.enum(defval = State.on, title = '',
    group = g_Ah, inline = '1', display = display.none)
i_AhHiClr = input.color(defval = CLR_AH_HI, title = 'High',
    group = g_Ah, inline = '1',display = display.none)
i_AhLoClr = input.color(defval = CLR_AH_LO, title = 'Low',
    group = g_Ah, inline = '1',display = display.none)

const string g_Yd = 'Yesterday'
i_YdOnOff = input.enum(defval = State.on, title = '',
    group = g_Yd, inline = 'YdHiLo', display = display.none)
i_YdHiClr = input.color(defval = CLR_YD_HI, title = 'High',
    group = g_Yd, inline = 'YdHiLo', display = display.none)
i_YdLoClr = input.color(defval = CLR_YD_LO, title = 'Low',
    group = g_Yd, inline = 'YdHiLo', display = display.none)

i_YdOpOnOff = input.enum(defval = State.on, title = '',
    group = g_Yd, inline = 'YdOp', display = display.none)
i_YdOpClr = input.color(defval = CLR_YD_OP, title = 'Open',
    group = g_Yd, inline = 'YdOp', display = display.none)

i_YdClOnOff = input.enum(defval = State.on, title = '',
    group = g_Yd, inline = 'YdCl', display = display.none)
i_YdClsClr = input.color(defval = CLR_YD_CL, title = 'Close',
    group = g_Yd, inline = 'YdCl', display = display.none)


const string g_S = 'Style'
// Label Settings
i_TxtClr = input.color(CLR_NONE, 'Label Text', inline = 'lblFgClr', group = g_S, display = display.none)
i_TxtClrMatch = input.bool(true, 'Match Line', inline = 'lblFgClr', group = g_S, display = display.none)

i_LblClr = input.color(CLR_NONE, 'Label BG Color', inline = 'lblBgClr', group = g_S, display = display.none)
i_LblClrMatch = input.bool(false, 'Match Line Color', inline = 'lblBgClr', group = g_S, display = display.none)

i_ShowLabel = input.bool(true, 'Show Labels', group = g_S, display = display.none)
i_Abbreviate = input.bool(false, 'Abbreviate label names', group = g_S, display = display.none)
i_InclPrice = input.bool(true, 'Append price to label names', group = g_S, display = display.none)

i_MergeLabels = input.bool(false, 'Merge labels within', inline = 'merge', group = g_S, display = display.none)
i_MergeThreshold = input.float(5, '', 0.01, 1000, 1, inline = 'merge', group = g_S, display = display.none)

const string g_L = 'Line'
// Line Settings
i_Lw = input.int(defval = 2, title = 'Width',
          minval = 1, maxval = 10, step = 1, group = g_L, inline = 'LwLs', display = display.none)
i_Ls = input.enum(defval = Ls.solid, title = 'Style', group = g_L, inline = 'LwLs', display = display.none)

i_Forward = input.bool(true, 'Extend line right', inline = 'forwardBars', group = g_L, display = display.none)
i_FwdBars = input.int(10, '', 0, inline = 'forwardBars', group = g_L, display = display.none)

i_Truncate = input.bool(false, 'Max line length', inline = 'maxLineLength', group = g_L, display = display.none)
i_TruncLen = input.int(15, '', 0, inline = 'maxLineLength', group = g_L, display = display.none)

//----------------------------------------------------< Market Hours Settings
const string g_MktHrs = 'Market Hours'

i_Tz = input(
      defval = 'America/New_York', title = 'Time Zone', group = g_MktHrs, inline = '1', display = display.none)

i_MktHrs_Equity = input.session(
      defval = '0930-1600', title = 'Equity Open/Close', group = g_MktHrs, inline = '2', display = display.none)

i_MktHrs_Futures = input.session(
      defval = '1800-1700', title = 'Futures Open/Close', group = g_MktHrs, inline = '3', display = display.none)

//-------------------------------------------------------------------------------------------------< CALCULATIONS >---//

bool useTd   = i_TdOnOff   == State.on ? true : false
bool usePm   = i_PmOnOff   == State.on ? true : false
bool useOv   = i_OvOnOff   == State.on ? true : false
bool useAh   = i_AhOnOff   == State.on ? true : false
bool useYd   = i_YdOnOff   == State.on ? true : false
bool useTdOp = i_TdOpOnOff == State.on ? true : false
bool useYdOp = i_YdOpOnOff == State.on ? true : false
bool useYdCl = i_YdClOnOff == State.on ? true : false

// Opening Range
bool use_GlobexOpen     = i_OnOff_GlobexOpen    == State.on ? true : false
bool use_NewYorkOpen    = i_OnOff_NewYorkOpen   == State.on ? true : false
bool use_LondonOpen     = i_OnOff_LondonOpen    == State.on ? true : false
bool use_AsiaOpen       = i_OnOff_AsiaOpen      == State.on ? true : false

// Styles
var string lineStyle = i_Ls == Ls.dotted ? line.style_dotted
          : i_Ls == Ls.dashed ? line.style_dashed
          : line.style_solid

var box globexOrBox = na

var int fwdLen = i_Forward ? i_FwdBars : 0
var string timezone = i_Tz == 'syminfo.timezone' ? syminfo.timezone : i_Tz
var string mktHrs = na

colorIsNotTransparent(color clr) =>
    color.t(clr) < 100

if syminfo.type == 'futures'
    mktHrs := i_MktHrs_Futures
else
    mktHrs := i_MktHrs_Equity


// Store the number of milliseconds per bar
var mspb = timeframe.multiplier * (timeframe.isseconds
          ? 1000 : timeframe.isminutes
          ? 60000 : timeframe.isdaily
          ? 86400000 : timeframe.isweekly
          ? 604800000 : timeframe.ismonthly
          ? 2629800000 : 1)

// @function Gets a standardized set of values from higher timeframes.
// @returns  [open, open[1], high, high[1], low, low[1], close, close[1], time, time[1]]
reqSec(string tf) =>
    request.security(
          symbol = ticker.modify(syminfo.tickerid, session.extended),
          timeframe = tf,
          expression = [open, open[1], high, high[1], low, low[1], close, close[1], time, time[1]],
          lookahead = barmerge.lookahead_on)

// @function Gets a standardized set of values from a one minute timeframe.
// @returns  [open, high, low, close, time, session.ismarket]
GetOneMinBarVals() =>
    // security_lower_tf requires a timeframe strictly lower than the chart.
    // On sub-minute charts (e.g. 15s) the chart IS finer than 1m, so wrap
    // the current bar's values in single-element arrays instead.
    if timeframe.isseconds
        [array.from(open), array.from(high), array.from(low), array.from(close),
              array.from(time), array.from(session.ismarket)]
    else
        request.security_lower_tf(
              symbol = ticker.modify(syminfo.tickerid, session.extended),
              timeframe = '1',
              expression = [open, high, low, close, time, session.ismarket])

// @type Object to store bar values.
type barVals
    array<float> Opens
    array<float> Highs
    array<float> Lows
    array<float> Closes
    array<int> Times
    float Open
    float High
    float Low
    float Close
    int Index
    bool isrealtime

// Store 1m bar values alongside the charts timeframe bar values
[mO, mH, mL, mC, mT, mM] = GetOneMinBarVals()
bar = barVals.new(
          mO, mH, mL, mC, mT,
          open, high, low, close, time,
          math.floor(time / mspb) == math.floor(timenow / mspb))
field_0 = bar.Index
BI1 = field_0[1]

// Track when there's a new one minute bar
varip bts = array.new_int(2, 0)
bts.push(bar.Times.size())
bts.shift()
// On sub-minute charts every new bar is finer than 1m, so treat each new bar
// as a "new one-minute bar" trigger so session/segment logic fires correctly.
newOneMinuteBar = timeframe.isseconds
          ? barstate.isnew
          : bar.isrealtime
          ? timeframe.multiplier == 1 and timeframe.isminutes
          ? barstate.isnew : bts.range() > 0 and bar.Times.size() > 0 : true

// Store 1D bar values
[dO, dO1, dH, dH1, dL, dL1, dC, dC1, dT, dT1] = reqSec('D')
dailyBar = barVals.new(
          array.from(dO, dO1),
          array.from(dH, dH1),
          array.from(dL, dL1),
          array.from(dC, dC1),
          array.from(dT, dT1),
          dO, dH, dL, dC, dT)

// @type Object to track key levels.
type level
    bool InitState
    bool Show
    color Color
    string Name
    string ToolTip
    float Level
    float Price
    int Index
    int OldIndex
    line Line
    label Label
    box Range

// @variable An array of all key levels in this script.
var keyLevels = array.new<level>()

// Constants to make working with keyLevels more human friendly
const int TD = 0
const int TDH = 0
const int TDL = 1

const int OP = 2
const int OPH = 2
const int OPL = 3

const int PM = 4
const int PMH = 4
const int PML = 5

const int OV = 6
const int OVH = 6
const int OVL = 7

const int AH = 8
const int AHH = 8
const int AHL = 9

const int YD = 10
const int YDH = 10
const int YDL = 11

const int TDO = 12
const int TDC = 13
const int YDO = 14
const int YDC = 15


// @function Checks if an integer is an even number.
evenNum(int i) =>
    i % 2 == 0

// @function Transforms a label string into an acronym.
acronym(string fullStr) =>
    wordList = str.split(fullStr, ' ')
    firstCharacters = ''
    for word in wordList
        firstCharacters := firstCharacters + str.match(word, '\\w')
        firstCharacters
    firstCharacters

// @function Assembles the name and tooltip for labels.
labelStrings(int i, string abrvStr, string fullStr) =>
    ohlca = ''
    possesive = ''
    if i <= YDL
        ohlca := evenNum(i) ? 'High' : 'Low'
        ohlca
    else
        ohlca := switch (i - TDO) % 2
            0 => 'Open'
            1 => 'Close'
        ohlca

    if i <= TDL or YDH <= i
        possesive := 's'
        possesive

    string customName = na
    string customTip = na
    switch i
        TDH =>
            customName := 'HoD'
            customTip := 'High of Day'
        TDL =>
            customName := 'LoD'
            customTip := 'Low of Day'
        YDH =>
            customName := 'yd HoD'
            customTip := 'Yesterday High of Day'
        YDL =>
            customName := 'yd LoD'
            customTip := 'Yesterday Low of Day'
        OPH =>
            customName := 'G op H'
            customTip := 'Globex Open High'
        OPL =>
            customName := 'G op L'
            customTip := 'Globex Open Low'
        TDO =>
            customName := 'Open'
            customTip := 'Today Open'
        YDO =>
            customName := 'yd Open'
            customTip := 'Yesterday Open'
        YDC =>
            customName := 'yd Close'
            customTip := 'Yesterday Close'

    name = i_Abbreviate
              ? na(customName) ? ' ' + abrvStr + acronym(ohlca) + ' ' : ' ' + customName + ' '
              : na(customName) ? ' ' + ohlca + ' • ' + fullStr + ' ' : ' ' + customTip + ' '

    tooltip = na(customTip) ? ' ' + fullStr + possesive + ' ' + ohlca + ' ' : ' ' + customTip + ' '
    [name, tooltip]

// Initialize keyLevels array
var Levels_Not_Initialized = true
if Levels_Not_Initialized

    // Create temporary arrays to help initialize the keyLevels object array
    fullStr = array.from('Today', 'Open', 'Premarket', 'Overnight', 'After Hours', 'Yesterday')
    abrvStr = array.from('TD', 'OR', 'PM', 'ON', 'AH', 'YD')
    colors = array.from(
              i_TdHiClr, i_TdLoClr,
              i_GlobexOpenColorLine, i_GlobexOpenColorLine,
              i_PmHiClr, i_PmLoClr,
              i_OvHiClr, i_OvLoClr,
              i_AhHiClr, i_AhLoClr,
              i_YdHiClr, i_YdLoClr,
              i_TdOpClr,
              CLR_NONE,
              i_YdOpClr,
              i_YdClsClr)
    lvlVis = array.from(
              useTd, useTd,
              use_GlobexOpen, use_GlobexOpen,
              usePm, usePm,
              useOv, useOv,
              useAh, useAh,
              useYd, useYd,
              useTdOp,
              false,
              useYdOp,
              useYdCl)

    // Set visibility
    for v in lvlVis
        keyLevels.push(level.new(v, v))

    // Set strings
    for [i, kl] in keyLevels
        if kl.Show
            kl.Color := colors.get(i)

            // All Open, High, Low, Close, & Average levels from Today through Last Year
            j = switch true
                i <= YDL => math.floor(i / 2)
                (TDO <= i and i <= YDC) => (i <= TDC ? TD : YD) / 2
            [name, tooltip] = labelStrings(i, abrvStr.get(j), fullStr.get(j))
            kl.Name := name
            kl.ToolTip := tooltip
            kl.ToolTip

    Levels_Not_Initialized := false
    Levels_Not_Initialized

// --------------------------------------------------- TIME LOGIC -------------------------------------------------- //

// @function Converts a bar_index into a bar_time.
numOfBars(int t) =>
    t * mspb

// @function Converts an integer into a 4 character time string.
normalizeTime(float t) =>
    s = str.tostring(int(t))
    while str.length(s) < 4
        s := '0' + s
        s
    s

// @function Calculates the Near Open start time.
setNearOpenStartTime(string mst) =>
    t = str.tonumber(mst)
    hr = math.floor(t / 100)
    m = t % 100
    m := m
    if m < 0
        while m < 0
            hr := hr - 1
            m := m + 60
            m
    normalizeTime(hr * 100 + m)

// @function Adds minutes to a HHMM string.
addMinutesToTime(string hhmm, int mins) =>
    t = str.tonumber(hhmm)
    hr = math.floor(t / 100)
    m = t % 100 + mins
    while m >= 60
        hr := hr + 1
        m := m - 60
        m
    while m < 0
        hr := hr - 1
        m := m + 60
        m
    normalizeTime(hr * 100 + m)

// @function Returns the first 4 characters in a 9 character time range string. Ex: "0000" in "0000-2400".
startTime(string t) =>
    str.substring(t, 0, 4)

// @function Returns the last 4 characters in a 9 character time range string. Ex: "2400" in "0000-2400".
stopTime(string t) =>
    str.substring(t, 5, 9)

// Convert trader specified market time variables into strings
var string mktStartTime = na
var string mktEndTime = na
var string marketHours = na
var string openingStartTime = na
var string sessionOpeningStartTime = na
var string nyOpeningStartTime = na
var string londonOpeningStartTime = na
var string asiaOpeningStartTime = na
var string preMktHours = na
var string bfrMktHours = na
var string noStartTime = na
var string nearOpenMin = na
var string openEndTime = na
var string openingMins = na
var string sessionOpenEndTime = na
var string nyOpenEndTime = na
var string londonOpenEndTime = na
var string asiaOpenEndTime = na
var string sessionOpeningMins = na
var string nyOpeningMins = na
var string londonOpeningMins = na
var string asiaOpeningMins = na
var string minutesInDay = na
var string mktCloseToMidnight = na
var Strings_Not_Initialized = true
if Strings_Not_Initialized
    mktStartTime := startTime(mktHrs)
    mktEndTime := stopTime(mktHrs)
    sessionOpeningStartTime := mktStartTime
    nyOpeningStartTime := i_NewYorkOpenStart
    londonOpeningStartTime := i_LondonOpenStart
    asiaOpeningStartTime := i_AsiaOpenStart
    sessionOpenEndTime := addMinutesToTime(sessionOpeningStartTime, i_GlobexOpenRange)
    nyOpenEndTime := addMinutesToTime(nyOpeningStartTime, i_NewYorkOpenRange)
    londonOpenEndTime := addMinutesToTime(londonOpeningStartTime, i_LondonOpenRange)
    asiaOpenEndTime := addMinutesToTime(asiaOpeningStartTime, i_AsiaOpenRange)
    sessionOpeningMins := sessionOpeningStartTime + '-' + sessionOpenEndTime
    nyOpeningMins := nyOpeningStartTime + '-' + nyOpenEndTime
    londonOpeningMins := londonOpeningStartTime + '-' + londonOpenEndTime
    asiaOpeningMins := asiaOpeningStartTime + '-' + asiaOpenEndTime
    openingStartTime := sessionOpeningStartTime
    marketHours := mktHrs
    preMktHours := '0000-' + mktStartTime
    bfrMktHours := mktEndTime + '-' + mktStartTime
    noStartTime := setNearOpenStartTime(mktStartTime)
    nearOpenMin := noStartTime + '-' + mktStartTime
    openEndTime := sessionOpenEndTime
    openingMins := sessionOpeningMins
    minutesInDay := '1440'
    mktCloseToMidnight := mktEndTime + '-2400'
    Strings_Not_Initialized := false
    Strings_Not_Initialized

// @function Converts UNIX time into a 4 character hour-minute string.
Time(int t, string output = 'string') =>
    h = str.tostring(hour(t, timezone))
    h := str.length(h) < 2 ? '0' + h : h
    m = str.tostring(minute(t, timezone))
    m := str.length(m) < 2 ? '0' + m : m
    str.format('{0}{1}', h, m)

// @function Checks if a bar_time occurs within a time range.
evalTime(string beg, int barTime, string end) =>
    t = str.tonumber(Time(barTime))
    str.tonumber(beg) <= t and t < str.tonumber(end)

// @function Iterates through an array of 1m bars and checks if any of them occur within a segment.
method isSegment(string segment = na) =>
    result = false
    beg = startTime(segment)
    end = stopTime(segment)
    pmh = str.tonumber(beg) > str.tonumber(end)
    for barTime in bar.Times
        if pmh ? evalTime(beg, barTime, '2400') or evalTime('0000', barTime, end) : evalTime(beg, barTime, end)
            result := true
            break
    result

// @function Iterates through an array of 1m bars and checks if any are session.ismarket.
sessionIsMarket() =>
    result = false
    for [i, sessionIsMarket] in mM
        if sessionIsMarket
            result := true
            break
    result

// Update segments only when there's a new one minute bar (rather than each chart update)
var newSession = false
var preSession = false
var mktSession = false
var premarket = false
var market = false
var marketOpen = false
var afterHours = false
var overnight = false
var PMS1 = false
var MKS1 = false
var PM1 = false
var NO1 = false
var MK1 = false
var AH1 = false
var ON1 = false
var OR1 = false
var mctm = false
var newSessionBeforeMidnight = false
var newDailyBar = false
var newWeek = false
var newMonth = false
var newQuarter = false
var newYear = false
var session = -1
var firstMarketBar = false
var firstOpenRangeBar = false
// Multi-session OR visibility toggles.
bool useGlobexOr = use_GlobexOpen
bool useNyOr = use_NewYorkOpen
bool useLondonOr = use_LondonOpen
bool useAsiaOr = use_AsiaOpen

// Globex OR (session start, usually 18:00 ET for futures).
var float sessionOrHigh = na
var float sessionOrLow = na
var bool sessionOrActive = false
var bool sessionOrWasActive = false
var int sessionOrStartIndex = na
var line sessionOrHighLine = na
var line sessionOrLowLine = na
var label sessionOrHighLabel = na
var label sessionOrLowLabel = na

// Additional OR tracks (New York, London, Asia).
var float nyOrHigh = na
var float nyOrLow = na
var bool nyOrActive = false
var bool nyOrWasActive = false
var int nyOrStartIndex = na
var line nyOrHighLine = na
var line nyOrLowLine = na
var label nyOrHighLabel = na
var label nyOrLowLabel = na

var float londonOrHigh = na
var float londonOrLow = na
var bool londonOrActive = false
var bool londonOrWasActive = false
var int londonOrStartIndex = na
var line londonOrHighLine = na
var line londonOrLowLine = na
var label londonOrHighLabel = na
var label londonOrLowLabel = na

var float asiaOrHigh = na
var float asiaOrLow = na
var bool asiaOrActive = false
var bool asiaOrWasActive = false
var int asiaOrStartIndex = na
var line asiaOrHighLine = na
var line asiaOrLowLine = na
var label asiaOrHighLabel = na
var label asiaOrLowLabel = na

// Opening Range fill boxes — one per session.
var box nyOrBox = na
var box londonOrBox = na
var box asiaOrBox = na
if newOneMinuteBar

    // Common intraday segments
    newSession := session.isfirstbar
    preSession := session.ispremarket
    PMS1 := preSession[1]
    mktSession := sessionIsMarket()
    MKS1 := mktSession[1]
    premarket := isSegment(preMktHours)
    PM1 := premarket[1]
    market := isSegment(marketHours)
    MK1 := market[1]
    marketOpen := isSegment(openingMins)
    OR1 := marketOpen[1]
    firstOpenRangeBar := marketOpen and (not OR1 or newSession)

    if useGlobexOr
        sessionOrActive := isSegment(sessionOpeningMins)
        sessionOrWasActive := sessionOrActive[1]
        if sessionOrActive and (not sessionOrWasActive or newSession)
            sessionOrHigh := na
            sessionOrLow := na
            sessionOrStartIndex := bar.Index

        if sessionOrActive
            for [i, t] in bar.Times
                if evalTime(sessionOpeningStartTime, t, sessionOpenEndTime)
                    h = bar.Highs.get(i)
                    l = bar.Lows.get(i)
                    sessionOrHigh := na(sessionOrHigh) ? h : math.max(sessionOrHigh, h)
                    sessionOrLow := na(sessionOrLow) ? l : math.min(sessionOrLow, l)

    if useNyOr
        nyOrActive := isSegment(nyOpeningMins)
        nyOrWasActive := nyOrActive[1]
        if nyOrActive and (not nyOrWasActive or newSession)
            nyOrHigh := na
            nyOrLow := na
            nyOrStartIndex := bar.Index

        if nyOrActive
            for [i, t] in bar.Times
                if evalTime(nyOpeningStartTime, t, nyOpenEndTime)
                    h = bar.Highs.get(i)
                    l = bar.Lows.get(i)
                    nyOrHigh := na(nyOrHigh) ? h : math.max(nyOrHigh, h)
                    nyOrLow := na(nyOrLow) ? l : math.min(nyOrLow, l)

    if useLondonOr
        londonOrActive := isSegment(londonOpeningMins)
        londonOrWasActive := londonOrActive[1]
        if londonOrActive and (not londonOrWasActive or newSession)
            londonOrHigh := na
            londonOrLow := na
            londonOrStartIndex := bar.Index

        if londonOrActive
            for [i, t] in bar.Times
                if evalTime(londonOpeningStartTime, t, londonOpenEndTime)
                    h = bar.Highs.get(i)
                    l = bar.Lows.get(i)
                    londonOrHigh := na(londonOrHigh) ? h : math.max(londonOrHigh, h)
                    londonOrLow := na(londonOrLow) ? l : math.min(londonOrLow, l)

    if useAsiaOr
        asiaOrActive := isSegment(asiaOpeningMins)
        asiaOrWasActive := asiaOrActive[1]
        if asiaOrActive and (not asiaOrWasActive or newSession)
            asiaOrHigh := na
            asiaOrLow := na
            asiaOrStartIndex := bar.Index

        if asiaOrActive
            for [i, t] in bar.Times
                if evalTime(asiaOpeningStartTime, t, asiaOpenEndTime)
                    h = bar.Highs.get(i)
                    l = bar.Lows.get(i)
                    asiaOrHigh := na(asiaOrHigh) ? h : math.max(asiaOrHigh, h)
                    asiaOrLow := na(asiaOrLow) ? l : math.min(asiaOrLow, l)

    // Less common intraday segments
    mctm := isSegment(mktCloseToMidnight)
    newSessionBeforeMidnight := newSession
              ? hour(time, timezone) * 60 + minute(time, timezone) < 1440 : market
              ? false : newSessionBeforeMidnight
    afterHours := mctm and not newSessionBeforeMidnight
    AH1 := afterHours[1]
    overnight := mctm and newSessionBeforeMidnight
    ON1 := overnight[1]

    // Common annual segments
    newDailyBar := mktSession and (not MKS1 or newSession)

    // Increment the session count. Used to disregard incomplete data from the first session on the chart
    session := session + (newSession and session < 1 ? 1 : 0)

    // Track first market bar
    firstMarketBar := market and (not MK1 or newSession)
    firstMarketBar

// @function Checks if any part of the current bar occurred within a given segment.
segment(int i) =>
    switch i
        OP => openingMins
        PM => useOv ? preMktHours : bfrMktHours
        OV => mktCloseToMidnight
        AH => mktCloseToMidnight
        TD => marketHours
        YD => marketHours
        TDO => marketHours
        YDO => marketHours

// @function Checks if any part of the current bar occurred during the first minute of a given segment.
isFirstMinOf(int i) =>
    switch i
        OP => firstOpenRangeBar
        PM => useOv ? premarket and (not PM1 or newSession)
                  : overnight and newSession or not ON1 and premarket and (not PM1 or newSession)
        OV => overnight and newSession
        AH => afterHours and not AH1
        TD => firstMarketBar
        YD => firstMarketBar
        TDO => firstMarketBar
        YDO => firstMarketBar
        YDC => true ? MK1 and (not market or newSession) : MKS1 and (not mktSession or newSession)

// ----------------------------------------------- RECENT LEVEL LOGIC ---------------------------------------------- //

// @function Synthesizes standard bar values.
method src(barVals b, string src, int n = na) =>
    float o = na
    float h = na
    float l = na
    float c = na
    switch_var_7 = na(n)
    switch switch_var_7
        true =>
            o := b.Open
            h := b.High
            l := b.Low
            c := b.Close
            c
        false =>
            o := b.Opens.get(n)
            h := b.Highs.get(n)
            l := b.Lows.get(n)
            c := b.Closes.get(n)
            c

// @function Saves the old indices of key levels.
saveOldIndices(int first, int last = na) =>
    for i = first to na(last) ? first : last by 1
        kl = keyLevels.get(i)
        kl.OldIndex := kl.Index
        kl.OldIndex

// @function Sets the Level and Index fields of a level object.
method set(int i, float lvl, int idx = na) =>
    kl = keyLevels.get(i)
    kl.Level := lvl
    kl.Index := na(idx) ? bar.Index : idx
    kl.Index

// The syminfo.session variable is "us_regular" (rather than "regular") when using a RTH chart with Futures,
// indicating the daily open, high, and low may have already occurred. Other tradables may have similar
// conditions, thus str.contains(syminfo.session, "_") was used instead of syminfo.type == "futures".
var irregularRTH = str.contains(syminfo.session, '_')

// @function Sets the high and low levels of a segment.
method set_high_low(int hi, bool True = true) =>
    if True
        lo = hi + 1
        useDailyBar = switch hi
            TD => not true
            YD => not true
            => false
        if isFirstMinOf(hi)
            saveOldIndices(hi, lo)
            if useDailyBar and irregularRTH
                hi.set(dailyBar.High)
                lo.set(dailyBar.Low)
            else
                hi.set(bar.Open)
                lo.set(bar.Open)
        seg = segment(hi)
        beg = startTime(seg)
        end = stopTime(seg)
        pmh = str.tonumber(beg) > str.tonumber(end)
        for [i, t] in bar.Times
            if pmh ? evalTime(beg, t, '2400') or evalTime('0000', t, end) : evalTime(beg, t, end)
                if bar.Highs.get(i) >= keyLevels.get(hi).Level
                    hi.set(bar.Highs.get(i))
                if bar.Lows.get(i) <= keyLevels.get(lo).Level
                    lo.set(bar.Lows.get(i))

// @function Sets the daily opening value.
method set_open(int o, bool True = true) =>
    if True and bar.Times.size() > 0
        int n = na
        bool rth = false
        string src = na
        switch o
            TDO =>
                rth := true
                src := O
                src
            YDO =>
                rth := true
                src := O
                src
        if rth
            for [i, t] in bar.Times
                if str.tonumber(Time(t)) == str.tonumber(startTime(marketHours))
                    n := i
                    break
        else if not irregularRTH
            for [i, sessionIsMarket] in mM
                if sessionIsMarket and (not MKS1 or newSession)
                    n := i
                    break
        v = not na(n) ? bar.src(src, n) : dailyBar.Open
        o.set(v)

// @function Sets the daily closing value.
method set_close(int c, bool True = true) =>
    if True and not na(bar[1])
        int n = na
        bool rth = false
        barVals b = na
        array<bool> m = na
        switch c
            TDC =>
                b := bar
                m := mM
                rth := true
                rth
            YDC =>
                b := bar[1]
                m := mM[1]
                rth := true
                rth
        if rth
            pmh = str.tonumber(mktStartTime) > str.tonumber(mktEndTime)
            for [i, t] in b.Times
                if pmh ? evalTime(mktStartTime, t, '2400') or evalTime('0000', t, mktEndTime) : evalTime(mktStartTime, t, mktEndTime)
                    n := i
                    n
        else if b.Closes.size() > 0
            for [i, sessionIsMarket] in m
                if sessionIsMarket
                    n := i
                    n
        v = not na(n) ? b.src(C, n) : market ? keyLevels.get(TDC).Level : dailyBar.Close
        c.set(v, b.Index)

// @function Checks if a level should be set.
ifOkayToSet(int i) =>
    switch i
        OP => use_GlobexOpen and marketOpen
        PM => usePm and (overnight and not useOv or premarket)
        OV => useOv and overnight
        AH => useAh and afterHours
        TD => useTd and not newSession and market
        YD => useYd and not newSession and market
        => false

// Unchanging variables placed outside ifOkayToSet() to reduce computational overhead
var ifOkayToSetTDO = useTdOp
var ifOkayToSetYDC = useYdCl

// Save specific bar indices that are necessary for showing old levels correctly, but may be overwritten
if newSession
    saveOldIndices(TDH, OVL)
    saveOldIndices(TDO, TDC)

// Track the recent highs & lows
for i = TD to YD by 1
    i.set_high_low(ifOkayToSet(i))

// Track the todays open & yesterdays close
TDO.set_open(ifOkayToSetTDO and isFirstMinOf(TDO))
YDC.set_close(ifOkayToSetYDC and isFirstMinOf(YDC))

// Set first market bar index
var firstMarketBarIndex = time
if isFirstMinOf(OP) and not newSession
    firstMarketBarIndex := bar.Index
    firstMarketBarIndex
else if isFirstMinOf(OP)[1] and newSession[1]
    field_1 = bar.Index
    firstMarketBarIndex := field_1[1]
    firstMarketBarIndex

// Set last market bar index.
var lastMarketBarIndex = time
if MK1 and (not market or newSession)
    field_2 = bar.Index
    lastMarketBarIndex := field_2[1]
    lastMarketBarIndex

// -------------------------------------------- OBJECT UPDATE FUNCTIONS -------------------------------------------- //

// @function Formats prices using the symbol mintick so trailing zeros are preserved.
formatPrice(float price) =>
    str.tostring(price, format.mintick)

// @function Assembles the text and tooltip strings for a label.
method new_label_string(level kl, string Type, array<string> strArr = na, array<float> fltArr = na) =>
    price = formatPrice(kl.Price)
    if Type == 'text'
        if na(strArr)
            (i_InclPrice ? ' ' + price + ' • ' : '') + str.trim(kl.Name) + ' '
        else
            priceRange = not na(fltArr) and fltArr.range() == 0
                  ? price
                  : formatPrice(fltArr.min()) + ' → ' + formatPrice(fltArr.max())
            (i_InclPrice ? ' ' + priceRange + ' • ' : '') + str.trim(strArr.join(i_Abbreviate ? '/' : ' / ')) + ' '
    else if not na(strArr) and not na(fltArr)
        for [i, str] in strArr
            strArr.set(i, str + '• ' + formatPrice(fltArr.get(i)))
        strArr.join('\n')
    else
        kl.ToolTip + '• ' + price

// @function Creates a new line for a level.
method new_line(level kl, int x1) =>
    kl.Line := line.new(x1, kl.Level, bar.Index + numOfBars(fwdLen), kl.Level,
              xloc.bar_time, extend.none, kl.Color, lineStyle, i_Lw)
    kl.Price := kl.Level
    kl.Price

// @function Creates a new label for a level.
method new_label(level kl, array<string> nameArr = na, array<string> toolArr = na, array<float> priceArr = na) =>
    if i_ShowLabel
        x = bar.Index + numOfBars(fwdLen)
        y = na(priceArr) ? kl.Price : math.avg(priceArr.min(), priceArr.max())
        bgc = not na(nameArr) ? CLR_NONE : i_LblClrMatch ? kl.Color : i_LblClr
        fgc = not na(nameArr)
              ? colorIsNotTransparent(CLR_NONE)
              ? CLR_NONE : color.new(chart.fg_color, 25) : i_TxtClrMatch
              ? color.new(kl.Color, color.t(kl.Color) / 2) : colorIsNotTransparent(CLR_NONE)
              ? i_TxtClr : color.new(chart.fg_color, 50)
        Text = kl.new_label_string('text', nameArr, priceArr)
        Tool = kl.new_label_string('tool', toolArr, priceArr)
        kl.Label := label.new(x, y,
              Text, xloc.bar_time, yloc.price, bgc, label.style_label_left, fgc, size.large, text.align_center, Tool)
        kl.Label

// @function Iterates through and displays a set of levels on their first bar.
showFirstBarLevels(int first, int last) =>
    for i = first to last by 1
        kl = keyLevels.get(i)
        if kl.Show
            kl.new_line(kl.Index)
            kl.new_label()

// @function Updates the Y coordinate of a levels line and label.
method update_y(level kl) =>
    kl.Price := kl.Level
    kl.Line.set_y1(kl.Level)
    kl.Line.set_y2(kl.Level)
    kl.Label.set_y(kl.Level)

// @function Updates the label strings of a level.
method update_label_strings(level kl) =>
    if not na(kl.Label)
        kl.Label.set_text(kl.new_label_string('text'))
        kl.Label.set_tooltip(kl.new_label_string('tool'))

// @function Updates how a level displays on the chart.
method updateLevel(int i) =>
    kl = keyLevels.get(i)
    if kl.Show and kl.Price != kl.Level
        kl.update_y()
        kl.update_label_strings()
        falseIf = array.from(OPH, OPL)
        if not falseIf.includes(i)
            kl.Line.set_x1(kl.Index)

// @function Manages recent high and low levels.
method manage_high_low(int hi, bool True = true) =>
    if True
        lo = hi < TDO ? hi + 1 : hi
        if isFirstMinOf(hi)
            showFirstBarLevels(hi, lo)
        if isSegment(segment(hi))
            hi.updateLevel()
            lo.updateLevel()

// @function Iterates through and hides a set of levels.
hideLevel(int first, int last) =>
    for i = first to last by 1
        kl = keyLevels.get(i)
        kl.Show := false
        kl.Show


// ----------------------------------------------- NEW SESSION LOGIC ----------------------------------------------- //

// On the first bar of a new session with complete data…
if newSession and session > 0

    // Reset level visibility to their intial states
    for kl in keyLevels
        kl.Show := kl.InitState
        kl.Show

    // Disable levels that are not meaningful or measurable…
    if timeframe.isdwm
        // Disable intrabar levels
        hideLevel(TDO, YDC)
        hideLevel(TDH, YDL)
    else
        if market and MK1
            // Disable all levels that are not part of market hours
            hideLevel(PMH, AHL)
        else
            if not AH1
                // Disable after hours levels
                hideLevel(AHH, AHL)
            if market
                // Disable near open, premarket, and overnight levels
                hideLevel(PMH, OVL)
            else if premarket
                // Disable overnight Levels
                hideLevel(OVH, OVL)

    // Delete the old stuff
    if not na(nyOrHighLine)
        nyOrHighLine.delete()
        nyOrHighLine := na
    if not na(nyOrLowLine)
        nyOrLowLine.delete()
        nyOrLowLine := na
    if not na(nyOrHighLabel)
        nyOrHighLabel.delete()
        nyOrHighLabel := na
    if not na(nyOrLowLabel)
        nyOrLowLabel.delete()
        nyOrLowLabel := na
    nyOrHigh := na
    nyOrLow := na
    nyOrStartIndex := na

    if not na(londonOrHighLine)
        londonOrHighLine.delete()
        londonOrHighLine := na
    if not na(londonOrLowLine)
        londonOrLowLine.delete()
        londonOrLowLine := na
    if not na(londonOrHighLabel)
        londonOrHighLabel.delete()
        londonOrHighLabel := na
    if not na(londonOrLowLabel)
        londonOrLowLabel.delete()
        londonOrLowLabel := na
    londonOrHigh := na
    londonOrLow := na
    londonOrStartIndex := na

    if not na(asiaOrHighLine)
        asiaOrHighLine.delete()
        asiaOrHighLine := na
    if not na(asiaOrLowLine)
        asiaOrLowLine.delete()
        asiaOrLowLine := na
    if not na(asiaOrHighLabel)
        asiaOrHighLabel.delete()
        asiaOrHighLabel := na
    if not na(asiaOrLowLabel)
        asiaOrLowLabel.delete()
        asiaOrLowLabel := na
    asiaOrHigh := na
    asiaOrLow := na
    asiaOrStartIndex := na
    if not na(nyOrBox)
        nyOrBox.delete()
        nyOrBox := na
    if not na(londonOrBox)
        londonOrBox.delete()
        londonOrBox := na
    if not na(asiaOrBox)
        asiaOrBox.delete()
        asiaOrBox := na
    for kl in keyLevels
        kl.Line.delete()
        kl.Label.delete()

    // Create new levels for the current session
    for [i, kl] in keyLevels
        if kl.Show
            x1 = kl.Index
            kl.new_line(x1)
            kl.new_label()
            if i < AHH or TDO <= i and i <= TDC
                kl.Line.delete()
                kl.Label.delete()

    // Reset bar indices…
    for [i, kl] in keyLevels
        // If key level will extend into the next session…
        if AHH <= i and i <= YDL or i == TDO or i == YDO or i == YDC
            kl.OldIndex := kl.Index
            kl.OldIndex
        // If key level is recent…
        else
            kl.Index := bar.Index
            kl.Index

    // Reset daily levels
    TD.set_high_low(useTd)
    YD.set_high_low(useYd)

// ----------------------------------------------- BAR BY BAR LOGIC ------------------------------------------------ //

// @function Builds the display text for an opening range label (price • name).
orLabelText(float price, string fullName, string abrvName) =>
    priceStr = formatPrice(price)
    i_Abbreviate
          ? (i_InclPrice ? ' ' + priceStr + ' • ' : ' ') + abrvName + ' '
          : (i_InclPrice ? ' ' + priceStr + ' • ' : ' ') + fullName + ' '

// @function Checks if a recent level should be managed.
manageCriteria(i) =>
    switch i
        OP => use_GlobexOpen and marketOpen
        PM => usePm and (premarket or overnight and not useOv)
        OV => useOv and overnight
        TD => useTd and mktSession
        TDO => useTdOp and isFirstMinOf(TDO)
        YDO => useYdOp and isFirstMinOf(YDO)
        => false

// Update recent levels
TDO.manage_high_low(manageCriteria(TDO))
YDO.set_open(manageCriteria(YDO))
for i = TD to OV by 1
    i.manage_high_low(manageCriteria(i))

// Keep Globex box, lines, and labels on one data source so they always stay in sync.
globexHigh = na(sessionOrHigh) ? keyLevels.get(OPH).Level : sessionOrHigh
globexLow = na(sessionOrLow) ? keyLevels.get(OPL).Level : sessionOrLow
if useGlobexOr and not timeframe.isdwm and not na(globexHigh) and not na(globexLow)
    x1SecondaryOr = na(sessionOrStartIndex) ? firstMarketBarIndex : sessionOrStartIndex
    x2SecondaryOr = bar.Index + numOfBars(fwdLen)
    secondaryOrColor = i_GlobexOpenColorLine

    if na(globexOrBox)
        globexOrBox := box.new(x1SecondaryOr, globexHigh, x2SecondaryOr, globexLow,
              xloc = xloc.bar_time, border_width = 0, border_color = CLR_NONE,
              bgcolor = color.new(secondaryOrColor, i_OrRangeTransparency))
    else
        globexOrBox.set_left(x1SecondaryOr)
        globexOrBox.set_right(x2SecondaryOr)
        globexOrBox.set_top(globexHigh)
        globexOrBox.set_bottom(globexLow)
        globexOrBox.set_bgcolor(color.new(secondaryOrColor, i_OrRangeTransparency))

    if na(sessionOrHighLine)
        sessionOrHighLine := line.new(x1SecondaryOr, globexHigh, x2SecondaryOr, globexHigh,
              xloc.bar_time, extend.none, secondaryOrColor, lineStyle, i_Lw)
        sessionOrLowLine := line.new(x1SecondaryOr, globexLow, x2SecondaryOr, globexLow,
              xloc.bar_time, extend.none, secondaryOrColor, lineStyle, i_Lw)
    else
        sessionOrHighLine.set_x1(x1SecondaryOr)
        sessionOrHighLine.set_x2(x2SecondaryOr)
        sessionOrHighLine.set_y1(globexHigh)
        sessionOrHighLine.set_y2(globexHigh)

        sessionOrLowLine.set_x1(x1SecondaryOr)
        sessionOrLowLine.set_x2(x2SecondaryOr)
        sessionOrLowLine.set_y1(globexLow)
        sessionOrLowLine.set_y2(globexLow)

    if i_ShowLabel
        if na(sessionOrHighLabel)
            sessionOrHighLabel := label.new(x2SecondaryOr, globexHigh, orLabelText(globexHigh, 'Globex Open High', 'G op H'),
                  xloc.bar_time, yloc.price, CLR_NONE, label.style_label_left, secondaryOrColor,
                  size.large, text.align_center, 'Globex Open High')
            sessionOrLowLabel := label.new(x2SecondaryOr, globexLow, orLabelText(globexLow, 'Globex Open Low', 'G op L'),
                  xloc.bar_time, yloc.price, CLR_NONE, label.style_label_left, secondaryOrColor,
                  size.large, text.align_center, 'Globex Open Low')
        else
            sessionOrHighLabel.set_x(x2SecondaryOr)
            sessionOrHighLabel.set_y(globexHigh)
            sessionOrHighLabel.set_text(orLabelText(globexHigh, 'Globex Open High', 'G op H'))
            sessionOrLowLabel.set_x(x2SecondaryOr)
            sessionOrLowLabel.set_y(globexLow)
            sessionOrLowLabel.set_text(orLabelText(globexLow, 'Globex Open Low', 'G op L'))
    else
        if not na(sessionOrHighLabel)
            sessionOrHighLabel.delete()
            sessionOrHighLabel := na
        if not na(sessionOrLowLabel)
            sessionOrLowLabel.delete()
            sessionOrLowLabel := na
else
    if not na(sessionOrHighLabel)
        sessionOrHighLabel.delete()
        sessionOrHighLabel := na
    if not na(sessionOrLowLabel)
        sessionOrLowLabel.delete()
        sessionOrLowLabel := na
    if not na(globexOrBox)
        globexOrBox.delete()
        globexOrBox := na
    if not na(sessionOrHighLine)
        sessionOrHighLine.delete()
        sessionOrHighLine := na
    if not na(sessionOrLowLine)
        sessionOrLowLine.delete()
        sessionOrLowLine := na

if useNyOr and not timeframe.isdwm and not na(nyOrHigh) and not na(nyOrLow)
    x1NyOr = na(nyOrStartIndex) ? bar.Index : nyOrStartIndex
    x2NyOr = bar.Index + numOfBars(fwdLen)

    if na(nyOrBox)
        nyOrBox := box.new(x1NyOr, nyOrHigh, x2NyOr, nyOrLow,
              xloc = xloc.bar_time, border_width = 0, border_color = CLR_NONE,
              bgcolor = color.new(i_NewYorkOpenColor, i_OrRangeTransparency))
    else
        nyOrBox.set_left(x1NyOr)
        nyOrBox.set_right(x2NyOr)
        nyOrBox.set_top(nyOrHigh)
        nyOrBox.set_bottom(nyOrLow)
        nyOrBox.set_bgcolor(color.new(i_NewYorkOpenColor, i_OrRangeTransparency))

    if na(nyOrHighLine)
        nyOrHighLine := line.new(x1NyOr, nyOrHigh, x2NyOr, nyOrHigh,
              xloc.bar_time, extend.none, i_NewYorkOpenColor, lineStyle, i_Lw)
        nyOrLowLine := line.new(x1NyOr, nyOrLow, x2NyOr, nyOrLow,
              xloc.bar_time, extend.none, i_NewYorkOpenColor, lineStyle, i_Lw)
    else
        nyOrHighLine.set_x1(x1NyOr)
        nyOrHighLine.set_x2(x2NyOr)
        nyOrHighLine.set_y1(nyOrHigh)
        nyOrHighLine.set_y2(nyOrHigh)

        nyOrLowLine.set_x1(x1NyOr)
        nyOrLowLine.set_x2(x2NyOr)
        nyOrLowLine.set_y1(nyOrLow)
        nyOrLowLine.set_y2(nyOrLow)

    if i_ShowLabel
        if na(nyOrHighLabel)
            nyOrHighLabel := label.new(x2NyOr, nyOrHigh, orLabelText(nyOrHigh, 'NY Open High', 'N op H'),
                  xloc.bar_time, yloc.price, CLR_NONE, label.style_label_left, i_NewYorkOpenColor,
                  size.large, text.align_center, 'New York Open High')
            nyOrLowLabel := label.new(x2NyOr, nyOrLow, orLabelText(nyOrLow, 'NY Open Low', 'N op L'),
                  xloc.bar_time, yloc.price, CLR_NONE, label.style_label_left, i_NewYorkOpenColor,
                  size.large, text.align_center, 'New York Open Low')
        else
            nyOrHighLabel.set_x(x2NyOr)
            nyOrHighLabel.set_y(nyOrHigh)
            nyOrHighLabel.set_text(orLabelText(nyOrHigh, 'NY Open High', 'N op H'))
            nyOrLowLabel.set_x(x2NyOr)
            nyOrLowLabel.set_y(nyOrLow)
            nyOrLowLabel.set_text(orLabelText(nyOrLow, 'NY Open Low', 'N op L'))
    else
        if not na(nyOrHighLabel)
            nyOrHighLabel.delete()
            nyOrHighLabel := na
        if not na(nyOrLowLabel)
            nyOrLowLabel.delete()
            nyOrLowLabel := na
else
    if not na(nyOrHighLabel)
        nyOrHighLabel.delete()
        nyOrHighLabel := na
    if not na(nyOrLowLabel)
        nyOrLowLabel.delete()
        nyOrLowLabel := na
    if not na(nyOrBox)
        nyOrBox.delete()
        nyOrBox := na
    if not na(nyOrHighLine)
        nyOrHighLine.delete()
        nyOrHighLine := na
    if not na(nyOrLowLine)
        nyOrLowLine.delete()
        nyOrLowLine := na

if useLondonOr and not timeframe.isdwm and not na(londonOrHigh) and not na(londonOrLow)
    x1LondonOr = na(londonOrStartIndex) ? bar.Index : londonOrStartIndex
    x2LondonOr = bar.Index + numOfBars(fwdLen)

    if na(londonOrBox)
        londonOrBox := box.new(x1LondonOr, londonOrHigh, x2LondonOr, londonOrLow,
              xloc = xloc.bar_time, border_width = 0, border_color = CLR_NONE,
              bgcolor = color.new(i_LondonOpenColor, i_OrRangeTransparency))
    else
        londonOrBox.set_left(x1LondonOr)
        londonOrBox.set_right(x2LondonOr)
        londonOrBox.set_top(londonOrHigh)
        londonOrBox.set_bottom(londonOrLow)
        londonOrBox.set_bgcolor(color.new(i_LondonOpenColor, i_OrRangeTransparency))

    if na(londonOrHighLine)
        londonOrHighLine := line.new(x1LondonOr, londonOrHigh, x2LondonOr, londonOrHigh,
              xloc.bar_time, extend.none, i_LondonOpenColor, lineStyle, i_Lw)
        londonOrLowLine := line.new(x1LondonOr, londonOrLow, x2LondonOr, londonOrLow,
              xloc.bar_time, extend.none, i_LondonOpenColor, lineStyle, i_Lw)
    else
        londonOrHighLine.set_x1(x1LondonOr)
        londonOrHighLine.set_x2(x2LondonOr)
        londonOrHighLine.set_y1(londonOrHigh)
        londonOrHighLine.set_y2(londonOrHigh)

        londonOrLowLine.set_x1(x1LondonOr)
        londonOrLowLine.set_x2(x2LondonOr)
        londonOrLowLine.set_y1(londonOrLow)
        londonOrLowLine.set_y2(londonOrLow)

    if i_ShowLabel
        if na(londonOrHighLabel)
            londonOrHighLabel := label.new(x2LondonOr, londonOrHigh, orLabelText(londonOrHigh, 'London Open High', 'L op H'),
                  xloc.bar_time, yloc.price, CLR_NONE, label.style_label_left, i_LondonOpenColor,
                  size.large, text.align_center, 'London Open High')
            londonOrLowLabel := label.new(x2LondonOr, londonOrLow, orLabelText(londonOrLow, 'London Open Low', 'L op L'),
                  xloc.bar_time, yloc.price, CLR_NONE, label.style_label_left, i_LondonOpenColor,
                  size.large, text.align_center, 'London Open Low')
        else
            londonOrHighLabel.set_x(x2LondonOr)
            londonOrHighLabel.set_y(londonOrHigh)
            londonOrHighLabel.set_text(orLabelText(londonOrHigh, 'London Open High', 'L op H'))
            londonOrLowLabel.set_x(x2LondonOr)
            londonOrLowLabel.set_y(londonOrLow)
            londonOrLowLabel.set_text(orLabelText(londonOrLow, 'London Open Low', 'L op L'))
    else
        if not na(londonOrHighLabel)
            londonOrHighLabel.delete()
            londonOrHighLabel := na
        if not na(londonOrLowLabel)
            londonOrLowLabel.delete()
            londonOrLowLabel := na
else
    if not na(londonOrHighLabel)
        londonOrHighLabel.delete()
        londonOrHighLabel := na
    if not na(londonOrLowLabel)
        londonOrLowLabel.delete()
        londonOrLowLabel := na
    if not na(londonOrBox)
        londonOrBox.delete()
        londonOrBox := na
    if not na(londonOrHighLine)
        londonOrHighLine.delete()
        londonOrHighLine := na
    if not na(londonOrLowLine)
        londonOrLowLine.delete()
        londonOrLowLine := na

if useAsiaOr and not timeframe.isdwm and not na(asiaOrHigh) and not na(asiaOrLow)
    x1AsiaOr = na(asiaOrStartIndex) ? bar.Index : asiaOrStartIndex
    x2AsiaOr = bar.Index + numOfBars(fwdLen)

    if na(asiaOrBox)
        asiaOrBox := box.new(x1AsiaOr, asiaOrHigh, x2AsiaOr, asiaOrLow,
              xloc = xloc.bar_time, border_width = 0, border_color = CLR_NONE,
              bgcolor = color.new(i_AsiaOpenColor, i_OrRangeTransparency))
    else
        asiaOrBox.set_left(x1AsiaOr)
        asiaOrBox.set_right(x2AsiaOr)
        asiaOrBox.set_top(asiaOrHigh)
        asiaOrBox.set_bottom(asiaOrLow)
        asiaOrBox.set_bgcolor(color.new(i_AsiaOpenColor, i_OrRangeTransparency))

    if na(asiaOrHighLine)
        asiaOrHighLine := line.new(x1AsiaOr, asiaOrHigh, x2AsiaOr, asiaOrHigh,
              xloc.bar_time, extend.none, i_AsiaOpenColor, lineStyle, i_Lw)
        asiaOrLowLine := line.new(x1AsiaOr, asiaOrLow, x2AsiaOr, asiaOrLow,
              xloc.bar_time, extend.none, i_AsiaOpenColor, lineStyle, i_Lw)
    else
        asiaOrHighLine.set_x1(x1AsiaOr)
        asiaOrHighLine.set_x2(x2AsiaOr)
        asiaOrHighLine.set_y1(asiaOrHigh)
        asiaOrHighLine.set_y2(asiaOrHigh)

        asiaOrLowLine.set_x1(x1AsiaOr)
        asiaOrLowLine.set_x2(x2AsiaOr)
        asiaOrLowLine.set_y1(asiaOrLow)
        asiaOrLowLine.set_y2(asiaOrLow)

    if i_ShowLabel
        if na(asiaOrHighLabel)
            asiaOrHighLabel := label.new(x2AsiaOr, asiaOrHigh, orLabelText(asiaOrHigh, 'Asia Open High', 'A op H'),
                  xloc.bar_time, yloc.price, CLR_NONE, label.style_label_left, i_AsiaOpenColor,
                  size.large, text.align_center, 'Asia Open High')
            asiaOrLowLabel := label.new(x2AsiaOr, asiaOrLow, orLabelText(asiaOrLow, 'Asia Open Low', 'A op L'),
                  xloc.bar_time, yloc.price, CLR_NONE, label.style_label_left, i_AsiaOpenColor,
                  size.large, text.align_center, 'Asia Open Low')
        else
            asiaOrHighLabel.set_x(x2AsiaOr)
            asiaOrHighLabel.set_y(asiaOrHigh)
            asiaOrHighLabel.set_text(orLabelText(asiaOrHigh, 'Asia Open High', 'A op H'))
            asiaOrLowLabel.set_x(x2AsiaOr)
            asiaOrLowLabel.set_y(asiaOrLow)
            asiaOrLowLabel.set_text(orLabelText(asiaOrLow, 'Asia Open Low', 'A op L'))
    else
        if not na(asiaOrHighLabel)
            asiaOrHighLabel.delete()
            asiaOrHighLabel := na
        if not na(asiaOrLowLabel)
            asiaOrLowLabel.delete()
            asiaOrLowLabel := na
else
    if not na(asiaOrHighLabel)
        asiaOrHighLabel.delete()
        asiaOrHighLabel := na
    if not na(asiaOrLowLabel)
        asiaOrLowLabel.delete()
        asiaOrLowLabel := na
    if not na(asiaOrBox)
        asiaOrBox.delete()
        asiaOrBox := na
    if not na(asiaOrHighLine)
        asiaOrHighLine.delete()
        asiaOrHighLine := na
    if not na(asiaOrLowLine)
        asiaOrLowLine.delete()
        asiaOrLowLine := na

// Adjust lines and labels
overlapped = map.new<float, string>()
x1Adj = bar.Index - numOfBars(i_TruncLen)
x2Adj = bar.Index + numOfBars(fwdLen)
var lastKeyLevelsIndex = keyLevels.size() - 1
for [i, kl] in keyLevels
    if kl.Show and not na(kl.Line)
        kl.Line.set_x2(x2Adj)

        if i_Truncate
            kl.Line.set_x1(x1Adj)

        if i_ShowLabel
            if i_MergeLabels
                price = math.round_to_mintick(math.round(kl.Price / i_MergeThreshold) * i_MergeThreshold)
                overlapped.put(price, overlapped.get(price) + str.tostring(i) + ' ')
            else
                kl.Label.set_x(x2Adj)

// Merge labels with identical levels
if i_ShowLabel and i_MergeLabels
    nameArr = array.new_string()
    toolArr = array.new_string()
    pricArr = array.new_float()
    for val in overlapped.values()
        str = str.trim(val)
        arr = str.split(str, ' ')
        i = int(str.tonumber(arr.get(0)))
        kl = keyLevels.get(i)

        // When more than one level has an identical rounded value…
        if arr.size() > 1

            // Get the names of each key level and delete its label
            for idx in arr
                lvl = keyLevels.get(int(str.tonumber(idx)))
                nameArr.push(lvl.Name)
                toolArr.push(lvl.ToolTip)
                pricArr.push(math.round_to_mintick(lvl.Price))
                lvl.Label.delete()

            // Give one of the levels a new label with all the names
            kl.new_label(nameArr, toolArr, pricArr)

            // Clear arrays for next iteration
            nameArr.clear()
            toolArr.clear()
            pricArr.clear()

        // Otherwise give the unique value a new label when the line is displayed
        else if not na(kl.Line)
            kl.Label.delete()
            kl.new_label()

//----------------------------------------------------< END-CODE >----------------------------------------------------//

Overview

These include premarket, today, overnight and after-hours highs and lows, the day's open, and shaded opening ranges for the Globex, Asia, London and New York sessions, each with an adjustable start time and range length. Every group has an on or off switch and colour options. It uses higher-timeframe session data, so levels can change while a session is still forming. No trade signals are produced.

Details

Published by

DarthBuddha on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Support & resistance, Sessions & time

Markets

Indices

Timeframes

M15-H1

Risk Notes

  • Signals can change on the live bar or after it closes (repainting)

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
i_PmOnOffenumState.oni_PmOnOff
HighcolorCLR_PM_HIi_PmHiClr
LowcolorCLR_PM_LOi_PmLoClr
i_TdOnOffenumState.oni_TdOnOff
HighcolorCLR_TD_HIi_TdHiClr
LowcolorCLR_TD_LOi_TdLoClr
i_TdOpOnOffenumState.offi_TdOpOnOff
OpencolorCLR_TD_OPi_TdOpClr
Range Transparencyint95i_OrRangeTransparency
i_OnOff_GlobexOpenenumState.oni_OnOff_GlobexOpen
i_GlobexOpenColorLinecolorCOLOR_GLOBEXi_GlobexOpenColorLine
Startstring1800i_GlobexOpenStart
Show all 40 settings
SettingTypeDefaultVariable
Rangeint15i_GlobexOpenRange
i_OnOff_AsiaOpenenumState.oni_OnOff_AsiaOpen
i_AsiaOpenColorcolorCOLOR_ASIAi_AsiaOpenColor
Startstring2000i_AsiaOpenStart
Rangeint15i_AsiaOpenRange
i_OnOff_LondonOpenenumState.oni_OnOff_LondonOpen
i_LondonOpenColorcolorCOLOR_LONDONi_LondonOpenColor
Startstring0300i_LondonOpenStart
Rangeint15i_LondonOpenRange
i_OnOff_NewYorkOpenenumState.oni_OnOff_NewYorkOpen
i_NewYorkOpenColorcolorCOLOR_NEWYORKi_NewYorkOpenColor
Startstring0930i_NewYorkOpenStart
Rangeint15i_NewYorkOpenRange
i_OvOnOffenumState.oni_OvOnOff
HighcolorCLR_OV_HIi_OvHiClr
LowcolorCLR_OV_LOi_OvLoClr
i_AhOnOffenumState.oni_AhOnOff
HighcolorCLR_AH_HIi_AhHiClr
LowcolorCLR_AH_LOi_AhLoClr
i_YdOnOffenumState.oni_YdOnOff
HighcolorCLR_YD_HIi_YdHiClr
LowcolorCLR_YD_LOi_YdLoClr
i_YdOpOnOffenumState.oni_YdOpOnOff
OpencolorCLR_YD_OPi_YdOpClr
i_YdClOnOffenumState.oni_YdClOnOff
ClosecolorCLR_YD_CLi_YdClsClr
Label TextcolorCLR_NONEi_TxtClr
Match Linebooltruei_TxtClrMatch
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

575 stars

This indicator builds Market Profile Time Price Opportunity (TPO) structures across defined session timeframes directly on the main price chart. It visualizes the Point of Control (POC) and Value Area boundaries using dynamic box drawings. An integrated multi-

Indicator v5 Support & resistanceScreeners & dashboards
Updated 1 year ago · MPL-2.0 GitHub repository of Market Profile with Screener
257 stars

This indicator plots auto trade signals for E-mini S&P 500 futures on the price chart. It computes an EMA200 filter along with day ETH RTH session levels and percentage-based entry zones. Signals trigger CALL or PUT cases when price crosses defined thresholds

Indicator v6 TrendSupport & resistance
Updated 6 weeks ago · None stated GitHub repository of Auto Trade Futures ES
219 stars

This indicator plots reference levels for previous daily high and low prices directly on the price chart. It requests daily timeframe data using the security function and plots the offset values as continuous lines, displaying resistance in red and support in

Indicator v4 Support & resistanceSessions & time
Updated 5 years ago · None stated GitHub repository of 昨日高低點標示

Golden Bullet Strategy

by TradersPost
218 stars

The Golden Bullet Strategy is an overlay signal indicator based on Smart Money Concepts and time-based liquidity models. It identifies liquidity sweeps of a designated reference session range during a specified hunt window. It then confirms Market Structure Sh

Indicator v6 SMC / ICT / price actionSessions & time
Updated 3 months ago · None stated GitHub repository of Golden Bullet Strategy

Moon Dev GEX Arrows

by moondevonyt
92 stars

This overlay indicator plots option strike walls and an optional session VWAP on the price chart for SPY or ES. It spaces major strikes by the selected instrument, draws a set number of strikes on each side, and marks wall-trap and sweep events after a cooldow

Indicator v5 Support & resistanceSessions & time
Updated 2 months ago · Custom licence GitHub repository of Moon Dev GEX Arrows

Key Levels

by cryptorife
75 stars

This indicator plots reference levels and session markers directly on the price chart. It calculates and draws lines for the daily open, weekly open, monthly open, previous day OHLC, and previous week extremes, along with a selectable day-of-week range and ini

Indicator v4 Support & resistanceSessions & time Repaints
Updated 3 years ago · GPL-3.0 GitHub repository of Key Levels

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers