//@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")