Indicator · Pine Script v6 · On the chart

Meridian - Futures Levels

by sukesan7

Meridian Futures Levels plots session structure for US index futures on the price chart.

View on GitHub

GitHub stars4on the repository
Pine versionv6current
Lines of code1,328
Settings40inputs
LicenceMPL-2.0
Last update7 weeks ago8 Sep 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,328 lines of Pine Script v6 from src/futures/meridian-futures-levels.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

meridian-futures-levels.pine
//@version=6
// SPDX-License-Identifier: MPL-2.0
// Meridian — Futures Levels · v0.2.0

indicator(
     title = "Meridian - Futures Levels",
     shorttitle = "Meridian Futures Levels",
     overlay = true,
     format = format.price,
     max_bars_back = 5000,
     max_lines_count = 220,
     max_labels_count = 220,
     max_boxes_count = 40,
     explicit_plot_zorder = true
 )

const string G_GENERAL  = "01 — General"
const string G_SESSIONS = "02 — Sessions"
const string G_OR       = "03 — Opening Range"
const string G_MARKERS  = "03A — Vertical Time Markers"
const string G_IB       = "04 — Initial Balance"
const string G_VWAP     = "05 — VWAP + Custom Deviation"
const string G_REFS     = "06 — Previous Day / Week"
const string G_FIB      = "07 — Fibonacci + Range Projections"
const string G_CLUSTER  = "08 — Level Clustering"
const string G_STATE    = "09 — Acceptance / Rejection"
const string G_THEME    = "10 — Meridian Theme"
const string G_HUD      = "11 — HUD + Alerts"

bool   indicatorEnabled = input.bool(true, "Enable indicator", group = G_GENERAL)
string sessionTimezone  = input.string("America/New_York", "Session time zone", group = G_GENERAL,
     tooltip = "Use an IANA time zone. America/New_York is recommended for CME index-futures session definitions.")
int    rightExtension   = input.int(18, "Right-side line extension (bars)", minval = 2, maxval = 300, group = G_GENERAL)
bool   showPriceInTags  = input.bool(true, "Show prices in horizontal labels", group = G_GENERAL)
bool   showLiveLevelTags = input.bool(true, "Show horizontal line labels", group = G_GENERAL)
string horizontalLabelMode = input.string("Compact", "Horizontal-label wording", options = ["Compact", "Descriptive"], group = G_GENERAL)
bool   showPriceScaleValues = input.bool(false, "Show plotted values on price scale", group = G_GENERAL,
     tooltip = "Off by default to keep the right-side price scale clean.")
int    horizontalLabelOffsetBars = input.int(1, "Horizontal-label offset (bars)", minval = 0, maxval = 20, group = G_GENERAL)
float  horizontalLabelOffsetTicks = input.float(3.0, "Horizontal-label height (ticks)", minval = 0.0, maxval = 40.0, step = 0.25, group = G_GENERAL)
string horizontalLabelTextSize = input.string("Small", "Horizontal-label text size", options = ["Tiny", "Small", "Normal"], group = G_GENERAL)

string futuresSession   = input.session("1800-1700:1234567", "Full futures session", group = G_SESSIONS)
string overnightSession = input.session("1800-0930:1234567", "Overnight session", group = G_SESSIONS)
string rthSession       = input.session("0930-1600:23456", "RTH session", group = G_SESSIONS)
int    rthOpenHour      = input.int(9, "RTH open (hour)", minval = 0, maxval = 23, inline = "rthopen", group = G_SESSIONS)
int    rthOpenMinute    = input.int(30, "Minute", minval = 0, maxval = 59, inline = "rthopen", group = G_SESSIONS)

bool showSessionShading = input.bool(false, "Shade sessions", group = G_SESSIONS)
bool showSessionOpens   = input.bool(true, "Show futures / RTH opens", group = G_SESSIONS)
bool showOvernight      = input.bool(true, "Show overnight high / low", group = G_SESSIONS)
bool showOvernightMid   = input.bool(false, "Show overnight midpoint", group = G_SESSIONS)
int  sessionLineWidth   = input.int(1, "Session level width", minval = 1, maxval = 4, group = G_SESSIONS)

bool showOR             = input.bool(true, "Show Opening Range", group = G_OR)
string openingRangeDuration = input.string("15 Minutes", "Opening Range duration", options = ["1 Minute", "5 Minutes", "15 Minutes", "30 Minutes"], group = G_OR)
bool showORDeveloping   = input.bool(true, "Show while developing", group = G_OR)
bool showORMid          = input.bool(true, "Show midpoint", group = G_OR)
bool fillOR             = input.bool(true, "Fill range", group = G_OR)
int  orLineWidth        = input.int(2, "Line width", minval = 1, maxval = 4, group = G_OR)

bool showTimeMarkers     = input.bool(false, "Show vertical time markers", group = G_MARKERS)
bool showTimeMarkerLabels = input.bool(true, "Show time-marker labels", group = G_MARKERS)
string timeMarkerLabelLayout = input.string("Stacked", "Marker-label layout", options = ["Single line", "Stacked"], group = G_MARKERS)
string timeMarkerTextSize = input.string("Small", "Marker-label text size", options = ["Tiny", "Small", "Normal"], group = G_MARKERS)
float timeMarkerTopPaddingAtr = input.float(0.75, "Marker-label top padding (ATR)", minval = 0.0, maxval = 5.0, step = 0.05, group = G_MARKERS,
     tooltip = "Places marker text above the current New York calendar-day high. Increase if labels still overlap candles.")
