Indicator · Pine Script v6 · On the chart

Hybrid Strategy Trend ORB MTF

by eliasvictor-trading· code credited to EliasVictor

An indicator for intraday futures that builds an opening range for each session and marks breakout signals once price clears it by a tick buffer, with gap filtering and one signal per direction per range.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code910
Settings40inputs
LicenceMPL-2.0
Last update5 months ago13 May 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

910 lines of Pine Script v6 from hybrid_strategy.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

hybrid_strategy.pine
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © EliasVictor
// Hybrid Strategy v2.4.6: Trend/ORB/MTF with Per-Session ORB, Session VWAP,
// Gap-Resistant Breakouts, Signal Dedup, MTF ORB Confluence & Configurable Volatility Regimes
//
// NOTES:
// - This indicator supports both time-based and tick charts.
// - Opening Range logic is always clock-time based, even on tick charts.
// - Example: a 5-minute ORB on a tick chart still tracks the first 5 real minutes of the session.
// - When Session 2 is enabled, each session builds its own independent ORB.
// - ORB data expires after a configurable window (default 18 hours) to prevent stale weekend/holiday signals.
// - Signals are deduplicated: one entry per direction per ORB session.
// - MTF VWAP column always uses exchange VWAP; live row and signals use the selected VWAP anchor mode.
//
// CHANGELOG v2.4.6a:
// - Dashboard polish only; no change to core entry/alert logic
// - Filled VOL rows with compact Risk/Product/Action/Dly context
// - Added compact GapY/GapN status to Exec row for gap-breakout filter visibility
// - Preserved v2.4.6 Trade State row and same dashboard-width layout
//
// CHANGELOG v2.4.6:
// - Renamed dashboard Theme output from BUY / SELL / SIT to LONG BIAS / SHORT BIAS / SIT
// - Added Trade State reason labels: ALIGNED, MIXED, OUT OF SESSION, VWAP N/A, ORB NOT READY
// - Preserved existing score logic based on live trend, HTF confirmation, ORB position, and session readiness
// - Updated VIX/VXN/GVZ daily requests from lookahead_on to lookahead_off to reduce forward-looking dashboard behavior
// - No changes to core entry logic, alerts, JSON payloads, ORB calculation, VWAP calculation, or MTF matrix logic
// - Kept VWAP dashboard column sizing closer to v2.4.5 by preserving the prior Trend/HTF/ORB column layout
//
// CHANGELOG v2.4.5:
// - Clarified VWAP source mismatch in dashboard when Session VWAP is selected
// - Added visual distinction between Live session VWAP context and MTF exchange VWAP context
// - Added optional dashboard Theme row showing BUY / SELL / SIT bias
// - Theme is derived from live trend, HTF confirmation, ORB position, and session readiness
// - No change to core entry or alert logic
//
// CHANGELOG v2.4.4:
// - Updated default Opening Range Duration from 15 minutes to 5 minutes
// - Updated default Trend Confirmation TF from 60 to 30
// - Swapped default EMA 200 and VWAP colors
// - Updated default MA colors: MA 1 = blue, MA 2 = orange
// - Updated default Breakout Buffer from 0 ticks to 2 ticks
// - Enabled Afternoon Session by default
//
// CHANGELOG v2.4.3:
// - Added PW H/L column to top MTF matrix using prior week's completed high/low
// - Updated Live row and all matrix TF rows to classify price relative to PW H/L
// - Kept existing defaults and dashboard layout intact by reusing the previously blank final matrix column
//
// CHANGELOG v2.4.2:
// - Fixed session VWAP to only accumulate during active session bars
// - Session VWAP now returns na outside session (hidden, not frozen)
// - Documented MTF VWAP mismatch: matrix rows use exchange VWAP regardless of anchor setting
// - Live VWAP dashboard cell shows "N/A" when session VWAP is unavailable outside session
//
// CHANGELOG v2.4.1:
// - Restored ORB breakout status in MTF dashboard rows
// - ORB levels are fixed prices once established; comparing each TF close
//   against the range provides valid multi-timeframe breakout confluence
//
// CHANGELOG v2.4:
// - Per-session ORB: S1 and S2 each build their own independent opening range
// - ORB staleness expiration (configurable max age, default 18 hours)
// - Signal deduplication: one entry per direction per session
// - Optional session-anchored VWAP (toggle between exchange and custom session)
// - Gap-resistant breakout detection (toggleable)
// - nearCheck proximity tie-breaking for tight-range days
// - HTF lag documented in tooltip and comments
// - Extracted version string for JSON alert consistency
// - Removed fixed dashboard cell geometry (auto-size)
// - Compressed changelog; prior versions in GitHub/description
//
// Prior versions: v2.0–v2.3.3 changelog available in GitHub commit history or TradingView script description.

//@version=6
VERSION     = "2.4.6a"
SCRIPT_NAME = "Hybrid Strategy: v" + VERSION

indicator(SCRIPT_NAME, overlay=true, max_lines_count=500, max_labels_count=500)

// ==========================================
// 1. Inputs - Session Settings
// ==========================================
grp_session = "Session Settings"

s1StartHour   = input.int(9,  "S1 Start Hour",   minval=0, maxval=23, group=grp_session)
s1StartMinute = input.int(30, "S1 Start Minute", minval=0, maxval=59, group=grp_session)
s1StopHour    = input.int(11, "S1 Stop Hour",    minval=0, maxval=23, group=grp_session)
s1StopMinute  = input.int(30, "S1 Stop Minute",  minval=0, maxval=59, group=grp_session)

useSession2   = input.bool(true, "Enable Afternoon Session", group=grp_session)
s2StartHour   = input.int(15, "S2 Start Hour",   minval=0, maxval=23, group=grp_session)
s2StartMinute = input.int(0,  "S2 Start Minute", minval=0, maxval=59, group=grp_session)
s2StopHour    = input.int(15, "S2 Stop Hour",    minval=0, maxval=23, group=grp_session)
s2StopMinute  = input.int(55, "S2 Stop Minute",  minval=0, maxval=59, group=grp_session)

// ==========================================
// 2. Inputs - Execution Settings
// ==========================================
grp_exec = "Execution Settings"

execMode = input.string(
     "On Close (Confirmed)",
     "Breakout Execution",
     options=["On Close (Confirmed)", "Intrabar (Instant)"],
     tooltip="On Close waits for candle close. Intrabar reacts to bar high/low breaches sooner, but is still evaluated from bar data rather than true tick-by-tick order flow.",
     group=grp_exec)

breakoutTicks = input.int(
     2,
     "Breakout Buffer (Ticks)",
     minval=0,
     tooltip="Price must exceed OR level by this many ticks to trigger signal. 0 = original behavior.",
     group=grp_exec)

filterGaps = input.bool(
     true,
     "Filter Gap Breakouts",
     tooltip="When enabled, breakout signals require the bar to have traded through the ORB level, filtering out gap-over events. Disable if you want gap breakouts to count.",
     group=grp_exec)

showLabelPrice = input.bool(true, "Show Price on Signal Labels", group=grp_exec)

