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