int  maxTimeMarkerEvents = input.int(48, "Maximum marker events", minval = 8, maxval = 160, group = G_MARKERS)
color timeMarkerColor    = input.color(#8B5CF6, "Marker color", group = G_MARKERS)
int  timeMarkerTransparency = input.int(82, "Marker transparency", minval = 65, maxval = 96, group = G_MARKERS)

bool showIB             = input.bool(true, "Show Initial Balance", group = G_IB)
int  initialBalanceMinutes = input.int(60, "Initial Balance duration (minutes)", minval = 1, maxval = 240, group = G_IB)
bool showIBDeveloping   = input.bool(false, "Show while developing", group = G_IB)
bool showIBMid          = input.bool(true, "Show midpoint", group = G_IB)
bool fillIB             = input.bool(false, "Fill range", group = G_IB)
int  ibLineWidth        = input.int(2, "Line width", minval = 1, maxval = 4, group = G_IB)

bool   showVWAP         = input.bool(true, "Show VWAP", group = G_VWAP)
string vwapAnchor       = input.string("RTH", "Anchor", options = ["RTH", "Full Futures Session", "Week"], group = G_VWAP)
float  vwapSource       = input.source(hlc3, "Source", group = G_VWAP)
string deviationMethod = input.string("Volume-weighted", "Deviation method",
     options = ["Volume-weighted", "Unweighted"], group = G_VWAP,
     tooltip = "Volume-weighted uses cumulative volume-weighted squared distance. Unweighted treats each chart bar equally.")
bool carryVWAP          = input.bool(false, "Carry VWAP outside anchor session", group = G_VWAP)

bool  showBand1         = input.bool(true, "+/− Band 1", inline = "b1", group = G_VWAP)
float bandMult1         = input.float(1.0, "Multiplier", minval = 0.1, step = 0.1, inline = "b1", group = G_VWAP)
bool  showBand2         = input.bool(true, "+/− Band 2", inline = "b2", group = G_VWAP)
float bandMult2         = input.float(2.0, "Multiplier", minval = 0.1, step = 0.1, inline = "b2", group = G_VWAP)
bool  showBand3         = input.bool(false, "+/− Band 3", inline = "b3", group = G_VWAP)
float bandMult3         = input.float(3.0, "Multiplier", minval = 0.1, step = 0.1, inline = "b3", group = G_VWAP)
int   vwapLineWidth     = input.int(2, "VWAP line width", minval = 1, maxval = 4, group = G_VWAP)
int   bandLineWidth     = input.int(1, "Band line width", minval = 1, maxval = 3, group = G_VWAP)
int   bandFillOpacity   = input.int(92, "Band fill transparency", minval = 70, maxval = 100, group = G_VWAP)

bool   showPreviousDay  = input.bool(true, "Show previous day high / low", group = G_REFS)
string previousDayBasis = input.string("Exchange daily bar", "Previous-day source",
     options = ["Exchange daily bar", "Custom RTH session"], group = G_REFS,
     tooltip = "Exchange daily bar uses the symbol's confirmed prior daily candle. Custom RTH uses the RTH session defined above.")
bool showPreviousWeek   = input.bool(true, "Show previous week high / low", group = G_REFS)
string referenceStyle   = input.string("Dashed", "Reference line style", options = ["Solid", "Dashed", "Dotted"], group = G_REFS)
int referenceLineWidth  = input.int(1, "Reference line width", minval = 1, maxval = 4, group = G_REFS)

bool   showFib          = input.bool(false, "Show Fibonacci levels", group = G_FIB)
string fibAnchor        = input.string("Initial Balance", "Anchor range",
     options = ["Opening Range", "Initial Balance", "Overnight", "Previous Day", "Previous Week"], group = G_FIB)
bool showFibRetracements = input.bool(true, "Show internal retracements", group = G_FIB)
bool showFibExtensions   = input.bool(true, "Show symmetric range projections", group = G_FIB)

float fibRetrace1       = input.float(0.382, "Retracement 1", minval = 0.0, maxval = 1.0, step = 0.001, inline = "fr1", group = G_FIB)
float fibRetrace2       = input.float(0.500, "Retracement 2", minval = 0.0, maxval = 1.0, step = 0.001, inline = "fr2", group = G_FIB)
float fibRetrace3       = input.float(0.618, "Retracement 3", minval = 0.0, maxval = 1.0, step = 0.001, inline = "fr3", group = G_FIB)
float fibProjection1    = input.float(0.272, "Projection 1", minval = 0.0, maxval = 5.0, step = 0.001, inline = "fp1", group = G_FIB,
     tooltip = "0.272 projects the range to the conventional 1.272 / −0.272 levels.")
float fibProjection2    = input.float(0.618, "Projection 2", minval = 0.0, maxval = 5.0, step = 0.001, inline = "fp2", group = G_FIB)
float fibProjection3    = input.float(1.000, "Projection 3", minval = 0.0, maxval = 5.0, step = 0.001, inline = "fp3", group = G_FIB)
string fibLineStyle     = input.string("Dotted", "Fib line style", options = ["Solid", "Dashed", "Dotted"], group = G_FIB)
int fibLineWidth        = input.int(1, "Fib line width", minval = 1, maxval = 3, group = G_FIB)

bool showClusters       = input.bool(true, "Show current confluence clusters", group = G_CLUSTER)
bool clusterReferences  = input.bool(true, "Include day / week references", group = G_CLUSTER)
bool clusterSessions    = input.bool(true, "Include OR / IB / overnight", group = G_CLUSTER)
bool clusterVWAP        = input.bool(true, "Include VWAP / bands", group = G_CLUSTER)
bool clusterFib         = input.bool(true, "Include visible Fib levels", group = G_CLUSTER)
string clusterToleranceMode = input.string("Ticks", "Tolerance mode", options = ["Ticks", "ATR"], group = G_CLUSTER)
float clusterTicks      = input.float(8.0, "Tolerance (ticks)", minval = 0.25, step = 0.25, group = G_CLUSTER)
int   clusterAtrLength  = input.int(14, "ATR length", minval = 2, maxval = 200, group = G_CLUSTER)
float clusterAtrFactor  = input.float(0.08, "ATR tolerance factor", minval = 0.001, maxval = 2.0, step = 0.005, group = G_CLUSTER)
int   clusterMinimum    = input.int(2, "Minimum contributors", minval = 2, maxval = 10, group = G_CLUSTER)
int   clusterMaximum    = input.int(5, "Maximum displayed clusters", minval = 1, maxval = 10, group = G_CLUSTER)
int   clusterLeftBars   = input.int(18, "Zone width left (bars)", minval = 1, maxval = 300, group = G_CLUSTER)
int   clusterRightBars  = input.int(24, "Zone width right (bars)", minval = 1, maxval = 300, group = G_CLUSTER)
float clusterPaddingTicks = input.float(1.0, "Zone padding (ticks)", minval = 0.0, maxval = 20.0, step = 0.25, group = G_CLUSTER)
bool  showClusterNames  = input.bool(false, "Show contributor names", group = G_CLUSTER)
int   clusterFillOpacity = input.int(89, "Zone transparency", minval = 60, maxval = 100, group = G_CLUSTER)

bool showStates          = input.bool(true, "Enable acceptance / rejection engine", group = G_STATE)
bool stateAwareColors    = input.bool(false, "Recolor levels by latest state", group = G_STATE)
string stateToleranceMode = input.string("Ticks", "Touch tolerance mode", options = ["Ticks", "ATR"], group = G_STATE)
float stateTouchTicks    = input.float(4.0, "Touch tolerance (ticks)", minval = 0.25, step = 0.25, group = G_STATE)
int   stateAtrLength     = input.int(14, "State ATR length", minval = 2, maxval = 200, group = G_STATE)
float stateTouchAtr      = input.float(0.04, "Touch tolerance (ATR)", minval = 0.001, maxval = 1.0, step = 0.005, group = G_STATE)
float stateAcceptTicks   = input.float(2.0, "Acceptance buffer (ticks)", minval = 0.0, step = 0.25, group = G_STATE)
int   stateAcceptCloses  = input.int(2, "Closes required for acceptance", minval = 1, maxval = 10, group = G_STATE)
float stateMinimumWick   = input.float(0.08, "Minimum rejection wick (ATR)", minval = 0.0, maxval = 2.0, step = 0.01, group = G_STATE)
bool  showStateLabels    = input.bool(false, "Show acceptance / rejection labels", group = G_STATE)
int   maximumStateLabels = input.int(60, "Maximum state labels", minval = 5, maxval = 180, group = G_STATE)
int   stateCooldownBars  = input.int(3, "Per-level rejection cooldown (bars)", minval = 0, maxval = 100, group = G_STATE)

color accentColor       = input.color(#8B5CF6, "Brand / header", group = G_THEME)
color secondaryColor    = input.color(#C4B5FD, "Secondary", group = G_THEME)
color vwapColor         = input.color(#2DD4BF, "VWAP", group = G_THEME)
color dayReferenceColor = input.color(#38BDF8, "Previous day", group = G_THEME)
color weekReferenceColor = input.color(#8B5CF6, "Previous week", group = G_THEME)
color overnightColor    = input.color(#94A3B8, "Overnight", group = G_THEME)
color supportColor      = input.color(#2DD4BF, "Support / bullish", group = G_THEME)
color resistanceColor   = input.color(#F87171, "Resistance / bearish", group = G_THEME)
color neutralColor      = input.color(#94A3B8, "Neutral", group = G_THEME)
color pendingColor      = input.color(#A1A1AA, "Pending / developing", group = G_THEME)
color warningColor      = input.color(#FBBF24, "Warning", group = G_THEME)
color informationColor  = input.color(#38BDF8, "Information", group = G_THEME)
color panelColor        = input.color(#090E17, "HUD background", group = G_THEME)
color borderColor       = input.color(#334155, "HUD border", group = G_THEME)
color textColor         = input.color(#F8FAFC, "HUD text", group = G_THEME)

bool   showHUD           = input.bool(true, "Show compact HUD", group = G_HUD)
string hudTextSize       = input.string("Small", "HUD text size", options = ["Tiny", "Small", "Normal"], group = G_HUD)
bool   enableDynamicAlerts = input.bool(false, "Enable dynamic JSON alert() calls", group = G_HUD,
     tooltip = "Create an alert using: Any alert() function call. State events are confirmed at bar close.")

f_line_style(string styleName) =>
    switch styleName
        "Solid"  => line.style_solid
        "Dashed" => line.style_dashed
        => line.style_dotted

f_label_size(string sizeName) =>
    switch sizeName
        "Tiny"   => size.tiny
        "Normal" => size.normal
        => size.small

f_session_active(string sessionString, string timezoneString) =>
    not na(time(timeframe.period, sessionString, timezoneString))

f_window_active(int hourValue, int minuteValue, int durationMinutes, string timezoneString) =>
    int startTimestamp = timestamp(timezoneString, year(time, timezoneString), month(time, timezoneString), dayofmonth(time, timezoneString), hourValue, minuteValue)
    int endTimestamp = startTimestamp + durationMinutes * 60 * 1000
    timeframe.isintraday and time < endTimestamp and time_close > startTimestamp

f_bar_reaches_time(int hourValue, int minuteValue, string timezoneString) =>
    int markerTimestamp = timestamp(timezoneString, year(time, timezoneString), month(time, timezoneString), dayofmonth(time, timezoneString), hourValue, minuteValue)
    timeframe.isintraday and time <= markerTimestamp and time_close >= markerTimestamp

f_level_tag(string name, float price) =>
    showPriceInTags ? name + "  " + str.tostring(price, format.mintick) : name

f_horizontal_label_y(float price) =>
    price + horizontalLabelOffsetTicks * syminfo.mintick

f_append_name(string existing, string newName, int contributorCount) =>
    contributorCount <= 6 ? (str.length(existing) == 0 ? newName : existing + " · " + newName) : existing

f_push_level(float[] prices, string[] names, float[] weights, bool condition, float price, string name, float weight) =>
    if condition and not na(price)
        array.push(prices, price)
        array.push(names, name)
        array.push(weights, weight)

bool inFutures   = f_session_active(futuresSession, sessionTimezone)
bool inOvernight = f_session_active(overnightSession, sessionTimezone)
bool inRTH       = f_session_active(rthSession, sessionTimezone)
int openingRangeMinutes = switch openingRangeDuration
    "1 Minute"  => 1
    "5 Minutes" => 5
    "30 Minutes" => 30
    => 15
bool inOR        = inRTH and f_window_active(rthOpenHour, rthOpenMinute, openingRangeMinutes, sessionTimezone)
bool inIB        = inRTH and f_window_active(rthOpenHour, rthOpenMinute, initialBalanceMinutes, sessionTimezone)

int localSessionDayKey = year(time, sessionTimezone) * 10000 + month(time, sessionTimezone) * 100 + dayofmonth(time, sessionTimezone)
int tradingDayKey = time_tradingday
bool tradingDayChanged = not na(tradingDayKey) and (na(tradingDayKey[1]) or tradingDayKey != tradingDayKey[1])
bool localSessionDayChanged = na(localSessionDayKey[1]) or localSessionDayKey != localSessionDayKey[1]

bool newFutures   = inFutures and (barstate.isfirst or not inFutures[1] or tradingDayChanged)
bool newOvernight = inOvernight and (barstate.isfirst or not inOvernight[1] or tradingDayChanged)
bool newRTH       = inRTH and (barstate.isfirst or not inRTH[1] or localSessionDayChanged)
bool endRTH       = not inRTH and inRTH[1]
bool newOR        = inOR and (newRTH or barstate.isfirst or not inOR[1])
bool newIB        = inIB and (newRTH or barstate.isfirst or not inIB[1])
bool newWeek      = timeframe.change("W") or barstate.isfirst

var int overnightStartBar = na
var int rthStartBar       = na
var int rthEndBar         = na
var int orStartBar        = na
var int ibStartBar        = na

if newOvernight or (na(overnightStartBar) and inOvernight)
    overnightStartBar := bar_index
if newRTH or (na(rthStartBar) and inRTH)
    rthStartBar := bar_index
    rthEndBar := na
if endRTH and not na(rthStartBar)
    rthEndBar := math.max(rthStartBar, bar_index - 1)
if newOR or (na(orStartBar) and inOR)
    orStartBar := bar_index
if newIB or (na(ibStartBar) and inIB)
    ibStartBar := bar_index

var float futuresOpen = na
var float rthOpen = na
if newFutures or (na(futuresOpen) and inFutures)
    futuresOpen := open
if newRTH or (na(rthOpen) and inRTH)
    rthOpen := open

var float rthHigh = na
var float rthLow = na
var float previousRthHigh = na
var float previousRthLow = na
var int activeRthDayKey = na
var int previousRthDayKey = na

if newRTH
    if not na(rthHigh) and not na(rthLow) and not na(activeRthDayKey)
        previousRthHigh := rthHigh
        previousRthLow := rthLow
        previousRthDayKey := activeRthDayKey
    activeRthDayKey := localSessionDayKey
    rthHigh := high
    rthLow := low
else if inRTH
    activeRthDayKey := na(activeRthDayKey) ? localSessionDayKey : activeRthDayKey
    rthHigh := na(rthHigh) ? high : math.max(rthHigh, high)
    rthLow := na(rthLow) ? low : math.min(rthLow, low)

var float overnightHigh = na
var float overnightLow = na
if newOvernight
    overnightHigh := high
    overnightLow := low
else if inOvernight
    overnightHigh := na(overnightHigh) ? high : math.max(overnightHigh, high)
    overnightLow := na(overnightLow) ? low : math.min(overnightLow, low)
float overnightMid = (overnightHigh + overnightLow) * 0.5

var float orHigh = na
var float orLow = na
if newRTH
    orHigh := na
    orLow := na
if newOR
    orHigh := high
    orLow := low
else if inOR
    orHigh := na(orHigh) ? high : math.max(orHigh, high)
    orLow := na(orLow) ? low : math.min(orLow, low)
float orMid = (orHigh + orLow) * 0.5
bool orReadyForDisplay = not na(orHigh) and not na(orLow) and (showORDeveloping or not inOR)
bool orReadyForState = not na(orHigh) and not na(orLow) and not inOR

var float ibHigh = na
var float ibLow = na
if newRTH
    ibHigh := na
    ibLow := na
if newIB
    ibHigh := high
    ibLow := low
else if inIB
    ibHigh := na(ibHigh) ? high : math.max(ibHigh, high)
    ibLow := na(ibLow) ? low : math.min(ibLow, low)
float ibMid = (ibHigh + ibLow) * 0.5
bool ibReadyForDisplay = not na(ibHigh) and not na(ibLow) and (showIBDeveloping or not inIB)
bool ibReadyForState = not na(ibHigh) and not na(ibLow) and not inIB

string standardTicker = ticker.standard(syminfo.tickerid)
[exchangePreviousHigh, exchangePreviousLow, exchangePreviousTime] = request.security(
     standardTicker, "D", [high[1], low[1], time[1]],
     gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)
[previousWeekHigh, previousWeekLow, previousWeekTime] = request.security(
     standardTicker, "W", [high[1], low[1], time[1]],
     gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)

float previousDayHigh = previousDayBasis == "Custom RTH session" ? previousRthHigh : exchangePreviousHigh
float previousDayLow  = previousDayBasis == "Custom RTH session" ? previousRthLow : exchangePreviousLow
int previousDayIdentity = previousDayBasis == "Custom RTH session" ? previousRthDayKey : exchangePreviousTime
int previousWeekIdentity = previousWeekTime

var int previousDayReferenceStartBar = na
var int previousWeekReferenceStartBar = na
var int lastPreviousDayIdentity = na
var int lastPreviousWeekIdentity = na
bool previousDayReferenceChanged = not na(previousDayIdentity) and (na(lastPreviousDayIdentity) or previousDayIdentity != lastPreviousDayIdentity)
bool previousWeekReferenceChanged = not na(previousWeekIdentity) and (na(lastPreviousWeekIdentity) or previousWeekIdentity != lastPreviousWeekIdentity)

if previousDayReferenceChanged
    previousDayReferenceStartBar := bar_index
    lastPreviousDayIdentity := previousDayIdentity
else if na(previousDayReferenceStartBar) and not na(previousDayHigh)
    previousDayReferenceStartBar := bar_index

if previousWeekReferenceChanged
    previousWeekReferenceStartBar := bar_index
    lastPreviousWeekIdentity := previousWeekIdentity
else if na(previousWeekReferenceStartBar) and not na(previousWeekHigh)
    previousWeekReferenceStartBar := bar_index

bool vwapInAnchor = switch vwapAnchor
    "RTH"                  => inRTH
    "Full Futures Session" => inFutures
    => true

bool vwapReset = switch vwapAnchor
    "RTH"                  => newRTH
    "Full Futures Session" => newFutures
    => newWeek

var float cumulativeVolume = 0.0
var float cumulativePV = 0.0
var float cumulativeP2V = 0.0
var float cumulativePrice = 0.0
var float cumulativePrice2 = 0.0
var int cumulativeBars = 0

float currentVolume = nz(volume, 0.0)
if vwapReset
    cumulativeVolume := 0.0
    cumulativePV := 0.0
    cumulativeP2V := 0.0
    cumulativePrice := 0.0
    cumulativePrice2 := 0.0
    cumulativeBars := 0

if vwapInAnchor
    cumulativeVolume += currentVolume
    cumulativePV += vwapSource * currentVolume
    cumulativeP2V += vwapSource * vwapSource * currentVolume
    cumulativePrice += vwapSource
    cumulativePrice2 += vwapSource * vwapSource
    cumulativeBars += 1

float customVWAP = cumulativeVolume > 0.0 ? cumulativePV / cumulativeVolume : na
float volumeWeightedVariance = cumulativeVolume > 0.0 and not na(customVWAP) ?
     math.max(cumulativeP2V / cumulativeVolume - customVWAP * customVWAP, 0.0) : na
float unweightedMean = cumulativeBars > 0 ? cumulativePrice / cumulativeBars : na
float unweightedVariance = cumulativeBars > 0 and not na(unweightedMean) ?
     math.max(cumulativePrice2 / cumulativeBars - unweightedMean * unweightedMean, 0.0) : na
float customDeviation = deviationMethod == "Volume-weighted" ? math.sqrt(volumeWeightedVariance) : math.sqrt(unweightedVariance)

float vwapUpper1 = customVWAP + customDeviation * bandMult1
float vwapLower1 = customVWAP - customDeviation * bandMult1
float vwapUpper2 = customVWAP + customDeviation * bandMult2
float vwapLower2 = customVWAP - customDeviation * bandMult2
float vwapUpper3 = customVWAP + customDeviation * bandMult3
float vwapLower3 = customVWAP - customDeviation * bandMult3
bool vwapVisible = showVWAP and (vwapInAnchor or carryVWAP or vwapAnchor == "Week")

float fibLow = switch fibAnchor
    "Opening Range"  => orReadyForState ? orLow : na
    "Initial Balance" => ibReadyForState ? ibLow : na
    "Overnight"      => not inOvernight ? overnightLow : na
    "Previous Day"   => previousDayLow
    => previousWeekLow

float fibHigh = switch fibAnchor
    "Opening Range"  => orReadyForState ? orHigh : na
    "Initial Balance" => ibReadyForState ? ibHigh : na
    "Overnight"      => not inOvernight ? overnightHigh : na
    "Previous Day"   => previousDayHigh
    => previousWeekHigh

int fibStartBar = switch fibAnchor
    "Opening Range"  => orStartBar
    "Initial Balance" => ibStartBar
    "Overnight"      => overnightStartBar
    "Previous Day"   => previousDayReferenceStartBar
    => previousWeekReferenceStartBar

float fibRange = fibHigh - fibLow
bool fibReady = showFib and not na(fibLow) and not na(fibHigh) and fibRange > syminfo.mintick

float fibR1 = fibReady ? fibLow + fibRange * fibRetrace1 : na
float fibR2 = fibReady ? fibLow + fibRange * fibRetrace2 : na
float fibR3 = fibReady ? fibLow + fibRange * fibRetrace3 : na
float fibUp1 = fibReady ? fibHigh + fibRange * fibProjection1 : na
float fibUp2 = fibReady ? fibHigh + fibRange * fibProjection2 : na
float fibUp3 = fibReady ? fibHigh + fibRange * fibProjection3 : na
float fibDown1 = fibReady ? fibLow - fibRange * fibProjection1 : na
float fibDown2 = fibReady ? fibLow - fibRange * fibProjection2 : na
float fibDown3 = fibReady ? fibLow - fibRange * fibProjection3 : na

const int TRACKED_LEVELS = 17
const int IDX_PDH  = 0
const int IDX_PDL  = 1
const int IDX_PWH  = 2
const int IDX_PWL  = 3
const int IDX_ORH  = 4
const int IDX_ORL  = 5
const int IDX_IBH  = 6
const int IDX_IBL  = 7
const int IDX_ONH  = 8
const int IDX_ONL  = 9
const int IDX_VWAP = 10
const int IDX_VU1  = 11
const int IDX_VL1  = 12
const int IDX_FU1  = 13
const int IDX_FD1  = 14
const int IDX_RTHO = 15
const int IDX_FUTO = 16

var float[] trackedPrices = array.new_float(TRACKED_LEVELS, na)
var string[] trackedNames = array.from(
     "PDH", "PDL", "PWH", "PWL", "ORH", "ORL", "IBH", "IBL", "ONH", "ONL",
     "VWAP", "VWAP +1σ", "VWAP −1σ", "Fib Up 1", "Fib Down 1", "RTH Open", "Session Open")
var float[] trackedWeights = array.from(
     2.20, 2.20, 2.40, 2.40, 1.70, 1.70, 1.90, 1.90, 1.45, 1.45,
     1.35, 1.10, 1.10, 0.80, 0.80, 1.15, 1.15)
var int[] aboveCounts = array.new_int(TRACKED_LEVELS, 0)
var int[] belowCounts = array.new_int(TRACKED_LEVELS, 0)
var int[] levelStates = array.new_int(TRACKED_LEVELS, 0)
var float[] priorTrackedPrices = array.new_float(TRACKED_LEVELS, na)
var int[] lastStateEventBars = array.new_int(TRACKED_LEVELS, -1000000)

array.set(trackedPrices, IDX_PDH,  showPreviousDay ? previousDayHigh : na)
array.set(trackedPrices, IDX_PDL,  showPreviousDay ? previousDayLow : na)
array.set(trackedPrices, IDX_PWH,  showPreviousWeek ? previousWeekHigh : na)
array.set(trackedPrices, IDX_PWL,  showPreviousWeek ? previousWeekLow : na)
array.set(trackedPrices, IDX_ORH,  showOR and inRTH and orReadyForState ? orHigh : na)
array.set(trackedPrices, IDX_ORL,  showOR and inRTH and orReadyForState ? orLow : na)
array.set(trackedPrices, IDX_IBH,  showIB and inRTH and ibReadyForState ? ibHigh : na)
array.set(trackedPrices, IDX_IBL,  showIB and inRTH and ibReadyForState ? ibLow : na)
array.set(trackedPrices, IDX_ONH,  showOvernight and not inOvernight ? overnightHigh : na)
array.set(trackedPrices, IDX_ONL,  showOvernight and not inOvernight ? overnightLow : na)
array.set(trackedPrices, IDX_VWAP, vwapVisible ? customVWAP : na)
array.set(trackedPrices, IDX_VU1,  vwapVisible and showBand1 ? vwapUpper1 : na)
array.set(trackedPrices, IDX_VL1,  vwapVisible and showBand1 ? vwapLower1 : na)
array.set(trackedPrices, IDX_FU1,  fibReady and showFibExtensions ? fibUp1 : na)
array.set(trackedPrices, IDX_FD1,  fibReady and showFibExtensions ? fibDown1 : na)
array.set(trackedPrices, IDX_RTHO, showSessionOpens and inRTH and not na(rthOpen) ? rthOpen : na)
array.set(trackedPrices, IDX_FUTO, showSessionOpens and inFutures and not na(futuresOpen) ? futuresOpen : na)

float clusterATR = ta.atr(clusterAtrLength)
float stateATR = ta.atr(stateAtrLength)
float stateTouchTolerance = stateToleranceMode == "ATR" ? nz(stateATR) * stateTouchAtr : stateTouchTicks * syminfo.mintick
float stateAcceptBuffer = stateAcceptTicks * syminfo.mintick
float upperWick = high - math.max(open, close)
float lowerWick = math.min(open, close) - low

int bestEventCode = 0
int bestEventIndex = -1
float bestEventPrice = na
float bestEventScore = -1.0

bool stateAcceptedAbove = false
bool stateAcceptedBelow = false
bool stateRejectedUp = false
bool stateRejectedDown = false

if indicatorEnabled and showStates and barstate.isconfirmed
    for i = 0 to TRACKED_LEVELS - 1
        float levelPrice = array.get(trackedPrices, i)
        float previousTrackedPrice = array.get(priorTrackedPrices, i)
        bool isDynamicVWAP = i >= IDX_VWAP and i <= IDX_VL1
        bool sourceIdentityReset = (
             (i >= IDX_VWAP and i <= IDX_VL1 and vwapReset) or
             ((i == IDX_PDH or i == IDX_PDL) and previousDayReferenceChanged) or
             ((i == IDX_PWH or i == IDX_PWL) and previousWeekReferenceChanged) or
             (i == IDX_RTHO and newRTH) or
             (i == IDX_FUTO and newFutures)
        )
        bool levelChanged = not na(levelPrice) and not na(previousTrackedPrice) and math.abs(levelPrice - previousTrackedPrice) > math.max(stateTouchTolerance, syminfo.mintick * 2.0)

        if sourceIdentityReset
            array.set(aboveCounts, i, 0)
            array.set(belowCounts, i, 0)
            array.set(levelStates, i, 0)
            array.set(priorTrackedPrices, i, na)
            array.set(lastStateEventBars, i, -1000000)
            previousTrackedPrice := na

        if na(levelPrice)
            array.set(aboveCounts, i, 0)
            array.set(belowCounts, i, 0)
            array.set(levelStates, i, 0)
        else
            if levelChanged and not isDynamicVWAP
                array.set(aboveCounts, i, 0)
                array.set(belowCounts, i, 0)
                array.set(levelStates, i, 0)
                array.set(lastStateEventBars, i, -1000000)
                previousTrackedPrice := na

            bool aboveLevel = close > levelPrice + stateAcceptBuffer
            bool belowLevel = close < levelPrice - stateAcceptBuffer
            int nextAboveCount = aboveLevel ? array.get(aboveCounts, i) + 1 : 0
            int nextBelowCount = belowLevel ? array.get(belowCounts, i) + 1 : 0
            array.set(aboveCounts, i, nextAboveCount)
            array.set(belowCounts, i, nextBelowCount)

            bool acceptedAbove = nextAboveCount == stateAcceptCloses
            bool acceptedBelow = nextBelowCount == stateAcceptCloses

            bool hasPriorReference = not na(previousTrackedPrice)
            bool priorBelow = hasPriorReference and close[1] < previousTrackedPrice
            bool priorAbove = hasPriorReference and close[1] > previousTrackedPrice
            bool bearishRejection = priorBelow and high >= levelPrice - stateTouchTolerance and close < levelPrice - stateAcceptBuffer and upperWick >= nz(stateATR) * stateMinimumWick
            bool bullishRejection = priorAbove and low <= levelPrice + stateTouchTolerance and close > levelPrice + stateAcceptBuffer and lowerWick >= nz(stateATR) * stateMinimumWick

            int rawEventCode = acceptedAbove ? 1 : acceptedBelow ? -1 : bullishRejection ? 2 : bearishRejection ? -2 : 0
            int previousEventBar = array.get(lastStateEventBars, i)
            bool cooldownComplete = math.abs(rawEventCode) == 1 or bar_index - previousEventBar > stateCooldownBars
            int eventCode = cooldownComplete ? rawEventCode : 0
            if eventCode != 0
                array.set(lastStateEventBars, i, bar_index)
                float eventPriority = (math.abs(eventCode) == 1 ? 2.0 : 1.0) * array.get(trackedWeights, i)
                float normalizedDistance = nz(stateATR) > 0.0 ? math.abs(close - levelPrice) / stateATR : math.abs(close - levelPrice) / syminfo.mintick
                float eventScore = eventPriority / (1.0 + normalizedDistance)
                if eventScore > bestEventScore
                    bestEventCode := eventCode
                    bestEventIndex := i
                    bestEventPrice := levelPrice
                    bestEventScore := eventScore

            if acceptedAbove
                array.set(levelStates, i, 1)
            else if acceptedBelow
                array.set(levelStates, i, -1)
            else if bullishRejection
                array.set(levelStates, i, 2)
            else if bearishRejection
                array.set(levelStates, i, -2)

        array.set(priorTrackedPrices, i, levelPrice)

stateAcceptedAbove := bestEventCode == 1
stateAcceptedBelow := bestEventCode == -1
stateRejectedUp := bestEventCode == 2
stateRejectedDown := bestEventCode == -2

string bestEventName = bestEventIndex >= 0 ? array.get(trackedNames, bestEventIndex) : ""
string bestEventState = switch bestEventCode
    1  => "accepted_above"
    -1 => "accepted_below"
    2  => "bullish_rejection"
    -2 => "bearish_rejection"
    => ""

var string lastEventName = "None"
var string lastEventState = "Waiting"
var float lastEventPrice = na
var int lastEventTime = na

if bestEventCode != 0
    lastEventName := bestEventName
    lastEventState := switch bestEventCode
        1  => "Accepted above"
        -1 => "Accepted below"
        2  => "Bullish rejection"
        => "Bearish rejection"
    lastEventPrice := bestEventPrice
    lastEventTime := time

var label[] stateEventLabels = array.new_label()
if bestEventCode != 0 and showStateLabels
    bool bullishEvent = bestEventCode > 0
    string eventText = switch bestEventCode
        1  => "ACCEPT ↑\n" + bestEventName
        -1 => "ACCEPT ↓\n" + bestEventName
        2  => "REJECT ↑\n" + bestEventName
        => "REJECT ↓\n" + bestEventName
    label eventLabel = label.new(
         x = bar_index,
         y = bestEventPrice,
         text = eventText,
         xloc = xloc.bar_index,
         style = bullishEvent ? label.style_label_up : label.style_label_down,
         color = color.new(bullishEvent ? supportColor : resistanceColor, 8),
         textcolor = color.white,
         size = size.tiny)
    array.push(stateEventLabels, eventLabel)
    if array.size(stateEventLabels) > maximumStateLabels
        label.delete(array.shift(stateEventLabels))

if enableDynamicAlerts and bestEventCode != 0
    string jsonMessage = "{" +
         "\"source\":\"meridian_futures_levels\"," +
         "\"version\":\"0.2.0\"," +
         "\"event\":\"level_state\"," +
         "\"symbol\":\"" + syminfo.tickerid + "\"," +
         "\"timeframe\":\"" + timeframe.period + "\"," +
         "\"level\":\"" + bestEventName + "\"," +
         "\"state\":\"" + bestEventState + "\"," +
         "\"price\":" + str.tostring(bestEventPrice, format.mintick) + "," +
         "\"close\":" + str.tostring(close, format.mintick) + "," +
         "\"bar_time\":" + str.tostring(time) + "}"
    alert(jsonMessage, alert.freq_once_per_bar_close)

f_state_color(int index, color baseColor) =>
    int currentState = array.get(levelStates, index)
    stateAwareColors and currentState > 0 ? supportColor : stateAwareColors and currentState < 0 ? resistanceColor : baseColor

color pdhColor = f_state_color(IDX_PDH, dayReferenceColor)
color pdlColor = f_state_color(IDX_PDL, dayReferenceColor)
color pwhColor = f_state_color(IDX_PWH, weekReferenceColor)
color pwlColor = f_state_color(IDX_PWL, weekReferenceColor)
color orhColor = f_state_color(IDX_ORH, accentColor)
color orlColor = f_state_color(IDX_ORL, accentColor)
color ibhColor = f_state_color(IDX_IBH, secondaryColor)
color iblColor = f_state_color(IDX_IBL, secondaryColor)
color onhColor = f_state_color(IDX_ONH, overnightColor)
color onlColor = f_state_color(IDX_ONL, overnightColor)
color liveVWAPColor = f_state_color(IDX_VWAP, vwapColor)

meridianPlotDisplay = showPriceScaleValues ? display.all : display.all - display.price_scale

color sessionShade = inOR ? color.new(accentColor, 91) : inRTH ? color.new(secondaryColor, 96) : inOvernight ? color.new(overnightColor, 97) : na
bgcolor(indicatorEnabled and showSessionShading ? sessionShade : na, title = "Session shading")

bool orVisualReady = indicatorEnabled and showOR and orReadyForDisplay
bool ibVisualReady = indicatorEnabled and showIB and ibReadyForDisplay

plot(orVisualReady and inRTH ? orHigh : na, "Opening Range High", color = orhColor, linewidth = 1,
     display = showPriceScaleValues ? display.price_scale : display.none)
plot(orVisualReady and inRTH ? orLow : na, "Opening Range Low", color = orlColor, linewidth = 1,
     display = showPriceScaleValues ? display.price_scale : display.none)
plot(orVisualReady and showORMid and inRTH ? orMid : na, "Opening Range Mid", color = color.new(accentColor, 18), linewidth = 1,
     display = showPriceScaleValues ? display.price_scale : display.none)
plot(ibVisualReady and inRTH ? ibHigh : na, "Initial Balance High", color = ibhColor, linewidth = 1,
     display = showPriceScaleValues ? display.price_scale : display.none)
plot(ibVisualReady and inRTH ? ibLow : na, "Initial Balance Low", color = iblColor, linewidth = 1,
     display = showPriceScaleValues ? display.price_scale : display.none)
plot(ibVisualReady and showIBMid and inRTH ? ibMid : na, "Initial Balance Mid", color = color.new(secondaryColor, 18), linewidth = 1,
     display = showPriceScaleValues ? display.price_scale : display.none)

var line orHighGuide = na
var line orLowGuide = na
var line orMidGuide = na
var box orRangeBox = na
var line ibHighGuide = na
var line ibLowGuide = na
var line ibMidGuide = na
var box ibRangeBox = na

if barstate.islast
    int rangeEndX = inRTH ? bar_index : nz(rthEndBar, bar_index)
    int safeOrStart = math.max(orStartBar, bar_index - 9999)
    int safeIbStart = math.max(ibStartBar, bar_index - 9999)

    if orVisualReady
        if na(orHighGuide)
            orHighGuide := line.new(safeOrStart, orHigh, rangeEndX, orHigh, xloc = xloc.bar_index,
                 extend = extend.none, color = orhColor, style = inOR ? line.style_dashed : line.style_solid, width = orLineWidth)
            orLowGuide := line.new(safeOrStart, orLow, rangeEndX, orLow, xloc = xloc.bar_index,
                 extend = extend.none, color = orlColor, style = inOR ? line.style_dashed : line.style_solid, width = orLineWidth)
        else
            line.set_xy1(orHighGuide, safeOrStart, orHigh)
            line.set_xy2(orHighGuide, rangeEndX, orHigh)
            line.set_color(orHighGuide, orhColor)
            line.set_style(orHighGuide, inOR ? line.style_dashed : line.style_solid)
            line.set_width(orHighGuide, orLineWidth)
            line.set_xy1(orLowGuide, safeOrStart, orLow)
            line.set_xy2(orLowGuide, rangeEndX, orLow)
            line.set_color(orLowGuide, orlColor)
            line.set_style(orLowGuide, inOR ? line.style_dashed : line.style_solid)
            line.set_width(orLowGuide, orLineWidth)

        if showORMid
            if na(orMidGuide)
                orMidGuide := line.new(safeOrStart, orMid, rangeEndX, orMid, xloc = xloc.bar_index,
                     extend = extend.none, color = color.new(accentColor, 12), style = line.style_dotted, width = 1)
            else
                line.set_xy1(orMidGuide, safeOrStart, orMid)
                line.set_xy2(orMidGuide, rangeEndX, orMid)
                line.set_color(orMidGuide, color.new(accentColor, 12))
                line.set_style(orMidGuide, line.style_dotted)
        else if not na(orMidGuide)
            line.delete(orMidGuide)
            orMidGuide := na

        if fillOR
            if na(orRangeBox)
                orRangeBox := box.new(safeOrStart, orHigh, rangeEndX, orLow, xloc = xloc.bar_index,
                     border_color = color.new(accentColor, 100), bgcolor = color.new(accentColor, 92))
            else
                box.set_left(orRangeBox, safeOrStart)
                box.set_right(orRangeBox, rangeEndX)
                box.set_top(orRangeBox, orHigh)
                box.set_bottom(orRangeBox, orLow)
                box.set_bgcolor(orRangeBox, color.new(accentColor, 92))
        else if not na(orRangeBox)
            box.delete(orRangeBox)
            orRangeBox := na
    else
        if not na(orHighGuide)
            line.delete(orHighGuide)
            orHighGuide := na
        if not na(orLowGuide)
            line.delete(orLowGuide)
            orLowGuide := na
        if not na(orMidGuide)
            line.delete(orMidGuide)
            orMidGuide := na
        if not na(orRangeBox)
            box.delete(orRangeBox)
            orRangeBox := na

    if ibVisualReady
        if na(ibHighGuide)
            ibHighGuide := line.new(safeIbStart, ibHigh, rangeEndX, ibHigh, xloc = xloc.bar_index,
                 extend = extend.none, color = ibhColor, style = inIB ? line.style_dashed : line.style_solid, width = ibLineWidth)
            ibLowGuide := line.new(safeIbStart, ibLow, rangeEndX, ibLow, xloc = xloc.bar_index,
                 extend = extend.none, color = iblColor, style = inIB ? line.style_dashed : line.style_solid, width = ibLineWidth)
        else
            line.set_xy1(ibHighGuide, safeIbStart, ibHigh)
            line.set_xy2(ibHighGuide, rangeEndX, ibHigh)
            line.set_color(ibHighGuide, ibhColor)
            line.set_style(ibHighGuide, inIB ? line.style_dashed : line.style_solid)
            line.set_width(ibHighGuide, ibLineWidth)
            line.set_xy1(ibLowGuide, safeIbStart, ibLow)
            line.set_xy2(ibLowGuide, rangeEndX, ibLow)
            line.set_color(ibLowGuide, iblColor)
            line.set_style(ibLowGuide, inIB ? line.style_dashed : line.style_solid)
            line.set_width(ibLowGuide, ibLineWidth)

        if showIBMid
            if na(ibMidGuide)
                ibMidGuide := line.new(safeIbStart, ibMid, rangeEndX, ibMid, xloc = xloc.bar_index,
                     extend = extend.none, color = color.new(secondaryColor, 18), style = line.style_dotted, width = 1)
            else
                line.set_xy1(ibMidGuide, safeIbStart, ibMid)
                line.set_xy2(ibMidGuide, rangeEndX, ibMid)
                line.set_color(ibMidGuide, color.new(secondaryColor, 18))
                line.set_style(ibMidGuide, line.style_dotted)
        else if not na(ibMidGuide)
            line.delete(ibMidGuide)
            ibMidGuide := na

        if fillIB
            if na(ibRangeBox)
                ibRangeBox := box.new(safeIbStart, ibHigh, rangeEndX, ibLow, xloc = xloc.bar_index,
                     border_color = color.new(secondaryColor, 100), bgcolor = color.new(secondaryColor, 93))
            else
                box.set_left(ibRangeBox, safeIbStart)
                box.set_right(ibRangeBox, rangeEndX)
                box.set_top(ibRangeBox, ibHigh)
                box.set_bottom(ibRangeBox, ibLow)
                box.set_bgcolor(ibRangeBox, color.new(secondaryColor, 93))
        else if not na(ibRangeBox)
            box.delete(ibRangeBox)
            ibRangeBox := na
    else
        if not na(ibHighGuide)
            line.delete(ibHighGuide)
            ibHighGuide := na
        if not na(ibLowGuide)
            line.delete(ibLowGuide)
            ibLowGuide := na
        if not na(ibMidGuide)
            line.delete(ibMidGuide)
            ibMidGuide := na
        if not na(ibRangeBox)
            box.delete(ibRangeBox)
            ibRangeBox := na

float onDisplayHigh = indicatorEnabled and showOvernight and (inRTH or inOvernight) ? overnightHigh : na
float onDisplayLow  = indicatorEnabled and showOvernight and (inRTH or inOvernight) ? overnightLow : na
float onDisplayMid  = indicatorEnabled and showOvernight and showOvernightMid and (inRTH or inOvernight) ? overnightMid : na
plot(onDisplayHigh, "Overnight High", color = onhColor, linewidth = sessionLineWidth, style = plot.style_linebr, display = meridianPlotDisplay)
plot(onDisplayLow, "Overnight Low", color = onlColor, linewidth = sessionLineWidth, style = plot.style_linebr, display = meridianPlotDisplay)
plot(onDisplayMid, "Overnight Mid", color = color.new(overnightColor, 45), linewidth = 1, style = plot.style_linebr, display = meridianPlotDisplay)

plot(indicatorEnabled and showSessionOpens and inFutures ? futuresOpen : na, "Futures Session Open",
     color = color.new(neutralColor, 35), linewidth = 1, style = plot.style_linebr, display = meridianPlotDisplay)
plot(indicatorEnabled and showSessionOpens and inRTH ? rthOpen : na, "RTH Open",
     color = color.new(textColor, 30), linewidth = 1, style = plot.style_linebr, display = meridianPlotDisplay)

float plottedVWAP = indicatorEnabled and vwapVisible ? customVWAP : na
float plottedVU1 = indicatorEnabled and vwapVisible and showBand1 ? vwapUpper1 : na
float plottedVL1 = indicatorEnabled and vwapVisible and showBand1 ? vwapLower1 : na
float plottedVU2 = indicatorEnabled and vwapVisible and showBand2 ? vwapUpper2 : na
float plottedVL2 = indicatorEnabled and vwapVisible and showBand2 ? vwapLower2 : na
float plottedVU3 = indicatorEnabled and vwapVisible and showBand3 ? vwapUpper3 : na
float plottedVL3 = indicatorEnabled and vwapVisible and showBand3 ? vwapLower3 : na

pVWAP = plot(plottedVWAP, "Custom VWAP", color = liveVWAPColor, linewidth = vwapLineWidth, style = plot.style_linebr, display = meridianPlotDisplay)
pVU1 = plot(plottedVU1, "VWAP Upper 1", color = color.new(vwapColor, 15), linewidth = bandLineWidth, style = plot.style_linebr, display = meridianPlotDisplay)
pVL1 = plot(plottedVL1, "VWAP Lower 1", color = color.new(vwapColor, 15), linewidth = bandLineWidth, style = plot.style_linebr, display = meridianPlotDisplay)
pVU2 = plot(plottedVU2, "VWAP Upper 2", color = color.new(vwapColor, 40), linewidth = bandLineWidth, style = plot.style_linebr, display = meridianPlotDisplay)
pVL2 = plot(plottedVL2, "VWAP Lower 2", color = color.new(vwapColor, 40), linewidth = bandLineWidth, style = plot.style_linebr, display = meridianPlotDisplay)
pVU3 = plot(plottedVU3, "VWAP Upper 3", color = color.new(vwapColor, 62), linewidth = bandLineWidth, style = plot.style_linebr, display = meridianPlotDisplay)
pVL3 = plot(plottedVL3, "VWAP Lower 3", color = color.new(vwapColor, 62), linewidth = bandLineWidth, style = plot.style_linebr, display = meridianPlotDisplay)
fill(pVU1, pVL1, color = indicatorEnabled and vwapVisible and showBand1 ? color.new(vwapColor, bandFillOpacity) : na, title = "VWAP Band 1 fill")
fill(pVU2, pVL2, color = indicatorEnabled and vwapVisible and showBand2 ? color.new(vwapColor, math.min(bandFillOpacity + 3, 100)) : na, title = "VWAP Band 2 fill")

bool compactHorizontalLabels = horizontalLabelMode == "Compact"
string labelORH = compactHorizontalLabels ? "ORH" : "OPENING RANGE HIGH"
string labelORL = compactHorizontalLabels ? "ORL" : "OPENING RANGE LOW"
string labelORM = compactHorizontalLabels ? "ORM" : "OPENING RANGE MID"
string labelIBH = compactHorizontalLabels ? "IBH" : "INITIAL BALANCE HIGH"
string labelIBL = compactHorizontalLabels ? "IBL" : "INITIAL BALANCE LOW"
string labelIBM = compactHorizontalLabels ? "IBM" : "INITIAL BALANCE MID"
string labelONH = compactHorizontalLabels ? "ONH" : "OVERNIGHT HIGH"
string labelONL = compactHorizontalLabels ? "ONL" : "OVERNIGHT LOW"
string labelSessionOpen = compactHorizontalLabels ? "SESSION OPEN" : "FUTURES SESSION OPEN"

var label[] liveLevelTags = array.new_label(11)
if barstate.islast
    float[] liveTagPrices = array.from(orHigh, orLow, orMid, ibHigh, ibLow, ibMid, overnightHigh, overnightLow, customVWAP, rthOpen, futuresOpen)
    string[] liveTagNames = array.from(labelORH, labelORL, labelORM, labelIBH, labelIBL, labelIBM, labelONH, labelONL, "VWAP", "RTH OPEN", labelSessionOpen)
    color[] liveTagColors = array.from(orhColor, orlColor, color.new(accentColor, 18), ibhColor, iblColor, color.new(secondaryColor, 18), onhColor, onlColor, liveVWAPColor, color.new(textColor, 20), color.new(neutralColor, 20))
    bool[] liveTagEnabled = array.from(
         indicatorEnabled and showLiveLevelTags and showOR and orReadyForDisplay,
         indicatorEnabled and showLiveLevelTags and showOR and orReadyForDisplay,
         indicatorEnabled and showLiveLevelTags and showOR and showORMid and orReadyForDisplay,
         indicatorEnabled and showLiveLevelTags and showIB and ibReadyForDisplay,
         indicatorEnabled and showLiveLevelTags and showIB and ibReadyForDisplay,
         indicatorEnabled and showLiveLevelTags and showIB and showIBMid and ibReadyForDisplay,
         indicatorEnabled and showLiveLevelTags and showOvernight and (inOvernight or inRTH),
         indicatorEnabled and showLiveLevelTags and showOvernight and (inOvernight or inRTH),
         indicatorEnabled and showLiveLevelTags and vwapVisible,
         indicatorEnabled and showLiveLevelTags and showSessionOpens and not na(rthOpen) and inRTH,
         indicatorEnabled and showLiveLevelTags and showSessionOpens and not na(futuresOpen) and inFutures)

    for i = 0 to 10
        label currentTag = array.get(liveLevelTags, i)
        float tagPrice = array.get(liveTagPrices, i)
        bool tagEnabled = array.get(liveTagEnabled, i) and not na(tagPrice)
        bool isRthRangeTag = i <= 5
        int activeRangeTagX = bar_index + horizontalLabelOffsetBars
        int completedRangeTagX = nz(rthEndBar, bar_index)
        int tagX = isRthRangeTag ? (inRTH ? activeRangeTagX : completedRangeTagX) : activeRangeTagX
        color tagColor = array.get(liveTagColors, i)
        string tagText = f_level_tag(array.get(liveTagNames, i), tagPrice)
        float tagY = f_horizontal_label_y(tagPrice)

        if tagEnabled
            if na(currentTag)
                currentTag := label.new(tagX, tagY, tagText, xloc = xloc.bar_index,
                     style = label.style_none, color = color.new(tagColor, 100),
                     textcolor = tagColor, textalign = text.align_right, size = f_label_size(horizontalLabelTextSize))
                array.set(liveLevelTags, i, currentTag)
            else
                label.set_xy(currentTag, tagX, tagY)
                label.set_text(currentTag, tagText)
                label.set_color(currentTag, color.new(tagColor, 100))
                label.set_textcolor(currentTag, tagColor)
                label.set_textalign(currentTag, text.align_right)
                label.set_size(currentTag, f_label_size(horizontalLabelTextSize))
        else
            if not na(currentTag)
                label.delete(currentTag)
                array.set(liveLevelTags, i, na)

var line[] timeMarkerLines = array.new_line()
var label[] timeMarkerLabels = array.new_label()
var int[] timeMarkerDayKeys = array.new_int()
var int lastTimeMarkerTimestamp = na

int markerDayKey = year(time, sessionTimezone) * 10000 + month(time, sessionTimezone) * 100 + dayofmonth(time, sessionTimezone)
var int activeMarkerDayKey = na
var float activeMarkerDayHigh = na
if na(activeMarkerDayKey) or markerDayKey != activeMarkerDayKey
    activeMarkerDayKey := markerDayKey
    activeMarkerDayHigh := high
else
    activeMarkerDayHigh := math.max(nz(activeMarkerDayHigh, high), high)

float markerAtr = nz(ta.atr(14), high - low)
float markerLabelY = activeMarkerDayHigh + markerAtr * timeMarkerTopPaddingAtr
float markerVerticalSpan = math.max(markerAtr * 2.5, syminfo.mintick * 50.0)
color markerLineColor = color.new(timeMarkerColor, timeMarkerTransparency)
color markerTextColor = color.new(timeMarkerColor, 0)

if indicatorEnabled and showTimeMarkers and timeframe.isintraday
    bool hitPremarket = f_bar_reaches_time(8, 30, sessionTimezone)
    bool hitOpen = f_bar_reaches_time(9, 30, sessionTimezone)
    bool hitKillzoneEnd = f_bar_reaches_time(11, 0, sessionTimezone)
    bool hitClose = f_bar_reaches_time(16, 0, sessionTimezone)

    if hitPremarket or hitOpen or hitKillzoneEnd or hitClose
        int markerTs = hitPremarket ? timestamp(sessionTimezone, year(time, sessionTimezone), month(time, sessionTimezone), dayofmonth(time, sessionTimezone), 8, 30) :
             hitOpen ? timestamp(sessionTimezone, year(time, sessionTimezone), month(time, sessionTimezone), dayofmonth(time, sessionTimezone), 9, 30) :
             hitKillzoneEnd ? timestamp(sessionTimezone, year(time, sessionTimezone), month(time, sessionTimezone), dayofmonth(time, sessionTimezone), 11, 0) :
             timestamp(sessionTimezone, year(time, sessionTimezone), month(time, sessionTimezone), dayofmonth(time, sessionTimezone), 16, 0)
        if na(lastTimeMarkerTimestamp) or markerTs != lastTimeMarkerTimestamp
            lastTimeMarkerTimestamp := markerTs
            string markerTextSingle = hitPremarket ? "PRE-MARKET" : hitOpen ? "MARKET OPEN" : hitKillzoneEnd ? "NY KILLZONE END" : "MARKET CLOSE"
            string markerText = timeMarkerLabelLayout == "Stacked" ? str.replace_all(markerTextSingle, " ", "\n") : markerTextSingle
            line markerLine = line.new(markerTs, high + markerVerticalSpan, markerTs, low - markerVerticalSpan,
                 xloc = xloc.bar_time, extend = extend.both, color = markerLineColor, style = line.style_solid, width = 1)
            array.push(timeMarkerLines, markerLine)
            if showTimeMarkerLabels
                label markerLabel = label.new(markerTs, markerLabelY, markerText,
                     xloc = xloc.bar_time, yloc = yloc.price, style = label.style_none,
                     color = color.new(timeMarkerColor, 100), textcolor = markerTextColor,
                     textalign = text.align_center, size = f_label_size(timeMarkerTextSize))
                array.push(timeMarkerLabels, markerLabel)
                array.push(timeMarkerDayKeys, markerDayKey)

    while array.size(timeMarkerLines) > maxTimeMarkerEvents
        line.delete(array.shift(timeMarkerLines))
    while array.size(timeMarkerLabels) > maxTimeMarkerEvents
        label.delete(array.shift(timeMarkerLabels))
        array.shift(timeMarkerDayKeys)

    if array.size(timeMarkerLabels) > 0
        for markerIndex = 0 to array.size(timeMarkerLabels) - 1
            label markerLabel = array.get(timeMarkerLabels, markerIndex)
            if barstate.islast
                label.set_size(markerLabel, f_label_size(timeMarkerTextSize))
            if array.get(timeMarkerDayKeys, markerIndex) == markerDayKey
                label.set_y(markerLabel, markerLabelY)
else if barstate.islast
    while array.size(timeMarkerLines) > 0
        line.delete(array.pop(timeMarkerLines))
    while array.size(timeMarkerLabels) > 0
        label.delete(array.pop(timeMarkerLabels))
    array.clear(timeMarkerDayKeys)

if barstate.islast and not showTimeMarkerLabels
    while array.size(timeMarkerLabels) > 0
        label.delete(array.pop(timeMarkerLabels))
    array.clear(timeMarkerDayKeys)

var line[] referenceLines = array.new_line(4)
var label[] referenceLabels = array.new_label(4)

if barstate.islast
    float[] referencePrices = array.from(previousDayHigh, previousDayLow, previousWeekHigh, previousWeekLow)
    string[] referenceNames = array.from(compactHorizontalLabels ? "PDH" : "PRIOR DAY HIGH", compactHorizontalLabels ? "PDL" : "PRIOR DAY LOW", compactHorizontalLabels ? "PWH" : "PRIOR WEEK HIGH", compactHorizontalLabels ? "PWL" : "PRIOR WEEK LOW")
    color[] referenceColors = array.from(pdhColor, pdlColor, pwhColor, pwlColor)
    bool[] referenceEnabled = array.from(
         indicatorEnabled and showPreviousDay,
         indicatorEnabled and showPreviousDay,
         indicatorEnabled and showPreviousWeek,
         indicatorEnabled and showPreviousWeek)

    for i = 0 to 3
        line existingLine = array.get(referenceLines, i)
        label existingLabel = array.get(referenceLabels, i)
        float levelPrice = array.get(referencePrices, i)
        bool levelEnabled = array.get(referenceEnabled, i) and not na(levelPrice)
        int referenceStartBar = i < 2 ? nz(previousDayReferenceStartBar, bar_index) : nz(previousWeekReferenceStartBar, bar_index)
        int startX = math.max(referenceStartBar, bar_index - 9999)
        int endX = bar_index + rightExtension
        string levelName = array.get(referenceNames, i)
        color levelColor = array.get(referenceColors, i)

        if levelEnabled
            if na(existingLine)
                existingLine := line.new(startX, levelPrice, endX, levelPrice,
                     xloc = xloc.bar_index, extend = extend.none, color = levelColor,
                     style = f_line_style(referenceStyle), width = referenceLineWidth)
                array.set(referenceLines, i, existingLine)
            else
                line.set_xy1(existingLine, startX, levelPrice)
                line.set_xy2(existingLine, endX, levelPrice)
                line.set_color(existingLine, levelColor)
                line.set_style(existingLine, f_line_style(referenceStyle))
                line.set_width(existingLine, referenceLineWidth)

            if showLiveLevelTags
                string tagText = f_level_tag(levelName, levelPrice)
                int labelX = bar_index + horizontalLabelOffsetBars
                float labelY = f_horizontal_label_y(levelPrice)
                if na(existingLabel)
                    existingLabel := label.new(labelX, labelY, tagText,
                         xloc = xloc.bar_index, style = label.style_none,
                         color = color.new(levelColor, 100), textcolor = levelColor,
                         textalign = text.align_right, size = f_label_size(horizontalLabelTextSize))
                    array.set(referenceLabels, i, existingLabel)
                else
                    label.set_xy(existingLabel, labelX, labelY)
                    label.set_text(existingLabel, tagText)
                    label.set_color(existingLabel, color.new(levelColor, 100))
                    label.set_textcolor(existingLabel, levelColor)
                    label.set_textalign(existingLabel, text.align_right)
                    label.set_size(existingLabel, f_label_size(horizontalLabelTextSize))
            else
                if not na(existingLabel)
                    label.delete(existingLabel)
                    array.set(referenceLabels, i, na)
        else
            if not na(existingLine)
                line.delete(existingLine)
                array.set(referenceLines, i, na)
            if not na(existingLabel)
                label.delete(existingLabel)
                array.set(referenceLabels, i, na)

var line[] fibLines = array.new_line(9)
var label[] fibLabels = array.new_label(9)

if barstate.islast
    float[] fibPrices = array.from(fibR1, fibR2, fibR3, fibUp1, fibUp2, fibUp3, fibDown1, fibDown2, fibDown3)
    string[] fibNames = array.from(
         str.tostring(fibRetrace1, "#.###"),
         str.tostring(fibRetrace2, "#.###"),
         str.tostring(fibRetrace3, "#.###"),
         str.tostring(1.0 + fibProjection1, "#.###"),
         str.tostring(1.0 + fibProjection2, "#.###"),
         str.tostring(1.0 + fibProjection3, "#.###"),
         str.tostring(-fibProjection1, "#.###"),
         str.tostring(-fibProjection2, "#.###"),
         str.tostring(-fibProjection3, "#.###"))

    for i = 0 to 8
        line fibLine = array.get(fibLines, i)
        label fibLabel = array.get(fibLabels, i)
        float levelPrice = array.get(fibPrices, i)
        bool isRetracement = i <= 2
        bool levelEnabled = indicatorEnabled and fibReady and not na(levelPrice) and (isRetracement ? showFibRetracements : showFibExtensions)
        int endX = bar_index + rightExtension
        color levelColor = isRetracement ? color.new(accentColor, 28) : color.new(accentColor, 5)

        if levelEnabled
            int safeFibStartBar = math.max(fibStartBar, bar_index - 9999)
            if na(fibLine)
                fibLine := line.new(safeFibStartBar, levelPrice, endX, levelPrice,
                     xloc = xloc.bar_index, extend = extend.none, color = levelColor,
                     style = f_line_style(fibLineStyle), width = fibLineWidth)
                array.set(fibLines, i, fibLine)
            else
                line.set_xy1(fibLine, safeFibStartBar, levelPrice)
                line.set_xy2(fibLine, endX, levelPrice)
                line.set_color(fibLine, levelColor)
                line.set_style(fibLine, f_line_style(fibLineStyle))
                line.set_width(fibLine, fibLineWidth)

            if showLiveLevelTags
                string fibTag = "FIB " + array.get(fibNames, i) + (showPriceInTags ? "  " + str.tostring(levelPrice, format.mintick) : "")
                int fibLabelX = bar_index + horizontalLabelOffsetBars
                float fibLabelY = f_horizontal_label_y(levelPrice)
                if na(fibLabel)
                    fibLabel := label.new(fibLabelX, fibLabelY, fibTag,
                         xloc = xloc.bar_index, style = label.style_none,
                         color = color.new(accentColor, 100), textcolor = accentColor,
                         textalign = text.align_right, size = f_label_size(horizontalLabelTextSize))
                    array.set(fibLabels, i, fibLabel)
                else
                    label.set_xy(fibLabel, fibLabelX, fibLabelY)
                    label.set_text(fibLabel, fibTag)
                    label.set_color(fibLabel, color.new(accentColor, 100))
                    label.set_textcolor(fibLabel, accentColor)
                    label.set_textalign(fibLabel, text.align_right)
                    label.set_size(fibLabel, f_label_size(horizontalLabelTextSize))
            else
                if not na(fibLabel)
                    label.delete(fibLabel)
                    array.set(fibLabels, i, na)
        else
            if not na(fibLine)
                line.delete(fibLine)
                array.set(fibLines, i, na)
            if not na(fibLabel)
                label.delete(fibLabel)
                array.set(fibLabels, i, na)

var box[] clusterBoxes = array.new_box()
var label[] clusterLabels = array.new_label()

if barstate.islast
    int clustersDrawn = 0

    if indicatorEnabled and showClusters
        float[] sourcePrices = array.new_float()
        string[] sourceNames = array.new_string()
        float[] sourceWeights = array.new_float()

        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterReferences and showPreviousDay, previousDayHigh, "PDH", 2.20)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterReferences and showPreviousDay, previousDayLow, "PDL", 2.20)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterReferences and showPreviousWeek, previousWeekHigh, "PWH", 2.40)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterReferences and showPreviousWeek, previousWeekLow, "PWL", 2.40)

        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterSessions and showOR and inRTH and orReadyForState, orHigh, "ORH", 1.70)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterSessions and showOR and inRTH and orReadyForState, orLow, "ORL", 1.70)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterSessions and showIB and inRTH and ibReadyForState, ibHigh, "IBH", 1.90)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterSessions and showIB and inRTH and ibReadyForState, ibLow, "IBL", 1.90)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterSessions and showOvernight and not inOvernight, overnightHigh, "ONH", 1.45)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterSessions and showOvernight and not inOvernight, overnightLow, "ONL", 1.45)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterSessions and showSessionOpens and inRTH, rthOpen, "RTH Open", 1.15)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterSessions and showSessionOpens and inFutures, futuresOpen, "Session Open", 1.15)

        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterVWAP and vwapVisible, customVWAP, "VWAP", 1.35)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterVWAP and vwapVisible and showBand1, vwapUpper1, "VWAP +1", 1.10)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterVWAP and vwapVisible and showBand1, vwapLower1, "VWAP −1", 1.10)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterVWAP and vwapVisible and showBand2, vwapUpper2, "VWAP +2", 0.95)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterVWAP and vwapVisible and showBand2, vwapLower2, "VWAP −2", 0.95)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterVWAP and vwapVisible and showBand3, vwapUpper3, "VWAP +3", 0.80)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterVWAP and vwapVisible and showBand3, vwapLower3, "VWAP −3", 0.80)

        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterFib and fibReady and showFibRetracements, fibR1, "Fib " + str.tostring(fibRetrace1, "#.###"), 0.70)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterFib and fibReady and showFibRetracements, fibR2, "Fib " + str.tostring(fibRetrace2, "#.###"), 0.75)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterFib and fibReady and showFibRetracements, fibR3, "Fib " + str.tostring(fibRetrace3, "#.###"), 0.70)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterFib and fibReady and showFibExtensions, fibUp1, "Fib " + str.tostring(1.0 + fibProjection1, "#.###"), 0.75)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterFib and fibReady and showFibExtensions, fibUp2, "Fib " + str.tostring(1.0 + fibProjection2, "#.###"), 0.75)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterFib and fibReady and showFibExtensions, fibUp3, "Fib " + str.tostring(1.0 + fibProjection3, "#.###"), 0.75)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterFib and fibReady and showFibExtensions, fibDown1, "Fib " + str.tostring(-fibProjection1, "#.###"), 0.75)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterFib and fibReady and showFibExtensions, fibDown2, "Fib " + str.tostring(-fibProjection2, "#.###"), 0.75)
        f_push_level(sourcePrices, sourceNames, sourceWeights, clusterFib and fibReady and showFibExtensions, fibDown3, "Fib " + str.tostring(-fibProjection3, "#.###"), 0.75)

        int sourceCount = array.size(sourcePrices)
        if sourceCount >= clusterMinimum
            int[] sortedIndices = array.sort_indices(sourcePrices, order.ascending)
            float tolerance = clusterToleranceMode == "ATR" ? nz(clusterATR) * clusterAtrFactor : clusterTicks * syminfo.mintick
            tolerance := math.max(tolerance, syminfo.mintick)

            float[] clusterCenters = array.new_float()
            float[] clusterLows = array.new_float()
            float[] clusterHighs = array.new_float()
            float[] clusterScores = array.new_float()
            int[] clusterCounts = array.new_int()
            string[] clusterNameLists = array.new_string()

            float workingWeightedSum = na
            float workingWeight = na
            float workingLow = na
            float workingHigh = na
            int workingCount = 0
            string workingNames = ""

            for sortedPosition = 0 to sourceCount - 1
                int sourceIndex = array.get(sortedIndices, sortedPosition)
                float sourcePrice = array.get(sourcePrices, sourceIndex)
                float sourceWeight = array.get(sourceWeights, sourceIndex)
                string sourceName = array.get(sourceNames, sourceIndex)

                if workingCount == 0
                    workingWeightedSum := sourcePrice * sourceWeight
                    workingWeight := sourceWeight
                    workingLow := sourcePrice
                    workingHigh := sourcePrice
                    workingCount := 1
                    workingNames := sourceName
                else
                    float workingCenter = workingWeightedSum / workingWeight
                    if math.abs(sourcePrice - workingCenter) <= tolerance
                        workingWeightedSum += sourcePrice * sourceWeight
                        workingWeight += sourceWeight
                        workingLow := math.min(workingLow, sourcePrice)
                        workingHigh := math.max(workingHigh, sourcePrice)
                        workingCount += 1
                        workingNames := f_append_name(workingNames, sourceName, workingCount)
                    else
                        if workingCount >= clusterMinimum
                            array.push(clusterCenters, workingWeightedSum / workingWeight)
                            array.push(clusterLows, workingLow)
                            array.push(clusterHighs, workingHigh)
                            array.push(clusterScores, workingWeight + workingCount * 0.25)
                            array.push(clusterCounts, workingCount)
                            array.push(clusterNameLists, workingNames)

                        workingWeightedSum := sourcePrice * sourceWeight
                        workingWeight := sourceWeight
                        workingLow := sourcePrice
                        workingHigh := sourcePrice
                        workingCount := 1
                        workingNames := sourceName

            if workingCount >= clusterMinimum
                array.push(clusterCenters, workingWeightedSum / workingWeight)
                array.push(clusterLows, workingLow)
                array.push(clusterHighs, workingHigh)
                array.push(clusterScores, workingWeight + workingCount * 0.25)
                array.push(clusterCounts, workingCount)
                array.push(clusterNameLists, workingNames)

            int clusterCount = array.size(clusterCenters)
            if clusterCount > 0
                int[] rankedClusters = array.sort_indices(clusterScores, order.descending)
                int numberToDraw = math.min(clusterMaximum, clusterCount)
                float zonePadding = clusterPaddingTicks * syminfo.mintick

                for rank = 0 to numberToDraw - 1
                    int clusterIndex = array.get(rankedClusters, rank)
                    float center = array.get(clusterCenters, clusterIndex)
                    float zoneLow = array.get(clusterLows, clusterIndex) - zonePadding
                    float zoneHigh = array.get(clusterHighs, clusterIndex) + zonePadding
                    float score = array.get(clusterScores, clusterIndex)
                    int contributors = array.get(clusterCounts, clusterIndex)
                    string contributorNames = array.get(clusterNameLists, clusterIndex)

                    bool clusterAbove = center > close + tolerance
                    bool clusterBelow = center < close - tolerance
                    color directionalColor = clusterAbove ? resistanceColor : clusterBelow ? supportColor : accentColor
                    int leftX = math.max(0, bar_index - clusterLeftBars)
                    int rightX = bar_index + clusterRightBars

                    box clusterBox = rank < array.size(clusterBoxes) ? array.get(clusterBoxes, rank) : na
                    if na(clusterBox)
                        clusterBox := box.new(
                             left = leftX, top = zoneHigh, right = rightX, bottom = zoneLow,
                             xloc = xloc.bar_index,
                             border_color = color.new(directionalColor, 18),
                             border_width = 1,
                             bgcolor = color.new(accentColor, clusterFillOpacity))
                        array.push(clusterBoxes, clusterBox)
                    else
                        box.set_left(clusterBox, leftX)
                        box.set_top(clusterBox, zoneHigh)
                        box.set_right(clusterBox, rightX)
                        box.set_bottom(clusterBox, zoneLow)
                        box.set_border_color(clusterBox, color.new(directionalColor, 18))
                        box.set_bgcolor(clusterBox, color.new(accentColor, clusterFillOpacity))

                    string clusterText = "CONFLUENCE ×" + str.tostring(contributors)
                    if showPriceInTags
                        clusterText += " · " + str.tostring(center, format.mintick)
                    if showClusterNames
                        clusterText += "\n" + contributorNames

                    label clusterLabel = rank < array.size(clusterLabels) ? array.get(clusterLabels, rank) : na
                    if na(clusterLabel)
                        clusterLabel := label.new(
                             x = rightX,
                             y = center,
                             text = clusterText,
                             xloc = xloc.bar_index,
                             style = label.style_label_left,
                             color = color.new(directionalColor, 70),
                             textcolor = textColor,
                             size = size.tiny)
                        array.push(clusterLabels, clusterLabel)
                    else
                        label.set_xy(clusterLabel, rightX, center)
                        label.set_text(clusterLabel, clusterText)
                        label.set_color(clusterLabel, color.new(directionalColor, 70))
                        label.set_textcolor(clusterLabel, textColor)

                    clustersDrawn += 1

    while array.size(clusterBoxes) > clustersDrawn
        box.delete(array.pop(clusterBoxes))
    while array.size(clusterLabels) > clustersDrawn
        label.delete(array.pop(clusterLabels))