// ==========================================
// 3. Inputs - Time & Timezone
// ==========================================
grp_time = "Timezone"
tzInput  = input.string(
     "America/New_York",
     "Timezone",
     options=["Exchange Timezone", "America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles", "Europe/London", "UTC"],
     group=grp_time)

tz = tzInput == "Exchange Timezone" ? syminfo.timezone : tzInput

// ==========================================
// 4. Inputs - Strategy Settings
// ==========================================
grp_strat = "Strategy Settings"

orbMins = input.int(
     5,
     "Opening Range Duration (Clock Minutes)",
     minval=1,
     tooltip="Opening Range duration in real clock minutes. This remains time-based even when the indicator is used on tick charts.",
     group=grp_strat)

orbMaxAgeHrs = input.float(
     18.0,
     "ORB Max Age (Hours)",
     minval=1.0,
     step=0.5,
     tooltip="ORB data expires after this many hours to prevent stale signals over weekends and holidays. 18 hours covers a full trading day.",
     group=grp_strat)

htf = input.timeframe(
     "30",
     "Trend Confirmation TF",
     tooltip="HTF trend updates only when a full bar completes on this timeframe. On lower chart TFs (e.g., 15s or 1m), the HTF filter may lag by up to one full bar duration.",
     group=grp_strat)

src = close

// ==========================================
// 5. Inputs - Moving Average Settings
// ==========================================
grp_ma      = "Moving Average Settings"
maType      = input.string("EMA", "MA Type", options=["EMA", "HMA"], tooltip="EMA = Exponential Moving Average. HMA = Hull Moving Average.", group=grp_ma)
ma1Len      = input.int(9, "MA 1 Length", minval=1, group=grp_ma)
ma2Len      = input.int(21, "MA 2 Length", minval=1, group=grp_ma)
showCloud   = input.bool(true, "Show MA Cloud Fill", group=grp_ma)
ma1Color    = input.color(color.rgb(0, 225, 255), "MA 1 Color", group=grp_ma)
ma2Color    = input.color(color.rgb(255, 165, 0), "MA 2 Color", group=grp_ma)
showEma200  = input.bool(true, "Show Anchor EMA 200", group=grp_ma)
ema200Color = input.color(color.rgb(255, 0, 255), "Anchor EMA 200 Color", group=grp_ma)
showVwap    = input.bool(true, "Show VWAP", group=grp_ma)
vwapColor   = input.color(color.rgb(255, 255, 255), "VWAP Color", group=grp_ma)

vwapAnchor = input.string(
     "Exchange",
     "VWAP Anchor",
     options=["Exchange", "Session"],
     tooltip="Exchange uses TradingView's built-in VWAP (anchored to exchange session, includes overnight for futures). Session resets VWAP at each custom session open (S1 or S2) and only accumulates during active session bars. Note: MTF matrix rows always use exchange VWAP regardless of anchor setting.",
     group=grp_ma)

// ==========================================
// 6. Inputs - Chart Level Settings
// ==========================================
grp_levels = "Chart Levels"

showDailyLevels  = input.bool(false, "Show Daily Levels on Chart", group=grp_levels)
showWeeklyLevels = input.bool(false, "Show Weekly Levels on Chart", group=grp_levels)

dailyHiColor      = input.color(color.rgb(0, 200, 0), "Daily High Color", group=grp_levels)
dailyLoColor      = input.color(color.rgb(200, 0, 0), "Daily Low Color", group=grp_levels)
prevDailyHiColor  = input.color(color.rgb(0, 200, 0, 50), "Prev Daily High Color", group=grp_levels)
prevDailyLoColor  = input.color(color.rgb(200, 0, 0, 50), "Prev Daily Low Color", group=grp_levels)

weeklyHiColor     = input.color(color.rgb(0, 150, 255), "Weekly High Color", group=grp_levels)
weeklyLoColor     = input.color(color.rgb(255, 150, 0), "Weekly Low Color", group=grp_levels)
prevWeeklyHiColor = input.color(color.rgb(0, 150, 255, 50), "Prev Weekly High Color", group=grp_levels)
prevWeeklyLoColor = input.color(color.rgb(255, 150, 0, 50), "Prev Weekly Low Color", group=grp_levels)

// ==========================================
// 7. Inputs - Dashboard Settings
// ==========================================
grp_dash      = "Dashboard Settings"
showDash      = input.bool(true, "Show Dashboard", group=grp_dash)
dashPos       = input.string("Top Right", "Position", options=["Top Right", "Bottom Right", "Top Left"], group=grp_dash)
dashSize      = input.string("Small", "Text Size", options=["Tiny", "Small", "Normal"], group=grp_dash)
showKeyLevels = input.bool(false, "Show Key Levels Section", group=grp_dash)
showThemeRow  = input.bool(true, "Show Trade State Row", group=grp_dash)

tf1_in = input.timeframe("5",   "Matrix TF 1", group=grp_dash)
tf2_in = input.timeframe("15",  "Matrix TF 2", group=grp_dash)
tf3_in = input.timeframe("30",  "Matrix TF 3", group=grp_dash)
tf4_in = input.timeframe("60",  "Matrix TF 4", group=grp_dash)
tf5_in = input.timeframe("240", "Matrix TF 5", group=grp_dash)

// ==========================================
// 7B. Inputs - Proximity Settings
// ==========================================
grp_prox = "Proximity Settings"

nearZonePctInput = input.float(
     0.20,
     "Dashboard Zone Proximity (%)",
     minval=0.01,
     step=0.01,
     tooltip="Broader proximity band used for dashboard Hi/Lo/Mid awareness around current and prior high/low zones.",
     group=grp_prox)

nearLevelPctInput = input.float(
     0.10,
     "Key Level Proximity (%)",
     minval=0.01,
     step=0.01,
     tooltip="Tighter proximity band used for exact key-level status rows such as Daily, Weekly, and ORB levels.",
     group=grp_prox)

nearZonePct  = nearZonePctInput / 100.0
nearLevelPct = nearLevelPctInput / 100.0

// ==========================================
// 7C. Inputs - Alert Settings
// ==========================================
grp_alert = "Alert Settings"

sendJsonAlerts = input.bool(
     false,
     "Send Dynamic JSON Alerts",
     tooltip="When enabled, the script sends dynamic JSON alerts using alert(). Use TradingView's 'Any alert() function call' condition for webhook automation.",
     group=grp_alert)

alertFreqInput = input.string(
     "Once Per Bar Close",
     "JSON Alert Frequency",
     options=["Once Per Bar", "Once Per Bar Close", "All"],
     group=grp_alert)

alertTag = input.string(
     "",
     "Alert Tag / Route (Optional)",
     tooltip="Optional field for account, route, bot name, or webhook-side tag. Avoid quotes and special escape characters for best compatibility.",
     group=grp_alert)

// ==========================================
// 7D. Inputs - Volatility Regime Settings
// ==========================================
grp_volReg = "Volatility Regime Settings"

vixThr1 = input.float(15.0, "VIX Threshold 1", step=0.1, group=grp_volReg)
vixThr2 = input.float(20.0, "VIX Threshold 2", step=0.1, group=grp_volReg)
vixThr3 = input.float(25.0, "VIX Threshold 3", step=0.1, group=grp_volReg)

