This overlay indicator scores up to ten equal-weight conditions on the five-minute chart and marks a signal when the total meets a configurable threshold.
// ============================================================================
// SPY 0DTE Scalper
// Timeframe: 5-minute
// Version: 1.0.0
// Author: Chris Thomas
// ============================================================================
// Purpose-built overlay for scalping SPY 0DTE options on the 5-minute chart.
// Targets swing-scalp trades with 5-25 minute hold times. The 5-minute chart
// produces ~78 RTH bars per session, so the signal engine is calibrated for
// higher conviction per signal with lower total signal count compared to the
// 1-minute variant.
//
// Uses a scoring engine (up to 10 equal-weight conditions) rather than the
// 1-minute's strict AND-gate. Each condition contributes +1; signals fire
// when the cumulative score meets a configurable minimum threshold. This
// allows partial confluence to still generate signals when enough independent
// conditions align. The indicator does not auto-trade.
//
// Key differences from 1-minute variant:
// - Scoring engine replaces AND-gate logic
// - EMA ribbon uses 9/15/21 (tighter fast/mid spacing)
// - Multi-timeframe layer pulls 1-min RSI, EMA trend, and VWAP cross
// - TICK integrated as scoring condition (requires >500 / <-500)
// - Volume Footprint delta available as optional scoring condition (#10)
// - Opening range default 15 minutes (3 bars)
//
// v1.1.0 CPU optimization:
// - TICK request moved from "1" to chart TF (eliminates 5x LTF bar mult)
// - max_bars_back reduced 5000 → 2000 (~25 sessions retained)
//
// Components:
// - EMA ribbon (9/15/21) with dynamic trend cloud fill
// - Session-anchored VWAP with standard deviation bands
// - RSI momentum (scoring condition)
// - ADX / DMI trend strength (scoring condition)
// - ATR realized volatility
// - TTM Squeeze detection (BB/KC compression, scoring condition)
// - NYSE TICK Index breadth (6-tier, scoring condition #9)
// - Volume Footprint via request.footprint() (dashboard + optional
// scoring condition #10). Requires TradingView Premium/Ultimate.
// - Multi-timeframe confirmation: 1-min RSI, EMA trend, VWAP cross
// - Candle pattern detection (engulfing, strong close, hammer, shooting star)
// - Key levels: Prior Day H/L/C, Pre-Market H/L, Opening Range, HOD/LOD
// - Level proximity detection for scoring
// - Regime classifier (BULLISH / BEARISH / RANGING / NO TRADE)
// - VWAP cross markers (diamond shapes)
// - Signal cooldown (configurable bar count)
// - 21-row heads-up dashboard
// - Dual alert system: 9 alertcondition + 4 dynamic alert() calls
// ============================================================================
// ============================================================================
// TradingView Pine Script v6
// ============================================================================
//@version=6
indicator(
title = "SPY 0DTE Scalper - 5m",
shorttitle = "SPY-0-5m",
overlay = true,
max_labels_count = 500,
max_lines_count = 500,
max_boxes_count = 200,
max_bars_back = 2000
)
// ============================================================================
// CONSTANTS
// ============================================================================
var string GP_EMA = "EMA Ribbon"
var string GP_VWAP = "VWAP"
var string GP_RSI = "RSI"
var string GP_ADX = "ADX"
var string GP_ATR = "ATR"
var string GP_SQUEEZE = "TTM Squeeze"
var string GP_TICK = "NYSE TICK"
var string GP_LEVELS = "Key Levels"
var string GP_SESSION = "Session & Time"
var string GP_SIGNALS = "Signal Logic"
var string GP_MTF = "Multi-Timeframe"
var string GP_DASH = "Dashboard"
// Colors — vibrant, dark-theme optimized
var color C_VWAP = #FFFFFF
var color C_VWAP_BAND = #00BCD4
var color C_EMA_FAST = #FFD600
var color C_EMA_MID = #FF9100
var color C_EMA_SLOW = #FF1744
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_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_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_DASH_BG = color.new(#1A1A2E, 5)
var color C_DASH_HEADER = #7C4DFF
var color C_DASH_TEXT = #E0E0E0
var color C_GREEN = #00E676
var color C_RED = #FF1744
var color C_YELLOW = #FFD600
var color C_ORANGE = #FF9100
var color C_GRAY = #616161
var color C_NEUTRAL = #B0BEC5
// Footprint colors
var string GP_FOOTPRINT = "Volume Footprint"
var color C_FP_POC = #E040FB
var color C_FP_DELTA_P = #00E676
var color C_FP_DELTA_N = #FF1744
// ============================================================================
// INPUTS
// ============================================================================
// --- EMA Ribbon ---
i_emaFastLen = input.int(9, "Fast EMA Length", minval=2, group=GP_EMA)
i_emaMidLen = input.int(15, "Mid EMA Length", minval=2, group=GP_EMA)
i_emaSlowLen = input.int(21, "Slow EMA Length", minval=2, group=GP_EMA)
i_showEmaCloud = input.bool(true, "Show EMA Cloud Fill", group=GP_EMA)
// --- VWAP ---
i_showVwap = input.bool(true, "Show VWAP", group=GP_VWAP)
i_showVwapBands = input.bool(true, "Show VWAP +/- 1 StdDev", group=GP_VWAP)
i_vwapBandMult = input.float(1.0, "VWAP Band Multiplier", minval=0.5, maxval=3.0, step=0.25, group=GP_VWAP)
// --- RSI ---
i_rsiLen = input.int(10, "RSI Length", minval=2, group=GP_RSI)
i_rsiOB = input.int(65, "RSI Overbought", minval=50, maxval=95, group=GP_RSI)
i_rsiOS = input.int(35, "RSI Oversold", minval=5, maxval=50, group=GP_RSI)
// --- ADX ---
i_adxLen = input.int(14, "ADX Length", minval=2, group=GP_ADX)
i_adxNoTrend = input.int(18, "ADX No-Trend Threshold", minval=5, maxval=30, group=GP_ADX)
i_adxTrend = input.int(25, "ADX Trending Threshold", minval=15, maxval=50, group=GP_ADX)
i_adxStrong = input.int(40, "ADX Strong Trend Threshold", minval=25, maxval=60, group=GP_ADX)
// --- ATR ---
i_atrLen = input.int(14, "ATR Length", minval=2, group=GP_ATR)
// --- TTM Squeeze ---
i_useSqueeze = input.bool(true, "Enable TTM Squeeze Detection", group=GP_SQUEEZE,
tooltip="Detects Bollinger Band compression inside Keltner Channels — volatility squeeze precedes breakouts.")
i_sqzBBLen = input.int(20, "BB Length", minval=5, maxval=50, group=GP_SQUEEZE)
i_sqzBBMult = input.float(2.0, "BB Multiplier", minval=0.5, maxval=4.0, step=0.25, group=GP_SQUEEZE)
i_sqzKCLen = input.int(20, "KC Length", minval=5, maxval=50, group=GP_SQUEEZE)
i_sqzKCMult = input.float(1.5, "KC Multiplier", minval=0.5, maxval=4.0, step=0.25, group=GP_SQUEEZE)
// --- NYSE TICK ---
i_useTick = input.bool(true, "Enable NYSE TICK Index", group=GP_TICK,
tooltip="Pulls NYSE TICK at chart timeframe for market breadth confirmation. Requires RTH data.")
i_tickSymbol = input.string("USI:TICK", "TICK Symbol", group=GP_TICK,
options=["USI:TICK", "USI:TICK.NQ"])
i_tickBull = input.int(500, "TICK Bullish Threshold", minval=100, maxval=1000, step=50, group=GP_TICK)
i_tickBear = input.int(-500, "TICK Bearish Threshold", minval=-1000, maxval=-100, step=50, group=GP_TICK)
i_tickExtreme = input.int(800, "TICK Extreme Threshold", minval=500, maxval=1500, step=50, group=GP_TICK)
// --- Volume Footprint ---
i_useFootprint = input.bool(true, "Enable Volume Footprint", group=GP_FOOTPRINT,
tooltip="Requests volume footprint data for the current bar. Requires TradingView Premium or Ultimate plan. Returns buy/sell volume, delta, POC, VAH, VAL. NOTE: Footprint data repaints by design — real-time values may differ slightly from historical recalculations.")
i_fpTicksPerRow = input.int(25, "Ticks Per Footprint Row", minval=1, maxval=500, group=GP_FOOTPRINT,
tooltip="Number of price ticks per row. For SPY ($0.01 mintick), 25 = $0.25 per row. Lower = more granular but noisier.")
i_fpVaPct = input.int(70, "Value Area Percentage", minval=50, maxval=95, step=5, group=GP_FOOTPRINT)
i_fpShowPOC = input.bool(true, "Show POC Level", group=GP_FOOTPRINT,
tooltip="Plots the Point of Control (highest-volume price row) as a step line on the chart.")
i_fpUseScoring = input.bool(false, "Use Delta in Scoring (#10)", group=GP_FOOTPRINT,
tooltip="When ON, positive delta adds 1 point for CALLS, negative delta adds 1 point for PUTS. This is a REPAINTING condition — disabled by default for signal integrity. Max score becomes 10.")
// --- Key Levels ---
i_showPM = input.bool(true, "Show Pre-Market High/Low", group=GP_LEVELS)
i_showPD = input.bool(true, "Show Prior Day H/L/C", group=GP_LEVELS)
i_showOR = input.bool(true, "Show Opening Range", group=GP_LEVELS)
i_orMinutes = input.int(15, "Opening Range Minutes (rounded up to nearest 5-min bar)", minval=5, maxval=60, step=5, group=GP_LEVELS)
i_showHODLOD = input.bool(true, "Show Current Session HOD/LOD", group=GP_LEVELS)
// --- Session ---
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(3, "Signal Cooldown (bars)", minval=1, maxval=20, group=GP_SIGNALS)
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_showVwapCross = input.bool(true, "Show VWAP Cross Markers", group=GP_SIGNALS)
i_vwapExtFilter = input.float(2.0, "Max VWAP Distance (ATR multiples)", minval=0.5, maxval=5.0, step=0.25, group=GP_SIGNALS)
i_minScore = input.int(4, "Minimum Signal Score", minval=2, maxval=10, group=GP_SIGNALS,
tooltip="Minimum conditions for signal. Core: 5. Enhancements: MTF trend (#6), level proximity (#7), squeeze fired (#8), TICK confirms (#9), FP delta (#10 opt-in). Max: 10 with footprint.")
// --- Multi-Timeframe ---
i_useMTF = input.bool(true, "Enable 1-Min MTF Confirmation", group=GP_MTF,
tooltip="Pulls 1-minute RSI and EMA trend via request.security for an additional confirmation layer.")
i_mtfRsiLen = input.int(14, "1-Min RSI Length", minval=2, group=GP_MTF)
i_mtfEmaFast = input.int(8, "1-Min Fast EMA Length", minval=2, group=GP_MTF)
i_mtfEmaSlow = input.int(21, "1-Min Slow EMA Length", minval=2, group=GP_MTF)
// --- Dashboard ---
i_showDash = input.bool(true, "Show Dashboard", group=GP_DASH)
i_dashSize = input.string("small", "Dashboard Text Size", options=["tiny", "small", "normal", "large"], group=GP_DASH)
// ============================================================================
// UTILITY FUNCTIONS
// ============================================================================
// Map dashboard size string to size constant
getDashSize(string s) =>
switch s
"tiny" => size.tiny
"small" => size.small
"large" => size.large
=> size.normal
// Check if current bar falls within a given session
inSession(string sess) =>
not na(time(timeframe.period, sess, i_tz))
// Format a float to 2 decimal places
f2(float val) =>
str.tostring(val, "#.##")
// Return green for positive, red for negative
posnegColor(float val) =>
val >= 0 ? C_GREEN : C_RED
// ============================================================================
// CORE INDICATOR CALCULATIONS
// ============================================================================
// --- EMA Ribbon ---
emaFast = ta.ema(close, i_emaFastLen)
emaMid = ta.ema(close, i_emaMidLen)
emaSlow = ta.ema(close, i_emaSlowLen)
bool emaBullish = emaFast > emaMid and emaMid > emaSlow
bool emaBearish = emaFast < emaMid and emaMid < emaSlow
color emaCloudColor = emaBullish ? C_CLOUD_BULL : emaBearish ? C_CLOUD_BEAR : C_CLOUD_MIXED
// --- VWAP + Standard Deviation Bands ---
float vwapValue = ta.vwap(hlc3)
float vwapCumVol = ta.cum(volume)
float vwapCumPV = ta.cum(hlc3 * volume)
float vwapCumPV2 = ta.cum(hlc3 * hlc3 * volume)
float vwapMean = vwapCumPV / vwapCumVol
float vwapVar = math.max(vwapCumPV2 / vwapCumVol - vwapMean * vwapMean, 0)
float vwapStdev = math.sqrt(vwapVar)
float vwapUpper = vwapValue + vwapStdev * i_vwapBandMult
float vwapLower = vwapValue - vwapStdev * i_vwapBandMult
float vwapDist = close - vwapValue
// --- RSI ---
float rsiValue = ta.rsi(close, i_rsiLen)
getRsiLabel() =>
if rsiValue > i_rsiOB
"OVERBOUGHT"
else if rsiValue < i_rsiOS
"OVERSOLD"
else
"NEUTRAL"
getRsiColor() =>
if rsiValue > i_rsiOB
C_RED
else if rsiValue < i_rsiOS
C_GREEN
else
C_NEUTRAL
// --- ADX with DI+/DI- ---
[diPlus, diMinus, adxValue] = ta.dmi(i_adxLen, i_adxLen)
getAdxLabel() =>
if adxValue >= i_adxStrong
"STRONG TREND"
else if adxValue >= i_adxTrend
"TRENDING"
else if adxValue >= i_adxNoTrend
"WEAK/RANGING"
else
"NO TREND"
getAdxColor() =>
if adxValue >= i_adxTrend
C_GREEN
else if adxValue >= i_adxNoTrend
C_YELLOW
else
C_RED
// --- ATR ---
float atrValue = ta.atr(i_atrLen)
// ============================================================================
// TTM SQUEEZE DETECTION
// ============================================================================
// Bollinger Bands
float sqzBBBasis = ta.sma(close, i_sqzBBLen)
float sqzBBDev = ta.stdev(close, i_sqzBBLen) * i_sqzBBMult
float sqzBBUpper = sqzBBBasis + sqzBBDev
float sqzBBLower = sqzBBBasis - sqzBBDev
// Keltner Channels
float sqzKCBasis = ta.ema(close, i_sqzKCLen)
float sqzKCRange = ta.atr(i_sqzKCLen) * i_sqzKCMult
float sqzKCUpper = sqzKCBasis + sqzKCRange
float sqzKCLower = sqzKCBasis - sqzKCRange
// Squeeze state: BB inside KC = squeeze ON (compression)
bool squeezeOn = sqzBBUpper < sqzKCUpper and sqzBBLower > sqzKCLower
bool squeezeFired = not squeezeOn and squeezeOn[1]
// Track bars since last squeeze fired for recency context
var int sqzBarsSinceFire = 999
if squeezeFired
sqzBarsSinceFire := 0
else
sqzBarsSinceFire += 1
// Squeeze momentum histogram (linear regression of midline delta)
float sqzMomSource = close - math.avg(sqzBBBasis, sqzKCBasis)
float sqzMomentum = ta.linreg(sqzMomSource, i_sqzBBLen, 0)
// ============================================================================
// NYSE TICK INDEX
// ============================================================================
// Pull TICK at chart timeframe to avoid LTF bar multiplication.
// On 5-min chart, this returns the TICK close at each 5-min bar boundary —
// functionally equivalent for threshold-based scoring.
float tickValue = i_useTick ? request.security(i_tickSymbol, timeframe.period, close, lookahead=barmerge.lookahead_off) : na
// ============================================================================
// VOLUME FOOTPRINT DATA
// ============================================================================
// Dashboard + optional scoring condition (#10).
// request.footprint() counts toward the 40-call request.*() budget.
// Total request.*() calls after tuple consolidation: 4
// 1x chart TF (TICK), 1x "1" (MTF tuple), 1x "D" (daily tuple), 1x footprint
// CPU optimization v1.1.0: TICK moved from "1" to chart TF, eliminating 5x
// LTF bar multiplication. max_bars_back reduced 5000 → 2000.
footprint fpData = i_useFootprint ? request.footprint(i_fpTicksPerRow, i_fpVaPct) : na
// Bar-level metrics
float fpBuyVol = not na(fpData) ? fpData.buy_volume() : na
float fpSellVol = not na(fpData) ? fpData.sell_volume() : na
float fpDelta = not na(fpData) ? fpData.delta() : na
// POC midpoint price
float fpPocPrice = na
if not na(fpData)
volume_row pocRow = fpData.poc()
if not na(pocRow)
fpPocPrice := (pocRow.up_price() + pocRow.down_price()) / 2.0
// Derived
float fpTotalVol = nz(fpBuyVol) + nz(fpSellVol)
float fpDeltaPct = fpTotalVol > 0 ? (fpDelta / fpTotalVol) * 100.0 : na
bool fpAvailable = not na(fpData)
// Delta strength classification for dashboard
fpDeltaLabel() =>
if na(fpDelta)
"N/A"
else if math.abs(nz(fpDeltaPct)) > 40
fpDelta > 0 ? "STRONG BUY" : "STRONG SELL"
else if math.abs(nz(fpDeltaPct)) > 20
fpDelta > 0 ? "BUY LEAN" : "SELL LEAN"
else
"BALANCED"
fpDeltaColor() =>
if na(fpDelta)
C_GRAY
else if math.abs(nz(fpDeltaPct)) > 40
fpDelta > 0 ? C_FP_DELTA_P : C_FP_DELTA_N
else if math.abs(nz(fpDeltaPct)) > 20
fpDelta > 0 ? color.new(C_FP_DELTA_P, 30) : color.new(C_FP_DELTA_N, 30)
else
C_NEUTRAL
// ============================================================================
// MULTI-TIMEFRAME (1-MIN) DATA
// ============================================================================
// Consolidated into a single request.security() tuple — 1 pass through 1-min data instead of 5
// NOTE: mtfVwapCrossUp/Dn were previously pulled but never used in scoring, dashboard, or alerts — removed as dead code.
[_mtfRsi, _mtfEmaFastVal, _mtfEmaSlowVal] = request.security(
syminfo.tickerid, "1",
[ta.rsi(close, i_mtfRsiLen), ta.ema(close, i_mtfEmaFast), ta.ema(close, i_mtfEmaSlow)],
lookahead=barmerge.lookahead_off)
float mtfRsi = i_useMTF ? _mtfRsi : na
float mtfEmaFastVal = i_useMTF ? _mtfEmaFastVal : na
float mtfEmaSlowVal = i_useMTF ? _mtfEmaSlowVal : na
bool mtfBullish = not na(mtfEmaFastVal) and mtfEmaFastVal > mtfEmaSlowVal
bool mtfBearish = not na(mtfEmaFastVal) and mtfEmaFastVal < mtfEmaSlowVal
getMtfTrendLabel() =>
if mtfBullish
"BULLISH"
else if mtfBearish
"BEARISH"
else
"MIXED"
getMtfTrendColor() =>
if mtfBullish
C_GREEN
else if mtfBearish
C_RED
else
C_YELLOW
getMtfRsiColor() =>
if not na(mtfRsi) and mtfRsi > i_rsiOB
C_RED
else if not na(mtfRsi) and mtfRsi < i_rsiOS
C_GREEN
else
C_NEUTRAL
getMtfRsiLabel() =>
if not na(mtfRsi) and mtfRsi > i_rsiOB
"OVERBOUGHT"
else if not na(mtfRsi) and mtfRsi < i_rsiOS
"OVERSOLD"
else
"NEUTRAL"
// ============================================================================
// SESSION DETECTION & KEY LEVELS
// ============================================================================
bool isRTH = inSession(i_rthSession)
bool isPM = inSession(i_pmSession)
bool rthStart = isRTH and not isRTH[1]
// --- 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)
// --- Prior Day High / Low / Close + Current Day Open ---
// Consolidated into a single request.security() tuple — 1 pass through daily data instead of 4
[pdHigh, pdLow, pdClose, dayOpen] = request.security(
syminfo.tickerid, "D",
[high[1], low[1], close[1], open],
lookahead=barmerge.lookahead_on)
// --- Opening Range ---
// On 5-min chart, each bar = 5 minutes. OR bar count = ceil(orMinutes / 5).
var float orHigh = na
var float orLow = na
var bool orComplete = false
var int orBars = 0
int orBarsNeeded = math.ceil(i_orMinutes / 5)
if rthStart
orHigh := high
orLow := low
orComplete := false
orBars := 1
else if isRTH and not orComplete
orBars += 1
orHigh := math.max(orHigh, high)
orLow := math.min(orLow, low)
if orBars >= orBarsNeeded
orComplete := true
// --- Current Session HOD / LOD ---
var float sessionHigh = na
var float sessionLow = na
if rthStart
sessionHigh := high
sessionLow := low
else if isRTH
sessionHigh := math.max(nz(sessionHigh, high), high)
sessionLow := math.min(nz(sessionLow, low), low)
// ============================================================================
// REGIME / MODE ENGINE
// ============================================================================
getMode() =>
if adxValue < i_adxNoTrend and math.abs(vwapDist) < atrValue * 0.5
"NO TRADE"
else if adxValue < i_adxNoTrend
"RANGING"
else if adxValue >= i_adxNoTrend and emaBullish and close > vwapValue
"BULLISH"
else if adxValue >= i_adxNoTrend and emaBearish and close < vwapValue
"BEARISH"
else if adxValue >= i_adxTrend
"TRANSITION"
else
"RANGING"
string currentMode = getMode()
getModeColor() =>
switch currentMode
"BULLISH" => C_GREEN
"BEARISH" => C_RED
"RANGING" => C_YELLOW
"TRANSITION" => C_ORANGE
"NO TRADE" => C_GRAY
=> C_NEUTRAL
// ============================================================================
// SIGNAL LOGIC
// ============================================================================
// --- Shared filter conditions ---
bool inSignalWindow = inSession(i_signalStart)
bool adxFilter = not i_requireADX or adxValue >= i_adxNoTrend
bool vwapExtended = math.abs(vwapDist) > atrValue * i_vwapExtFilter
bool barConfirmed = barstate.isconfirmed
// --- Cooldown tracking ---
var int barsSinceSignal = 999
barsSinceSignal += 1
// --- Candle pattern detection (5-min optimized) ---
// Engulfing patterns (unchanged from 1-min)
bool bullishEngulfing = close > open and close > high[1] and open <= low[1]
bool bearishEngulfing = close < open and close < low[1] and open >= high[1]
// Strong close — body > 55% of range (raised from 50% for 5-min)
bool strongBullClose = close > open and (close - open) > (high - low) * 0.55
bool strongBearClose = close < open and (open - close) > (high - low) * 0.55
// Hammer / Shooting star — tail > 1.8x body (relaxed from 2.0 for 5-min)
bool hammer = close > open and (open - low) > (close - open) * 1.8 and (high - close) < (close - open) * 0.5
bool shootingStar = close < open and (high - open) > (open - close) * 1.8 and (close - low) < (open - close) * 0.5
// Inside bar breakout (NEW for 5-min) — prior bar is inside bar, current bar breaks out
bool priorInsideBar = high[1] < high[2] and low[1] > low[2]
bool insideBarBreakUp = priorInsideBar and close > high[1] and close > open
bool insideBarBreakDn = priorInsideBar and close < low[1] and close < open
// 3-bar momentum (NEW for 5-min) — 3 consecutive directional closes
bool threeBarMomentumUp = close > close[1] and close[1] > close[2] and close[2] > close[3]
bool threeBarMomentumDn = close < close[1] and close[1] < close[2] and close[2] < close[3]
// --- Level proximity detection (within 1.0 ATR) ---
bool nearSupport = (not na(pmLow) and math.abs(close - pmLow) < atrValue * 1.0) or
(not na(orLow) and orComplete and math.abs(close - orLow) < atrValue * 1.0) or
(not na(pdLow) and math.abs(close - pdLow) < atrValue * 1.0) or
(not na(sessionLow) and math.abs(close - sessionLow) < atrValue * 1.0)
bool nearResistance = (not na(pmHigh) and math.abs(close - pmHigh) < atrValue * 1.0) or
(not na(orHigh) and orComplete and math.abs(close - orHigh) < atrValue * 1.0) or
(not na(pdHigh) and math.abs(close - pdHigh) < atrValue * 1.0) or
(not na(sessionHigh) and math.abs(close - sessionHigh) < atrValue * 1.0)
// --- CALLS (bullish entry) scoring ---
bool callsGate = barConfirmed and inSignalWindow and adxFilter and not vwapExtended and barsSinceSignal >= i_cooldownBars
// Core conditions (1-5)
bool callsVwapOk = close > vwapValue or (close > vwapValue - atrValue * 0.3 and close[1] < vwapValue)
bool callsEmaOk = emaFast > emaSlow or (close > emaSlow and close[1] < emaSlow)
bool callsRsiOk = rsiValue > i_rsiOS and rsiValue < i_rsiOB
bool callsCandle = bullishEngulfing or strongBullClose or hammer or insideBarBreakUp or threeBarMomentumUp
bool callsAdxOk = adxValue >= i_adxNoTrend
// Enhancement conditions (6-9, optional)
bool callsMtfOk = i_useMTF and not na(mtfEmaFastVal) and mtfBullish
bool callsLevelOk = nearSupport
bool callsSqzOk = i_useSqueeze and (squeezeFired or sqzBarsSinceFire <= 3)
bool callsTickOk = i_useTick and not na(tickValue) and tickValue > i_tickBull
// Score calculation
int callsScore = 0
if callsVwapOk
callsScore += 1
if callsEmaOk
callsScore += 1
if callsRsiOk
callsScore += 1
if callsCandle
callsScore += 1
if callsAdxOk
callsScore += 1
if callsMtfOk
callsScore += 1
if callsLevelOk
callsScore += 1
if callsSqzOk
callsScore += 1
if callsTickOk
callsScore += 1
// Enhancement #10: Footprint Delta alignment (optional, repainting)
bool callsFpDeltaOk = i_useFootprint and i_fpUseScoring and not na(fpDelta) and fpDelta > 0
if callsFpDeltaOk
callsScore += 1
bool callsSignal = callsGate and callsScore >= i_minScore
// --- PUTS (bearish entry) scoring ---
bool putsGate = barConfirmed and inSignalWindow and adxFilter and not vwapExtended and barsSinceSignal >= i_cooldownBars
// Core conditions (1-5)
bool putsVwapOk = close < vwapValue or (close < vwapValue + atrValue * 0.3 and close[1] > vwapValue)
bool putsEmaOk = emaFast < emaSlow or (close < emaSlow and close[1] > emaSlow)
bool putsRsiOk = rsiValue > i_rsiOS and rsiValue < i_rsiOB
bool putsCandle = bearishEngulfing or strongBearClose or shootingStar or insideBarBreakDn or threeBarMomentumDn
bool putsAdxOk = adxValue >= i_adxNoTrend
// Enhancement conditions (6-9, optional)
bool putsMtfOk = i_useMTF and not na(mtfEmaFastVal) and mtfBearish
bool putsLevelOk = nearResistance
bool putsSqzOk = i_useSqueeze and (squeezeFired or sqzBarsSinceFire <= 3)
bool putsTickOk = i_useTick and not na(tickValue) and tickValue < i_tickBear
// Score calculation
int putsScore = 0
if putsVwapOk
putsScore += 1
if putsEmaOk
putsScore += 1
if putsRsiOk
putsScore += 1
if putsCandle
putsScore += 1
if putsAdxOk
putsScore += 1
if putsMtfOk
putsScore += 1
if putsLevelOk
putsScore += 1
if putsSqzOk
putsScore += 1
if putsTickOk
putsScore += 1
// Enhancement #10: Footprint Delta alignment (optional, repainting)
bool putsFpDeltaOk = i_useFootprint and i_fpUseScoring and not na(fpDelta) and fpDelta < 0
if putsFpDeltaOk
putsScore += 1
bool putsSignal = putsGate and putsScore >= i_minScore
// Reset cooldown on signal fire
if callsSignal or putsSignal
barsSinceSignal := 0
// --- VWAP cross detection ---
bool vwapCrossUp = ta.crossover(close, vwapValue)
bool vwapCrossDown = ta.crossunder(close, vwapValue)
// ============================================================================
// PLOTS — EMA RIBBON
// ============================================================================
pFast = plot(emaFast, "EMA Fast", C_EMA_FAST, linewidth=1)
pMid = plot(emaMid, "EMA Mid", C_EMA_MID, linewidth=1)
pSlow = plot(emaSlow, "EMA Slow", C_EMA_SLOW, linewidth=1)
fill(pFast, pSlow, color=i_showEmaCloud ? emaCloudColor : color.new(color.white, 100), title="EMA Cloud")
// ============================================================================
// PLOTS — VWAP + BANDS
// ============================================================================
plot(i_showVwap ? vwapValue : na, "VWAP", C_VWAP, linewidth=2)
plot(i_showVwapBands ? vwapUpper : na, "VWAP +1 SD", C_VWAP_BAND, linewidth=1, style=plot.style_circles)
plot(i_showVwapBands ? vwapLower : na, "VWAP -1 SD", C_VWAP_BAND, linewidth=1, style=plot.style_circles)
// --- Footprint POC Level ---
plot(i_useFootprint and i_fpShowPOC ? fpPocPrice : na,
"Footprint POC", C_FP_POC, linewidth=2, style=plot.style_stepline)
// ============================================================================
// KEY LEVELS — HORIZONTAL LINES WITH LABELS
// ============================================================================
// --- Pre-Market High / Low ---
var line pmHighLine = na
var line pmLowLine = na
var label pmHighLbl = na
var label pmLowLbl = na
if barstate.islast and i_showPM and not na(pmHigh) and isRTH
if not na(pmHighLine)
line.delete(pmHighLine)
line.delete(pmLowLine)
label.delete(pmHighLbl)
label.delete(pmLowLbl)
pmHighLine := line.new(bar_index - 60, pmHigh, bar_index + 5, pmHigh,
color=C_PM_HIGH, style=line.style_dashed, width=1)
pmLowLine := line.new(bar_index - 60, pmLow, bar_index + 5, pmLow,
color=C_PM_LOW, style=line.style_dashed, width=1)
pmHighLbl := label.new(bar_index + 7, pmHigh, "PM HIGH " + f2(pmHigh),
style=label.style_label_left, color=color.new(C_PM_HIGH, 80),
textcolor=C_PM_HIGH, size=size.small)
pmLowLbl := label.new(bar_index + 7, pmLow, "PM LOW " + f2(pmLow),
style=label.style_label_left, color=color.new(C_PM_LOW, 80),
textcolor=C_PM_LOW, size=size.small)
// --- Prior Day High / Low / Close ---
var line pdhLine = na
var line pdlLine = na
var line pdcLine = na
var label pdhLbl = na
var label pdlLbl = na
var label pdcLbl = na
if barstate.islast and i_showPD and not na(pdHigh)
if not na(pdhLine)
line.delete(pdhLine)
line.delete(pdlLine)
line.delete(pdcLine)
label.delete(pdhLbl)
label.delete(pdlLbl)
label.delete(pdcLbl)
pdhLine := line.new(bar_index - 60, pdHigh, bar_index + 5, pdHigh,
color=C_PDH, style=line.style_dotted, width=1)
pdlLine := line.new(bar_index - 60, pdLow, bar_index + 5, pdLow,
color=C_PDL, style=line.style_dotted, width=1)
pdcLine := line.new(bar_index - 60, pdClose, bar_index + 5, pdClose,
color=C_PDC, style=line.style_dotted, width=1)
pdhLbl := label.new(bar_index + 7, pdHigh, "PDH " + f2(pdHigh),
style=label.style_label_left, color=color.new(C_PDH, 80),
textcolor=C_PDH, size=size.small)
pdlLbl := label.new(bar_index + 7, pdLow, "PDL " + f2(pdLow),
style=label.style_label_left, color=color.new(C_PDL, 80),
textcolor=C_PDL, size=size.small)
pdcLbl := label.new(bar_index + 7, pdClose, "PDC " + f2(pdClose),
style=label.style_label_left, color=color.new(C_PDC, 80),
textcolor=C_PDC, size=size.small)
// --- Opening Range ---
var line orHighLine = na
var line orLowLine = na
var label orHighLbl = na
var label orLowLbl = na
if barstate.islast and i_showOR and orComplete and not na(orHigh)
if not na(orHighLine)
line.delete(orHighLine)
line.delete(orLowLine)
label.delete(orHighLbl)
label.delete(orLowLbl)
orHighLine := line.new(bar_index - 60, orHigh, bar_index + 5, orHigh,
color=C_OR_HIGH, style=line.style_dashed, width=1)
orLowLine := line.new(bar_index - 60, orLow, bar_index + 5, orLow,
color=C_OR_LOW, style=line.style_dashed, width=1)
orHighLbl := label.new(bar_index + 7, orHigh, "OR HIGH " + f2(orHigh),
style=label.style_label_left, color=color.new(C_OR_HIGH, 80),
textcolor=C_OR_HIGH, size=size.small)
orLowLbl := label.new(bar_index + 7, orLow, "OR LOW " + f2(orLow),
style=label.style_label_left, color=color.new(C_OR_LOW, 80),
textcolor=C_OR_LOW, size=size.small)
// --- Current Session HOD / LOD ---
var line hodLine = na
var line lodLine = na
var label hodLbl = na
var label lodLbl = na
if barstate.islast and i_showHODLOD and isRTH and not na(sessionHigh)
if not na(hodLine)
line.delete(hodLine)
line.delete(lodLine)
label.delete(hodLbl)
label.delete(lodLbl)
hodLine := line.new(bar_index - 60, sessionHigh, bar_index + 5, sessionHigh,
color=C_GREEN, style=line.style_solid, width=1)
lodLine := line.new(bar_index - 60, sessionLow, bar_index + 5, sessionLow,
color=C_RED, style=line.style_solid, width=1)
hodLbl := label.new(bar_index + 7, sessionHigh, "HOD " + f2(sessionHigh),
style=label.style_label_left, color=color.new(C_GREEN, 80),
textcolor=C_GREEN, size=size.small)
lodLbl := label.new(bar_index + 7, sessionLow, "LOD " + f2(sessionLow),
style=label.style_label_left, color=color.new(C_RED, 80),
textcolor=C_RED, size=size.small)
// ============================================================================
// SIGNAL VISUAL CUES
// ============================================================================
// Background highlight on signal bars
bgcolor(i_showSignals and callsSignal ? C_BG_CALLS : na, title="CALLS Background Flash")
bgcolor(i_showSignals and putsSignal ? C_BG_PUTS : na, title="PUTS Background Flash")
// Arrow shapes for fast scanning
plotshape(i_showSignals and callsSignal, title="CALLS Arrow", style=shape.triangleup,
location=location.belowbar, color=C_CALLS, size=size.tiny)
plotshape(i_showSignals and putsSignal, title="PUTS Arrow", style=shape.triangledown,
location=location.abovebar, color=C_PUTS, size=size.tiny)
// Detailed text labels with hover tooltip
if i_showSignals and callsSignal
string sqzTip = i_useSqueeze ? "\nSqueeze: " + (squeezeOn ? "ON" : squeezeFired ? "FIRED" : "OFF") : ""
string tickTip = i_useTick and not na(tickValue) ? "\nTICK: " + str.tostring(math.round(tickValue)) : ""
string tip = "Score: " + str.tostring(callsScore) + "/" + str.tostring(i_minScore) + "+" +
"\nRSI: " + f2(rsiValue) + " | ADX: " + f2(adxValue) +
"\nVWAP Dist: " + f2(vwapDist) + " | Mode: " + currentMode +
"\nATR: " + f2(atrValue) + " | EMA: " + (emaBullish ? "BULL" : emaBearish ? "BEAR" : "MIXED") +
(i_useMTF ? "\n1m Trend: " + getMtfTrendLabel() + " | 1m RSI: " + f2(nz(mtfRsi)) : "") +
sqzTip + tickTip
label.new(bar_index, low, "CALLS",
style=label.style_label_up, color=C_CALLS,
textcolor=color.black, size=size.small, tooltip=tip)
if i_showSignals and putsSignal
string sqzTip = i_useSqueeze ? "\nSqueeze: " + (squeezeOn ? "ON" : squeezeFired ? "FIRED" : "OFF") : ""
string tickTip = i_useTick and not na(tickValue) ? "\nTICK: " + str.tostring(math.round(tickValue)) : ""
string tip = "Score: " + str.tostring(putsScore) + "/" + str.tostring(i_minScore) + "+" +
"\nRSI: " + f2(rsiValue) + " | ADX: " + f2(adxValue) +
"\nVWAP Dist: " + f2(vwapDist) + " | Mode: " + currentMode +
"\nATR: " + f2(atrValue) + " | EMA: " + (emaBullish ? "BULL" : emaBearish ? "BEAR" : "MIXED") +
(i_useMTF ? "\n1m Trend: " + getMtfTrendLabel() + " | 1m RSI: " + f2(nz(mtfRsi)) : "") +
sqzTip + tickTip
label.new(bar_index, high, "PUTS",
style=label.style_label_down, color=C_PUTS,
textcolor=color.white, size=size.small, tooltip=tip)
// VWAP cross markers
plotshape(i_showVwapCross and vwapCrossUp and barConfirmed, title="VWAP Cross Bullish",
style=shape.diamond, location=location.belowbar, color=#76FF03, size=size.tiny)
plotshape(i_showVwapCross and vwapCrossDown and barConfirmed, title="VWAP Cross Bearish",
style=shape.diamond, location=location.abovebar, color=C_PUTS, size=size.tiny)
// ============================================================================
// DASHBOARD TABLE
// ============================================================================
var table dash = table.new(position.bottom_right, 3, 21,
bgcolor = C_DASH_BG,
border_width = 0,
border_color = color.new(#FFFFFF, 85),
frame_width = 1,
frame_color = color.new(#FFFFFF, 80))
// dayOpen already fetched in the consolidated daily tuple above
float avgVol = ta.sma(volume, 20)
if barstate.islast and i_showDash
string sz = i_dashSize
cellSize = getDashSize(sz)
// --- Row 0: Header ---
table.cell(dash, 0, 0, " SPY 0DTE Scalper - 5m ", text_color=#FFFFFF,
bgcolor=C_DASH_HEADER, text_size=cellSize, text_halign=text.align_center)
table.cell(dash, 1, 0, "", bgcolor=C_DASH_HEADER, text_size=cellSize)
table.cell(dash, 2, 0, "", bgcolor=C_DASH_HEADER, text_size=cellSize)
table.merge_cells(dash, 0, 0, 2, 0)
// --- Row 1: Price ---
color priceClr = close >= open ? C_GREEN : C_RED
string dayType = close >= open ? "Green Day" : "Red Day"
table.cell(dash, 0, 1, "Price", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 1, f2(close), text_color=priceClr, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 1, dayType, text_color=priceClr, text_size=cellSize, text_halign=text.align_right)
// --- Row 2: Mode ---
string mAdvice = currentMode == "NO TRADE" ? "AVOID" : currentMode == "RANGING" ? "CAUTION" : "ACTIVE"
table.cell(dash, 0, 2, "Mode", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 2, currentMode, text_color=getModeColor(), text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 2, mAdvice, text_color=getModeColor(), text_size=cellSize, text_halign=text.align_right)
// --- Row 3: RSI ---
table.cell(dash, 0, 3, "RSI (" + str.tostring(i_rsiLen) + ")",
text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 3, f2(rsiValue), text_color=getRsiColor(), text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 3, getRsiLabel(), text_color=getRsiColor(), text_size=cellSize, text_halign=text.align_right)
// --- Row 4: ADX ---
table.cell(dash, 0, 4, "ADX", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 4, f2(adxValue), text_color=getAdxColor(), text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 4, getAdxLabel(), text_color=getAdxColor(), text_size=cellSize, text_halign=text.align_right)
// --- Row 5: VWAP Distance ---
float absVD = math.abs(vwapDist)
color vdClr = absVD > atrValue ? C_RED : absVD > atrValue * 0.5 ? C_YELLOW : C_GREEN
string vdLbl = absVD > atrValue ? "EXTENDED" : "NEAR VWAP"
table.cell(dash, 0, 5, "VWAP Dist", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 5, f2(vwapDist), text_color=vdClr, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 5, vdLbl, text_color=vdClr, text_size=cellSize, text_halign=text.align_right)
// --- Row 6: PM High ---
if not na(pmHigh)
float pmhD = close - pmHigh
string pmhLbl = close < pmHigh ? "RESISTANCE" : "CLEARED"
color pmhClr = close < pmHigh ? C_RED : C_GREEN
table.cell(dash, 0, 6, "PM High", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 6, f2(pmhD), text_color=pmhClr, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 6, pmhLbl, text_color=pmhClr, text_size=cellSize, text_halign=text.align_right)
else
table.cell(dash, 0, 6, "PM High", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 6, "N/A", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 6, "", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
// --- Row 7: PM Low ---
if not na(pmLow)
float pmlD = close - pmLow
string pmlLbl = close > pmLow ? "SUPPORT" : "BROKEN"
color pmlClr = close > pmLow ? C_GREEN : C_RED
table.cell(dash, 0, 7, "PM Low", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 7, f2(pmlD), text_color=pmlClr, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 7, pmlLbl, text_color=pmlClr, text_size=cellSize, text_halign=text.align_right)
else
table.cell(dash, 0, 7, "PM Low", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 7, "N/A", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 7, "", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
// --- Row 8: ATR ---
table.cell(dash, 0, 8, "ATR (" + str.tostring(i_atrLen) + ")",
text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 8, f2(atrValue), text_color=C_NEUTRAL, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 8, "", text_size=cellSize)
// --- Row 9: EMA Trend ---
string emaTrStr = emaBullish ? "BULLISH" : emaBearish ? "BEARISH" : "MIXED"
color emaTrClr = emaBullish ? C_GREEN : emaBearish ? C_RED : C_YELLOW
table.cell(dash, 0, 9, "EMA Trend", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 9, emaTrStr, text_color=emaTrClr, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 9, "", text_size=cellSize)
// --- Row 10: Last Signal ---
string sigStr = callsSignal ? "CALLS" : putsSignal ? "PUTS" : "—"
color sigClr = callsSignal ? C_GREEN : putsSignal ? C_RED : C_GRAY
string sigScore = callsSignal ? str.tostring(callsScore) + "/9" : putsSignal ? str.tostring(putsScore) + "/9" : ""
table.cell(dash, 0, 10, "Signal", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 10, sigStr, text_color=sigClr, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 10, sigScore, text_color=sigClr, text_size=cellSize, text_halign=text.align_right)
// --- Row 11: Day Change % ---
float dayChg = not na(dayOpen) and dayOpen != 0 ? ((close - dayOpen) / dayOpen) * 100 : 0.0
string dcStr = (dayChg >= 0 ? "+" : "") + f2(dayChg) + "%"
table.cell(dash, 0, 11, "Day Chg", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 11, dcStr, text_color=posnegColor(dayChg), text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 11, "", text_size=cellSize)
// --- Row 12: Relative Volume ---
float rVol = avgVol > 0 ? volume / avgVol : 0.0
string rvStr = f2(rVol) + "x"
color rvClr = rVol > 1.5 ? C_GREEN : rVol > 1.0 ? C_YELLOW : C_GRAY
string rvLbl = rVol > 1.5 ? "SPIKE" : rVol > 1.0 ? "ABOVE AVG" : "BELOW AVG"
table.cell(dash, 0, 12, "Rel Vol", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 12, rvStr, text_color=rvClr, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 12, rvLbl, text_color=rvClr, text_size=cellSize, text_halign=text.align_right)
// --- Row 13: DI+/DI- Spread ---
float diSpread = diPlus - diMinus
string diStr = "+" + f2(diPlus) + " / -" + f2(diMinus)
color diClr = diSpread > 0 ? C_GREEN : C_RED
string diLbl = diSpread > 0 ? "BULLS" : "BEARS"
table.cell(dash, 0, 13, "DI Spread", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 13, diStr, text_color=diClr, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 13, diLbl, text_color=diClr, text_size=cellSize, text_halign=text.align_right)
// --- Row 14: 1m Trend (MTF) ---
if i_useMTF
table.cell(dash, 0, 14, "1m Trend", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 14, getMtfTrendLabel(), text_color=getMtfTrendColor(), text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 14, "", text_size=cellSize)
else
table.cell(dash, 0, 14, "1m Trend", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 14, "OFF", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 14, "", text_size=cellSize)
// --- Row 15: 1m RSI (MTF) ---
if i_useMTF and not na(mtfRsi)
table.cell(dash, 0, 15, "1m RSI", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 15, f2(mtfRsi), text_color=getMtfRsiColor(), text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 15, getMtfRsiLabel(), text_color=getMtfRsiColor(), text_size=cellSize, text_halign=text.align_right)
else
table.cell(dash, 0, 15, "1m RSI", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 15, i_useMTF ? "N/A" : "OFF", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 15, "", text_size=cellSize)
// --- Row 16: TTM Squeeze ---
if i_useSqueeze
string sqzStr = squeezeOn ? "ON" : squeezeFired ? "FIRED" : "OFF"
color sqzClr = squeezeOn ? C_ORANGE : squeezeFired ? C_GREEN : sqzBarsSinceFire <= 3 ? C_GREEN : C_GRAY
string sqzLbl = squeezeOn ? "BUILDING" : squeezeFired ? "BREAKOUT" : sqzBarsSinceFire <= 3 ? str.tostring(sqzBarsSinceFire) + " bars ago" : "NORMAL"
table.cell(dash, 0, 16, "Squeeze", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 16, sqzStr, text_color=sqzClr, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 16, sqzLbl, text_color=sqzClr, text_size=cellSize, text_halign=text.align_right)
else
table.cell(dash, 0, 16, "Squeeze", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 16, "OFF", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 16, "", text_size=cellSize)
// --- Row 17: NYSE TICK ---
if i_useTick and not na(tickValue)
float tv = tickValue
int tvRnd = math.round(tv)
string tvStr = str.tostring(tvRnd)
color tvClr = tv > i_tickExtreme ? C_GREEN : tv > i_tickBull ? C_GREEN : tv > 0 ? C_NEUTRAL : tv < -i_tickExtreme ? C_RED : tv < i_tickBear ? C_RED : tv < 0 ? C_NEUTRAL : C_GRAY
string tvLbl = tv > i_tickExtreme ? "EXTREME BULL" : tv > i_tickBull ? "BULLISH" : tv > 0 ? "MILD BULL" : tv < -i_tickExtreme ? "EXTREME BEAR" : tv < i_tickBear ? "BEARISH" : tv < 0 ? "MILD BEAR" : "FLAT"
table.cell(dash, 0, 17, "TICK", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 17, tvStr, text_color=tvClr, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 17, tvLbl, text_color=tvClr, text_size=cellSize, text_halign=text.align_right)
else
table.cell(dash, 0, 17, "TICK", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 17, i_useTick ? "N/A" : "OFF", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 17, "", text_size=cellSize)
// --- Row 18: Footprint Status ---
if i_useFootprint
string fpStat = fpAvailable ? "ACTIVE" + (i_fpUseScoring ? " | #10" : " | INFO") : "N/A"
table.cell(dash, 0, 18, "FP Status", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 18, fpStat, text_color=fpAvailable ? C_GREEN : C_GRAY, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 18, "", text_size=cellSize)
else
table.cell(dash, 0, 18, "FP Status", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 18, "OFF", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 18, "", text_size=cellSize)
// --- Row 19: Footprint Delta ---
if i_useFootprint and fpAvailable
string fpDStr = fpDeltaLabel() + " | " + str.tostring(math.round(nz(fpDelta))) + " (" + f2(nz(fpDeltaPct)) + "%)"
table.cell(dash, 0, 19, "FP Delta", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 19, fpDStr, text_color=fpDeltaColor(), text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 19, "", text_size=cellSize)
else
table.cell(dash, 0, 19, "FP Delta", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 19, i_useFootprint ? "N/A" : "OFF", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 19, "", text_size=cellSize)
// --- Row 20: Footprint POC ---
if i_useFootprint and not na(fpPocPrice)
table.cell(dash, 0, 20, "FP POC", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 20, f2(fpPocPrice), text_color=C_FP_POC, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 20, "", text_size=cellSize)
else
table.cell(dash, 0, 20, "FP POC", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_left)
table.cell(dash, 1, 20, i_useFootprint ? "N/A" : "OFF", text_color=C_GRAY, text_size=cellSize, text_halign=text.align_right)
table.cell(dash, 2, 20, "", text_size=cellSize)
// ============================================================================
// ALERTS — STANDARD (alertcondition) + DYNAMIC (alert)
// ============================================================================
// alertcondition() — appears in TradingView "Create Alert" dropdown
alertcondition(callsSignal, title="0DTE-5m: CALLS Signal",
message="CALLS signal fired on SPY (5m) — bullish confluence detected")
alertcondition(putsSignal, title="0DTE-5m: PUTS Signal",
message="PUTS signal fired on SPY (5m) — bearish confluence detected")
alertcondition(vwapCrossUp, title="0DTE-5m: VWAP Cross Bullish",
message="SPY price crossed above VWAP (5m)")
alertcondition(vwapCrossDown, title="0DTE-5m: VWAP Cross Bearish",
message="SPY price crossed below VWAP (5m)")
alertcondition(currentMode != currentMode[1], title="0DTE-5m: Regime Mode Change",
message="SPY regime mode has changed (5m)")
alertcondition(i_useSqueeze and squeezeFired, title="0DTE-5m: Squeeze Fired",
message="TTM Squeeze has fired (5m) — volatility expansion detected")
// alert() — dynamic messages with interpolated context
if callsSignal
string sqzCtx = i_useSqueeze ? " | Sqz: " + (squeezeOn ? "ON" : "OFF") : ""
string tickCtx = i_useTick and not na(tickValue) ? " | TICK: " + str.tostring(math.round(tickValue)) : ""
string fpCtx = i_useFootprint and not na(fpDelta) ? " | FP: " + fpDeltaLabel() + " (d:" + str.tostring(math.round(fpDelta)) + ")" + (not na(fpPocPrice) ? " POC:" + f2(fpPocPrice) : "") : ""
int maxScoreDisplay = 9 + (i_useFootprint and i_fpUseScoring ? 1 : 0)
alert("0DTE-5m CALLS | SPY " + f2(close) +
" | Score " + str.tostring(callsScore) + "/" + str.tostring(maxScoreDisplay) +
" | RSI " + f2(rsiValue) +
" | ADX " + f2(adxValue) +
" | VWAP Dist " + f2(vwapDist) +
" | Mode: " + currentMode +
(i_useMTF ? " | 1m: " + getMtfTrendLabel() : "") +
sqzCtx + tickCtx + fpCtx,
alert.freq_once_per_bar_close)
if putsSignal
string sqzCtx = i_useSqueeze ? " | Sqz: " + (squeezeOn ? "ON" : "OFF") : ""
string tickCtx = i_useTick and not na(tickValue) ? " | TICK: " + str.tostring(math.round(tickValue)) : ""
string fpCtx = i_useFootprint and not na(fpDelta) ? " | FP: " + fpDeltaLabel() + " (d:" + str.tostring(math.round(fpDelta)) + ")" + (not na(fpPocPrice) ? " POC:" + f2(fpPocPrice) : "") : ""
int maxScoreDisplay = 9 + (i_useFootprint and i_fpUseScoring ? 1 : 0)
alert("0DTE-5m PUTS | SPY " + f2(close) +
" | Score " + str.tostring(putsScore) + "/" + str.tostring(maxScoreDisplay) +
" | RSI " + f2(rsiValue) +
" | ADX " + f2(adxValue) +
" | VWAP Dist " + f2(vwapDist) +
" | Mode: " + currentMode +
(i_useMTF ? " | 1m: " + getMtfTrendLabel() : "") +
sqzCtx + tickCtx + fpCtx,
alert.freq_once_per_bar_close)
if currentMode != currentMode[1]
alert("0DTE-5m MODE SHIFT | " + str.tostring(currentMode[1]) + " -> " + currentMode +
" | SPY " + f2(close),
alert.freq_once_per_bar)
if i_useSqueeze and squeezeFired
alert("0DTE-5m SQUEEZE FIRED | SPY " + f2(close) +
" | Momentum: " + (sqzMomentum > 0 ? "BULLISH" : "BEARISH") +
" | ADX " + f2(adxValue) +
" | Mode: " + currentMode,
alert.freq_once_per_bar_close)
// Footprint delta alert conditions
alertcondition(
i_useFootprint and not na(fpDelta) and fpDelta > 0 and nz(fpDeltaPct) > 30,
title="0DTE-5m: Strong Positive FP Delta (>30%)",
message="SPY 5m: Strong positive footprint delta — aggressive buying dominant")
alertcondition(
i_useFootprint and not na(fpDelta) and fpDelta < 0 and nz(fpDeltaPct) < -30,
title="0DTE-5m: Strong Negative FP Delta (>30%)",
message="SPY 5m: Strong negative footprint delta — aggressive selling dominant")
// ============================================================================
// END OF SCRIPT
// ============================================================================
Overview
SPY 0DTE Scalper - 5m plots a 9/15/21 EMA ribbon with optional cloud fill, session-anchored VWAP and standard-deviation bands, and key levels such as prior day, pre-market, opening range, and session high and low. RSI, ADX, ATR, TTM squeeze, NYSE TICK, optional volume footprint, one-minute confirmation, and candle patterns feed the score. A regime label and dashboard summarize bias. It does not place trades or set exits.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Fast EMA Length
int
9
i_emaFastLen
Mid EMA Length
int
15
i_emaMidLen
Slow EMA Length
int
21
i_emaSlowLen
Show EMA Cloud Fill
bool
true
i_showEmaCloud
Show VWAP
bool
true
i_showVwap
Show VWAP +/- 1 StdDev
bool
true
i_showVwapBands
VWAP Band Multiplier
float
1.0
i_vwapBandMult
RSI Length
int
10
i_rsiLen
RSI Overbought
int
65
i_rsiOB
RSI Oversold
int
35
i_rsiOS
ADX Length
int
14
i_adxLen
ADX No-Trend Threshold
int
18
i_adxNoTrend
Show all 40 settings
Setting
Type
Default
Variable
ADX Trending Threshold
int
25
i_adxTrend
ADX Strong Trend Threshold
int
40
i_adxStrong
ATR Length
int
14
i_atrLen
Enable TTM Squeeze Detection
bool
true
i_useSqueeze
BB Length
int
20
i_sqzBBLen
BB Multiplier
float
2.0
i_sqzBBMult
KC Length
int
20
i_sqzKCLen
KC Multiplier
float
1.5
i_sqzKCMult
Enable NYSE TICK Index
bool
true
i_useTick
TICK Symbol
string
USI:TICK
i_tickSymbol
TICK Bullish Threshold
int
500
i_tickBull
TICK Bearish Threshold
int
-500
i_tickBear
TICK Extreme Threshold
int
800
i_tickExtreme
Enable Volume Footprint
bool
true
i_useFootprint
Ticks Per Footprint Row
int
25
i_fpTicksPerRow
Value Area Percentage
int
70
i_fpVaPct
Show POC Level
bool
true
i_fpShowPOC
Use Delta in Scoring (#10)
bool
false
i_fpUseScoring
Show Pre-Market High/Low
bool
true
i_showPM
Show Prior Day H/L/C
bool
true
i_showPD
Show Opening Range
bool
true
i_showOR
Opening Range Minutes (rounded up to nearest 5-min bar)
int
15
i_orMinutes
Show Current Session HOD/LOD
bool
true
i_showHODLOD
Pre-Market Session
session
0400-0930
i_pmSession
Regular Trading Hours
session
0930-1600
i_rthSession
Timezone
string
America/New_York
i_tz
Show CALLS / PUTS Signals
bool
true
i_showSignals
Signal Cooldown (bars)
int
3
i_cooldownBars
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.
This indicator scans NSE-listed stocks for momentum signals using Supertrend, multiple EMAs, MACD, and stochastic oscillator. It supports continuous or flip signal modes and displays matching setups in a customizable table at the bottom left of the chart. The
This indicator calculates pivot high and pivot low points on the price chart based on configurable left and right bar counts. Confirmed pivot highs and lows are plotted with negative bar offsets directly on the price chart using line and cross styles. Addition
This overlay indicator marks call and put setups from EMAs, RSI, MACD, and a SPY volume ratio. A call needs higher highs, RSI above its minimum, rising MACD, above-average proxy volume, and a rising trend EMA; a put reverses those tests. Early markers use an E
The Hedge Scalp indicator calculates an Exponential Moving Average (EMA) and generates four long grid levels and one hedged short grid level at user-defined percentage distances below the EMA. It models grid entry execution and average entry take-profit levels
This indicator collection calculates and plots previous daily high and low price levels on the chart to identify potential supply and demand zones. It includes real-time intraday high and low calculations, RSI divergence detection across a customizable period
This script provides an indicator-based backtesting engine framework that simulates trade entries, exits, and filtering directly on the price chart. It evaluates selected entry rules and multi-layered filters, including moving average trends, volume increases,