var table hud = table.new(
     position.top_right, 2, 7,
     bgcolor = color.new(panelColor, 10),
     frame_color = color.new(borderColor, 35), frame_width = 1,
     border_color = color.new(borderColor, 72), border_width = 1)

if barstate.isfirst
    table.merge_cells(hud, 0, 0, 1, 0)

if barstate.islast
    string activeSession = inOR ? "Opening Range" : inIB ? "Initial Balance" : inRTH ? "RTH" : inOvernight ? "Overnight" : inFutures ? "Globex" : "Closed"
    color activeSessionColor = inRTH ? supportColor : inOvernight ? informationColor : neutralColor
    bool timeframeWarning = not timeframe.isintraday or timeframe.in_seconds() > 30 * 60
    bool nonFuturesSymbol = syminfo.type != "futures"
    int freshnessThresholdMs = math.max(int(timeframe.in_seconds() * 3000.0), 15 * 60 * 1000)
    bool dataStale = timenow > time_close and timenow - time_close > freshnessThresholdMs
    string dataState = barstate.isrealtime ? "LIVE" : dataStale ? "STALE / CLOSED" : "CONFIRMED"
    string orText = not na(orHigh) and not na(orLow) ? str.tostring(orHigh - orLow, format.mintick) : "—"
    string ibText = not na(ibHigh) and not na(ibLow) ? str.tostring(ibHigh - ibLow, format.mintick) : "—"
    string vwapText = not na(customVWAP) ? str.tostring(customVWAP, format.mintick) : "—"
    string vwapLabel = "VWAP " + (vwapAnchor == "Full Futures Session" ? "SESSION" : str.upper(vwapAnchor))
    string lastStateText = lastEventName == "None" ? "Waiting" : lastEventName + " · " + lastEventState
    string dataDetail = timeframeWarning ? "USE ≤30M" : nonFuturesSymbol ? "NON-FUTURES" : dataState
    color dataDetailColor = timeframeWarning or nonFuturesSymbol or dataStale ? warningColor : barstate.isrealtime ? informationColor : neutralColor

    color panel = showHUD and indicatorEnabled ? color.new(panelColor, 10) : color.new(panelColor, 100)
    color alternate = showHUD and indicatorEnabled ? color.new(panelColor, 4) : color.new(panelColor, 100)
    color visibleText = showHUD and indicatorEnabled ? textColor : color.new(textColor, 100)
    color mutedText = showHUD and indicatorEnabled ? neutralColor : color.new(neutralColor, 100)

    table.cell(hud, 0, 0, "Meridian -- Futures Levels", bgcolor = showHUD and indicatorEnabled ? color.new(accentColor, 18) : color.new(accentColor, 100), text_color = visibleText, text_size = f_label_size(hudTextSize), text_halign = text.align_left)
    table.cell(hud, 0, 1, "Session", bgcolor = panel, text_color = mutedText, text_size = f_label_size(hudTextSize), text_halign = text.align_left)
    table.cell(hud, 1, 1, activeSession, bgcolor = panel, text_color = showHUD and indicatorEnabled ? activeSessionColor : color.new(activeSessionColor, 100), text_size = f_label_size(hudTextSize), text_halign = text.align_right)
    table.cell(hud, 0, 2, "OR", bgcolor = alternate, text_color = mutedText, text_size = f_label_size(hudTextSize), text_halign = text.align_left)
    table.cell(hud, 1, 2, orText, bgcolor = alternate, text_color = visibleText, text_size = f_label_size(hudTextSize), text_halign = text.align_right)
    table.cell(hud, 0, 3, "IB", bgcolor = panel, text_color = mutedText, text_size = f_label_size(hudTextSize), text_halign = text.align_left)
    table.cell(hud, 1, 3, ibText, bgcolor = panel, text_color = visibleText, text_size = f_label_size(hudTextSize), text_halign = text.align_right)
    table.cell(hud, 0, 4, vwapLabel, bgcolor = alternate, text_color = mutedText, text_size = f_label_size(hudTextSize), text_halign = text.align_left)
    table.cell(hud, 1, 4, vwapText, bgcolor = alternate, text_color = showHUD and indicatorEnabled ? vwapColor : color.new(vwapColor, 100), text_size = f_label_size(hudTextSize), text_halign = text.align_right)
    table.cell(hud, 0, 5, "State", bgcolor = panel, text_color = mutedText, text_size = f_label_size(hudTextSize), text_halign = text.align_left)
    table.cell(hud, 1, 5, lastStateText, bgcolor = panel, text_color = visibleText, text_size = f_label_size(hudTextSize), text_halign = text.align_right)
    table.cell(hud, 0, 6, "Data", bgcolor = alternate, text_color = mutedText, text_size = f_label_size(hudTextSize), text_halign = text.align_left)
    table.cell(hud, 1, 6, dataDetail, bgcolor = alternate, text_color = showHUD and indicatorEnabled ? dataDetailColor : color.new(dataDetailColor, 100), text_size = f_label_size(hudTextSize), text_halign = text.align_right)