vxnThr1 = input.float(18.0, "VXN Threshold 1", step=0.1, group=grp_volReg)
vxnThr2 = input.float(23.0, "VXN Threshold 2", step=0.1, group=grp_volReg)
vxnThr3 = input.float(28.0, "VXN Threshold 3", step=0.1, group=grp_volReg)

gvzThr1 = input.float(
     15.0, "GVZ Threshold 1", step=0.1,
     tooltip="GVZ uses volatility magnitude labels (Low Vol / Moderate / Elevated / Extreme), not bullish/bearish. Gold is a safe haven — higher GVZ is not inherently bearish.",
     group=grp_volReg)

gvzThr2 = input.float(
     20.0, "GVZ Threshold 2", step=0.1,
     tooltip="GVZ uses volatility magnitude labels (Low Vol / Moderate / Elevated / Extreme), not bullish/bearish. Gold is a safe haven — higher GVZ is not inherently bearish.",
     group=grp_volReg)

gvzThr3 = input.float(
     30.0, "GVZ Threshold 3", step=0.1,
     tooltip="GVZ uses volatility magnitude labels (Low Vol / Moderate / Elevated / Extreme), not bullish/bearish. Gold is a safe haven — higher GVZ is not inherently bearish.",
     group=grp_volReg)

// ==========================================
// 8. Session Calculations
// ==========================================
s1Session = str.format("{0,number,00}{1,number,00}-{2,number,00}{3,number,00}", s1StartHour, s1StartMinute, s1StopHour, s1StopMinute)
s2Session = str.format("{0,number,00}{1,number,00}-{2,number,00}{3,number,00}", s2StartHour, s2StartMinute, s2StopHour, s2StopMinute)

tfForSession = (timeframe.isseconds or timeframe.isticks) ? "1" : timeframe.period

inSession1 = not na(time(tfForSession, s1Session, tz))
inSession2 = useSession2 and not na(time(tfForSession, s2Session, tz))
inSession  = inSession1 or inSession2

// Per-session edge detection
newSession1 = inSession1 and not inSession1[1]
newSession2 = inSession2 and not inSession2[1]
newSession  = newSession1 or newSession2

alertFreq = alert.freq_once_per_bar_close
if alertFreqInput == "Once Per Bar"
    alertFreq := alert.freq_once_per_bar
else if alertFreqInput == "All"
    alertFreq := alert.freq_all

// ==========================================
// 9. MA Helper
// ==========================================
maCalc(_src, _len) =>
    maType == "HMA" ? ta.hma(_src, _len) : ta.ema(_src, _len)

// ==========================================
// 10. Core Indicators
// ==========================================
ma1          = maCalc(src, ma1Len)
ma2          = maCalc(src, ma2Len)
anchorEma200 = ta.ema(src, 200)

// ==========================================
// 10B. Session-Anchored VWAP
// ==========================================
// When set to "Session", VWAP resets at each custom session open (S1 or S2)
// and only accumulates during active session bars. Outside session, the
// accumulated value is preserved internally but not displayed (returns na).
//
// When set to "Exchange", uses TradingView's built-in ta.vwap which anchors
// to the exchange session (includes overnight for futures).
//
// NOTE: MTF matrix rows always use exchange VWAP (ta.vwap) via request.security(),
// even when the live row and signal engine use session-anchored VWAP. This is a
// known limitation — computing session VWAP inside each request.security() call
// would require duplicating the accumulation logic per timeframe. The MTF VWAP
// column reflects exchange-anchored context; the Live row and signals reflect
// the selected anchor mode.
var float cumPV  = 0.0
var float cumVol = 0.0

if newSession
    cumPV  := hlc3 * volume
    cumVol := volume
else if inSession
    cumPV  += hlc3 * volume
    cumVol += volume
// Outside session: accumulation stops and session VWAP is hidden (na).

sessionVwapRaw = cumVol > 0 ? cumPV / cumVol : na
sessionVwap    = inSession ? sessionVwapRaw : na
vwapVal        = vwapAnchor == "Session" ? sessionVwap : ta.vwap

bullishTrend = (src > vwapVal) and (ma1 > ma2)
bearishTrend = (src < vwapVal) and (ma1 < ma2)

// ==========================================
// 11. Opening Range Breakout (ORB) Logic
// ==========================================
// ORB is always clock-time based.
// On tick charts, the range still tracks elapsed real session time,
// not a fixed number of bars.
//
// Each session (S1 and S2) builds its own independent ORB.
// ORB data expires after orbMaxAgeHrs to prevent stale signals over weekends and holidays.
// Signal dedup flags reset on each new session.
var float orh           = na
var float orl           = na
var int   orbEndTime    = na
var float boLongLevel   = na
var float boShortLevel  = na
var bool  longFired     = false
var bool  shortFired    = false

orbMaxAgeMs = int(orbMaxAgeHrs * 60 * 60 * 1000)

if newSession
    orbEndTime   := time + (orbMins * 60 * 1000)
    orh          := high
    orl          := low
    boLongLevel  := na
    boShortLevel := na
    longFired    := false
    shortFired   := false
else if not na(orbEndTime) and time < orbEndTime
    orh := math.max(orh, high)
    orl := math.min(orl, low)
else if na(boLongLevel) and not na(orh)
    buffer       = breakoutTicks * syminfo.mintick
    boLongLevel  := orh + buffer
    boShortLevel := orl - buffer

// ORB is active only if levels are set AND not expired
orbExpired = na(orbEndTime) or (time - orbEndTime > orbMaxAgeMs)
orbActive  = not na(orbEndTime) and time >= orbEndTime and not na(boLongLevel) and not orbExpired

// ==========================================
// 12. Daily Levels
// ==========================================
// Current day levels: develop honestly intraday
d_h  = request.security(syminfo.tickerid, "D", high, lookahead=barmerge.lookahead_off)
d_l  = request.security(syminfo.tickerid, "D", low,  lookahead=barmerge.lookahead_off)

// Previous day levels: completed prior day
pd_h = request.security(syminfo.tickerid, "D", high[1], lookahead=barmerge.lookahead_on)
pd_l = request.security(syminfo.tickerid, "D", low[1],  lookahead=barmerge.lookahead_on)

// ==========================================
// 13. Weekly Levels
// ==========================================
// Current week levels: develop honestly intraday
w_h  = request.security(syminfo.tickerid, "W", high, lookahead=barmerge.lookahead_off)
w_l  = request.security(syminfo.tickerid, "W", low,  lookahead=barmerge.lookahead_off)

// Previous week levels: completed prior week
pw_h = request.security(syminfo.tickerid, "W", high[1], lookahead=barmerge.lookahead_on)
pw_l = request.security(syminfo.tickerid, "W", low[1],  lookahead=barmerge.lookahead_on)

// ==========================================
// 14. VIX/VXN/GVZ Data
// ==========================================
vixValue = request.security("CBOE:VIX", "D", close, lookahead=barmerge.lookahead_off)
vxnValue = request.security("CBOE:VXN", "D", close, lookahead=barmerge.lookahead_off)
gvzValue = request.security("CBOE:GVZ", "D", close, lookahead=barmerge.lookahead_off)

