Indicator · Pine Script v6 · On the chart

Equity Intraday Analyst

by chris-c-thomas

Equity Intraday Analyst is a comprehensive intraday overlay indicator for US equities and ETFs.

View on GitHub

GitHub stars14on the repository
Pine versionv6current
Lines of code990
Settings40inputs
LicenceMIT
Last update6 months ago8 Apr 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

990 lines of Pine Script v6 from scripts/intraday-analyst/equity-intraday-analyst.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

equity-intraday-analyst.pine
// ============================================================================
// Equity Intraday Analyst
// Timeframe: 1-minute, 5-minute, 10-minute, 15-minute (adaptive)
// Version: 1.0.0
// Author: Chris Thomas
// ============================================================================
// Ticker-agnostic intraday overlay for any liquid US equity or ETF.
// Works on NVDA, MSFT, TSLA, GOOGL, AAPL, AMD, SPY, QQQ, IWM, etc.
//
// Full volume-based component stack (unlike the SPX variant which has no
// volume). VWAP, OBV, and relative volume are all native here.
//
// Timeframe-adaptive: auto-detects chart TF and adjusts opening range
// duration, ATR lookbacks, divergence sensitivity, and session bar math.
//
// Optional modules:
//   - VIX Context: OFF by default. Enable for SPY/QQQ where implied vol
//     is directly relevant. Less useful for individual stocks.
//   - NYSE TICK: ON by default. Directly relevant for ETFs (basket
//     products), useful as broad sentiment context for stocks.
//
// Components:
//   - Multi-EMA ribbon (configurable) + HTF anchor EMA
//   - Session-anchored VWAP with standard deviation bands
//   - OBV trend confirmation (slope + price/OBV divergence check)
//   - Relative volume (20-bar SMA baseline, 4-tier classification)
//   - RSI with pivot-based divergence detection
//   - MACD momentum
//   - ADX / DMI trend strength + slope
//   - Stochastic K/D (fast momentum confirmation)
//   - TTM Squeeze (BB/KC compression)
//   - NYSE TICK breadth (6-tier, optional)
//   - VIX context (level, slope, intraday change — optional)
//   - ATR regime (percentile-ranked)
//   - BB width percentile (compression detection)
//   - Session levels: Prior Day H/L/C, Prior Day VWAP, Opening Range, HOD/LOD
//   - Pre-Market High/Low
//   - Session phase detection (6 phases)
//   - Weighted scoring engine (-15 to +15)
//   - Signal generation (CALLS / PUTS) with cooldown
//   - Regime classifier (BULLISH / BEARISH / RANGING / NO TRADE)
//   - 26-row heads-up dashboard
//   - Dual alert system
// ============================================================================

// ============================================================================
// TradingView Pine Script v6
// ============================================================================
//@version=6
indicator(
     title          = "Equity Intraday Analyst",
     shorttitle     = "EQ-IA",
     overlay        = true,
     max_labels_count  = 500,
     max_lines_count   = 500,
     max_boxes_count   = 200,
     max_bars_back     = 5000
 )

// ============================================================================
// CONSTANTS
// ============================================================================
var string GP_EMA      = "EMA Ribbon"
var string GP_VWAP     = "Session VWAP"
var string GP_VOL      = "Volume / OBV"
var string GP_RSI      = "RSI"
var string GP_MACD     = "MACD"
var string GP_ADX      = "ADX / DMI"
var string GP_STOCH    = "Stochastic"
var string GP_ATR      = "ATR / Volatility"
var string GP_SQUEEZE  = "TTM Squeeze"
var string GP_TICK     = "NYSE TICK"
var string GP_VIX      = "VIX Context"
var string GP_LEVELS   = "Key Levels"
var string GP_SESSION  = "Session & Time"
var string GP_SIGNALS  = "Signal Logic"
var string GP_HTF      = "Higher Timeframe"
var string GP_DIV      = "RSI Divergence"
var string GP_DASH     = "Dashboard"

// Colors — vibrant, dark-theme optimized
var color C_BULL         = #00E676
var color C_BEAR         = #FF1744
var color C_NEUTRAL      = #B0BEC5
var color C_YELLOW       = #FFD600
var color C_ORANGE       = #FF9100
var color C_CYAN         = #00BCD4
var color C_PURPLE       = #7C4DFF
var color C_GRAY         = #616161
var color C_WHITE        = #FFFFFF