alertcondition(stateAcceptedAbove, "Meridian — Level accepted above", "Meridian Futures Levels: price accepted above a tracked level.")
alertcondition(stateAcceptedBelow, "Meridian — Level accepted below", "Meridian Futures Levels: price accepted below a tracked level.")
alertcondition(stateRejectedUp, "Meridian — Bullish level rejection", "Meridian Futures Levels: bullish rejection from a tracked level.")
alertcondition(stateRejectedDown, "Meridian — Bearish level rejection", "Meridian Futures Levels: bearish rejection from a tracked level.")

Overview

Meridian - Futures Levels marks the full futures session, overnight high and low, an optional overnight midpoint, and the regular-trading-hours open. It draws a configurable opening range with midpoint, fill, and a developing state, plus optional vertical time markers. Grouped settings also cover initial balance, session VWAP with custom deviations, previous day and week references, Fibonacci and range projections, level clustering, and an acceptance or rejection HUD. Users control time zone, session windows, line width, labels, and right-side extension.

Details

Published by

sukesan7 on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Sessions & time, Support & resistance, Volume

Markets

Indices

Timeframes

M15-H1

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Topics

fair-value-gapfutures-tradingindex-futuresmarket-profilemarket-structureoptions-tradingorder-blockspine-scriptpine-script-v6pinescript