// ==========================================
// 15. MTF Matrix Data
// ==========================================
// Note: 17 request.security calls total (TradingView limit is 40).
[c1, v1, m11, m12] = request.security(syminfo.tickerid, tf1_in, [close, ta.vwap, maCalc(close, ma1Len), maCalc(close, ma2Len)], lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off)
[c2, v2, m21, m22] = request.security(syminfo.tickerid, tf2_in, [close, ta.vwap, maCalc(close, ma1Len), maCalc(close, ma2Len)], lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off)
[c3, v3, m31, m32] = request.security(syminfo.tickerid, tf3_in, [close, ta.vwap, maCalc(close, ma1Len), maCalc(close, ma2Len)], lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off)
[c4, v4, m41, m42] = request.security(syminfo.tickerid, tf4_in, [close, ta.vwap, maCalc(close, ma1Len), maCalc(close, ma2Len)], lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off)
[c5, v5, m51, m52] = request.security(syminfo.tickerid, tf5_in, [close, ta.vwap, maCalc(close, ma1Len), maCalc(close, ma2Len)], lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off)

// HTF Trend Confirmation
// Note: HTF trend lags by up to one full HTF bar.
// On a 15-second chart with a 30-minute HTF, the filter reflects
// the prior completed HTF bar until the current one closes.
[htfClose, htfMa2] = request.security(syminfo.tickerid, htf, [close, maCalc(close, ma2Len)], lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off)
htfBull = htfClose > htfMa2
htfBear = htfClose < htfMa2

// ==========================================
// 16. Signal Logic with Execution Modes
// ==========================================
// Gap-resistant breakout detection:
// When filterGaps is enabled, the bar must have traded through the breakout level
// (low <= level for longs, high >= level for shorts) to confirm the move wasn't a gap.
//
// Signal deduplication:
// longFired / shortFired flags ensure one signal per direction per ORB session.
// Flags reset when a new session (S1 or S2) opens.

bool longLevelSet  = not na(boLongLevel)
bool shortLevelSet = not na(boShortLevel)

bool bullCrossRaw = longLevelSet and (execMode == "Intrabar (Instant)" ? (high > boLongLevel and high[1] <= boLongLevel) : (close > boLongLevel and close[1] <= boLongLevel))
bool bearCrossRaw = shortLevelSet and (execMode == "Intrabar (Instant)" ? (low < boShortLevel and low[1] >= boShortLevel) : (close < boShortLevel and close[1] >= boShortLevel))

// Gap filter: bar must have straddled the breakout level
bool bullCross = bullCrossRaw and (not filterGaps or low <= boLongLevel)
bool bearCross = bearCrossRaw and (not filterGaps or high >= boShortLevel)

buySignal  = orbActive and bullishTrend and htfBull and bullCross and inSession and not longFired
sellSignal = orbActive and bearishTrend and htfBear and bearCross and inSession and not shortFired

if buySignal
    longFired := true
if sellSignal
    shortFired := true

// ==========================================
// 16B. JSON Alert Payloads
// ==========================================
safeAlertTag = str.replace_all(alertTag, "\\", "/")
safeAlertTag := str.replace_all(safeAlertTag, "\"", "'")

buyJson  = "{\"script\":\"" + SCRIPT_NAME + "\",\"ticker\":\"" + syminfo.tickerid + "\",\"timeframe\":\"" + timeframe.period + "\",\"signal\":\"BUY\",\"price\":\"" + str.tostring(close, format.mintick) + "\",\"ma_type\":\"" + maType + "\",\"exec_mode\":\"" + execMode + "\",\"tag\":\"" + safeAlertTag + "\"}"
sellJson = "{\"script\":\"" + SCRIPT_NAME + "\",\"ticker\":\"" + syminfo.tickerid + "\",\"timeframe\":\"" + timeframe.period + "\",\"signal\":\"SELL\",\"price\":\"" + str.tostring(close, format.mintick) + "\",\"ma_type\":\"" + maType + "\",\"exec_mode\":\"" + execMode + "\",\"tag\":\"" + safeAlertTag + "\"}"

// ==========================================
// 17. Visuals - ORB Lines
// ==========================================
orbHighPlot = plot(orbActive ? orh : na, "ORB High", color=color.new(color.green, 20), style=plot.style_linebr)
orbLowPlot  = plot(orbActive ? orl : na, "ORB Low", color=color.new(color.red, 20), style=plot.style_linebr)
fill(orbHighPlot, orbLowPlot, color=color.new(color.navy, 90))

plot(orbActive and breakoutTicks > 0 ? boLongLevel : na, "BO Long Level", color=color.new(color.green, 50), style=plot.style_linebr, linewidth=1)
plot(orbActive and breakoutTicks > 0 ? boShortLevel : na, "BO Short Level", color=color.new(color.red, 50), style=plot.style_linebr, linewidth=1)

// ==========================================
// 18. Visuals - Daily & Weekly Levels
// ==========================================
plot(showDailyLevels ? d_h : na, "Daily High", color=dailyHiColor, style=plot.style_line, linewidth=2)
plot(showDailyLevels ? d_l : na, "Daily Low", color=dailyLoColor, style=plot.style_line, linewidth=2)
plot(showDailyLevels ? pd_h : na, "Prev Daily High", color=prevDailyHiColor, style=plot.style_line, linewidth=1)
plot(showDailyLevels ? pd_l : na, "Prev Daily Low", color=prevDailyLoColor, style=plot.style_line, linewidth=1)

plot(showWeeklyLevels ? w_h : na, "Weekly High", color=weeklyHiColor, style=plot.style_line, linewidth=2)
plot(showWeeklyLevels ? w_l : na, "Weekly Low", color=weeklyLoColor, style=plot.style_line, linewidth=2)
plot(showWeeklyLevels ? pw_h : na, "Prev Weekly High", color=prevWeeklyHiColor, style=plot.style_line, linewidth=1)
plot(showWeeklyLevels ? pw_l : na, "Prev Weekly Low", color=prevWeeklyLoColor, style=plot.style_line, linewidth=1)

// ==========================================
// 19. Visuals - Moving Averages
// ==========================================
maPlot1 = plot(ma1, "MA 1", color=ma1Color, linewidth=2)
maPlot2 = plot(ma2, "MA 2", color=ma2Color, linewidth=2)
maCloudColor = ma1 > ma2 ? color.new(color.green, 80) : color.new(color.red, 80)
fill(maPlot1, maPlot2, color=showCloud ? maCloudColor : na, title="MA Cloud")

plot(showEma200 ? anchorEma200 : na, "Anchor EMA 200", color=ema200Color, linewidth=2)
plot(showVwap ? vwapVal : na, "VWAP", color=vwapColor, linewidth=2)

// ==========================================
// 20. Signal Labels
// ==========================================
buyLabelText  = showLabelPrice ? "BUY\n" + str.tostring(close, format.mintick) : "BUY"
sellLabelText = showLabelPrice ? "SELL\n" + str.tostring(close, format.mintick) : "SELL"

if buySignal
    label.new(bar_index, low, buyLabelText, style=label.style_label_up, color=color.green, textcolor=color.white, size=size.small)