var color C_EMA_FAST     = #FFD600
var color C_EMA_MID      = #FF9100
var color C_EMA_SLOW     = #FF1744
var color C_EMA_ANCHOR   = #7C4DFF
var color C_CLOUD_BULL   = color.new(#00E676, 85)
var color C_CLOUD_BEAR   = color.new(#FF1744, 85)
var color C_CLOUD_MIXED  = color.new(#FFD600, 90)

var color C_VWAP         = #FFFFFF
var color C_VWAP_BAND    = #00BCD4
var color C_PD_VWAP      = #40C4FF

var color C_PM_HIGH      = #FF5252
var color C_PM_LOW       = #69F0AE
var color C_PDH          = #FF6E40
var color C_PDL          = #64FFDA
var color C_PDC          = #B0BEC5
var color C_OR_HIGH      = #E040FB
var color C_OR_LOW       = #B388FF
var color C_HOD          = #00E676
var color C_LOD          = #FF1744

var color C_CALLS        = #00E676
var color C_PUTS         = #FF1744
var color C_BG_CALLS     = color.new(#00E676, 92)
var color C_BG_PUTS      = color.new(#FF1744, 92)
var color C_BG_BIAS_BULL = color.new(#00E676, 96)
var color C_BG_BIAS_BEAR = color.new(#FF1744, 96)

var color C_DASH_BG      = color.new(#1A1A2E, 5)
var color C_DASH_HEADER  = #7C4DFF
var color C_DASH_TEXT     = #E0E0E0

// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
f2(float v) => str.tostring(v, "#.##")
posnegColor(float v) => v > 0 ? C_BULL : v < 0 ? C_BEAR : C_NEUTRAL
inSession(string sess, string tz) => not na(time(timeframe.period, sess, tz))

// ============================================================================
// INPUTS
// ============================================================================

// --- EMA Ribbon ---
i_emaFastLen   = input.int(9,     "Fast EMA Length",         minval=2,  group=GP_EMA)
i_emaMidLen    = input.int(21,    "Mid EMA Length",          minval=2,  group=GP_EMA)
i_emaSlowLen   = input.int(50,    "Slow EMA Length",         minval=2,  group=GP_EMA)
i_showCloud    = input.bool(true, "Show EMA Cloud",                     group=GP_EMA)

// --- Session VWAP ---
i_showVWAP     = input.bool(true,  "Show Session VWAP",                group=GP_VWAP)
i_vwapBandMult = input.float(1.5,  "VWAP Band Multiplier (Std Dev)",   minval=0.5, maxval=3.0, step=0.1, group=GP_VWAP)

// --- Volume / OBV ---
i_useOBV       = input.bool(true,  "Enable OBV Trend Confirmation",    group=GP_VOL)
i_obvEmaLen    = input.int(20,     "OBV EMA Length",         minval=5,  group=GP_VOL)
i_relVolLen    = input.int(20,     "Relative Volume SMA Length", minval=5, group=GP_VOL)

// --- RSI ---
i_rsiLen       = input.int(14,    "RSI Length",              minval=2,  group=GP_RSI)
i_rsiOB        = input.int(70,    "RSI Overbought",         minval=60, maxval=90, group=GP_RSI)
i_rsiOS        = input.int(30,    "RSI Oversold",           minval=10, maxval=40, group=GP_RSI)

// --- MACD ---
i_macdFast     = input.int(12,    "MACD Fast Length",        minval=2,  group=GP_MACD)
i_macdSlow     = input.int(26,    "MACD Slow Length",        minval=2,  group=GP_MACD)
i_macdSignal   = input.int(9,     "MACD Signal Length",      minval=2,  group=GP_MACD)

// --- ADX / DMI ---
i_adxLen       = input.int(14,    "ADX Length",              minval=2,  group=GP_ADX)
i_adxNoTrend   = input.int(18,    "ADX No-Trend Threshold",  minval=10, maxval=30, group=GP_ADX)

// --- Stochastic ---
i_stochK       = input.int(14,    "Stochastic K",            minval=2,  group=GP_STOCH)
i_stochD       = input.int(3,     "Stochastic D (Smoothing)", minval=1, group=GP_STOCH)
i_stochSmooth  = input.int(3,     "Stochastic K Smoothing",  minval=1,  group=GP_STOCH)

// --- ATR / Volatility ---
i_atrLen       = input.int(14,    "ATR Length",              minval=2,  group=GP_ATR)
i_atrPctlLB    = input.int(0,     "ATR Percentile Lookback (0 = auto)", minval=0, group=GP_ATR,
                   tooltip="Auto: 1m=390, 5m=200, 10m=150, 15m=100 bars.")
i_bbLen        = input.int(20,    "BB Length",               minval=5,  group=GP_ATR)
i_bbMult       = input.float(2.0, "BB Multiplier",           minval=1.0, maxval=3.0, step=0.1, group=GP_ATR)
i_bbwPctlLB    = input.int(0,     "BB Width Pctl Lookback (0 = auto)", minval=0, group=GP_ATR)

// --- TTM Squeeze ---
i_useSqueeze   = input.bool(true,  "Enable TTM Squeeze",               group=GP_SQUEEZE)
i_sqzBBLen     = input.int(20,     "Squeeze BB Length",      minval=5,  group=GP_SQUEEZE)
i_sqzBBMult    = input.float(2.0,  "Squeeze BB Multiplier",  minval=1.0, maxval=3.0, step=0.1, group=GP_SQUEEZE)
i_sqzKCLen     = input.int(20,     "Squeeze KC Length",      minval=5,  group=GP_SQUEEZE)
i_sqzKCMult    = input.float(1.5,  "Squeeze KC Multiplier",  minval=0.5, maxval=3.0, step=0.1, group=GP_SQUEEZE)

// --- NYSE TICK ---
i_useTick      = input.bool(true,      "Enable NYSE TICK",                 group=GP_TICK)
i_tickSymbol   = input.string("USI:TICK", "TICK Symbol",                   group=GP_TICK)
i_tickBull     = input.int(500,        "TICK Bullish Threshold",  minval=100, group=GP_TICK)
i_tickBear     = input.int(-500,       "TICK Bearish Threshold",  maxval=-100, group=GP_TICK)
i_tickExtreme  = input.int(1000,       "TICK Extreme Threshold",  minval=500, group=GP_TICK)

// --- VIX Context (optional — most useful for SPY/QQQ) ---
i_useVIX       = input.bool(false,     "Enable VIX Context",               group=GP_VIX,
                   tooltip="Enable for SPY/QQQ. VIX measures implied vol on S&P 500 options. Less directly relevant for individual stocks.")
i_vixSymbol    = input.string("CBOE:VIX", "VIX Symbol",                    group=GP_VIX)
i_vixLow       = input.float(15.0,     "VIX Low Threshold",     minval=5,  step=1, group=GP_VIX)
i_vixElevated  = input.float(20.0,     "VIX Elevated Threshold", minval=10, step=1, group=GP_VIX)
i_vixHigh      = input.float(25.0,     "VIX High Threshold",    minval=15, step=1, group=GP_VIX)
i_vixExtreme   = input.float(30.0,     "VIX Extreme Threshold", minval=20, step=1, group=GP_VIX)
i_vixSlopeLen  = input.int(5,          "VIX Slope Lookback",    minval=2,  group=GP_VIX)

// --- Key Levels ---
i_showPMHL     = input.bool(true, "Show Pre-Market High / Low",         group=GP_LEVELS)
i_showPDHLC    = input.bool(true, "Show Prior Day H / L / C",           group=GP_LEVELS)
i_showPDVwap   = input.bool(true, "Show Prior Day VWAP Close",          group=GP_LEVELS)
i_showOR       = input.bool(true, "Show Opening Range",                 group=GP_LEVELS)
i_orMinutes    = input.int(0,     "Opening Range Minutes (0 = auto)",   minval=0, maxval=60, group=GP_LEVELS,
                   tooltip="Auto: 1m=5min, 5m=15min, 10m=20min, 15m=30min.")
i_showHODLOD   = input.bool(true, "Show Session HOD / LOD",             group=GP_LEVELS)

// --- Session & Time ---
i_pmSession    = input.session("0400-0930", "Pre-Market Session",       group=GP_SESSION)
i_rthSession   = input.session("0930-1600", "Regular Trading Hours",    group=GP_SESSION)
i_tz           = input.string("America/New_York", "Timezone",           group=GP_SESSION,
                   options=["America/New_York", "America/Chicago", "America/Los_Angeles", "UTC"])

// --- Signal Logic ---
i_showSignals  = input.bool(true,  "Show CALLS / PUTS Signals",        group=GP_SIGNALS)
i_cooldownBars = input.int(0,      "Signal Cooldown Bars (0 = auto)",   minval=0, maxval=20, group=GP_SIGNALS,
                   tooltip="Auto: 1m=5, 5m=3, 10m=2, 15m=2.")
i_signalStart  = input.session("0945-1545", "Signal Time Window",      group=GP_SIGNALS)
i_requireADX   = input.bool(true,  "Require ADX Above No-Trend",       group=GP_SIGNALS)
i_useWeighted  = input.bool(true,  "Use Weighted Scoring (2x/1x)",     group=GP_SIGNALS,
                   tooltip="Structural: EMA stack, VWAP pos, HTF anchor (2x each). Confirmation: ADX/DI, RSI, MACD, OBV, TICK, Squeeze, VIX, 50 EMA slope (1x each). Weighted max: +/-15. Equal max: +/-11.")
i_minScore     = input.int(7,      "Minimum Signal Score",             minval=2, maxval=16, group=GP_SIGNALS)
i_vwapExtFilter = input.float(2.5, "Max VWAP Distance (ATR multiples)", minval=0.5, maxval=5.0, step=0.25, group=GP_SIGNALS)
i_showBgTint   = input.bool(false, "Show Directional Background Tint", group=GP_SIGNALS)
i_bgThreshold  = input.int(6,     "Background Tint Threshold",         minval=2, maxval=15, group=GP_SIGNALS)

// --- Higher Timeframe ---
i_useHTF       = input.bool(true,  "Enable HTF Anchor EMA",            group=GP_HTF)
i_htfTF        = input.string("",  "HTF Timeframe (blank = auto)",     group=GP_HTF,
                   tooltip="Auto: 1m->5m, 5m->15m, 10m->30m, 15m->60m.")
i_htfEmaLen    = input.int(50,     "HTF Anchor EMA Length",            minval=10, group=GP_HTF)

// --- RSI Divergence ---
i_useDivergence = input.bool(true, "Enable RSI Divergence Detection",  group=GP_DIV)
i_divPivotLeft  = input.int(0,     "Pivot Left Bars (0 = auto)",       minval=0, maxval=10, group=GP_DIV)
i_divPivotRight = input.int(0,     "Pivot Right Bars (0 = auto)",      minval=0, maxval=10, group=GP_DIV)
i_divLookback   = input.int(0,     "Divergence Lookback (0 = auto)",   minval=0, maxval=50, group=GP_DIV)

// --- Dashboard ---
i_showDash     = input.bool(true,    "Show Dashboard",                 group=GP_DASH)
i_dashPos      = input.string("Top Right", "Dashboard Position",       group=GP_DASH,
                   options=["Top Right", "Top Left", "Bottom Right", "Bottom Left"])
i_dashSize     = input.string("Small", "Dashboard Size",               group=GP_DASH,
                   options=["Tiny", "Small", "Normal"])

// ============================================================================
// TIMEFRAME ADAPTATION
// ============================================================================
float tfMinutes = timeframe.in_seconds() / 60.0
bool  intradayChart = timeframe.isintraday

int orMinutesAdapt = i_orMinutes > 0 ? i_orMinutes : tfMinutes <= 1 ? 5 : tfMinutes <= 5 ? 15 : tfMinutes <= 10 ? 20 : 30
int cooldownAdapt = i_cooldownBars > 0 ? i_cooldownBars : tfMinutes <= 1 ? 5 : tfMinutes <= 5 ? 3 : tfMinutes <= 10 ? 2 : 2
int atrPctlAdapt = i_atrPctlLB > 0 ? i_atrPctlLB : tfMinutes <= 1 ? 390 : tfMinutes <= 5 ? 200 : tfMinutes <= 10 ? 150 : 100
int bbwPctlAdapt = i_bbwPctlLB > 0 ? i_bbwPctlLB : tfMinutes <= 1 ? 390 : tfMinutes <= 5 ? 200 : tfMinutes <= 10 ? 150 : 100
int divPL = i_divPivotLeft > 0 ? i_divPivotLeft : tfMinutes <= 1 ? 5 : tfMinutes <= 5 ? 4 : tfMinutes <= 10 ? 3 : 3
int divPR = i_divPivotRight > 0 ? i_divPivotRight : tfMinutes <= 1 ? 3 : tfMinutes <= 5 ? 2 : tfMinutes <= 10 ? 2 : 2
int divLB = i_divLookback > 0 ? i_divLookback : tfMinutes <= 1 ? 30 : tfMinutes <= 5 ? 20 : tfMinutes <= 10 ? 15 : 10
string htfTF = i_htfTF != "" ? i_htfTF : tfMinutes <= 1 ? "5" : tfMinutes <= 5 ? "15" : tfMinutes <= 10 ? "30" : "60"

// ============================================================================
// EMA RIBBON
// ============================================================================
float emaFast = ta.ema(close, i_emaFastLen)
float emaMid  = ta.ema(close, i_emaMidLen)
float emaSlow = ta.ema(close, i_emaSlowLen)

bool emaBullish = emaFast > emaMid and emaMid > emaSlow
bool emaBearish = emaFast < emaMid and emaMid < emaSlow
color cloudColor = emaBullish ? C_CLOUD_BULL : emaBearish ? C_CLOUD_BEAR : C_CLOUD_MIXED

plot(emaFast, "Fast EMA",  C_EMA_FAST, 1)
plot(emaMid,  "Mid EMA",   C_EMA_MID,  1)
plot(emaSlow, "Slow EMA",  C_EMA_SLOW, 2)

pFastFill = plot(i_showCloud ? emaFast : na, "Cloud Fast", display=display.none)
pSlowFill = plot(i_showCloud ? emaSlow : na, "Cloud Slow", display=display.none)
fill(pFastFill, pSlowFill, cloudColor, "EMA Cloud")

float emaSlope    = emaSlow - emaSlow[5]
bool  emaSlopePos = emaSlope > 0
bool  emaSlopeNeg = emaSlope < 0

// ============================================================================
// HTF ANCHOR EMA
// ============================================================================
float htfAnchorEma = i_useHTF ? request.security(syminfo.tickerid, htfTF, ta.ema(close, i_htfEmaLen), lookahead=barmerge.lookahead_off) : na

plot(i_useHTF and not na(htfAnchorEma) ? htfAnchorEma : na, "HTF Anchor EMA",
     C_EMA_ANCHOR, 3, plot.style_stepline)

// ============================================================================
// SESSION-ANCHORED VWAP + BANDS
// ============================================================================
bool newSession = inSession(i_rthSession, i_tz) and not inSession(i_rthSession, i_tz)[1]

float vwapVal = ta.vwap

// VWAP std dev bands via cumulative variance
var float vwapSumVP   = 0.0
var float vwapSumVP2  = 0.0
var float vwapSumVol  = 0.0

if newSession
    vwapSumVP  := volume * hlc3
    vwapSumVP2 := volume * hlc3 * hlc3
    vwapSumVol := volume
else
    vwapSumVP  += volume * hlc3
    vwapSumVP2 += volume * hlc3 * hlc3
    vwapSumVol += volume

float vwapMean = vwapSumVol > 0 ? vwapSumVP / vwapSumVol : hlc3
float vwapVar  = vwapSumVol > 0 ? (vwapSumVP2 / vwapSumVol) - (vwapMean * vwapMean) : 0.0
float vwapSD   = math.sqrt(math.max(vwapVar, 0.0))

float vwapUpper = vwapVal + i_vwapBandMult * vwapSD
float vwapLower = vwapVal - i_vwapBandMult * vwapSD

plot(i_showVWAP and intradayChart ? vwapVal   : na, "VWAP",       C_VWAP,      2)
plot(i_showVWAP and intradayChart ? vwapUpper : na, "VWAP Upper", C_VWAP_BAND, 1)
plot(i_showVWAP and intradayChart ? vwapLower : na, "VWAP Lower", C_VWAP_BAND, 1)

// Prior Day VWAP: latch yesterday's closing VWAP at session boundary
var float pdVwap = na
if intradayChart and newSession and not na(vwapVal[1])
    pdVwap := vwapVal[1]

plot(i_showPDVwap and intradayChart and not na(pdVwap) ? pdVwap : na, "Prior Day VWAP",
     C_PD_VWAP, 2, plot.style_cross)

// ============================================================================
// OBV + RELATIVE VOLUME
// ============================================================================
float obvVal    = ta.obv
float obvEma    = ta.ema(obvVal, i_obvEmaLen)
float obvSlope  = obvEma - obvEma[5]

// OBV vs price divergence check
bool priceUp   = close > close[5]
bool priceDown = close < close[5]
bool obvUp     = obvSlope > 0
bool obvDown   = obvSlope < 0

string obvStr = i_useOBV ? ((priceUp and obvUp) or (priceDown and obvDown) ? "CONFIRMING" : (priceUp and obvDown) or (priceDown and obvUp) ? "DIVERGING" : "NEUTRAL") : "OFF"
color  obvClr = obvStr == "CONFIRMING" ? C_BULL : obvStr == "DIVERGING" ? C_ORANGE : obvStr == "OFF" ? C_GRAY : C_NEUTRAL

// Relative volume
float avgVol   = ta.sma(volume, i_relVolLen)
float relVol   = avgVol != 0 ? volume / avgVol : 0.0
string relVolStr = relVol > 2.0 ? "SPIKE" : relVol > 1.2 ? "ABOVE" : relVol > 0.8 ? "NORMAL" : "DRY"
color  relVolClr = relVol > 2.0 ? C_ORANGE : relVol > 1.2 ? C_BULL : relVol > 0.8 ? C_NEUTRAL : C_GRAY

// ============================================================================
// RSI
// ============================================================================
float rsiVal = ta.rsi(close, i_rsiLen)

// ============================================================================
// MACD
// ============================================================================
[macdLine, macdSignalLine, macdHist] = ta.macd(close, i_macdFast, i_macdSlow, i_macdSignal)
bool macdAboveSignal = macdLine > macdSignalLine

// ============================================================================
// ADX / DMI
// ============================================================================
[diPlus, diMinus, adxVal] = ta.dmi(i_adxLen, i_adxLen)

float adxSlope = adxVal - nz(adxVal[5])
string adxSlopeStr = adxSlope > 1 ? "RISING" : adxSlope < -1 ? "FALLING" : "FLAT"
color  adxSlopeClr = adxSlope > 1 ? C_BULL : adxSlope < -1 ? C_BEAR : C_NEUTRAL

string adxTier = adxVal > 45 ? "EXTREME" : adxVal > 30 ? "STRONG" : adxVal > 20 ? "MODERATE" : "WEAK"
color  adxTierClr = adxVal > 45 ? C_ORANGE : adxVal > 30 ? C_BULL : adxVal > 20 ? C_YELLOW : C_GRAY

// ============================================================================
// STOCHASTIC
// ============================================================================
float stochK = ta.sma(ta.stoch(close, high, low, i_stochK), i_stochSmooth)
float stochD = ta.sma(stochK, i_stochD)

// ============================================================================
// ATR + REGIME
// ============================================================================
float atrVal = ta.atr(i_atrLen)
int atrRank = 0
for i = 1 to atrPctlAdapt
    if atrVal > nz(atrVal[i])
        atrRank += 1
float atrPctl = atrPctlAdapt > 0 ? atrRank / float(atrPctlAdapt) * 100 : 50.0

string atrRegime = atrPctl >= 90 ? "EXTREME" : atrPctl >= 70 ? "HIGH" : atrPctl >= 30 ? "NORMAL" : "LOW"
color  atrClr    = atrPctl >= 90 ? C_ORANGE : atrPctl >= 70 ? C_YELLOW : atrPctl >= 30 ? C_NEUTRAL : C_CYAN

// ============================================================================
// BOLLINGER BAND WIDTH PERCENTILE
// ============================================================================
[bbMid, bbUp, bbLow] = ta.bb(close, i_bbLen, i_bbMult)
float bbWidth = bbMid != 0 ? (bbUp - bbLow) / bbMid * 100 : 0.0
int bbwRank = 0
for i = 1 to bbwPctlAdapt
    if bbWidth > nz(bbWidth[i])
        bbwRank += 1
float bbwPctl = bbwPctlAdapt > 0 ? bbwRank / float(bbwPctlAdapt) * 100 : 50.0

string bbwStr = bbwPctl < 10 ? "COMPRESSED" : bbwPctl < 30 ? "NARROW" : bbwPctl > 90 ? "EXPANDED" : bbwPctl > 70 ? "WIDE" : "NORMAL"
color  bbwClr = bbwPctl < 10 ? C_CYAN : bbwPctl < 30 ? color.new(C_CYAN, 40) : bbwPctl > 90 ? C_ORANGE : bbwPctl > 70 ? C_YELLOW : C_NEUTRAL

// ============================================================================
// TTM SQUEEZE
// ============================================================================
float sqzBBUpper = 0.0
float sqzBBLower = 0.0
float sqzKCUpper = 0.0
float sqzKCLower = 0.0
string sqzState  = "OFF"
color  sqzClr    = C_GRAY
float  sqzMom    = 0.0

if i_useSqueeze
    sqzBBUpper := ta.sma(close, i_sqzBBLen) + i_sqzBBMult * ta.stdev(close, i_sqzBBLen)
    sqzBBLower := ta.sma(close, i_sqzBBLen) - i_sqzBBMult * ta.stdev(close, i_sqzBBLen)
    sqzKCUpper := ta.ema(close, i_sqzKCLen) + i_sqzKCMult * ta.atr(i_sqzKCLen)
    sqzKCLower := ta.ema(close, i_sqzKCLen) - i_sqzKCMult * ta.atr(i_sqzKCLen)

    bool sqzOn     = sqzBBLower > sqzKCLower and sqzBBUpper < sqzKCUpper
    bool sqzOnPrev = nz(sqzBBLower[1]) > nz(sqzKCLower[1]) and nz(sqzBBUpper[1]) < nz(sqzKCUpper[1])

    sqzState := sqzOn ? "SQUEEZE ON" : (not sqzOn and sqzOnPrev) ? "FIRED" : "OFF"
    sqzClr   := sqzState == "SQUEEZE ON" ? C_ORANGE : sqzState == "FIRED" ? C_BULL : C_GRAY
    sqzMom   := ta.linreg(close - ta.sma(close, i_sqzBBLen), i_sqzBBLen, 0)

// ============================================================================
// NYSE TICK INDEX
// ============================================================================
float tickVal = i_useTick ? request.security(i_tickSymbol, "1", close, lookahead=barmerge.lookahead_off) : na
float tickSMA = i_useTick and not na(tickVal) ? request.security(i_tickSymbol, "1", ta.sma(close, 5), lookahead=barmerge.lookahead_off) : na

string tickStr = na
color  tickClr = C_GRAY

if i_useTick and not na(tickSMA)
    if tickSMA > i_tickExtreme
        tickStr := "EXTREME BULL"
        tickClr := C_BULL
    else if tickSMA > i_tickBull
        tickStr := "BULLISH"
        tickClr := color.new(C_BULL, 30)
    else if tickSMA > 200
        tickStr := "LEAN BULL"
        tickClr := color.new(C_BULL, 60)
    else if tickSMA < -i_tickExtreme
        tickStr := "EXTREME BEAR"
        tickClr := C_BEAR
    else if tickSMA < i_tickBear
        tickStr := "BEARISH"
        tickClr := color.new(C_BEAR, 30)
    else if tickSMA < -200
        tickStr := "LEAN BEAR"
        tickClr := color.new(C_BEAR, 60)
    else
        tickStr := "NEUTRAL"
        tickClr := C_NEUTRAL
else if i_useTick
    tickStr := "N/A"

// ============================================================================
// VIX CONTEXT MODULE (optional — best for SPY/QQQ)
// ============================================================================
float vixVal    = i_useVIX ? request.security(i_vixSymbol, timeframe.period, close, lookahead=barmerge.lookahead_off) : na
float vixOpen   = i_useVIX ? request.security(i_vixSymbol, "D", open, lookahead=barmerge.lookahead_off) : na
float vixSlope  = i_useVIX and not na(vixVal) ? vixVal - nz(vixVal[i_vixSlopeLen]) : na
float vixChgPct = i_useVIX and not na(vixVal) and not na(vixOpen) and vixOpen != 0 ? (vixVal - vixOpen) / vixOpen * 100 : na

string vixTier = na
color  vixClr  = C_GRAY

if i_useVIX and not na(vixVal)
    if vixVal >= i_vixExtreme
        vixTier := "EXTREME"
        vixClr  := C_BEAR
    else if vixVal >= i_vixHigh
        vixTier := "HIGH"
        vixClr  := C_ORANGE
    else if vixVal >= i_vixElevated
        vixTier := "ELEVATED"
        vixClr  := C_YELLOW
    else if vixVal >= i_vixLow
        vixTier := "NORMAL"
        vixClr  := C_NEUTRAL
    else
        vixTier := "LOW"
        vixClr  := C_BULL

string vixSlopeStr = na
color  vixSlopeClr = C_GRAY
if i_useVIX and not na(vixSlope)
    vixSlopeStr := vixSlope > 0.5 ? "RISING" : vixSlope < -0.5 ? "FALLING" : "FLAT"
    vixSlopeClr := vixSlope > 0.5 ? C_BEAR : vixSlope < -0.5 ? C_BULL : C_NEUTRAL

// ============================================================================
// SESSION DETECTION & PHASE
// ============================================================================
bool isRTH = inSession(i_rthSession, i_tz)
bool isPM  = inSession(i_pmSession, i_tz)

int   hourET    = hour(time, i_tz)
int   minuteET  = minute(time, i_tz)
float timeDec   = hourET + minuteET / 60.0

string sessionPhase = "PRE/POST"
color  phaseClr     = C_GRAY
if isRTH
    if timeDec < 10.0
        sessionPhase := "OPEN DRIVE"
        phaseClr     := C_ORANGE
    else if timeDec < 11.5
        sessionPhase := "MORNING"
        phaseClr     := C_BULL
    else if timeDec < 14.0
        sessionPhase := "MIDDAY"
        phaseClr     := C_GRAY
    else if timeDec < 15.5
        sessionPhase := "POWER HOUR"
        phaseClr     := C_CYAN
    else
        sessionPhase := "CLOSE"
        phaseClr     := C_YELLOW

// ============================================================================
// KEY LEVELS
// ============================================================================

// --- Pre-Market High / Low ---
var float pmHigh = na
var float pmLow  = na
if isPM
    if na(pmHigh) or not isPM[1]
        pmHigh := high
        pmLow  := low
    else
        pmHigh := math.max(pmHigh, high)
        pmLow  := math.min(pmLow, low)

plot(i_showPMHL and isRTH and not na(pmHigh) ? pmHigh : na, "PM High", C_PM_HIGH, 1, plot.style_linebr)
plot(i_showPMHL and isRTH and not na(pmLow)  ? pmLow  : na, "PM Low",  C_PM_LOW,  1, plot.style_linebr)

// --- Prior Day High / Low / Close ---
float pdHigh  = request.security(syminfo.tickerid, "D", high[1],  lookahead=barmerge.lookahead_on)
float pdLow   = request.security(syminfo.tickerid, "D", low[1],   lookahead=barmerge.lookahead_on)
float pdClose = request.security(syminfo.tickerid, "D", close[1], lookahead=barmerge.lookahead_on)

plot(i_showPDHLC and intradayChart and not na(pdHigh)  ? pdHigh  : na, "PDH", C_PDH, 1, plot.style_stepline)
plot(i_showPDHLC and intradayChart and not na(pdLow)   ? pdLow   : na, "PDL", C_PDL, 1, plot.style_stepline)
plot(i_showPDHLC and intradayChart and not na(pdClose) ? pdClose : na, "PDC", C_PDC, 1, plot.style_stepline)

// --- Opening Range ---
var float orHigh     = na
var float orLow      = na
var bool  orComplete = false
var int   orBarsCount = 0
int orBarsNeeded = math.max(1, math.ceil(orMinutesAdapt / timeframe.multiplier))

if newSession
    orHigh      := high
    orLow       := low
    orComplete  := false
    orBarsCount := 1
else if isRTH and not orComplete
    orBarsCount += 1
    orHigh := math.max(nz(orHigh, high), high)
    orLow  := math.min(nz(orLow, low), low)
    if orBarsCount >= orBarsNeeded
        orComplete := true

plot(i_showOR and isRTH and orComplete and not na(orHigh) ? orHigh : na, "OR High", C_OR_HIGH, 1, plot.style_linebr)
plot(i_showOR and isRTH and orComplete and not na(orLow)  ? orLow  : na, "OR Low",  C_OR_LOW,  1, plot.style_linebr)

// --- Session HOD / LOD ---
var float sessionHigh = na
var float sessionLow  = na
if newSession
    sessionHigh := high
    sessionLow  := low
else if isRTH
    sessionHigh := math.max(nz(sessionHigh, high), high)
    sessionLow  := math.min(nz(sessionLow, low), low)

plot(i_showHODLOD and isRTH and not na(sessionHigh) ? sessionHigh : na, "Session HOD", C_HOD, 1, plot.style_circles)
plot(i_showHODLOD and isRTH and not na(sessionLow)  ? sessionLow  : na, "Session LOD", C_LOD, 1, plot.style_circles)

// HOD/LOD position
string hodlodPos = "MID"
if isRTH and not na(sessionHigh) and not na(sessionLow)
    float sessRange = sessionHigh - sessionLow
    if sessRange > 0
        float posInRange = (close - sessionLow) / sessRange
        hodlodPos := posInRange > 0.95 ? "AT HOD" : posInRange < 0.05 ? "AT LOD" : posInRange > 0.7 ? "UPPER" : posInRange < 0.3 ? "LOWER" : "MID"

// OR position
string orPos = "N/A"
if orComplete and not na(orHigh) and not na(orLow)
    orPos := close > orHigh ? "ABOVE OR" : close < orLow ? "BELOW OR" : "IN OR"

// ============================================================================
// RSI DIVERGENCE DETECTION
// ============================================================================
var int   lastBullDivBar = 0
var int   lastBearDivBar = 0
string    divStr         = "NONE"
color     divClr         = C_NEUTRAL

// Pivot calls at global scope for consistent execution
float pivLowPrice  = ta.pivotlow(low,    divPL, divPR)
float pivHighPrice = ta.pivothigh(high,  divPL, divPR)
float pivLowRSI    = ta.pivotlow(rsiVal, divPL, divPR)
float pivHighRSI   = ta.pivothigh(rsiVal, divPL, divPR)

var array<float> pivLowPriceHist  = array.new_float(0)
var array<float> pivLowRSIHist    = array.new_float(0)
var array<float> pivHighPriceHist = array.new_float(0)
var array<float> pivHighRSIHist   = array.new_float(0)

if not na(pivLowPrice) and not na(pivLowRSI)
    array.unshift(pivLowPriceHist, pivLowPrice)
    array.unshift(pivLowRSIHist, pivLowRSI)
    if array.size(pivLowPriceHist) > 20
        array.pop(pivLowPriceHist)
        array.pop(pivLowRSIHist)

if not na(pivHighPrice) and not na(pivHighRSI)
    array.unshift(pivHighPriceHist, pivHighPrice)
    array.unshift(pivHighRSIHist, pivHighRSI)
    if array.size(pivHighPriceHist) > 20
        array.pop(pivHighPriceHist)
        array.pop(pivHighRSIHist)

if i_useDivergence
    if not na(pivLowPrice) and not na(pivLowRSI) and array.size(pivLowPriceHist) > 1
        for i = 1 to math.min(array.size(pivLowPriceHist) - 1, 10)
            float prevPrice = array.get(pivLowPriceHist, i)
            float prevRSI   = array.get(pivLowRSIHist, i)
            if pivLowPrice < prevPrice and pivLowRSI > prevRSI
                lastBullDivBar := bar_index
                break

    if not na(pivHighPrice) and not na(pivHighRSI) and array.size(pivHighPriceHist) > 1
        for i = 1 to math.min(array.size(pivHighPriceHist) - 1, 10)
            float prevPrice = array.get(pivHighPriceHist, i)
            float prevRSI   = array.get(pivHighRSIHist, i)
            if pivHighPrice > prevPrice and pivHighRSI < prevRSI
                lastBearDivBar := bar_index
                break

    int divRecency = divLB * 2
    bool recentBull = (bar_index - lastBullDivBar) <= divRecency and lastBullDivBar > 0
    bool recentBear = (bar_index - lastBearDivBar) <= divRecency and lastBearDivBar > 0

    divStr := recentBull and recentBear ? "BULL + BEAR" : recentBull ? "BULLISH" : recentBear ? "BEARISH" : "NONE"
    divClr := recentBull and not recentBear ? C_BULL : recentBear and not recentBull ? C_BEAR : recentBull and recentBear ? C_YELLOW : C_NEUTRAL

// ============================================================================
// COMPOSITE SCORING ENGINE
// ============================================================================
// STRUCTURAL (2x): EMA stack, VWAP position, HTF anchor
// CONFIRMATION (1x): ADX/DI, RSI, MACD, OBV, TICK, Squeeze, VIX, 50 EMA slope
// Weighted max: 3*2 + 8*1 = 14.  With VIX: +1 = 15.  Equal max: 11.

int wStructural = i_useWeighted ? 2 : 1
int wConfirm    = 1
int biasScore   = 0

// --- Structural ---
biasScore += emaBullish ? wStructural : emaBearish ? -wStructural : 0

if intradayChart and not na(vwapVal)
    biasScore += close > vwapVal ? wStructural : close < vwapVal ? -wStructural : 0

if i_useHTF and not na(htfAnchorEma)
    biasScore += close > htfAnchorEma ? wStructural : close < htfAnchorEma ? -wStructural : 0

// --- Confirmation ---
if adxVal > i_adxNoTrend
    biasScore += diPlus > diMinus ? wConfirm : diMinus > diPlus ? -wConfirm : 0

biasScore += rsiVal > 55 ? wConfirm : rsiVal < 45 ? -wConfirm : 0
biasScore += macdAboveSignal ? wConfirm : -wConfirm

if i_useOBV
    biasScore += obvSlope > 0 ? wConfirm : obvSlope < 0 ? -wConfirm : 0

if i_useTick and not na(tickSMA)
    biasScore += tickSMA > i_tickBull ? wConfirm : tickSMA < i_tickBear ? -wConfirm : 0

if i_useSqueeze and (sqzState == "FIRED" or (sqzState == "OFF" and nz(sqzMom) != 0))
    biasScore += sqzMom > 0 ? wConfirm : sqzMom < 0 ? -wConfirm : 0

if i_useVIX and not na(vixVal) and not na(vixSlope)
    bool vixBullish = vixVal < i_vixElevated and vixSlope < -0.3
    bool vixBearish = vixVal > i_vixElevated and vixSlope > 0.3
    biasScore += vixBullish ? wConfirm : vixBearish ? -wConfirm : 0

biasScore += emaSlopePos ? wConfirm : emaSlopeNeg ? -wConfirm : 0

// Score metadata
int maxScore = 3 * wStructural + (i_useVIX ? 8 : 7) * wConfirm

float scoreAvg = ta.sma(biasScore, 5)
string scoreTrend = biasScore > scoreAvg + 0.5 ? "IMPROVING" : biasScore < scoreAvg - 0.5 ? "FADING" : "STABLE"
color  scoreTrendClr = scoreTrend == "IMPROVING" ? C_BULL : scoreTrend == "FADING" ? C_BEAR : C_NEUTRAL

string biasLabel = biasScore >= 7 ? "STRONG BULL" : biasScore >= 4 ? "LEAN BULL" : biasScore >= 1 ? "WEAK BULL" : biasScore == 0 ? "NEUTRAL" : biasScore >= -3 ? "WEAK BEAR" : biasScore >= -6 ? "LEAN BEAR" : "STRONG BEAR"

color biasClr = biasScore >= 7 ? C_BULL : biasScore >= 4 ? color.new(C_BULL, 30) : biasScore >= 1 ? color.new(C_BULL, 60) : biasScore == 0 ? C_NEUTRAL : biasScore >= -3 ? color.new(C_BEAR, 60) : biasScore >= -6 ? color.new(C_BEAR, 30) : C_BEAR

// ============================================================================
// REGIME CLASSIFIER
// ============================================================================
string regimeStr = "NO TRADE"
color  regimeClr = C_GRAY

bool vwapBullish = intradayChart and not na(vwapVal) ? close > vwapVal : true
bool vwapBearish = intradayChart and not na(vwapVal) ? close < vwapVal : true

if adxVal >= i_adxNoTrend and emaBullish and vwapBullish
    regimeStr := "BULLISH"
    regimeClr := C_BULL
else if adxVal >= i_adxNoTrend and emaBearish and vwapBearish
    regimeStr := "BEARISH"
    regimeClr := C_BEAR
else if adxVal >= i_adxNoTrend
    regimeStr := "RANGING"
    regimeClr := C_YELLOW

// ============================================================================
// SIGNAL ENGINE
// ============================================================================
var int lastSignalBar = 0

bool inSignalWindow = inSession(i_signalStart, i_tz)
bool cooldownOk     = (bar_index - lastSignalBar) >= cooldownAdapt
bool adxOk          = not i_requireADX or adxVal >= i_adxNoTrend

bool vwapDistOk = true
if intradayChart and not na(vwapVal) and atrVal > 0
    float vwapDist = math.abs(close - vwapVal) / atrVal
    vwapDistOk := vwapDist <= i_vwapExtFilter

bool callsCondition = biasScore >= i_minScore and inSignalWindow and cooldownOk and adxOk and vwapDistOk
bool putsCondition  = biasScore <= -i_minScore and inSignalWindow and cooldownOk and adxOk and vwapDistOk

bool callsSignal = i_showSignals and barstate.isconfirmed and callsCondition
bool putsSignal  = i_showSignals and barstate.isconfirmed and putsCondition

if callsSignal or putsSignal
    lastSignalBar := bar_index

if callsSignal
    label.new(bar_index, low, "CALLS", style=label.style_label_up, color=C_CALLS, textcolor=color.white, size=size.small)
if putsSignal
    label.new(bar_index, high, "PUTS", style=label.style_label_down, color=C_PUTS, textcolor=color.white, size=size.small)

bgcolor(i_showBgTint and biasScore >= i_bgThreshold ? C_BG_BIAS_BULL : i_showBgTint and biasScore <= -i_bgThreshold ? C_BG_BIAS_BEAR : na)
bgcolor(callsSignal ? C_BG_CALLS : putsSignal ? C_BG_PUTS : na)

// ============================================================================
// DASHBOARD
// ============================================================================
if i_showDash and barstate.islast
    var table dash = table.new(
         i_dashPos == "Top Right"    ? position.top_right    :
         i_dashPos == "Top Left"     ? position.top_left     :
         i_dashPos == "Bottom Right" ? position.bottom_right :
         position.bottom_left,
         columns = 2,
         rows    = 28,
         bgcolor = C_DASH_BG,
         border_width = 1,
         border_color = color.new(#FFFFFF, 85)
     )

    string tblSize = i_dashSize == "Tiny" ? size.tiny : i_dashSize == "Small" ? size.small : size.normal
    int r = 0

    // Header
    table.cell(dash, 0, r, "EQ INTRADAY", text_color=C_DASH_HEADER, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, syminfo.ticker + " " + str.tostring(tfMinutes, "#") + "m", text_color=C_DASH_HEADER, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // Regime
    table.cell(dash, 0, r, "Regime",    text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, regimeStr,   text_color=regimeClr,   text_size=tblSize, text_halign=text.align_right)
    r += 1

    // Bias
    table.cell(dash, 0, r, "Bias",      text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, biasLabel + " (" + str.tostring(biasScore) + "/" + str.tostring(maxScore) + ")", text_color=biasClr, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // Score Trend
    table.cell(dash, 0, r, "Score Trend", text_color=C_DASH_TEXT,   text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, scoreTrend,    text_color=scoreTrendClr, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // Phase
    table.cell(dash, 0, r, "Phase",        text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, sessionPhase,   text_color=phaseClr,    text_size=tblSize, text_halign=text.align_right)
    r += 1

    // EMA Stack
    string emaStr = emaBullish ? "BULLISH" : emaBearish ? "BEARISH" : "MIXED"
    color  emaClr = emaBullish ? C_BULL : emaBearish ? C_BEAR : C_YELLOW
    table.cell(dash, 0, r, "EMA Stack", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, emaStr,      text_color=emaClr,      text_size=tblSize, text_halign=text.align_right)
    r += 1

    // VWAP
    if intradayChart and not na(vwapVal)
        string vwapStr = close > vwapVal ? "ABOVE" : close < vwapVal ? "BELOW" : "AT"
        color  vwapColor = close > vwapVal ? C_BULL : close < vwapVal ? C_BEAR : C_NEUTRAL
        table.cell(dash, 0, r, "VWAP",  text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 1, r, vwapStr + " (" + f2(vwapVal) + ")", text_color=vwapColor, text_size=tblSize, text_halign=text.align_right)
    else
        table.cell(dash, 0, r, "VWAP",  text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 1, r, "N/A",   text_color=C_GRAY,      text_size=tblSize, text_halign=text.align_right)
    r += 1

    // HTF Anchor
    if i_useHTF and not na(htfAnchorEma)
        string htfStr = close > htfAnchorEma ? "ABOVE" : "BELOW"
        color  htfClr2 = close > htfAnchorEma ? C_BULL : C_BEAR
        table.cell(dash, 0, r, "HTF " + htfTF + "m EMA", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 1, r, htfStr + " (" + f2(htfAnchorEma) + ")", text_color=htfClr2, text_size=tblSize, text_halign=text.align_right)
    else
        table.cell(dash, 0, r, "HTF Anchor", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 1, r, i_useHTF ? "N/A" : "OFF", text_color=C_GRAY, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // ADX
    table.cell(dash, 0, r, "ADX",       text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, f2(adxVal) + " " + adxTier, text_color=adxTierClr, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // ADX Slope
    table.cell(dash, 0, r, "ADX Slope", text_color=C_DASH_TEXT,  text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, adxSlopeStr, text_color=adxSlopeClr,  text_size=tblSize, text_halign=text.align_right)
    r += 1

    // DI
    string diStr = "DI+ " + f2(diPlus) + " / DI- " + f2(diMinus)
    color  diClr = diPlus > diMinus ? C_BULL : diMinus > diPlus ? C_BEAR : C_NEUTRAL
    table.cell(dash, 0, r, "DI",        text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, diStr,       text_color=diClr,       text_size=tblSize, text_halign=text.align_right)
    r += 1

    // RSI
    color rsiClr = rsiVal > i_rsiOB ? C_BEAR : rsiVal < i_rsiOS ? C_BULL : rsiVal > 55 ? color.new(C_BULL, 30) : rsiVal < 45 ? color.new(C_BEAR, 30) : C_NEUTRAL
    string rsiState = rsiVal > i_rsiOB ? "OB" : rsiVal < i_rsiOS ? "OS" : ""
    table.cell(dash, 0, r, "RSI",       text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, f2(rsiVal) + (rsiState != "" ? " " + rsiState : ""), text_color=rsiClr, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // RSI Divergence
    if i_useDivergence
        table.cell(dash, 0, r, "RSI Div", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 1, r, divStr,    text_color=divClr,      text_size=tblSize, text_halign=text.align_right)
        r += 1

    // MACD
    string macdStr = macdAboveSignal ? "BULLISH" : "BEARISH"
    color  macdClr = macdAboveSignal ? C_BULL : C_BEAR
    table.cell(dash, 0, r, "MACD",      text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, macdStr + " (" + f2(macdHist) + ")", text_color=macdClr, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // OBV
    if i_useOBV
        table.cell(dash, 0, r, "OBV",   text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 1, r, obvStr,   text_color=obvClr,      text_size=tblSize, text_halign=text.align_right)
        r += 1

    // Relative Volume
    table.cell(dash, 0, r, "Rel Vol",   text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, relVolStr + " (" + f2(relVol) + "x)", text_color=relVolClr, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // Stochastic
    color stochClr = stochK > 80 ? C_BEAR : stochK < 20 ? C_BULL : stochK > stochD ? color.new(C_BULL, 30) : color.new(C_BEAR, 30)
    string stochState = stochK > 80 ? " OB" : stochK < 20 ? " OS" : ""
    table.cell(dash, 0, r, "Stoch K/D", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, f2(stochK) + " / " + f2(stochD) + stochState, text_color=stochClr, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // ATR
    table.cell(dash, 0, r, "ATR",       text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, f2(atrVal) + " " + atrRegime + " (" + f2(atrPctl) + "%)", text_color=atrClr, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // BB Width
    table.cell(dash, 0, r, "BB Width",  text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, bbwStr + " (" + f2(bbwPctl) + "%)", text_color=bbwClr, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // Squeeze
    if i_useSqueeze
        string sqzFull = sqzState + (sqzState != "OFF" ? " " + (sqzMom > 0 ? "+" : "") + f2(sqzMom) : "")
        table.cell(dash, 0, r, "Squeeze", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 1, r, sqzFull,   text_color=sqzClr,      text_size=tblSize, text_halign=text.align_right)
        r += 1

    // TICK
    if i_useTick
        string tickFull = (na(tickStr) ? "N/A" : tickStr) + (not na(tickSMA) ? " (" + str.tostring(math.round(tickSMA)) + ")" : "")
        table.cell(dash, 0, r, "TICK",   text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 1, r, tickFull, text_color=tickClr,     text_size=tblSize, text_halign=text.align_right)
        r += 1

    // VIX
    if i_useVIX
        if not na(vixVal)
            table.cell(dash, 0, r, "VIX",       text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
            table.cell(dash, 1, r, f2(vixVal) + " " + (na(vixTier) ? "" : vixTier), text_color=vixClr, text_size=tblSize, text_halign=text.align_right)
            r += 1
            string vixSlopeFull = (na(vixSlopeStr) ? "N/A" : vixSlopeStr) + (not na(vixChgPct) ? " (" + (vixChgPct > 0 ? "+" : "") + f2(vixChgPct) + "%)" : "")
            table.cell(dash, 0, r, "VIX Slope", text_color=C_DASH_TEXT,   text_size=tblSize, text_halign=text.align_left)
            table.cell(dash, 1, r, vixSlopeFull, text_color=vixSlopeClr,  text_size=tblSize, text_halign=text.align_right)
            r += 1
        else
            table.cell(dash, 0, r, "VIX",       text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
            table.cell(dash, 1, r, "N/A",       text_color=C_GRAY,     text_size=tblSize, text_halign=text.align_right)
            r += 1

    // HOD/LOD
    if isRTH
        table.cell(dash, 0, r, "HOD/LOD", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 1, r, hodlodPos, text_color=hodlodPos == "AT HOD" ? C_BULL : hodlodPos == "AT LOD" ? C_BEAR : C_NEUTRAL, text_size=tblSize, text_halign=text.align_right)
        r += 1

    // OR
    if orComplete
        color orPosClr = orPos == "ABOVE OR" ? C_BULL : orPos == "BELOW OR" ? C_BEAR : C_YELLOW
        table.cell(dash, 0, r, "OR",      text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 1, r, orPos,     text_color=orPosClr,    text_size=tblSize, text_halign=text.align_right)
        r += 1

    // Score Mode
    table.cell(dash, 0, r, "Score Mode", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 1, r, i_useWeighted ? "WEIGHTED" : "EQUAL", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_right)
    r += 1

// ============================================================================
// ALERTS
// ============================================================================
alertcondition(callsSignal, title="CALLS Signal", message="Equity Intraday Analyst: CALLS signal fired")
alertcondition(putsSignal,  title="PUTS Signal",  message="Equity Intraday Analyst: PUTS signal fired")
alertcondition(biasScore >= 7,  title="Strong Bullish Bias", message="Bias score reached strong bullish territory")
alertcondition(biasScore <= -7, title="Strong Bearish Bias", message="Bias score reached strong bearish territory")
alertcondition(i_useSqueeze and sqzState == "FIRED", title="Squeeze Fired", message="TTM Squeeze has fired")
alertcondition(i_useVIX and not na(vixVal) and vixVal >= i_vixExtreme, title="VIX Extreme", message="VIX has reached extreme levels")
alertcondition(i_useDivergence and divStr == "BULLISH",  title="RSI Bull Divergence", message="RSI bullish divergence detected")
alertcondition(i_useDivergence and divStr == "BEARISH",  title="RSI Bear Divergence", message="RSI bearish divergence detected")
alertcondition(isRTH and not na(sessionHigh) and high >= sessionHigh and high > high[1], title="New HOD", message="New session high")
alertcondition(isRTH and not na(sessionLow) and low <= sessionLow and low < low[1],      title="New LOD", message="New session low")

// Dynamic alerts
if callsSignal
    alert("CALLS " + syminfo.ticker + " | Score: " + str.tostring(biasScore) + "/" + str.tostring(maxScore) +
          " | Regime: " + regimeStr +
          " | ATR: " + atrRegime +
          " | RelVol: " + f2(relVol) + "x" +
          (i_useVIX and not na(vixVal) ? " | VIX: " + f2(vixVal) + " " + (na(vixTier) ? "" : vixTier) : "") +
          (i_useTick and not na(tickSMA) ? " | TICK: " + str.tostring(math.round(tickSMA)) : ""),
          alert.freq_once_per_bar_close)

if putsSignal
    alert("PUTS " + syminfo.ticker + " | Score: " + str.tostring(biasScore) + "/" + str.tostring(maxScore) +
          " | Regime: " + regimeStr +
          " | ATR: " + atrRegime +
          " | RelVol: " + f2(relVol) + "x" +
          (i_useVIX and not na(vixVal) ? " | VIX: " + f2(vixVal) + " " + (na(vixTier) ? "" : vixTier) : "") +
          (i_useTick and not na(tickSMA) ? " | TICK: " + str.tostring(math.round(tickSMA)) : ""),
          alert.freq_once_per_bar_close)

// ============================================================================
// END
// ============================================================================

Overview

Equity Intraday Analyst evaluates multi-EMA ribbons, session-anchored VWAP bands, OBV trends, momentum oscillators, TTM Squeeze, and optional market breadth indicators like NYSE TICK and VIX. The script calculates a composite directional score from -15 to +15 to classify market regimes and issue trade signals. Key intraday levels, prior day high/low, and opening ranges are plotted directly on the main chart, while key metrics are organized in an on-screen dashboard panel.

Details

Published by

chris-c-thomas on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Trend, Volume, Sessions & time, Screeners & dashboards

Markets

Stocks, Indices

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.

Topics

financeoptionspinescriptpinescript-indicatorspinescript-strategiestradingtradingvewtradingview-chart-analysischarting

Settings

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

SettingTypeDefaultVariable
Fast EMA Lengthint9i_emaFastLen
Mid EMA Lengthint21i_emaMidLen
Slow EMA Lengthint50i_emaSlowLen
Show EMA Cloudbooltruei_showCloud
Show Session VWAPbooltruei_showVWAP
VWAP Band Multiplier (Std Dev)float1.5i_vwapBandMult
Enable OBV Trend Confirmationbooltruei_useOBV
OBV EMA Lengthint20i_obvEmaLen
Relative Volume SMA Lengthint20i_relVolLen
RSI Lengthint14i_rsiLen
RSI Overboughtint70i_rsiOB
RSI Oversoldint30i_rsiOS
Show all 40 settings
SettingTypeDefaultVariable
MACD Fast Lengthint12i_macdFast
MACD Slow Lengthint26i_macdSlow
MACD Signal Lengthint9i_macdSignal
ADX Lengthint14i_adxLen
ADX No-Trend Thresholdint18i_adxNoTrend
Stochastic Kint14i_stochK
Stochastic D (Smoothing)int3i_stochD
Stochastic K Smoothingint3i_stochSmooth
ATR Lengthint14i_atrLen
ATR Percentile Lookback (0 = auto)int0i_atrPctlLB
BB Lengthint20i_bbLen
BB Multiplierfloat2.0i_bbMult
BB Width Pctl Lookback (0 = auto)int0i_bbwPctlLB
Enable TTM Squeezebooltruei_useSqueeze
Squeeze BB Lengthint20i_sqzBBLen
Squeeze BB Multiplierfloat2.0i_sqzBBMult
Squeeze KC Lengthint20i_sqzKCLen
Squeeze KC Multiplierfloat1.5i_sqzKCMult
Enable NYSE TICKbooltruei_useTick
TICK SymbolstringUSI:TICKi_tickSymbol
TICK Bullish Thresholdint500i_tickBull
TICK Bearish Thresholdint-500i_tickBear
TICK Extreme Thresholdint1000i_tickExtreme
Enable VIX Contextboolfalsei_useVIX
VIX SymbolstringCBOE:VIXi_vixSymbol
VIX Low Thresholdfloat15.0i_vixLow
VIX Elevated Thresholdfloat20.0i_vixElevated
VIX High Thresholdfloat25.0i_vixHigh
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

257 stars

This indicator overlays an intraday SPX and ES chart with a midnight session range, regular-trading-hours opening levels, and a VWAP projection box. It draws the midnight high, mid, and low, flags edge rejections within an absolute tolerance, and plots optiona

Indicator v6 TrendSupport & resistance
Updated 6 weeks ago · None stated GitHub repository of SPX Midnight Box + Snag Logic
92 stars

This indicator classifies the market into gamma regime types (negative gamma trend, positive gamma mean-reversion, or transition) by calculating a composite score based on daily ATR ratios, session VWAP crossover frequencies, and opening range displacement. It

Indicator v5 Volatility & bandsTrend
Updated 8 weeks ago · Custom licence GitHub repository of Moon Dev GEX Regime & Trap
9 stars

Peak Trading Activity Graphs displays historical market activity across cyclical timeframes directly on the main chart using an on-screen dashboard table. It evaluates volume or volatility across selected periods, including hours of the day, days of the week,

Indicator v6 VolumeVolatility & bands
Updated 4 months ago · MIT GitHub repository of Peak Trading Activity Graphs [LuxAlgo]
9 stars

Smart Money Dashboard plots key Smart Money Concepts on the price chart, including Order Blocks with mitigation tracking, Fair Value Gaps, and Equal Highs or Lows sweeps. It analyzes Cumulative Volume Delta for divergence and combines multi-timeframe structure

Indicator v6 SMC / ICT / price actionVolume
Updated yesterday · MIT GitHub repository of Smart Money Dashboard

Momentum Checklist

by eddiebelaval
7 stars

This overlay indicator calculates ADX, DI+ and DI- from directional movement and true range, and a Money Flow Index from typical price and volume. It classifies bias as long when DI+ exceeds 25 and DI- stays under 20, short under the opposite condition, and ne

Indicator v6 Oscillators & momentumTrend
Updated 3 months ago · None stated GitHub repository of Momentum Checklist

This indicator plots a Supertrend-based trend-following setup in a separate pane and skips conditions it treats as a sideways market. A volatility range is built from an ATR value, with RMA, SMA, EMA, or WMA smoothing, or from a fixed point distance. Optional

Indicator v5 TrendVolatility & bands
Updated 1 year ago · MPL-2.0 GitHub repository of Trend Following Setup - Sideways Market Skipper

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