Settings

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

SettingTypeDefaultVariable
Enable indicatorbooltrueindicatorEnabled
Session time zonestringAmerica/New_YorksessionTimezone
Right-side line extension (bars)int18rightExtension
Show prices in horizontal labelsbooltrueshowPriceInTags
Show horizontal line labelsbooltrueshowLiveLevelTags
Horizontal-label wordingstringCompacthorizontalLabelMode
Show plotted values on price scaleboolfalseshowPriceScaleValues
Horizontal-label offset (bars)int1horizontalLabelOffsetBars
Horizontal-label height (ticks)float3.0horizontalLabelOffsetTicks
Horizontal-label text sizestringSmallhorizontalLabelTextSize
Full futures sessionsession1800-1700:1234567futuresSession
Overnight sessionsession1800-0930:1234567overnightSession
Show all 40 settings
SettingTypeDefaultVariable
RTH sessionsession0930-1600:23456rthSession
RTH open (hour)int9rthOpenHour
Minuteint30rthOpenMinute
Shade sessionsboolfalseshowSessionShading
Show futures / RTH opensbooltrueshowSessionOpens
Show overnight high / lowbooltrueshowOvernight
Show overnight midpointboolfalseshowOvernightMid
Session level widthint1sessionLineWidth
Show Opening RangebooltrueshowOR
Opening Range durationstring15 MinutesopeningRangeDuration
Show while developingbooltrueshowORDeveloping
Show midpointbooltrueshowORMid
Fill rangebooltruefillOR
Line widthint2orLineWidth
Show vertical time markersboolfalseshowTimeMarkers
Show time-marker labelsbooltrueshowTimeMarkerLabels
Marker-label layoutstringStackedtimeMarkerLabelLayout
Marker-label text sizestringSmalltimeMarkerTextSize
Marker-label top padding (ATR)float0.75timeMarkerTopPaddingAtr
Maximum marker eventsint48maxTimeMarkerEvents
Marker colorcolor#8B5CF6timeMarkerColor
Marker transparencyint82timeMarkerTransparency
Show Initial BalancebooltrueshowIB
Initial Balance duration (minutes)int60initialBalanceMinutes
Show while developingboolfalseshowIBDeveloping
Show midpointbooltrueshowIBMid
Fill rangeboolfalsefillIB
Line widthint2ibLineWidth
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