if sellSignal
    label.new(bar_index, high, sellLabelText, style=label.style_label_down, color=color.red, textcolor=color.white, size=size.small)

// ==========================================
// 21. Dashboard
// ==========================================
var table dash = table.new(
     dashPos == "Top Right" ? position.top_right : dashPos == "Bottom Right" ? position.bottom_right : position.top_left,
     7,
     30,
     border_width=1)

// Color palette
colG            = color.new(color.lime, 0)
colR            = color.new(color.red, 0)
colLightG       = color.new(#90EE90, 0)
colOrange       = color.new(color.orange, 0)
colN            = color.new(#334455, 0)
colTxt          = color.white
colHead         = color.new(#1a2a3a, 0)
colBg           = color.new(#0d1a26, 0)
colOutOfSession = color.new(#2a3a4a, 0)
colSection      = color.new(#1e3a5f, 0)

txtSize = dashSize == "Tiny" ? size.tiny : dashSize == "Small" ? size.small : size.normal

// Dashboard cell helper — consistent call signature, auto-sized geometry
dashCell(col, row, txt, bg, tcol) =>
    table.cell(dash, col, row, txt, bgcolor=bg, text_color=tcol, text_size=txtSize)

// Helper: Check proximity to High/Low zones (tie-breaking for tight ranges)
nearCheck(p, h, l) =>
    distH = math.abs(p - h)
    distL = math.abs(p - l)
    isNearH = distH <= (p * nearZonePct)
    isNearL = distL <= (p * nearZonePct)
    if isNearH and isNearL
        distH <= distL ? 1 : -1
    else
        isNearH ? 1 : isNearL ? -1 : 0

// Helper: Check proximity to exact level
nearLevel(p, level) =>
    if na(p) or na(level)
        0
    else
        isNear = math.abs(p - level) <= (p * nearLevelPct)
        isNear ? 1 : 0

// Helper: Trend state based on actual structure logic
trendState(c, v, fastMa, slowMa) =>
    c > v and fastMa > slowMa ? 1 : c < v and fastMa < slowMa ? -1 : 0

// Helper: Dashboard Trade State
// v2.4.6 changes Theme language from BUY / SELL / SIT to
// LONG BIAS / SHORT BIAS / SIT with a reason field.
// This preserves the existing score inputs and does not change signals or alerts.
tradeStateCalc(_inSession, _liveVwapNA, _trendState, _htfBull, _htfBear, _orbActive, _close, _orh, _orl) =>
    int score = 0
    string state = "SIT"
    string reason = "MIXED"

    if not _inSession
        state := "SIT"
        reason := "OUT OF SESSION"
        [state, reason, score]
    else if _liveVwapNA
        state := "SIT"
        reason := "VWAP N/A"
        [state, reason, score]
    else if not _orbActive
        state := "SIT"
        reason := "ORB NOT READY"
        [state, reason, score]
    else
        int trendScore = _trendState
        int htfScore   = _htfBull ? 1 : _htfBear ? -1 : 0
        int orbScore   = _close > _orh ? 1 : _close < _orl ? -1 : 0

        score := trendScore + htfScore + orbScore

        if score >= 2
            state := "LONG BIAS"
            reason := "ALIGNED"
        else if score <= -2
            state := "SHORT BIAS"
            reason := "ALIGNED"
        else
            state := "SIT"
            reason := "MIXED"

        [state, reason, score]

// Helper: Draw Matrix Row
drawRow(rIdx, name, c, v, fastMa, slowMa, d_h_val, d_l_val, pd_h_val, pd_l_val, pw_h_val, pw_l_val) =>
    tState  = trendState(c, v, fastMa, slowMa)
    isVwap  = c > v
    d_stat  = nearCheck(c, d_h_val, d_l_val)
    pd_stat = nearCheck(c, pd_h_val, pd_l_val)
    pw_stat = nearCheck(c, pw_h_val, pw_l_val)

    trendText = tState == 1 ? "Bull" : tState == -1 ? "Bear" : "Mixed"
    trendBg   = tState == 1 ? colG : tState == -1 ? colR : colN
    trendTxt  = tState == 0 ? color.white : color.black

    // ORB levels are fixed prices once established.
    // Comparing each TF close against the range shows MTF breakout confluence.
    isOrb     = (orbActive and c > orh) ? 1 : (orbActive and c < orl) ? -1 : 0
    orbColor  = isOrb == 1 ? colG : isOrb == -1 ? colR : colN
    clrORB    = not inSession ? colOutOfSession : orbColor
    orbTxtClr = (isOrb == 0 or not inSession) ? color.white : color.black

    dashCell(0, rIdx, name,      colBg, colTxt)
    dashCell(1, rIdx, trendText, trendBg, trendTxt)
    dashCell(2, rIdx, isVwap ? "> VW" : "< VW", isVwap ? colG : colR, color.black)
    dashCell(3, rIdx, isOrb == 1 ? "BrkOut" : isOrb == -1 ? "BrkDwn" : "Range",
             clrORB, orbTxtClr)
    dashCell(4, rIdx, d_stat == 1 ? "Hi" : d_stat == -1 ? "Lo" : "Mid",
             d_stat == 1 ? colG : d_stat == -1 ? colR : colN,
             d_stat == 0 ? color.white : color.black)
    dashCell(5, rIdx, pd_stat == 1 ? "Hi" : pd_stat == -1 ? "Lo" : "Mid",
             pd_stat == 1 ? colG : pd_stat == -1 ? colR : colN,
             pd_stat == 0 ? color.white : color.black)
    dashCell(6, rIdx, pw_stat == 1 ? "Hi" : pw_stat == -1 ? "Lo" : "Mid",
             pw_stat == 1 ? colG : pw_stat == -1 ? colR : colN,
             pw_stat == 0 ? color.white : color.black)

// Helper: Draw Level Row with price + proximity
drawLevelRow(rIdx, name, levelHi, levelLo, hiColor, loColor) =>
    nearHi = nearLevel(close, levelHi)
    nearLo = nearLevel(close, levelLo)

    hiStatus   = nearHi == 1 ? "NEAR" : (close > levelHi ? "Above" : "Below")
    loStatus   = nearLo == 1 ? "NEAR" : (close < levelLo ? "Below" : "Above")
    hiBg       = nearHi == 1 ? color.new(color.yellow, 30) : (close > levelHi ? colG : colN)
    loBg       = nearLo == 1 ? color.new(color.yellow, 30) : (close < levelLo ? colR : colN)
    hiTxtColor = nearHi == 1 ? color.black : (close > levelHi ? color.black : color.white)
    loTxtColor = nearLo == 1 ? color.black : (close < levelLo ? color.black : color.white)

    dashCell(0, rIdx, name, colBg, colTxt)
    dashCell(1, rIdx, "H:", colBg, colTxt)
    dashCell(2, rIdx, str.tostring(levelHi, format.mintick), colBg, hiColor)
    dashCell(3, rIdx, hiStatus, hiBg, hiTxtColor)
    dashCell(4, rIdx, "L:", colBg, colTxt)
    dashCell(5, rIdx, str.tostring(levelLo, format.mintick), colBg, loColor)
    dashCell(6, rIdx, loStatus, loBg, loTxtColor)

vixThresholdsInvalid = not (vixThr1 < vixThr2 and vixThr2 < vixThr3)
vxnThresholdsInvalid = not (vxnThr1 < vxnThr2 and vxnThr2 < vxnThr3)
gvzThresholdsInvalid = not (gvzThr1 < gvzThr2 and gvzThr2 < gvzThr3)
volThresholdsInvalid = vixThresholdsInvalid or vxnThresholdsInvalid or gvzThresholdsInvalid

// Compact VOL action labels for dashboard only.
// OK  = normal/acceptable regime
// Ctn = caution/elevated regime
// Blk = high/extreme regime by configured thresholds
volAction(_value, _thr2, _thr3) =>
    _value < _thr2 ? "OK" : _value < _thr3 ? "Ctn" : "Blk"

volActionColor(_action) =>
    _action == "OK" ? colG : _action == "Ctn" ? colOrange : colR

if barstate.islast and showDash
    // === MTF MATRIX HEADER ===
    dashCell(0, 0, "TF",     colHead, colTxt)
    dashCell(1, 0, "Trend",  colHead, colTxt)
    dashCell(2, 0, vwapAnchor == "Session" ? "VWAP*" : "VWAP", colHead, colTxt)
    dashCell(3, 0, "ORB",    colHead, colTxt)
    dashCell(4, 0, "D H/L",  colHead, colTxt)
    dashCell(5, 0, "PD H/L", colHead, colTxt)
    dashCell(6, 0, "PW H/L", colHead, colTxt)

    // === LIVE ROW ===
    colLive        = color.new(#1a3a1a, 0)
    liveVwapNA     = na(vwapVal)
    liveTrendState = liveVwapNA ? 0 : trendState(close, vwapVal, ma1, ma2)

    // When session VWAP is selected but unavailable (outside session),
    // both Trend and VWAP cells show N/A to indicate the session engine is idle.
    liveTrendText  = liveVwapNA ? "N/A" : liveTrendState == 1 ? "Bull" : liveTrendState == -1 ? "Bear" : "Mixed"
    liveTrendBg    = liveVwapNA ? colOutOfSession : liveTrendState == 1 ? colG : liveTrendState == -1 ? colR : colN
    liveTrendTxt   = (liveVwapNA or liveTrendState == 0) ? color.white : color.black

    liveIsVwap     = not liveVwapNA and close > vwapVal
    liveIsOrb      = (orbActive and close > orh) ? 1 : (orbActive and close < orl) ? -1 : 0
    liveD_stat     = nearCheck(close, d_h, d_l)
    livePD_stat    = nearCheck(close, pd_h, pd_l)
    livePW_stat    = nearCheck(close, pw_h, pw_l)
    liveOrbColor   = liveIsOrb == 1 ? colG : liveIsOrb == -1 ? colR : colN
    liveClrORB     = not inSession ? colOutOfSession : liveOrbColor
    liveOrbTxtClr  = (liveIsOrb == 0 or not inSession) ? color.white : color.black

    liveVwapText   = liveVwapNA ? "N/A" : (liveIsVwap ? "> VW" : "< VW")
    liveVwapBg     = liveVwapNA ? colOutOfSession : (liveIsVwap ? colG : colR)
    liveVwapTxt    = liveVwapNA ? color.white : color.black

    dashCell(0, 1, vwapAnchor == "Session" ? "Live(S)" : "Live", colLive, color.rgb(100, 255, 100))
    dashCell(1, 1, liveTrendText, liveTrendBg, liveTrendTxt)
    dashCell(2, 1, liveVwapText, liveVwapBg, liveVwapTxt)
    dashCell(3, 1, liveIsOrb == 1 ? "BrkOut" : liveIsOrb == -1 ? "BrkDwn" : "Range", liveClrORB, liveOrbTxtClr)
    dashCell(4, 1, liveD_stat == 1 ? "Hi" : liveD_stat == -1 ? "Lo" : "Mid",
             liveD_stat == 1 ? colG : liveD_stat == -1 ? colR : colN,
             liveD_stat == 0 ? color.white : color.black)
    dashCell(5, 1, livePD_stat == 1 ? "Hi" : livePD_stat == -1 ? "Lo" : "Mid",
             livePD_stat == 1 ? colG : livePD_stat == -1 ? colR : colN,
             livePD_stat == 0 ? color.white : color.black)
    dashCell(6, 1, livePW_stat == 1 ? "Hi" : livePW_stat == -1 ? "Lo" : "Mid",
             livePW_stat == 1 ? colG : livePW_stat == -1 ? colR : colN,
             livePW_stat == 0 ? color.white : color.black)

    // === MTF ROWS ===
    drawRow(2, tf1_in, c1, v1, m11, m12, d_h, d_l, pd_h, pd_l, pw_h, pw_l)
    drawRow(3, tf2_in, c2, v2, m21, m22, d_h, d_l, pd_h, pd_l, pw_h, pw_l)
    drawRow(4, tf3_in, c3, v3, m31, m32, d_h, d_l, pd_h, pd_l, pw_h, pw_l)
    drawRow(5, tf4_in, c4, v4, m41, m42, d_h, d_l, pd_h, pd_l, pw_h, pw_l)
    drawRow(6, tf5_in, c5, v5, m51, m52, d_h, d_l, pd_h, pd_l, pw_h, pw_l)

    rowIdx = 7

    if vwapAnchor == "Session"
        dashCell(0, rowIdx, "VWAP",     colSection, colTxt)
        dashCell(1, rowIdx, "Live",     colBg, colTxt)
        dashCell(2, rowIdx, "Session",  color.new(color.blue, 70), colTxt)
        dashCell(3, rowIdx, "MTF",      colBg, colTxt)
        dashCell(4, rowIdx, "Exchange", color.new(color.orange, 70), colTxt)
        dashCell(5, rowIdx, "",         colBg, colTxt)
        dashCell(6, rowIdx, "",         colBg, colTxt)
        rowIdx += 1

    if showThemeRow
        [stateText, reasonText, themeScore] = tradeStateCalc(inSession, liveVwapNA, liveTrendState, htfBull, htfBear, orbActive, close, orh, orl)

        stateBg  = stateText == "LONG BIAS" ? colG : stateText == "SHORT BIAS" ? colR : colN
        stateTxt = stateText == "SIT" ? color.white : color.black
        reasonBg = reasonText == "ALIGNED" ? color.new(color.green, 70) : reasonText == "MIXED" ? colN : colOutOfSession

        trendLeg = liveTrendState == 1 ? "Trend+" : liveTrendState == -1 ? "Trend-" : "Trend0"
        htfLeg   = htfBull ? "HTF+" : htfBear ? "HTF-" : "HTF0"
        orbLeg   = orbActive ? (close > orh ? "ORB+" : close < orl ? "ORB-" : "ORB0") : "ORB N/A"

        // Keep dashboard column widths close to v2.4.5:
        // Column 2 remains the compact Trend leg instead of a longer Reason label.
        reasonShort = reasonText == "ALIGNED" ? "OK" :
             reasonText == "MIXED" ? "Mix" :
             reasonText == "OUT OF SESSION" ? "Off" :
             reasonText == "VWAP N/A" ? "VW N/A" :
             reasonText == "ORB NOT READY" ? "ORB N/A" :
             reasonText

        dashCell(0, rowIdx, "State", colSection, colTxt)
        dashCell(1, rowIdx, stateText, stateBg, stateTxt)
        dashCell(2, rowIdx, trendLeg, colBg, colTxt)
        dashCell(3, rowIdx, htfLeg, colBg, colTxt)
        dashCell(4, rowIdx, orbLeg, colBg, colTxt)
        dashCell(5, rowIdx, reasonShort, reasonBg, colTxt)
        dashCell(6, rowIdx, str.tostring(themeScore), colBg, colTxt)
        rowIdx += 1

    if volThresholdsInvalid
        dashCell(0, rowIdx, "WARNING", color.new(color.red, 15), colTxt)
        dashCell(1, rowIdx, "Check",   color.new(color.red, 15), colTxt)
        dashCell(2, rowIdx, "Vol",     color.new(color.red, 15), colTxt)
        dashCell(3, rowIdx, "Order",   color.new(color.red, 15), colTxt)
        dashCell(4, rowIdx, "",        color.new(color.red, 15), colTxt)
        dashCell(5, rowIdx, "",        color.new(color.red, 15), colTxt)
        dashCell(6, rowIdx, "",        color.new(color.red, 15), colTxt)
        rowIdx += 1

    // === VOL SECTION HEADER ===
    dashCell(0, rowIdx, "VOL",   colSection, colTxt)
    dashCell(1, rowIdx, "INDEX", colSection, colTxt)
    dashCell(2, rowIdx, "",      colSection, colTxt)
    dashCell(3, rowIdx, "",      colSection, colTxt)
    dashCell(4, rowIdx, "",      colSection, colTxt)
    dashCell(5, rowIdx, "",      colSection, colTxt)
    dashCell(6, rowIdx, "",      colSection, colTxt)
    rowIdx += 1

    // === VIX ROW ===
    vixColor  = vixValue < vixThr1 ? colG : vixValue < vixThr2 ? colLightG : vixValue < vixThr3 ? colOrange : colR
    vixLabel  = vixValue < vixThr1 ? "Bullish" : vixValue < vixThr2 ? "Sl. Bullish" : vixValue < vixThr3 ? "Sl. Bearish" : "Bearish"
    vixAction = volAction(vixValue, vixThr2, vixThr3)
    vixActBg  = volActionColor(vixAction)
    dashCell(0, rowIdx, "VIX (ES)", colBg, colTxt)
    dashCell(1, rowIdx, str.tostring(vixValue, "#.##"), colBg, vixColor)
    dashCell(2, rowIdx, "Risk", colBg, colTxt)
    dashCell(3, rowIdx, vixLabel, vixColor, color.black)
    dashCell(4, rowIdx, "ES", vixColor, color.black)
    dashCell(5, rowIdx, vixAction, vixActBg, color.black)
    dashCell(6, rowIdx, "Dly", colBg, colTxt)
    rowIdx += 1

    // === VXN ROW ===
    vxnColor  = vxnValue < vxnThr1 ? colG : vxnValue < vxnThr2 ? colLightG : vxnValue < vxnThr3 ? colOrange : colR
    vxnLabel  = vxnValue < vxnThr1 ? "Bullish" : vxnValue < vxnThr2 ? "Sl. Bullish" : vxnValue < vxnThr3 ? "Sl. Bearish" : "Bearish"
    vxnAction = volAction(vxnValue, vxnThr2, vxnThr3)
    vxnActBg  = volActionColor(vxnAction)
    dashCell(0, rowIdx, "VXN (NQ)", colBg, colTxt)
    dashCell(1, rowIdx, str.tostring(vxnValue, "#.##"), colBg, vxnColor)
    dashCell(2, rowIdx, "Risk", colBg, colTxt)
    dashCell(3, rowIdx, vxnLabel, vxnColor, color.black)
    dashCell(4, rowIdx, "NQ", vxnColor, color.black)
    dashCell(5, rowIdx, vxnAction, vxnActBg, color.black)
    dashCell(6, rowIdx, "Dly", colBg, colTxt)
    rowIdx += 1

    // === GVZ ROW ===
    gvzColor  = gvzValue < gvzThr1 ? colG : gvzValue < gvzThr2 ? colLightG : gvzValue < gvzThr3 ? colOrange : colR
    gvzLabel  = gvzValue < gvzThr1 ? "Low Vol" : gvzValue < gvzThr2 ? "Moderate" : gvzValue < gvzThr3 ? "Elevated" : "Extreme"
    gvzAction = volAction(gvzValue, gvzThr2, gvzThr3)
    gvzActBg  = volActionColor(gvzAction)
    dashCell(0, rowIdx, "GVZ (GC)", colBg, colTxt)
    dashCell(1, rowIdx, str.tostring(gvzValue, "#.##"), colBg, gvzColor)
    dashCell(2, rowIdx, "Risk", colBg, colTxt)
    dashCell(3, rowIdx, gvzLabel, gvzColor, color.black)
    dashCell(4, rowIdx, "GC", gvzColor, color.black)
    dashCell(5, rowIdx, gvzAction, gvzActBg, color.black)
    dashCell(6, rowIdx, "Dly", colBg, colTxt)
    rowIdx += 1

    // === KEY LEVELS SECTION ===
    if showKeyLevels
        dashCell(0, rowIdx, "KEY",    colSection, colTxt)
        dashCell(1, rowIdx, "LEVELS", colSection, colTxt)
        dashCell(2, rowIdx, "",       colSection, colTxt)
        dashCell(3, rowIdx, "",       colSection, colTxt)
        dashCell(4, rowIdx, "",       colSection, colTxt)
        dashCell(5, rowIdx, "",       colSection, colTxt)
        dashCell(6, rowIdx, "",       colSection, colTxt)
        rowIdx += 1

        drawLevelRow(rowIdx, "Daily", d_h, d_l, color.lime, color.red)
        rowIdx += 1
        drawLevelRow(rowIdx, "Prev D", pd_h, pd_l, color.lime, color.red)
        rowIdx += 1
        drawLevelRow(rowIdx, "Weekly", w_h, w_l, color.lime, color.red)
        rowIdx += 1
        drawLevelRow(rowIdx, "Prev W", pw_h, pw_l, color.lime, color.red)
        rowIdx += 1

        if orbActive
            drawLevelRow(rowIdx, "ORB", orh, orl, color.green, color.red)
        else
            dashCell(0, rowIdx, "ORB",        colBg, colTxt)
            dashCell(1, rowIdx, "",           colOutOfSession, colTxt)
            dashCell(2, rowIdx, "Not Active", colOutOfSession, colTxt)
            dashCell(3, rowIdx, "",           colOutOfSession, colTxt)
            dashCell(4, rowIdx, "",           colOutOfSession, colTxt)
            dashCell(5, rowIdx, "",           colOutOfSession, colTxt)
            dashCell(6, rowIdx, "",           colOutOfSession, colTxt)
        rowIdx += 1

    // === EXEC ROW ===
    execText    = execMode == "On Close (Confirmed)" ? "On Close" : "Intrabar"
    tickText    = breakoutTicks > 0 ? str.tostring(breakoutTicks) + "t" : "-"
    gapText     = filterGaps ? "GapY" : "GapN"
    sessionText = inSession ? (inSession1 ? "S1" : "S2") : "OFF"
    execBg      = color.new(color.purple, 70)
    sessBg      = inSession ? color.new(color.green, 70) : colOutOfSession

    dashCell(0, rowIdx, "Exec",      execBg, colTxt)
    dashCell(1, rowIdx, execText,    execBg, colTxt)
    dashCell(2, rowIdx, tickText,    execBg, colTxt)
    dashCell(3, rowIdx, maType,      execBg, colTxt)
    dashCell(4, rowIdx, gapText,     execBg, colTxt)
    dashCell(5, rowIdx, "Sess:",     sessBg, colTxt)
    dashCell(6, rowIdx, sessionText, sessBg, colTxt)

// ==========================================
// 22. Alerts (Plain + Optional JSON Webhook)
// ==========================================
alertcondition(buySignal, "Long Signal", "Long Breakout Detected")
alertcondition(sellSignal, "Short Signal", "Short Breakout Detected")

if sendJsonAlerts and buySignal
    alert(buyJson, alertFreq)

if sendJsonAlerts and sellSignal
    alert(sellJson, alertFreq)

Overview

A trend filter uses two moving averages and a higher timeframe, with optional VWAP, a 200 EMA and daily and weekly levels. A dashboard shows trend, VWAP, range breakout and level status across several timeframes, plus VIX-type volatility context and optional alert payloads. It draws on the price chart.

Details

Published by

eliasvictor-trading on GitHub

Code credit

EliasVictor, as written in the script's header

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Breakout, Sessions & time, Screeners & dashboards

Markets

Indices, Gold

Timeframes

M1-M5, 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.

Settings

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

SettingTypeDefaultVariable
S1 Start Hourint9s1StartHour
S1 Start Minuteint30s1StartMinute
S1 Stop Hourint11s1StopHour
S1 Stop Minuteint30s1StopMinute
Enable Afternoon SessionbooltrueuseSession2
S2 Start Hourint15s2StartHour
S2 Start Minuteint0s2StartMinute
S2 Stop Hourint15s2StopHour
S2 Stop Minuteint55s2StopMinute
Breakout ExecutionstringOn Close (Confirmed)execMode
Breakout Buffer (Ticks)int2breakoutTicks
Filter Gap BreakoutsbooltruefilterGaps
Show all 40 settings
SettingTypeDefaultVariable
Show Price on Signal LabelsbooltrueshowLabelPrice
TimezonestringAmerica/New_YorktzInput
Opening Range Duration (Clock Minutes)int5orbMins
ORB Max Age (Hours)float18.0orbMaxAgeHrs
Trend Confirmation TFtimeframe30htf
MA TypestringEMAmaType
MA 1 Lengthint9ma1Len
MA 2 Lengthint21ma2Len
Show MA Cloud FillbooltrueshowCloud
MA 1 Colorcolorcolor.rgb(0, 225, 255)ma1Color
MA 2 Colorcolorcolor.rgb(255, 165, 0)ma2Color
Show Anchor EMA 200booltrueshowEma200
Anchor EMA 200 Colorcolorcolor.rgb(255, 0, 255)ema200Color
Show VWAPbooltrueshowVwap
VWAP Colorcolorcolor.rgb(255, 255, 255)vwapColor
VWAP AnchorstringExchangevwapAnchor
Show Daily Levels on ChartboolfalseshowDailyLevels
Show Weekly Levels on ChartboolfalseshowWeeklyLevels
Daily High Colorcolorcolor.rgb(0, 200, 0)dailyHiColor
Daily Low Colorcolorcolor.rgb(200, 0, 0)dailyLoColor
Prev Daily High Colorcolorcolor.rgb(0, 200, 0, 50)prevDailyHiColor
Prev Daily Low Colorcolorcolor.rgb(200, 0, 0, 50)prevDailyLoColor
Weekly High Colorcolorcolor.rgb(0, 150, 255)weeklyHiColor
Weekly Low Colorcolorcolor.rgb(255, 150, 0)weeklyLoColor
Prev Weekly High Colorcolorcolor.rgb(0, 150, 255, 50)prevWeeklyHiColor
Prev Weekly Low Colorcolorcolor.rgb(255, 150, 0, 50)prevWeeklyLoColor
Show DashboardbooltrueshowDash
PositionstringTop RightdashPos
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

IB / ORB Live Stats

by TamaraKGreen
46 stars

This overlay indicator marks the New York initial balance and a configurable opening range on the price chart and reports live range statistics in two tables. It builds an IB box for a default 09:30–10:30 ET window, an ORB box from 09:30 for a set number of mi

Indicator v6 Sessions & timeBreakout
Updated 7 weeks ago · None stated GitHub repository of IB / ORB Live Stats

ORB Pro - Research-Based ORB

by Mrshahidali420
16 stars

The ORB Pro indicator calculates and plots Opening Range Breakout levels, stop-loss and take-profit zones, signal labels, and an status dashboard on the price chart. Built on academic research, it evaluates trades using an opening-candle direction filter, open

Indicator v6 BreakoutSessions & time Repaints
Updated 3 months ago · None stated GitHub repository of ORB Pro - Research-Based ORB

SMC Strategy Hub + Alerts

by eddiebelaval
7 stars

This indicator marks market structure shifts when price closes beyond the prior lookback high or low, then labels a buy or sell with an ATR-based stop and a take-profit distance set by the TP multiplier. Optional boxes highlight bullish and bearish fair value

Indicator v5 SMC / ICT / price actionBreakout
Updated 3 months ago · None stated GitHub repository of SMC Strategy Hub + Alerts

Advanced ORB + ICT System

by alboogycOdR
6 stars

Advanced ORB + ICT System plots Opening Range Breakout (ORB) levels alongside Inner Circle Trader (ICT) concepts directly on the price chart. It highlights ORB boxes, Fair Value Gaps (FVGs), Order Blocks, liquidity grabs, and key support and resistance levels

Indicator v5 SMC / ICT / price actionBreakout
Updated 7 months ago · None stated GitHub repository of Advanced ORB + ICT System

ORB Algo | Flux Charts

by vaskosmihaylov
5 stars

This indicator calculates and visualizes Opening Range Breakout (ORB) zones directly on the price chart. Breakout signals are identified when the close or an EMA crosses the opening range boundary, with entry sensitivity managed by retest requirements. It mode

Indicator v6 BreakoutSessions & time
Updated 2 months ago · GPL-3.0 GitHub repository of ORB Algo | Flux Charts

CH033 ORB+ VWAPs Indicator

by sandovalchristian033
1 stars

This indicator plots multiple anchored VWAPs, session ranges, fair value gaps, and multi-instrument monitoring tables directly on the price chart. It signals long or short setups when price breaks the initial fifteen-minute New York opening range, re-enters th

Indicator v6 BreakoutSessions & time
Updated 2 months ago · None stated GitHub repository of CH033 ORB+ VWAPs Indicator

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