All-in-one — SPX Edge Dashboard
Single indicator that runs the whole strategy. Toggle each module on/off independently. A bottom-right status table shows the regime, yesterday's NR7 flag, today's gap class, and the recommended setup for each module — updated live as the day unfolds. Recommended for traders who want one TradingView indicator instead of three.
//@version=5
// =====================================================================
// SPX EDGE DASHBOARD — Master indicator
//
// One indicator that runs the whole strategy. Loads on a 5-min SPX chart
// and tells you, in real time:
//
// 1. Which REGIME we're in (StrongUp / Mixed / StrongDown)
// 2. Whether yesterday was an NR7 (premium-selling skip flag)
// 3. Today's GAP class (continuation vs flat vs counter-trend)
// 4. Which MODULES are active, and what setup is recommended
// 5. All the relevant levels & signals on chart
//
// Each of the three modules can be toggled independently in the inputs.
//
// Best used on: SPX, 5-min chart
// Also works on: ES, SPY, XSP and on 1/3/15-min timeframes
// Module 1 (swing) signals only fire on the daily timeframe — change the
// chart to Daily to see those.
// =====================================================================
indicator("SPX Edge Dashboard", overlay=true,
max_lines_count=500, max_labels_count=200)
// =====================================================================
// INPUTS
// =====================================================================
grpDisp = "Display"
showRegime = input.bool(true, "Tint background by regime", group=grpDisp)
panelPos = input.string("bottom_right", "Status panel position", options=["top_left","top_right","bottom_left","bottom_right","middle_right"], group=grpDisp)
panelSize = input.string("normal", "Panel text size", options=["tiny","small","normal","large"], group=grpDisp)
grpM1 = "Module 1 — Swing Long"
showM1 = input.bool(true, "Enable Module 1", group=grpM1)
showSMAs = input.bool(true, "Show 20 / 50 / 200 SMAs", group=grpM1)
showM1Signals = input.bool(true, "Show entry/exit markers", group=grpM1)
showNR7Mark = input.bool(true, "Mark NR7 days", group=grpM1)
grpM2 = "Module 2 — 0DTE Credit Spread"
showM2 = input.bool(true, "Enable Module 2", group=grpM2)
showLevels = input.bool(true, "Show prior-day projection levels", group=grpM2)
keepHistory = input.bool(true, "Keep historical days' levels", group=grpM2)
gapBigPct = input.float(0.5, "Big gap threshold (%)", step=0.1, group=grpM2)
gapSmallPct = input.float(0.1, "Small gap threshold (%)", step=0.05, group=grpM2)
grpM3 = "Module 3 — ORB Long"
showM3 = input.bool(true, "Enable Module 3", group=grpM3)
orMinutes = input.int(15, "Opening Range minutes", minval=5, maxval=60, step=5, group=grpM3)
sessStart = input.session("0930-1600", "Cash session (exchange time)", group=grpM3)
showOR = input.bool(true, "Show OR high/low", group=grpM3)
showM3Signal = input.bool(true, "Show ORB entry marker", group=grpM3)
// =====================================================================
// DAILY-CONTEXT DATA (yesterday's OHLC, daily SMAs, NR7)
// These return the most recent CLOSED daily values — safe for plotting.
// =====================================================================
priorHigh = request.security(syminfo.tickerid, "D", high[1], lookahead=barmerge.lookahead_on)
priorLow = request.security(syminfo.tickerid, "D", low[1], lookahead=barmerge.lookahead_on)
priorClose = request.security(syminfo.tickerid, "D", close[1], lookahead=barmerge.lookahead_on)
priorRange = priorHigh - priorLow
// daily SMAs (use yesterday's value so it's known at today's open)
dSMA20 = request.security(syminfo.tickerid, "D", ta.sma(close, 20)[1], lookahead=barmerge.lookahead_on)
dSMA50 = request.security(syminfo.tickerid, "D", ta.sma(close, 50)[1], lookahead=barmerge.lookahead_on)
dSMA200 = request.security(syminfo.tickerid, "D", ta.sma(close, 200)[1], lookahead=barmerge.lookahead_on)
aboveAll = priorClose > dSMA20 and priorClose > dSMA50 and priorClose > dSMA200
belowAll = priorClose < dSMA20 and priorClose < dSMA50 and priorClose < dSMA200
mixed = not aboveAll and not belowAll
regimeStr = aboveAll ? "StrongUp" : belowAll ? "StrongDown" : "Mixed"
// NR7: yesterday's range = smallest of last 7 daily ranges (as of yesterday's close)
priorRangeMin7 = request.security(syminfo.tickerid, "D",
ta.lowest(high - low, 7)[1],
lookahead=barmerge.lookahead_on)
isNR7 = not na(priorRange) and not na(priorRangeMin7) and priorRange == priorRangeMin7
// =====================================================================
// TODAY'S OPEN + GAP CLASS (captured at first bar of the day on intraday)
// =====================================================================
isNewDay = ta.change(time("D")) != 0
var float todayOpen = na
if isNewDay
todayOpen := open
gapPct = (todayOpen - priorClose) / priorClose * 100
gapClass = na(gapPct) ? "—" :
gapPct > gapBigPct ? "GapUp_Big" :
gapPct > gapSmallPct ? "GapUp_Small" :
gapPct < -gapBigPct ? "GapDn_Big" :
gapPct < -gapSmallPct ? "GapDn_Small" : "Flat"
// Distance from each SMA (in %)
distSMA20 = (priorClose - dSMA20) / dSMA20 * 100
distSMA50 = (priorClose - dSMA50) / dSMA50 * 100
distSMA200 = (priorClose - dSMA200) / dSMA200 * 100
// =====================================================================
// REGIME BACKGROUND
// =====================================================================
regimeColor = aboveAll ? color.new(color.green, 92) :
belowAll ? color.new(color.red, 92) :
color.new(color.gray, 95)
bgcolor(showRegime ? regimeColor : na, title="Regime")
// =====================================================================
// MODULE 1 — SMAs and signals (signals only fire on daily timeframe)
// =====================================================================
sma20 = ta.sma(close, 20)
sma50 = ta.sma(close, 50)
sma200 = ta.sma(close, 200)
plot(showM1 and showSMAs ? sma20 : na, "SMA 20", color=color.new(color.aqua, 0), linewidth=2)
plot(showM1 and showSMAs ? sma50 : na, "SMA 50", color=color.new(color.orange, 0), linewidth=2)
plot(showM1 and showSMAs ? sma200 : na, "SMA 200", color=color.new(color.purple, 0), linewidth=2)
// NR7 marker (fires on daily timeframe — current day's range vs lowest 7)
todayRange = high - low
nr7Today = todayRange == ta.lowest(todayRange, 7)
isDaily = timeframe.isdaily
plotshape(showM1 and showNR7Mark and isDaily and nr7Today, title="NR7",
style=shape.triangledown, location=location.abovebar,
color=color.new(color.yellow, 0), size=size.tiny)
// M1 entry/exit signals (daily timeframe only)
m1AboveAll = isDaily and (close > sma20 and close > sma50 and close > sma200)
pullbackLong = m1AboveAll and low <= sma20 and close > sma20
isFriday = dayofweek == dayofweek.friday
fridayLong = m1AboveAll and isFriday and barstate.isconfirmed
addAfterNR7 = m1AboveAll and nr7Today
plotshape(showM1 and showM1Signals and pullbackLong, title="M1: 20-DMA Pullback Long",
style=shape.triangleup, location=location.belowbar, color=color.new(color.lime, 0), size=size.small, text="20MA")
plotshape(showM1 and showM1Signals and fridayLong, title="M1: Friday StrongUp",
style=shape.triangleup, location=location.belowbar, color=color.new(color.green, 0), size=size.small, text="FRI")
plotshape(showM1 and showM1Signals and addAfterNR7, title="M1: Add after NR7",
style=shape.diamond, location=location.belowbar, color=color.new(color.aqua, 0), size=size.tiny, text="NR7+")
// M1 exits
exitBelow20 = isDaily and close < sma20 and close[1] >= sma20[1]
ret = (close - close[1]) / close[1] * 100
bigUp = isDaily and ret > 2.0
plotshape(showM1 and showM1Signals and exitBelow20, title="M1: Exit < 20-DMA",
style=shape.xcross, location=location.abovebar, color=color.new(color.red, 0), size=size.small, text="EXIT")
plotshape(showM1 and showM1Signals and bigUp, title="M1: Exit +2% day",
style=shape.xcross, location=location.abovebar, color=color.new(color.orange, 0), size=size.small, text="+2%")
// =====================================================================
// MODULE 2 — Per-day projection lines
// =====================================================================
var line lPDH = na
var line lPDL = na
var line lPDmid = na
var line lPDHp25 = na
var line lPDHp50 = na
var line lPDHp1 = na
var line lPDLm25 = na
var line lPDLm50 = na
var line lPDLm1 = na
cPDH = color.new(color.green, 0)
cPDL = color.new(color.red, 0)
cMid = color.new(color.gray, 50)
cP25 = color.new(color.green, 50)
cP50 = color.new(color.green, 30)
cP1 = color.new(color.green, 10)
cM25 = color.new(color.red, 50)
cM50 = color.new(color.red, 30)
cM1 = color.new(color.red, 10)
if showM2 and showLevels and isNewDay and not na(priorHigh)
if not keepHistory
line.delete(lPDH)
line.delete(lPDL)
line.delete(lPDmid)
line.delete(lPDHp25)
line.delete(lPDHp50)
line.delete(lPDHp1)
line.delete(lPDLm25)
line.delete(lPDLm50)
line.delete(lPDLm1)
midY = (priorHigh + priorLow) / 2
lPDH := line.new(bar_index, priorHigh, bar_index, priorHigh, color=cPDH, width=2)
lPDL := line.new(bar_index, priorLow, bar_index, priorLow, color=cPDL, width=2)
lPDmid := line.new(bar_index, midY, bar_index, midY, color=cMid, width=1, style=line.style_dotted)
lPDHp25 := line.new(bar_index, priorHigh + 0.25 * priorRange, bar_index, priorHigh + 0.25 * priorRange, color=cP25, width=1, style=line.style_dotted)
lPDHp50 := line.new(bar_index, priorHigh + 0.50 * priorRange, bar_index, priorHigh + 0.50 * priorRange, color=cP50, width=1, style=line.style_dotted)
lPDHp1 := line.new(bar_index, priorHigh + 1.00 * priorRange, bar_index, priorHigh + 1.00 * priorRange, color=cP1, width=1, style=line.style_dotted)
lPDLm25 := line.new(bar_index, priorLow - 0.25 * priorRange, bar_index, priorLow - 0.25 * priorRange, color=cM25, width=1, style=line.style_dotted)
lPDLm50 := line.new(bar_index, priorLow - 0.50 * priorRange, bar_index, priorLow - 0.50 * priorRange, color=cM50, width=1, style=line.style_dotted)
lPDLm1 := line.new(bar_index, priorLow - 1.00 * priorRange, bar_index, priorLow - 1.00 * priorRange, color=cM1, width=1, style=line.style_dotted)
if showM2 and showLevels and not isNewDay and not na(lPDH)
line.set_x2(lPDH, bar_index)
line.set_x2(lPDL, bar_index)
line.set_x2(lPDmid, bar_index)
line.set_x2(lPDHp25, bar_index)
line.set_x2(lPDHp50, bar_index)
line.set_x2(lPDHp1, bar_index)
line.set_x2(lPDLm25, bar_index)
line.set_x2(lPDLm50, bar_index)
line.set_x2(lPDLm1, bar_index)
// =====================================================================
// MODULE 3 — Opening Range + ORB long entry
// =====================================================================
inSession = not na(time(timeframe.period, sessStart))
isFirstBar = inSession and not inSession[1]
orBars = math.max(1, math.floor(orMinutes / math.max(timeframe.multiplier, 1)))
inOR = inSession and ta.barssince(isFirstBar) < orBars
var float orHigh = na
var float orLow = na
var bool orDone = false
var bool brokeUp = false
var float entryPx = na
var int entryBar = na
if isFirstBar
orHigh := high
orLow := low
orDone := false
brokeUp := false
entryPx := na
entryBar := na
if inOR and not isFirstBar
orHigh := math.max(orHigh, high)
orLow := math.min(orLow, low)
if inSession and not inOR and not orDone
orDone := true
m3Trigger = false
if orDone and inSession and not brokeUp and high > orHigh
brokeUp := true
entryPx := orHigh
entryBar := bar_index
m3Trigger := true
m3FilterOK = aboveAll and gapPct > -0.5
m3StrongFilter = m3FilterOK and todayOpen > priorHigh
plot(showM3 and showOR and orDone ? orHigh : na, "OR High",
color=color.new(color.aqua, 0), linewidth=2, style=plot.style_linebr)
plot(showM3 and showOR and orDone ? orLow : na, "OR Low",
color=color.new(color.fuchsia, 0), linewidth=2, style=plot.style_linebr)
m3EntryValid = m3Trigger and m3FilterOK
plotshape(showM3 and showM3Signal and m3EntryValid, title="M3: ORB Long Entry",
style=shape.triangleup, location=location.belowbar,
color=color.new(color.green, 0), size=size.normal, text="LONG")
// =====================================================================
// DECISION LOGIC — what to do today
// =====================================================================
// M1 status
m1Status = ""
m1Color = color.gray
if not showM1
m1Status := "off"
m1Color := color.gray
else if aboveAll
m1Status := isNR7 ? "ADD ½ unit (NR7)" : "ACTIVE — buy 20MA pullback"
m1Color := color.lime
else if mixed
m1Status := "Half size — pullbacks only"
m1Color := color.yellow
else
m1Status := "STAND DOWN (StrongDown)"
m1Color := color.red
// M2 status
m2Status = ""
m2Color = color.gray
if not showM2
m2Status := "off"
m2Color := color.gray
else if isNR7
m2Status := "🚫 SKIP — NR7 yesterday"
m2Color := color.orange
else if gapClass == "GapUp_Big" and aboveAll
m2Status := "PUT spread @ PDL−0.25R"
m2Color := color.lime
else if gapClass == "GapDn_Big" and belowAll
m2Status := "CALL spread @ PDH+0.25R"
m2Color := color.lime
else if (gapClass == "Flat" or gapClass == "GapUp_Small" or gapClass == "GapDn_Small") and (aboveAll or belowAll)
m2Status := "IRON CONDOR ±R"
m2Color := color.lime
else if (gapClass == "GapUp_Big" and belowAll) or (gapClass == "GapDn_Big" and aboveAll)
m2Status := "Counter-trend gap — half size"
m2Color := color.yellow
else
m2Status := "Mixed regime — half size"
m2Color := color.yellow
// M3 status
m3Status = ""
m3Color = color.gray
if not showM3
m3Status := "off"
m3Color := color.gray
else if not m3FilterOK
m3Status := belowAll ? "STAND DOWN (StrongDown)" :
gapPct < -0.5 ? "Skip — big gap-down" :
"Skip — filters failed"
m3Color := color.red
else if not orDone
m3Status := "⏳ OR forming…"
m3Color := color.silver
else if not brokeUp
m3Status := m3StrongFilter ? "Wait — open > PDH ✓ (high conviction)" : "Wait for break > OR High"
m3Color := color.aqua
else
m3Status := "📈 LONG entered " + str.tostring(entryPx, "#.##") + " — exit MOC"
m3Color := color.lime
// =====================================================================
// STATUS PANEL (table)
// =====================================================================
posMap = panelPos == "top_left" ? position.top_left :
panelPos == "top_right" ? position.top_right :
panelPos == "bottom_left" ? position.bottom_left :
panelPos == "middle_right" ? position.middle_right :
position.bottom_right
sizeMap = panelSize == "tiny" ? size.tiny :
panelSize == "small" ? size.small :
panelSize == "large" ? size.large :
size.normal
var table panel = table.new(posMap, 2, 13,
bgcolor=color.new(#0e1116, 8),
border_width=1, border_color=color.new(color.gray, 60),
frame_width=1, frame_color=color.new(color.gray, 40))
// Helpers
regimeFG = aboveAll ? color.lime : belowAll ? color.red : color.silver
nr7FG = isNR7 ? color.orange : color.silver
gapFG = gapClass == "GapUp_Big" ? color.lime :
gapClass == "GapDn_Big" ? color.red :
gapClass == "Flat" ? color.silver :
color.aqua
dSMAOK = aboveAll ? color.lime : belowAll ? color.red : color.yellow
if barstate.islast
// Header row
table.cell(panel, 0, 0, " SPX EDGE DASHBOARD ", text_color=color.white, bgcolor=color.new(#1F4E78, 0), text_size=sizeMap, text_halign=text.align_center)
table.cell(panel, 1, 0, " " + regimeStr + " ", text_color=regimeFG, bgcolor=color.new(#1F4E78, 0), text_size=sizeMap, text_halign=text.align_center)
// SMAs section
table.cell(panel, 0, 1, " 20-DMA ", text_color=color.silver, text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 1, " " + (priorClose > dSMA20 ? "▲ " : "▼ ") + str.tostring(distSMA20, "+0.0") + "% ", text_color=priorClose > dSMA20 ? color.lime : color.red, text_size=sizeMap, text_halign=text.align_right)
table.cell(panel, 0, 2, " 50-DMA ", text_color=color.silver, text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 2, " " + (priorClose > dSMA50 ? "▲ " : "▼ ") + str.tostring(distSMA50, "+0.0") + "% ", text_color=priorClose > dSMA50 ? color.lime : color.red, text_size=sizeMap, text_halign=text.align_right)
table.cell(panel, 0, 3, " 200-DMA ", text_color=color.silver, text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 3, " " + (priorClose > dSMA200 ? "▲ " : "▼ ") + str.tostring(distSMA200, "+0.0") + "% ", text_color=priorClose > dSMA200 ? color.lime : color.red, text_size=sizeMap, text_halign=text.align_right)
// Yesterday section
table.cell(panel, 0, 4, " Yesterday ", text_color=color.aqua, bgcolor=color.new(#1c232c, 0), text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 4, "", bgcolor=color.new(#1c232c, 0))
table.cell(panel, 0, 5, " Range R ", text_color=color.silver, text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 5, " " + str.tostring(priorRange, "#.##") + " ", text_color=color.white, text_size=sizeMap, text_halign=text.align_right)
table.cell(panel, 0, 6, " NR7? ", text_color=color.silver, text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 6, " " + (isNR7 ? "YES — skip M2" : "no") + " ", text_color=nr7FG, text_size=sizeMap, text_halign=text.align_right)
// Today section
table.cell(panel, 0, 7, " Today ", text_color=color.aqua, bgcolor=color.new(#1c232c, 0), text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 7, "", bgcolor=color.new(#1c232c, 0))
table.cell(panel, 0, 8, " Gap ", text_color=color.silver, text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 8, " " + str.tostring(gapPct, "+0.00") + "% " + gapClass + " ", text_color=gapFG, text_size=sizeMap, text_halign=text.align_right)
// Modules section
table.cell(panel, 0, 9, " Active modules ", text_color=color.aqua, bgcolor=color.new(#1c232c, 0), text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 9, "", bgcolor=color.new(#1c232c, 0))
table.cell(panel, 0, 10, " M1 Swing Long ", text_color=color.white, text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 10, " " + m1Status + " ", text_color=m1Color, text_size=sizeMap, text_halign=text.align_right)
table.cell(panel, 0, 11, " M2 0DTE Cr Spread ", text_color=color.white, text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 11, " " + m2Status + " ", text_color=m2Color, text_size=sizeMap, text_halign=text.align_right)
table.cell(panel, 0, 12, " M3 ORB Long ", text_color=color.white, text_size=sizeMap, text_halign=text.align_left)
table.cell(panel, 1, 12, " " + m3Status + " ", text_color=m3Color, text_size=sizeMap, text_halign=text.align_right)
// =====================================================================
// ALERTS — one named alert per actionable signal
// =====================================================================
alertcondition(pullbackLong, "M1: 20-DMA Pullback Long", "M1: SPX touched 20-DMA in StrongUp regime — long entry")
alertcondition(fridayLong, "M1: Friday StrongUp Long", "M1: Friday close in StrongUp — long entry")
alertcondition(addAfterNR7, "M1: Add after NR7", "M1: NR7 in regime — add half unit")
alertcondition(exitBelow20, "M1: Exit below 20-DMA", "M1: Close below 20-DMA — exit long")
alertcondition(bigUp, "M1: Exit +2% day", "M1: +2% single-day move — mean-revert exit")
alertcondition(isNR7, "M2: SKIP NR7", "M2: NR7 detected — skip 0DTE today")
alertcondition(gapClass == "GapUp_Big" and aboveAll, "M2: Sell PUT spread", "M2: Big gap-up + StrongUp — sell put credit spread below PDL")
alertcondition(gapClass == "GapDn_Big" and belowAll, "M2: Sell CALL spread", "M2: Big gap-down + StrongDown — sell call credit spread above PDH")
alertcondition((gapClass == "Flat" or gapClass == "GapUp_Small" or gapClass == "GapDn_Small") and (aboveAll or belowAll), "M2: Iron Condor", "M2: Flat / small gap — iron condor at PDH+0.25R / PDL-0.25R")
alertcondition(m3EntryValid, "M3: ORB Long Entry", "M3: ORB break above OR high in StrongUp regime — long entry, exit MOC")
Module 1 — Swing Long
Daily chart. Long-only trend-regime continuation. Plots 20/50/200-day SMAs, regime background, NR7 markers, plus pullback / Friday-in-regime / NR7-add entry triangles and close-below-20DMA / +2% exit X's.
//@version=5
// =====================================================================
// SPX Module 1 — Swing Long
// Trend-regime momentum system. Long-only.
//
// Signals plotted:
// • Background colour — regime (StrongUp / StrongDown / Mixed)
// • SMAs — 20 / 50 / 200 day
// • Yellow ▽ above bar — NR7 (today's range = smallest of last 7)
// • Lime ▲ "20MA" — pullback to 20-DMA in StrongUp regime
// • Green ▲ "FRI" — Friday close in StrongUp regime
// • Aqua ◇ "NR7+" — add half unit after NR7 in regime
// • Red ✕ "EXIT" — close below 20-DMA from above
// • Orange ✕ "+2%" — single-day +2% move (mean-revert exit)
// =====================================================================
indicator("SPX Module 1 — Swing Long", overlay=true, max_labels_count=200)
// === Inputs ===
showSMAs = input.bool(true, "Show SMAs (20 / 50 / 200)")
showRegimeBG = input.bool(true, "Tint background by regime")
showNR7 = input.bool(true, "Mark NR7 days")
showEntries = input.bool(true, "Show entry signals")
showExits = input.bool(true, "Show exit signals")
// === Regime ===
sma20 = ta.sma(close, 20)
sma50 = ta.sma(close, 50)
sma200 = ta.sma(close, 200)
aboveAll = close > sma20 and close > sma50 and close > sma200
belowAll = close < sma20 and close < sma50 and close < sma200
mixed = not aboveAll and not belowAll
regimeColor = aboveAll ? color.new(color.green, 92) : belowAll ? color.new(color.red, 92) : color.new(color.gray, 95)
bgcolor(showRegimeBG ? regimeColor : na, title="Regime")
// === SMA plots ===
plot(showSMAs ? sma20 : na, "SMA20", color=color.new(color.aqua, 0), linewidth=2)
plot(showSMAs ? sma50 : na, "SMA50", color=color.new(color.orange, 0), linewidth=2)
plot(showSMAs ? sma200 : na, "SMA200", color=color.new(color.purple, 0), linewidth=2)
// === NR7 ===
todayRange = high - low
nr7 = todayRange == ta.lowest(todayRange, 7)
plotshape(showNR7 and nr7, title="NR7", style=shape.triangledown, location=location.abovebar, color=color.new(color.yellow, 0), size=size.tiny)
// === Entry signals ===
// (1) Pullback to 20-DMA inside StrongUp regime
pullbackLong = aboveAll and low <= sma20 and close > sma20
plotshape(showEntries and pullbackLong, title="20-DMA Pullback Long",
style=shape.triangleup, location=location.belowbar,
color=color.new(color.lime, 0), size=size.small, text="20MA")
// (2) Friday close in StrongUp
isFriday = dayofweek == dayofweek.friday
fridayLong = aboveAll and isFriday and barstate.isconfirmed
plotshape(showEntries and fridayLong, title="Friday StrongUp Long",
style=shape.triangleup, location=location.belowbar,
color=color.new(color.green, 0), size=size.small, text="FRI")
// (3) Add half-unit after NR7 in regime
addAfterNR7 = aboveAll and nr7
plotshape(showEntries and addAfterNR7, title="Add after NR7",
style=shape.diamond, location=location.belowbar,
color=color.new(color.aqua, 0), size=size.tiny, text="NR7+")
// === Exit signals ===
exitBelow20 = close < sma20 and close[1] >= sma20[1]
plotshape(showExits and exitBelow20, title="Exit: close < 20-DMA",
style=shape.xcross, location=location.abovebar,
color=color.new(color.red, 0), size=size.small, text="EXIT")
ret = (close - close[1]) / close[1] * 100
bigUp = ret > 2.0
plotshape(showExits and bigUp, title="Exit: +2% day",
style=shape.xcross, location=location.abovebar,
color=color.new(color.orange, 0), size=size.small, text="+2%")
// === Status label on most recent bar ===
var label statusLbl = na
if barstate.islast
label.delete(statusLbl)
regimeStr = aboveAll ? "STRONG UP" : belowAll ? "STRONG DOWN" : "MIXED"
qualifier = nr7 ? " | NR7" : ""
statusLbl := label.new(bar_index, high, text="Module 1 — " + regimeStr + qualifier,
style=label.style_label_down,
color=color.new(color.black, 20),
textcolor=color.white, size=size.normal)
// === Alerts ===
alertcondition(pullbackLong, "M1: 20-DMA Pullback Long", "M1: SPX touched 20-DMA in StrongUp — long entry")
alertcondition(fridayLong, "M1: Friday StrongUp Long", "M1: Friday close in StrongUp — long entry")
alertcondition(addAfterNR7, "M1: Add after NR7", "M1: NR7 in regime — add half unit")
alertcondition(exitBelow20, "M1: Exit below 20-DMA", "M1: Close below 20-DMA — exit long")
alertcondition(bigUp, "M1: Exit +2% day", "M1: +2% single-day — mean-revert exit")
Module 2 — 0DTE Credit Spread
Intraday chart (5-min or 15-min). Plots prior-day projection levels (PDH, PDL, mid, ±0.25R/0.5R/1R) as discrete per-day segments, regime tint, and an always-visible bottom-left status panel that recommends today's structure (put spread / call spread / iron condor / skip).
//@version=5
// =====================================================================
// SPX Module 2 — 0DTE Credit Spread Setup Identifier
//
// Per-day prior-range projection levels + always-visible setup panel.
//
// What it plots:
// • Background tint — daily regime (StrongUp / StrongDown / Mixed)
// • PDH / PDL — prior-day high (green) / prior-day low (red)
// • PD Mid — prior-day midpoint (dotted grey)
// • +0.25R / +0.5R / +1R — projections above PDH (faint green dotted)
// • -0.25R / -0.5R / -1R — projections below PDL (faint red dotted)
// • Bottom-left table — regime + gap class + suggested structure,
// always visible on top of bars
//
// R = prior day's range (PDH − PDL).
// Each day's lines are isolated to that trading day — they do NOT
// connect across days.
//
// Best used on a 5-min or 15-min intraday SPX chart.
// =====================================================================
indicator("SPX Module 2 — 0DTE Credit Spread", overlay=true,
max_lines_count=500, max_labels_count=10)
// =====================================================================
// Inputs
// =====================================================================
showLevels = input.bool(true, "Show prior-day projection levels")
showRegime = input.bool(true, "Tint background by regime")
showSetupTbl = input.bool(true, "Show suggested-setup panel")
keepHistory = input.bool(true, "Keep historical days' levels on chart")
gapBigPct = input.float(0.5, "Big gap threshold (%)", step=0.1)
gapSmallPct = input.float(0.1, "Small gap threshold (%)", step=0.05)
// =====================================================================
// Daily data (prior-day OHLC + daily SMAs + NR7 flag)
// =====================================================================
priorHigh = request.security(syminfo.tickerid, "D", high[1], lookahead=barmerge.lookahead_on)
priorLow = request.security(syminfo.tickerid, "D", low[1], lookahead=barmerge.lookahead_on)
priorClose = request.security(syminfo.tickerid, "D", close[1], lookahead=barmerge.lookahead_on)
priorRange = priorHigh - priorLow
dSMA20 = request.security(syminfo.tickerid, "D", ta.sma(close, 20)[1], lookahead=barmerge.lookahead_on)
dSMA50 = request.security(syminfo.tickerid, "D", ta.sma(close, 50)[1], lookahead=barmerge.lookahead_on)
dSMA200 = request.security(syminfo.tickerid, "D", ta.sma(close, 200)[1], lookahead=barmerge.lookahead_on)
aboveAll = priorClose > dSMA20 and priorClose > dSMA50 and priorClose > dSMA200
belowAll = priorClose < dSMA20 and priorClose < dSMA50 and priorClose < dSMA200
priorRangeMin7 = request.security(syminfo.tickerid, "D",
ta.lowest(high - low, 7)[1],
lookahead=barmerge.lookahead_on)
isNR7 = not na(priorRange) and not na(priorRangeMin7) and priorRange == priorRangeMin7
// =====================================================================
// Day boundary + today's open + gap classification
// =====================================================================
isNewDay = ta.change(time("D")) != 0
var float todayOpen = na
if isNewDay
todayOpen := open
gapPct = (todayOpen - priorClose) / priorClose * 100
gapClass = gapPct > gapBigPct ? "GapUp_Big" :
gapPct > gapSmallPct ? "GapUp_Small" :
gapPct < -gapBigPct ? "GapDn_Big" :
gapPct < -gapSmallPct ? "GapDn_Small" : "Flat"
// =====================================================================
// Background regime tint
// =====================================================================
regimeColor = aboveAll ? color.new(color.green, 92) :
belowAll ? color.new(color.red, 92) :
color.new(color.gray, 95)
bgcolor(showRegime ? regimeColor : na, title="Regime")
// =====================================================================
// Per-day projection LINES (drawn fresh each day, not connected across days)
// =====================================================================
var line lPDH = na
var line lPDL = na
var line lPDmid = na
var line lPDHp25 = na
var line lPDHp50 = na
var line lPDHp1 = na
var line lPDLm25 = na
var line lPDLm50 = na
var line lPDLm1 = na
cPDH = color.new(color.green, 0)
cPDL = color.new(color.red, 0)
cMid = color.new(color.gray, 50)
cP25 = color.new(color.green, 50)
cP50 = color.new(color.green, 30)
cP1 = color.new(color.green, 10)
cM25 = color.new(color.red, 50)
cM50 = color.new(color.red, 30)
cM1 = color.new(color.red, 10)
// On a NEW day: optionally delete prior lines, then create new ones for today
if showLevels and isNewDay and not na(priorHigh)
if not keepHistory
line.delete(lPDH)
line.delete(lPDL)
line.delete(lPDmid)
line.delete(lPDHp25)
line.delete(lPDHp50)
line.delete(lPDHp1)
line.delete(lPDLm25)
line.delete(lPDLm50)
line.delete(lPDLm1)
midY = (priorHigh + priorLow) / 2
lPDH := line.new(bar_index, priorHigh, bar_index, priorHigh, color=cPDH, width=2)
lPDL := line.new(bar_index, priorLow, bar_index, priorLow, color=cPDL, width=2)
lPDmid := line.new(bar_index, midY, bar_index, midY, color=cMid, width=1, style=line.style_dotted)
lPDHp25 := line.new(bar_index, priorHigh + 0.25 * priorRange, bar_index, priorHigh + 0.25 * priorRange, color=cP25, width=1, style=line.style_dotted)
lPDHp50 := line.new(bar_index, priorHigh + 0.50 * priorRange, bar_index, priorHigh + 0.50 * priorRange, color=cP50, width=1, style=line.style_dotted)
lPDHp1 := line.new(bar_index, priorHigh + 1.00 * priorRange, bar_index, priorHigh + 1.00 * priorRange, color=cP1, width=1, style=line.style_dotted)
lPDLm25 := line.new(bar_index, priorLow - 0.25 * priorRange, bar_index, priorLow - 0.25 * priorRange, color=cM25, width=1, style=line.style_dotted)
lPDLm50 := line.new(bar_index, priorLow - 0.50 * priorRange, bar_index, priorLow - 0.50 * priorRange, color=cM50, width=1, style=line.style_dotted)
lPDLm1 := line.new(bar_index, priorLow - 1.00 * priorRange, bar_index, priorLow - 1.00 * priorRange, color=cM1, width=1, style=line.style_dotted)
// Within the day: extend each line's right end to the current bar
if showLevels and not isNewDay and not na(lPDH)
line.set_x2(lPDH, bar_index)
line.set_x2(lPDL, bar_index)
line.set_x2(lPDmid, bar_index)
line.set_x2(lPDHp25, bar_index)
line.set_x2(lPDHp50, bar_index)
line.set_x2(lPDHp1, bar_index)
line.set_x2(lPDLm25, bar_index)
line.set_x2(lPDLm50, bar_index)
line.set_x2(lPDLm1, bar_index)
// =====================================================================
// Suggested-setup logic
// =====================================================================
var string setupHeader = ""
var string setupBody = ""
setupColor = color.lime
if isNR7
setupHeader := "🚫 SKIP — NR7 day yesterday"
setupBody := "96.6% chance today expands beyond prior range"
setupColor := color.orange
else if gapClass == "GapUp_Big" and aboveAll
setupHeader := "✓ PUT credit spread"
setupBody := "Short strike near PDL − 0.25R\n(only ~10% of big-gap-up days touch PDL)"
setupColor := color.lime
else if gapClass == "GapDn_Big" and belowAll
setupHeader := "✓ CALL credit spread"
setupBody := "Short strike near PDH + 0.25R\n(only ~10% of big-gap-down days touch PDH)"
setupColor := color.lime
else if (gapClass == "Flat" or gapClass == "GapUp_Small" or gapClass == "GapDn_Small") and (aboveAll or belowAll)
setupHeader := "✓ IRON CONDOR"
setupBody := "Shorts at PDH + 0.25R and PDL − 0.25R\nWidth ≥ ±1% spot, target 50% credit"
setupColor := color.lime
else if gapClass == "GapUp_Big" and belowAll
setupHeader := "⚠ Counter-trend gap up"
setupBody := "Gap UP into StrongDown — reduce or skip"
setupColor := color.yellow
else if gapClass == "GapDn_Big" and aboveAll
setupHeader := "⚠ Counter-trend gap down"
setupBody := "Gap DOWN into StrongUp — reduce or skip"
setupColor := color.yellow
else
setupHeader := "⚖ Mixed regime"
setupBody := "Half size or skip"
setupColor := color.silver
// =====================================================================
// Status TABLE — bottom-left, always on top of bars
// =====================================================================
var table panel = table.new(position.bottom_left, 1, 7,
bgcolor=color.new(#0e1116, 5),
border_width=1, border_color=color.new(color.gray, 60),
frame_width=1, frame_color=color.new(color.gray, 40))
if barstate.islast and showSetupTbl
regimeStr = aboveAll ? "StrongUp" : belowAll ? "StrongDown" : "Mixed"
regimeFG = aboveAll ? color.lime : belowAll ? color.red : color.silver
table.cell(panel, 0, 0, " Module 2 — 0DTE Credit Spread ",
text_color=color.white, bgcolor=color.new(#1F4E78, 0),
text_size=size.normal, text_halign=text.align_center)
table.cell(panel, 0, 1, " Regime: " + regimeStr + (isNR7 ? " | NR7" : "") + " ",
text_color=regimeFG, text_size=size.small, text_halign=text.align_left)
table.cell(panel, 0, 2, " Gap: " + gapClass + " (" + str.tostring(gapPct, "#.##") + "%) ",
text_color=color.white, text_size=size.small, text_halign=text.align_left)
table.cell(panel, 0, 3, " PDH: " + str.tostring(priorHigh, "#.##") + " PDL: " + str.tostring(priorLow, "#.##") + " R: " + str.tostring(priorRange, "#.##") + " ",
text_color=color.white, text_size=size.small, text_halign=text.align_left)
table.cell(panel, 0, 4, " ──────────────────────── ",
text_color=color.gray, text_size=size.small, text_halign=text.align_center)
table.cell(panel, 0, 5, " " + setupHeader + " ",
text_color=setupColor, text_size=size.normal, text_halign=text.align_left)
table.cell(panel, 0, 6, " " + setupBody + " ",
text_color=color.new(color.white, 20), text_size=size.small, text_halign=text.align_left)
// =====================================================================
// Alerts
// =====================================================================
alertcondition(isNR7, "M2: SKIP NR7", "M2: NR7 detected — skip 0DTE today")
alertcondition(gapClass == "GapUp_Big" and aboveAll, "M2: Sell PUT spread", "M2: Big gap-up + StrongUp — sell put credit spread below PDL")
alertcondition(gapClass == "GapDn_Big" and belowAll, "M2: Sell CALL spread", "M2: Big gap-down + StrongDown — sell call credit spread above PDH")
alertcondition((gapClass == "Flat" or gapClass == "GapUp_Small" or gapClass == "GapDn_Small") and (aboveAll or belowAll), "M2: Iron Condor", "M2: Flat / small gap — iron condor at PDH+0.25R / PDL-0.25R")
Module 3 — ORB Long
5-min chart. Auto-detects the 15-min opening range, applies regime + gap + open-above-PDH filters, and only fires the "LONG" entry shape if all filters pass. Stop = OR low. Exit at MOC.
//@version=5
// =====================================================================
// SPX Module 3 — Opening Range Breakout (Long Only)
//
// What it plots:
// • Background tint — daily regime
// • Aqua / fuchsia line — 15-min OR high / low (drawn after OR completes)
// • Green ▲ "LONG" — first break above OR high, only when filters pass
// • Status label — regime, gap, OR levels, setup status
//
// Filter rules built in:
// • Regime must be StrongUp (above 20 / 50 / 200 daily SMA)
// • Gap must NOT be a big gap-down (gapPct ≥ -0.5%)
// • "High-conviction" tag if today's open prints above prior-day high
//
// Best used on a 5-min SPX chart.
// =====================================================================
indicator("SPX Module 3 — ORB Long", overlay=true, max_labels_count=10)
// === Inputs ===
orMinutes = input.int(15, "Opening Range minutes", minval=5, maxval=60, step=5)
sessStart = input.session("0930-1600", "Cash session (exchange time)")
showRegime = input.bool(true, "Tint background by regime")
showOR = input.bool(true, "Show OR high/low")
showEntry = input.bool(true, "Show breakout entry")
// === Session detection ===
inSession = not na(time(timeframe.period, sessStart))
isFirstBar = inSession and not inSession[1]
orBars = math.max(1, math.floor(orMinutes / math.max(timeframe.multiplier, 1)))
inOR = inSession and ta.barssince(isFirstBar) < orBars
// === OR state machine ===
var float orHigh = na
var float orLow = na
var bool orDone = false
var bool brokeUp = false
var float entryPx = na
var int entryBar = na
var float todayOpen = na
if isFirstBar
orHigh := high
orLow := low
orDone := false
brokeUp := false
entryPx := na
entryBar := na
todayOpen := open
if inOR and not isFirstBar
orHigh := math.max(orHigh, high)
orLow := math.min(orLow, low)
if inSession and not inOR and not orDone
orDone := true
// First break above OR high after OR completes
trigger = false
if orDone and inSession and not brokeUp and high > orHigh
brokeUp := true
entryPx := orHigh
entryBar := bar_index
trigger := true
// === Daily regime + gap (from daily timeframe) ===
priorClose = request.security(syminfo.tickerid, "D", close[1], lookahead=barmerge.lookahead_on)
priorHigh = request.security(syminfo.tickerid, "D", high[1], lookahead=barmerge.lookahead_on)
dSMA20 = request.security(syminfo.tickerid, "D", ta.sma(close, 20)[1], lookahead=barmerge.lookahead_on)
dSMA50 = request.security(syminfo.tickerid, "D", ta.sma(close, 50)[1], lookahead=barmerge.lookahead_on)
dSMA200 = request.security(syminfo.tickerid, "D", ta.sma(close, 200)[1], lookahead=barmerge.lookahead_on)
aboveAll = priorClose > dSMA20 and priorClose > dSMA50 and priorClose > dSMA200
belowAll = priorClose < dSMA20 and priorClose < dSMA50 and priorClose < dSMA200
gapPct = (todayOpen - priorClose) / priorClose * 100
filterOK = aboveAll and gapPct > -0.5
strongFilter = filterOK and todayOpen > priorHigh
regimeColor = aboveAll ? color.new(color.green, 92) :
belowAll ? color.new(color.red, 92) :
color.new(color.gray, 95)
bgcolor(showRegime ? regimeColor : na)
// === Plots ===
plot(showOR and orDone ? orHigh : na, "OR High",
color=color.new(color.aqua, 0), linewidth=2, style=plot.style_linebr)
plot(showOR and orDone ? orLow : na, "OR Low",
color=color.new(color.fuchsia, 0), linewidth=2, style=plot.style_linebr)
validTrigger = trigger and filterOK
plotshape(showEntry and validTrigger, title="ORB Long Entry",
style=shape.triangleup, location=location.belowbar,
color=color.new(color.green, 0), size=size.normal, text="LONG")
// === Status label on latest bar ===
var label statusLbl = na
if barstate.islast
label.delete(statusLbl)
regimeStr = aboveAll ? "StrongUp" : belowAll ? "StrongDown" : "Mixed"
setupTxt = filterOK
? (strongFilter ? "✅ HIGH-CONVICTION (open above PDH)"
: "✅ STANDARD long-only setup")
: "🚫 SKIP — filters failed"
statusTxt = brokeUp ? ("📈 ENTERED at " + str.tostring(entryPx, "#.##") +
" — exit MOC, stop OR Low")
: (orDone ? "⏳ Waiting for break above OR High..."
: "⏳ OR forming...")
fullText = "Module 3 — ORB Long\n" +
"─────────────────\n" +
"Regime: " + regimeStr + "\n" +
"Gap: " + str.tostring(gapPct, "#.##") + "%\n" +
"OR Hi: " + str.tostring(orHigh, "#.##") + "\n" +
"OR Lo: " + str.tostring(orLow, "#.##") + "\n" +
"─────────────────\n" + setupTxt + "\n" + statusTxt
statusLbl := label.new(bar_index, high, text=fullText,
style=label.style_label_down,
color=color.new(color.black, 20),
textcolor=color.white, size=size.normal)
// === Alerts ===
alertcondition(validTrigger, "M3: ORB Long Entry",
"M3: ORB break above OR high in StrongUp regime — long entry, exit MOC")