VP

by dearvn
257 stars

This indicator calculates and plots a session-based volume profile directly on the price chart. It maps volume distribution across price levels, highlighting key structural reference points including the Point of Control, Value Area High, and Value Area Low. C

Indicator v5 VolumeSupport & resistance
Updated 6 weeks ago · None stated GitHub repository of VP

Midnight VWAP

by TradersPost
218 stars

The Midnight VWAP indicator calculates and overlays two distinct Volume Weighted Average Price (VWAP) lines on the price chart based on customizable session timeframes. It tracks an overnight or pre-market session alongside regular trading hours, resetting cal

Indicator v6 Moving averagesSessions & time
Updated 3 months ago · None stated GitHub repository of Midnight VWAP

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 8 weeks ago · Custom licence GitHub repository of Moon Dev GEX Arrows

This overlay indicator draws a volume profile for a chosen trading session directly on the price chart. It plots the point of control, value area high and low, an optional value area box, a live zone, and a session box with labels. Users select Tokyo, London,

Indicator v5 Sessions & timeVolume
Updated 7 weeks ago · None stated GitHub repository of Sessions & VP with prev session VP & daily/weekly opens

LoD Intraday Entry

by ChartsLector
41 stars

This overlay indicator marks the current day's low and a suggested entry zone a fraction of daily ATR below that low. It compares daily volume with a volume moving average, plots daily SMA and EMA distance, and draws an optional ATR-width price cloud. Labels c

Indicator v6 Support & resistanceVolatility & bands
Updated 2 days ago · None stated GitHub repository of LoD Intraday Entry

RS: Market Profile

by agejevasv
12 stars

This indicator plots a Market Profile on the price chart using time-price opportunity letters or symbols. It marks the point of control, a configurable value area, and the initial balance, and it can highlight a volume-profile point of control and session VWAP

Indicator v5 VolumeSupport & resistance
Updated 2 years ago · None stated GitHub repository of RS: Market Profile

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