// AUTO-EXPORTED BY BLVK LAB V0.6 · 2026-08-30T05:19:58.738750+00:00 // SHADOW ONLY · profile=EVO_G02_025_MOMENTUM_A0.650_W15_B0.50_T0.02_Q0.60_7E568E7F · status=LOCKED_TEST_FAILED // Stable CONTROL was NOT modified. Unsupported prefilters=1 //@version=6 // © BLVK — SNIPER V0.6 AI CHALLENGER · SHADOW INDICATOR // CONTROL engine geometry is preserved from the V0.5.9 prototype. // BLVK Lab/GPT is the learning brain; this Pine file is the exported SHADOW challenger. // Engine: REGIME -> FRESH TRANSITION -> DISPLACEMENT -> BOS -> FIRST PULLBACK -> CONFIRMATION -> CONTROL EXEC. // SNIPER challenger observes the CONTROL white line and waits for its exact 1M trigger. // This is an INDICATOR: it does not place orders. Run it beside the stable CONTROL strategy. indicator("BLVK SNIPER · V0.6 AI CHALLENGER", shorttitle="BLVK V060", overlay=true, max_boxes_count=40, max_lines_count=60, max_labels_count=120) // ============================================================================ // SETTINGS // ============================================================================ // CONTROL calculations are preserved; TradingView order simulation stays in the separate stable CONTROL strategy. // --- CORE ENGINE --- len13 = input.int(13, "13 EMA Length", group="CORE ENGINE", minval=2) len50 = input.int(50, "50 SMA Length", group="CORE ENGINE", minval=5) len200 = input.int(200, "200 EMA Length", group="CORE ENGINE", minval=20) atrLen = input.int(14, "ATR Length", group="CORE ENGINE", minval=2) // --- SIGNAL QUALITY --- minScore = input.int(70, "B Grade Threshold (display only)", group="SIGNAL QUALITY", minval=0, maxval=100) aThresh = input.int(80, "Minimum Signal Quality (A)", group="SIGNAL QUALITY", minval=0, maxval=100) aPlusThresh = input.int(90, "Elite Signal Threshold (A+)", group="SIGNAL QUALITY", minval=0, maxval=100) // --- MARKET STRUCTURE --- swingLen = input.int(5, "Structure Sensitivity", group="MARKET STRUCTURE", minval=2, tooltip="Bars required on each side to confirm a swing high/low.") evidenceWindowBars = input.int(5, "Structure Confirmation Window (bars)", group="MARKET STRUCTURE", minval=1, tooltip="BOS and FVG only count toward a setup if they occur within this many bars of ITS displacement leg. Keeps evidence tied to one move.") // --- MOMENTUM / DISPLACEMENT --- dispAtrMult = input.float(0.75, "Minimum Displacement Strength (ATR)", group="MOMENTUM / DISPLACEMENT", minval=0.1, step=0.05) bodyQualityThresh = input.float(0.55, "Displacement Close Quality", group="MOMENTUM / DISPLACEMENT", minval=0.5, maxval=1.0, step=0.05) useVolFilter = input.bool(true, "Use Volume Confirmation", group="MOMENTUM / DISPLACEMENT") volMult = input.float(1.2, "Volume Confirmation Multiple", group="MOMENTUM / DISPLACEMENT", minval=1.0, step=0.1) // --- CHOP FILTER --- compressionWindow = input.int(20, "Chop Lookback (bars)", group="CHOP FILTER", minval=5) compressionHighThresh = input.int(65, "Chop / Compression Block Level", group="CHOP FILTER", minval=0, maxval=100) // --- ENTRY MODEL --- maxFreshBars = input.int(20, "Setup Expiry (bars)", group="ENTRY MODEL", minval=5) pullbackDepthATR = input.float(1.0, "Maximum Pullback Depth (ATR)", group="ENTRY MODEL", minval=0.2, step=0.1) // --- RISK MODEL --- tp1R = input.float(1.5, "Primary Target (R)", group="RISK MODEL", minval=0.5, step=0.1) tp2R = input.float(2.0, "Runner Target (R) — Runner mode only", group="RISK MODEL", minval=0.5, step=0.1) stopBufferATR = input.float(0.25, "Structural Stop Buffer (ATR)", group="RISK MODEL", minval=0.0, step=0.05) minStopATR = input.float(0.5, "Minimum Structural Stop Distance (ATR)", group="RISK MODEL", minval=0.1, step=0.1) maxExtensionATR = input.float(2.5, "Maximum Entry Extension From 13 EMA (ATR)", group="RISK MODEL", minval=1.0, step=0.1) tradeMode = input.string("Fixed 1.5R", "Trade Management", options=["Fixed 1.5R", "TP1 + Runner"], group="RISK MODEL") enableBreakeven = input.bool(true, "Move Runner Stop to Breakeven at Primary Target", group="RISK MODEL") // --- DISPLAY --- showTable = input.bool(true, "Show BLVK EDGE Panel", group="DISPLAY") tablePosInput = input.string("Top Right", "Panel Position", options=["Top Right", "Top Left", "Bottom Right", "Bottom Left", "Middle Right"], group="DISPLAY") showDetails = input.bool(true, "Show Setup / Risk / Target Details", group="DISPLAY") showSetupZone = input.bool(true, "Show Entry Zone", group="DISPLAY") showEntryLine = input.bool(true, "Show Entry Line", group="DISPLAY") showStopLine = input.bool(true, "Show Stop Line", group="DISPLAY") showTargetLines = input.bool(true, "Show Target Lines", group="DISPLAY") showFVG = input.bool(true, "Show Active FVG", group="DISPLAY") showSignalLabels = input.bool(true, "Show Current Entry Signal Label", group="DISPLAY") showHistoricalSignals = input.bool(true, "Show Historical Entry Markers", group="DISPLAY") showSignalBanner = input.bool(true, "Show Signal Pop-Up Banner", group="DISPLAY") showChallengerLine = input.bool(true, "Show AI Challenger Entry Line", group="DISPLAY") signalBannerBars = input.int(3, "Signal Pop-Up Duration (bars)", minval=1, maxval=20, group="DISPLAY") challengerLineBars = input.int(20, "Challenger Entry Guide (bars)", minval=5, maxval=100, group="DISPLAY") // --- DEVELOPER / RESEARCH --- researchMode = input.bool(false, "Developer / Research Diagnostics", group="DEVELOPER / RESEARCH") // --- BLVK SNIPER V060 · A/B PROTOTYPE --- // Research/shadow layer only. It NEVER changes EXEC entry, stop, targets, lines or orders. sniperEnabled = input.bool(true, "Enable SNIPER V1", group="BLVK SNIPER V060") sniperContextMode = input.string("BALANCED", "Context Gate", options=["ALL EXEC", "BALANCED", "ELITE"], group="BLVK SNIPER V060", tooltip="BALANCED keeps more setups; ELITE uses the strongest historical low-compression prototype. Research only.") sniperTimingMode = input.string("MOMENTUM", "Exact-Candle Timing", options=["AUTO", "RECLAIM HOLD", "RECLAIM + BOS", "FAST HOLD", "BOS OR RECLAIM", "BOS", "MOMENTUM", "STRICT"], group="BLVK SNIPER V060", tooltip="AUTO uses RECLAIM HOLD for balanced setups and RECLAIM + BOS for elite setups. Other modes are AI-challenger research exports from BLVK Lab.") sniperBalancedCompMax = input.float(13.50, "Balanced Max Compression", minval=0.0, maxval=100.0, step=0.10, group="BLVK SNIPER V060") sniperEliteCompMax = input.float(6.94, "Elite Max Compression", minval=0.0, maxval=100.0, step=0.01, group="BLVK SNIPER V060") sniperMaxAdverseR = input.float(0.65, "Max Adverse Penetration (R)", minval=0.10, maxval=1.50, step=0.05, group="BLVK SNIPER V060") sniperMaxWaitMin = input.int(15, "Maximum Wait After EXEC (minutes)", minval=5, maxval=60, step=5, group="BLVK SNIPER V060") sniperBodyMinATR = input.float(0.5, "Minimum 1M Body / ATR", minval=0.0, maxval=1.50, step=0.05, group="BLVK SNIPER V060") sniperTouchToleranceR = input.float(0.02, "White-Line Touch Tolerance (R)", minval=0.0, maxval=0.50, step=0.01, group="BLVK SNIPER V060") sniperCloseQualityMin = input.float(0.6, "Minimum Directional Close Quality", minval=0.0, maxval=1.0, step=0.05, group="BLVK SNIPER V060") sniperShowMarkers = input.bool(true, "Show Exact 1M SNIPER Marker", group="BLVK SNIPER V060") sniperShowExecCompare = input.bool(true, "Keep Original EXEC Markers", group="BLVK SNIPER V060") sniperDebug = input.bool(false, "Show Skip Diagnostics", group="BLVK SNIPER V060") // ============================================================================ // COLORS // ============================================================================ c_bg = color.new(#080b10, 4) c_bgHeader = color.new(#111722, 0) c_border = color.new(#273142, 5) c_bull = color.new(#20f2a0, 0) c_bear = color.new(#ff6b1a, 0) c_wait = color.new(#ffd166, 0) c_neutral = color.new(#8792a6, 0) c_text = color.new(#f4f7fb, 0) c_textDim = color.new(#7c8799, 0) c_accent = color.new(#ff7a18, 0) c_black = color.new(#05070a, 0) // ============================================================================ // 1. INTERNAL CALCULATIONS (not plotted — trader already has MAs on chart) // ============================================================================ ema13 = ta.ema(close, len13) sma50 = ta.sma(close, len50) ema200 = ta.ema(close, len200) atr = ta.atr(atrLen) atrSafe = atr <= 0 ? syminfo.mintick : atr volAvg = ta.sma(volume, 20) // ============================================================================ // BLVK SNIPER V060 · CLOSED 1M OBSERVATION ARRAYS // request.security_lower_tf() returns only intrabars that belong to the current host bar. // The SNIPER state only consumes bars AFTER the confirmed EXEC timestamp. No lookahead. // ============================================================================ [m1TimeA, m1OpenA, m1HighA, m1LowA, m1CloseA, m1AtrA, m1Ema13A, m1PrevEma13A, m1PrevCloseA, m1PriorHigh5A, m1PriorLow5A] = request.security_lower_tf( syminfo.tickerid, "1", [time_close, open, high, low, close, ta.atr(14), ta.ema(close, 13), ta.ema(close, 13)[1], close[1], ta.highest(high[1], 5), ta.lowest(low[1], 5)]) // ============================================================================ // 2. MARKET REGIME // ============================================================================ bullishRegime = ema13 > sma50 bearishRegime = ema13 < sma50 maAlignedBull = bullishRegime and close > ema200 maAlignedBear = bearishRegime and close < ema200 sepATR = math.abs(ema13 - sma50) / atrSafe slopeLookback = 10 slope13v = (ema13 - ema13[slopeLookback]) / atrSafe slope50v = (sma50 - sma50[slopeLookback]) / atrSafe combinedSlopeATR = (math.abs(slope13v) + math.abs(slope50v)) / 2.0 freshBullTransition = bullishRegime and not bullishRegime[1] freshBearTransition = bearishRegime and not bearishRegime[1] // Whipsaw guard: a cross only starts a candidate if the MAs are already showing some // separation momentum, not just twitching across each other. Cross itself is NEVER traded. minSlopeToStart = 0.03 // ============================================================================ // COMPRESSION SCORE (0-100) — replaces the old binary compression flag // ============================================================================ maCompPts = math.max(0.0, (0.5 - math.min(sepATR, 0.5)) / 0.5) * 30.0 dist13_200 = math.abs(ema13 - ema200) / atrSafe dist50_200 = math.abs(sma50 - ema200) / atrSafe avgDist200 = (dist13_200 + dist50_200) / 2.0 proxPts = math.max(0.0, (1.0 - math.min(avgDist200, 1.0)) / 1.0) * 20.0 slopePts = math.max(0.0, (0.3 - math.min(combinedSlopeATR, 0.3)) / 0.3) * 20.0 int crossCount = 0 for i = 1 to compressionWindow bool curAbove = ema13[i] > sma50[i] bool prevAbove = ema13[i + 1] > sma50[i + 1] if curAbove != prevAbove crossCount += 1 crossPts = math.min(crossCount / 4.0, 1.0) * 20.0 float pathLen = 0.0 for i = 0 to 9 pathLen += math.abs(close[i] - close[i + 1]) netMove10 = math.abs(close - close[10]) effRatio = pathLen > 0 ? netMove10 / pathLen : 1.0 effPts = math.max(0.0, (0.5 - math.min(effRatio, 0.5)) / 0.5) * 10.0 compressionScore = math.max(0.0, math.min(100.0, maCompPts + proxPts + slopePts + crossPts + effPts)) compressionHigh = compressionScore >= compressionHighThresh compressionLabel = compressionScore >= compressionHighThresh ? "HIGH" : compressionScore >= (compressionHighThresh * 0.5) ? "MEDIUM" : "LOW" // ============================================================================ // 3. STRUCTURE (swing pivots + break of structure) // NOTE: pivots confirm `swingLen` bars after they form. The pivot's PRICE is historical // and known at confirmation time — this is real confirmation lag, never future data. // ============================================================================ var float lastSwingHigh = na var float lastSwingLow = na ph = ta.pivothigh(swingLen, swingLen) pl = ta.pivotlow(swingLen, swingLen) if not na(ph) lastSwingHigh := ph if not na(pl) lastSwingLow := pl bullishBOS = not na(lastSwingHigh) and ta.crossover(close, lastSwingHigh) bearishBOS = not na(lastSwingLow) and ta.crossunder(close, lastSwingLow) // ============================================================================ // 4. DISPLACEMENT (volatility-relative, produces a 0-100 relative strength value) // ============================================================================ bodySize = math.abs(close - open) candleRange = high - low closeLoc = candleRange != 0 ? (close - low) / candleRange : 0.5 displacementBullCandle = close > open and bodySize >= atrSafe * dispAtrMult and closeLoc >= bodyQualityThresh displacementBearCandle = close < open and bodySize >= atrSafe * dispAtrMult and closeLoc <= (1 - bodyQualityThresh) // f_dispStrength: 0-100 relative strength of a displacement candle. NOT a win-probability // figure — purely "how strong was this move relative to normal volatility." f_dispStrength(isBull) => float bodyRatio = bodySize / atrSafe float bodyPts = math.min(bodyRatio / (dispAtrMult * 2.0), 1.0) * 50.0 float locRel = isBull ? closeLoc : (1.0 - closeLoc) float locPts = math.max(0.0, (locRel - 0.5) / 0.5) * 25.0 float seqMove = math.abs(close - close[2]) / atrSafe float seqPts = math.min(seqMove / (dispAtrMult * 2.5), 1.0) * 15.0 float volPts = useVolFilter ? (volume > volAvg * volMult ? 10.0 : 0.0) : 10.0 // neutral if filter disabled math.max(0.0, math.min(100.0, bodyPts + locPts + seqPts + volPts)) // ============================================================================ // 5. FVG (conventional 3-candle fair value gap — raw detection only; setup binding happens // in the state machine below, so a random unrelated FVG can never attach to a setup) // ============================================================================ bullishFVG = low > high[2] bearishFVG = high < low[2] // ============================================================================ // STATE VARIABLES // ============================================================================ var string setupDir = na // "BULL" / "BEAR" / na var int setupTransitionBar = na var int setupDisplacementBar = na var float displacementStrength = 0.0 // 0-100 var bool setupDisplacementOK = false var bool setupBOSOK = false var float setupBreakoutLevel = na var bool setupFVGOK = false var float setupFvgTop = na var float setupFvgBot = na var bool setupPullbackActive = false var bool setupPullbackReached = false var float setupPullbackExtreme = na var string setupPullbackQuality = "" // "IDEAL" / "DEEP" var float pullbackZoneTop = na var float pullbackZoneBottom = na var bool setupConfirmed = false var float setupQuality = 0.0 var string setupGrade = "" var bool tradeActive = false var string tradeDir = na var float tradeEntry = na var float originalStop = na // frozen at entry — never modified var float activeStop = na // can move to breakeven var float originalRiskDistance = na // frozen at entry — R is ALWAYS measured against this var float tradeTP1 = na var float tradeTP2 = na var string tradeStatus = "HOLD" var bool tp1Hit = false var float tradeQualityAtEntry = na var string tradeGradeAtEntry = "" var float tradeCompressionAtEntry = na // frozen for alert payloads (compressionScore is live and keeps moving after entry) var float tradeDispStrengthAtEntry = na // frozen for alert payloads (displacementStrength is cleared by resetSetup()) var box zoneBox = na var box fvgBox = na var label signalLabel = na var line entryLine = na var label entryPriceLabel = na var line stopLine = na var line tp1Line = na var line tp2Line = na var table t = na bool evSetupForming = false bool evPullbackReady = false bool evConfirmation = false bool evInvalidated = false bool evLongA = false bool evLongAPlus = false bool evShortA = false bool evShortAPlus = false bool evTPHit = false bool evStopHit = false string invalidReason = "" // BLVK SNIPER V060 state. Completely separate from EXEC/trade state. var bool sniperArmed = false var string sniperDir = na var float sniperWhite = na var float sniperStructuralStop = na var float sniperExecRisk = na var float sniperCompressionAtArm = na var bool sniperEliteContext = false var int sniperArmTime = na var int sniperArmBar = na var bool sniperTouched = false var bool sniperAdverseSeen = false var float sniperMaxAdverseSeenR = 0.0 var bool sniperReclaimPending = false var float sniperReclaimExtreme = na var int sniperReclaimTime = na var string sniperState = "IDLE" var int sniperLastSignalTime = na var float sniperLastSignalPrice = na var string sniperLastSignalTier = "" var label sniperSignalLabel = na var line challengerEntryLine = na var label challengerEntryLabel = na var int challengerLineExpiryBar = na var table signalBanner = na var int signalBannerExpiryBar = na var string signalBannerTitle = "" var string signalBannerDetail = "" var color signalBannerColor = c_neutral bool evSniperLong = false bool evSniperShort = false bool evSniperSkip = false // ============================================================================ // HELPER FUNCTIONS // Setup resets are intentionally performed inline because Pine user-defined functions cannot // reassign global state variables. // ============================================================================ // ============================================================================ // 8. QUALITY SCORE — RESEARCH WEIGHTS — NOT WIN PROBABILITY // These weights are provisional BLVK research assumptions, not statistically calibrated // probabilities. A score of 94 means "94/100 against BLVK's own rules," nothing more. // Replace this function with calibrated weights once historical ES/S&P testing is complete. // ============================================================================ f_score(alignOK, sepAtrVal, slopeAtrVal, compScore, dispStrength, bosOK, fvgOK, pullbackQuality, freshRatio, confirmQualityPts, extPenalty) => float s = 0.0 s += alignOK ? 15.0 : 0.0 // 200 EMA directional alignment s += math.min(sepAtrVal / 1.0, 1.0) * 10.0 // 13/50 separation s += math.min(slopeAtrVal / 1.0, 1.0) * 8.0 // MA slope s += math.max(0.0, (100.0 - compScore) / 100.0) * 7.0 // lack of compression s += (dispStrength / 100.0) * 20.0 // displacement strength s += bosOK ? 12.0 : 0.0 // BOS tied to this displacement s += fvgOK ? 8.0 : 0.0 // FVG tied to this displacement s += pullbackQuality == "IDEAL" ? 10.0 : pullbackQuality == "DEEP" ? 5.0 : 0.0 s += freshRatio * 5.0 // freshness s += confirmQualityPts // confirmation candle quality (0-5) s -= extPenalty // extension penalty math.max(0.0, math.min(100.0, s)) f_grade(score, aPlus, aThr, minS) => string g = score >= aPlus ? "A+" : score >= aThr ? "A" : score >= minS ? "B" : "NO TRADE" g // ============================================================================ // 9/10. TRADE STATE & RISK MANAGEMENT (runs first — only one thing happens at a time) // R is ALWAYS measured against originalRiskDistance, which is frozen at entry and never // touched again — this is the fix for the breakeven division-by-zero bug. // ============================================================================ if tradeActive if tradeDir == "BULL" if low <= activeStop tradeStatus := "STOPPED" evStopHit := true tradeActive := false else if not tp1Hit and high >= tradeTP1 tp1Hit := true tradeStatus := "TARGET HIT" evTPHit := true if tradeMode == "Fixed 1.5R" tradeActive := false else if enableBreakeven activeStop := tradeEntry else if tp1Hit and high >= tradeTP2 tradeStatus := "TARGET HIT" evTPHit := true tradeActive := false else if tp1Hit tradeStatus := "BREAKEVEN" else tradeStatus := "HOLD" else // BEAR if high >= activeStop tradeStatus := "STOPPED" evStopHit := true tradeActive := false else if not tp1Hit and low <= tradeTP1 tp1Hit := true tradeStatus := "TARGET HIT" evTPHit := true if tradeMode == "Fixed 1.5R" tradeActive := false else if enableBreakeven activeStop := tradeEntry else if tp1Hit and low <= tradeTP2 tradeStatus := "TARGET HIT" evTPHit := true tradeActive := false else if tp1Hit tradeStatus := "BREAKEVEN" else tradeStatus := "HOLD" // DISPLAY SAFETY: EXEC / stop / target guides are LIVE-TRADE guides only. // Once BLVK's trade state is no longer active, remove them so a completed // execution can never look like a fresh instruction while STATE = SCANNING. if not tradeActive if not na(entryLine) line.delete(entryLine) entryLine := na if not na(entryPriceLabel) label.delete(entryPriceLabel) entryPriceLabel := na if not na(stopLine) line.delete(stopLine) stopLine := na if not na(tp1Line) line.delete(tp1Line) tp1Line := na if not na(tp2Line) line.delete(tp2Line) tp2Line := na // ============================================================================ // MAIN STATE MACHINE (SCANNING -> FORMING -> WAIT -> LONG/SHORT -> MANAGE) // Every piece of evidence below (displacement, BOS, FVG) is gated to belong to the SAME // setupDir candidate, within `evidenceWindowBars` of ITS displacement bar. Nothing here can // borrow evidence from an unrelated earlier or later move. // ============================================================================ if not tradeActive // --- Invalidation: regime flips against an active candidate --- if setupDir == "BULL" and bearishRegime setupDir := na setupTransitionBar := na setupDisplacementBar := na displacementStrength := 0.0 setupDisplacementOK := false setupBOSOK := false setupBreakoutLevel := na setupFVGOK := false setupFvgTop := na setupFvgBot := na setupPullbackActive := false setupPullbackReached := false setupPullbackExtreme := na setupPullbackQuality := "" pullbackZoneTop := na pullbackZoneBottom := na setupConfirmed := false setupQuality := 0.0 setupGrade := "" evInvalidated := true invalidReason := "REGIME SHIFT" if setupDir == "BEAR" and bullishRegime setupDir := na setupTransitionBar := na setupDisplacementBar := na displacementStrength := 0.0 setupDisplacementOK := false setupBOSOK := false setupBreakoutLevel := na setupFVGOK := false setupFvgTop := na setupFvgBot := na setupPullbackActive := false setupPullbackReached := false setupPullbackExtreme := na setupPullbackQuality := "" pullbackZoneTop := na pullbackZoneBottom := na setupConfirmed := false setupQuality := 0.0 setupGrade := "" evInvalidated := true invalidReason := "REGIME SHIFT" // --- Invalidation: candidate went stale before producing a valid pullback --- if not na(setupDir) and not setupConfirmed and (bar_index - setupTransitionBar) > maxFreshBars setupDir := na setupTransitionBar := na setupDisplacementBar := na displacementStrength := 0.0 setupDisplacementOK := false setupBOSOK := false setupBreakoutLevel := na setupFVGOK := false setupFvgTop := na setupFvgBot := na setupPullbackActive := false setupPullbackReached := false setupPullbackExtreme := na setupPullbackQuality := "" pullbackZoneTop := na pullbackZoneBottom := na setupConfirmed := false setupQuality := 0.0 setupGrade := "" evInvalidated := true invalidReason := "CANCELLED" // --- Invalidation: high compression appears before pullback is reached --- if not na(setupDir) and compressionHigh and not setupPullbackReached setupDir := na setupTransitionBar := na setupDisplacementBar := na displacementStrength := 0.0 setupDisplacementOK := false setupBOSOK := false setupBreakoutLevel := na setupFVGOK := false setupFvgTop := na setupFvgBot := na setupPullbackActive := false setupPullbackReached := false setupPullbackExtreme := na setupPullbackQuality := "" pullbackZoneTop := na pullbackZoneBottom := na setupConfirmed := false setupQuality := 0.0 setupGrade := "" evInvalidated := true invalidReason := "COMPRESSION" // --- 2/3. Fresh directional transition starts a new candidate (cross itself is never traded) --- if na(setupDir) and not compressionHigh if freshBullTransition and combinedSlopeATR >= minSlopeToStart setupDir := "BULL" setupTransitionBar := bar_index evSetupForming := true else if freshBearTransition and combinedSlopeATR >= minSlopeToStart setupDir := "BEAR" setupTransitionBar := bar_index evSetupForming := true // --- BULL candidate: displacement -> windowed BOS/FVG -> zone -> pullback --- if setupDir == "BULL" if not setupDisplacementOK and displacementBullCandle and (bar_index - setupTransitionBar) <= maxFreshBars setupDisplacementOK := true setupDisplacementBar := bar_index displacementStrength := f_dispStrength(true) setupPullbackActive := true if setupDisplacementOK and not setupBOSOK and (bar_index - setupDisplacementBar) <= evidenceWindowBars if bullishBOS setupBOSOK := true setupBreakoutLevel := lastSwingHigh if setupDisplacementOK and not setupFVGOK and (bar_index - setupDisplacementBar) <= evidenceWindowBars if bullishFVG setupFVGOK := true setupFvgTop := low setupFvgBot := high[2] if setupPullbackActive and not setupConfirmed // Zone geometry computed ONCE here and reused for both gating and the chart box — // the visible box always matches what the entry logic actually uses. baseBottom = math.max(sma50, ema13 - pullbackDepthATR * atrSafe) pullbackZoneTop := ema13 + 0.15 * atrSafe pullbackZoneBottom := (setupFVGOK and not na(setupFvgBot)) ? math.min(baseBottom, setupFvgBot) : baseBottom if not setupPullbackReached if not na(lastSwingLow) and low < lastSwingLow setupDir := na setupTransitionBar := na setupDisplacementBar := na displacementStrength := 0.0 setupDisplacementOK := false setupBOSOK := false setupBreakoutLevel := na setupFVGOK := false setupFvgTop := na setupFvgBot := na setupPullbackActive := false setupPullbackReached := false setupPullbackExtreme := na setupPullbackQuality := "" pullbackZoneTop := na pullbackZoneBottom := na setupConfirmed := false setupQuality := 0.0 setupGrade := "" evInvalidated := true invalidReason := "STRUCTURE BROKEN" else if low <= pullbackZoneTop setupPullbackReached := true setupPullbackExtreme := low setupPullbackQuality := low >= pullbackZoneBottom ? "IDEAL" : "DEEP" evPullbackReady := true else setupPullbackExtreme := math.min(setupPullbackExtreme, low) setupPullbackQuality := setupPullbackExtreme >= pullbackZoneBottom ? "IDEAL" : "DEEP" if not na(lastSwingLow) and setupPullbackExtreme < lastSwingLow setupDir := na setupTransitionBar := na setupDisplacementBar := na displacementStrength := 0.0 setupDisplacementOK := false setupBOSOK := false setupBreakoutLevel := na setupFVGOK := false setupFvgTop := na setupFvgBot := na setupPullbackActive := false setupPullbackReached := false setupPullbackExtreme := na setupPullbackQuality := "" pullbackZoneTop := na pullbackZoneBottom := na setupConfirmed := false setupQuality := 0.0 setupGrade := "" evInvalidated := true invalidReason := "STRUCTURE BROKEN" // --- BEAR candidate: mirror of BULL --- if setupDir == "BEAR" if not setupDisplacementOK and displacementBearCandle and (bar_index - setupTransitionBar) <= maxFreshBars setupDisplacementOK := true setupDisplacementBar := bar_index displacementStrength := f_dispStrength(false) setupPullbackActive := true if setupDisplacementOK and not setupBOSOK and (bar_index - setupDisplacementBar) <= evidenceWindowBars if bearishBOS setupBOSOK := true setupBreakoutLevel := lastSwingLow if setupDisplacementOK and not setupFVGOK and (bar_index - setupDisplacementBar) <= evidenceWindowBars if bearishFVG setupFVGOK := true setupFvgTop := low[2] setupFvgBot := high if setupPullbackActive and not setupConfirmed baseTop = math.min(sma50, ema13 + pullbackDepthATR * atrSafe) pullbackZoneBottom := ema13 - 0.15 * atrSafe pullbackZoneTop := (setupFVGOK and not na(setupFvgTop)) ? math.max(baseTop, setupFvgTop) : baseTop if not setupPullbackReached if not na(lastSwingHigh) and high > lastSwingHigh setupDir := na setupTransitionBar := na setupDisplacementBar := na displacementStrength := 0.0 setupDisplacementOK := false setupBOSOK := false setupBreakoutLevel := na setupFVGOK := false setupFvgTop := na setupFvgBot := na setupPullbackActive := false setupPullbackReached := false setupPullbackExtreme := na setupPullbackQuality := "" pullbackZoneTop := na pullbackZoneBottom := na setupConfirmed := false setupQuality := 0.0 setupGrade := "" evInvalidated := true invalidReason := "STRUCTURE BROKEN" else if high >= pullbackZoneBottom setupPullbackReached := true setupPullbackExtreme := high setupPullbackQuality := high <= pullbackZoneTop ? "IDEAL" : "DEEP" evPullbackReady := true else setupPullbackExtreme := math.max(setupPullbackExtreme, high) setupPullbackQuality := setupPullbackExtreme <= pullbackZoneTop ? "IDEAL" : "DEEP" if not na(lastSwingHigh) and setupPullbackExtreme > lastSwingHigh setupDir := na setupTransitionBar := na setupDisplacementBar := na displacementStrength := 0.0 setupDisplacementOK := false setupBOSOK := false setupBreakoutLevel := na setupFVGOK := false setupFvgTop := na setupFvgBot := na setupPullbackActive := false setupPullbackReached := false setupPullbackExtreme := na setupPullbackQuality := "" pullbackZoneTop := na pullbackZoneBottom := na setupConfirmed := false setupQuality := 0.0 setupGrade := "" evInvalidated := true invalidReason := "STRUCTURE BROKEN" // --- Live quality score — updates every bar so the score visibly progresses --- if not na(setupDir) alignOK = setupDir == "BULL" ? maAlignedBull : maAlignedBear freshRatio = math.max(0.0, math.min(1.0, 1.0 - (bar_index - setupTransitionBar) / maxFreshBars)) extendedNow = setupDir == "BULL" ? (close - ema13) / atrSafe > maxExtensionATR : (ema13 - close) / atrSafe > maxExtensionATR setupQuality := f_score(alignOK, sepATR, combinedSlopeATR, compressionScore, displacementStrength, setupBOSOK, setupFVGOK, setupPullbackQuality, freshRatio, 0.0, extendedNow ? 10.0 : 0.0) setupGrade := f_grade(setupQuality, aPlusThresh, aThresh, minScore) // --- 11. Confirmation trigger + final signal decision (only the FIRST valid pullback gets one) --- if not na(setupDir) and setupPullbackReached and not setupConfirmed confirmTrig = setupDir == "BULL" ? (close > high[1] and close > open) : (close < low[1] and close < open) if confirmTrig evConfirmation := true setupConfirmed := true confirmLocRel = setupDir == "BULL" ? closeLoc : (1.0 - closeLoc) confirmQualityPts = math.max(0.0, (confirmLocRel - 0.5) / 0.5) * 5.0 alignOKc = setupDir == "BULL" ? maAlignedBull : maAlignedBear freshRatioC = math.max(0.0, math.min(1.0, 1.0 - (bar_index - setupTransitionBar) / maxFreshBars)) extendedNowC = setupDir == "BULL" ? (close - ema13) / atrSafe > maxExtensionATR : (ema13 - close) / atrSafe > maxExtensionATR finalScore = f_score(alignOKc, sepATR, combinedSlopeATR, compressionScore, displacementStrength, setupBOSOK, setupFVGOK, setupPullbackQuality, freshRatioC, confirmQualityPts, extendedNowC ? 10.0 : 0.0) setupQuality := finalScore setupGrade := f_grade(finalScore, aPlusThresh, aThresh, minScore) // Hard gates (spec #7, #13): BOS and extension are NOT optional score inputs at the // moment of entry. A displacement leg that never broke structure isn't a BOS setup, // and an extended close doesn't get a pass just because other factors scored well. if finalScore >= aThresh and setupBOSOK and not extendedNowC buffer = stopBufferATR * atrSafe minDist = minStopATR * atrSafe if setupDir == "BULL" rawStop = setupPullbackExtreme - buffer finalStop = (close - rawStop) < minDist ? close - minDist : rawStop tradeEntry := close originalStop := finalStop activeStop := finalStop originalRiskDistance := tradeEntry - originalStop tradeTP1 := tradeEntry + originalRiskDistance * tp1R tradeTP2 := tradeEntry + originalRiskDistance * tp2R tradeDir := "BULL" if finalScore >= aPlusThresh evLongAPlus := true else evLongA := true else rawStop = setupPullbackExtreme + buffer finalStop = (rawStop - close) < minDist ? close + minDist : rawStop tradeEntry := close originalStop := finalStop activeStop := finalStop originalRiskDistance := originalStop - tradeEntry tradeTP1 := tradeEntry - originalRiskDistance * tp1R tradeTP2 := tradeEntry - originalRiskDistance * tp2R tradeDir := "BEAR" if finalScore >= aPlusThresh evShortAPlus := true else evShortA := true tradeActive := true tradeStatus := "HOLD" tp1Hit := false tradeQualityAtEntry := finalScore tradeGradeAtEntry := setupGrade tradeCompressionAtEntry := compressionScore tradeDispStrengthAtEntry := displacementStrength if not na(zoneBox) box.delete(zoneBox) zoneBox := na if not na(fvgBox) box.delete(fvgBox) fvgBox := na if showSignalLabels if not na(signalLabel) label.delete(signalLabel) lblText = (tradeDir == "BULL" ? "BLVK EDGE LONG\n" : "BLVK EDGE SHORT\n") + tradeGradeAtEntry + " · " + str.tostring(math.round(tradeQualityAtEntry), "#") signalLabel := label.new(bar_index, tradeDir == "BULL" ? low - atrSafe * 0.6 : high + atrSafe * 0.6, text=lblText, style=tradeDir == "BULL" ? label.style_label_up : label.style_label_down, color=tradeDir == "BULL" ? c_bull : c_bear, textcolor=color.white, size=size.small) if showEntryLine if not na(entryLine) line.delete(entryLine) if not na(entryPriceLabel) label.delete(entryPriceLabel) entryLine := line.new(bar_index, tradeEntry, bar_index + 1, tradeEntry, extend=extend.right, color=c_text, style=line.style_dashed, width=2) entryPriceLabel := label.new(bar_index + 1, tradeEntry, "CONTROL EXEC @ " + str.tostring(tradeEntry, format.mintick), xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, color=color.new(#111827, 0), textcolor=c_text, size=size.small) if showStopLine if not na(stopLine) line.delete(stopLine) stopLine := line.new(bar_index, originalStop, bar_index + 1, originalStop, extend=extend.right, color=c_bear, style=line.style_dashed, width=1) if showTargetLines if not na(tp1Line) line.delete(tp1Line) tp1Line := line.new(bar_index, tradeTP1, bar_index + 1, tradeTP1, extend=extend.right, color=c_bull, style=line.style_dashed, width=1) if not na(tp2Line) line.delete(tp2Line) tp2Line := line.new(bar_index, tradeTP2, bar_index + 1, tradeTP2, extend=extend.right, color=color.new(c_bull, 40), style=line.style_dotted, width=1) setupDir := na setupTransitionBar := na setupDisplacementBar := na displacementStrength := 0.0 setupDisplacementOK := false setupBOSOK := false setupBreakoutLevel := na setupFVGOK := false setupFvgTop := na setupFvgBot := na setupPullbackActive := false setupPullbackReached := false setupPullbackExtreme := na setupPullbackQuality := "" pullbackZoneTop := na pullbackZoneBottom := na setupConfirmed := false setupQuality := 0.0 setupGrade := "" else evInvalidated := true invalidReason := not setupBOSOK ? "NO STRUCTURE BREAK" : extendedNowC ? "TOO EXTENDED" : "LOW QUALITY" setupDir := na setupTransitionBar := na setupDisplacementBar := na displacementStrength := 0.0 setupDisplacementOK := false setupBOSOK := false setupBreakoutLevel := na setupFVGOK := false setupFvgTop := na setupFvgBot := na setupPullbackActive := false setupPullbackReached := false setupPullbackExtreme := na setupPullbackQuality := "" pullbackZoneTop := na pullbackZoneBottom := na setupConfirmed := false setupQuality := 0.0 setupGrade := "" // ============================================================================ // BLVK SNIPER V060 · EXACT-CANDLE A/B PROTOTYPE // EXEC already did the location job. This state machine only decides whether a later, // already-closed 1M candle deserves a ⚡ marker. Original strategy orders are untouched. // ============================================================================ if sniperEnabled and (evLongAPlus or evLongA or evShortAPlus or evShortA) bool contextBalanced = tradeCompressionAtEntry <= sniperBalancedCompMax bool contextElite = tradeCompressionAtEntry <= sniperEliteCompMax bool contextAllowed = sniperContextMode == "ALL EXEC" ? true : sniperContextMode == "ELITE" ? contextElite : contextBalanced if contextAllowed sniperArmed := true sniperDir := tradeDir sniperWhite := tradeEntry sniperStructuralStop := originalStop sniperExecRisk := originalRiskDistance sniperCompressionAtArm := tradeCompressionAtEntry sniperEliteContext := contextElite sniperArmTime := time_close sniperArmBar := bar_index sniperTouched := false sniperAdverseSeen := false sniperMaxAdverseSeenR := 0.0 sniperReclaimPending := false sniperReclaimExtreme := na sniperReclaimTime := na sniperState := contextElite ? "ARMED · ELITE" : "ARMED · BALANCED" else sniperArmed := false sniperState := "SKIP · CONTEXT" evSniperSkip := true if sniperArmed and bar_index > sniperArmBar and not na(sniperWhite) and not na(sniperStructuralStop) and not na(sniperExecRisk) and sniperExecRisk > 0 int intrabars = array.size(m1CloseA) if intrabars > 0 for j = 0 to intrabars - 1 if sniperArmed int ts = array.get(m1TimeA, j) float o1 = array.get(m1OpenA, j) float h1 = array.get(m1HighA, j) float l1 = array.get(m1LowA, j) float c1 = array.get(m1CloseA, j) float a1 = array.get(m1AtrA, j) float e1 = array.get(m1Ema13A, j) float pe1 = array.get(m1PrevEma13A, j) float pc1 = array.get(m1PrevCloseA, j) float priorH5 = array.get(m1PriorHigh5A, j) float priorL5 = array.get(m1PriorLow5A, j) float atr1 = na(a1) or a1 <= 0 ? syminfo.mintick : a1 float range1 = math.max(h1 - l1, syminfo.mintick) float bodyATR1 = math.abs(c1 - o1) / atr1 float closeLoc1 = (c1 - l1) / range1 float dirCloseQuality = sniperDir == "BULL" ? closeLoc1 : 1.0 - closeLoc1 float elapsedMin = (ts - sniperArmTime) / 60000.0 bool stopHit = sniperDir == "BULL" ? l1 <= sniperStructuralStop : h1 >= sniperStructuralStop float adverseR = sniperDir == "BULL" ? math.max(0.0, (sniperWhite - l1) / sniperExecRisk) : math.max(0.0, (h1 - sniperWhite) / sniperExecRisk) sniperMaxAdverseSeenR := math.max(sniperMaxAdverseSeenR, adverseR) float nearest = math.min(math.min(math.abs(l1 - sniperWhite), math.abs(h1 - sniperWhite)), math.abs(c1 - sniperWhite)) bool nearWhite = (l1 <= sniperWhite and h1 >= sniperWhite) or nearest <= sniperTouchToleranceR * sniperExecRisk sniperTouched := sniperTouched or nearWhite bool adverseClose = sniperDir == "BULL" ? c1 < sniperWhite : c1 > sniperWhite sniperAdverseSeen := sniperAdverseSeen or adverseClose bool reclaimWhite = sniperDir == "BULL" ? (c1 > sniperWhite and pc1 <= sniperWhite) : (c1 < sniperWhite and pc1 >= sniperWhite) bool microReclaim = sniperDir == "BULL" ? (c1 > e1 and pc1 <= pe1) : (c1 < e1 and pc1 >= pe1) bool microBos = sniperDir == "BULL" ? (not na(priorH5) and c1 > priorH5) : (not na(priorL5) and c1 < priorL5) bool microSweep = sniperDir == "BULL" ? (not na(priorL5) and l1 < priorL5 and c1 > priorL5) : (not na(priorH5) and h1 > priorH5 and c1 < priorH5) bool directional = sniperDir == "BULL" ? c1 > sniperWhite : c1 < sniperWhite bool bodyOK = bodyATR1 >= sniperBodyMinATR bool qualityOK = dirCloseQuality >= sniperCloseQualityMin bool priorPending = sniperReclaimPending float priorExtreme = sniperReclaimExtreme bool brokeReclaim = priorPending and not na(priorExtreme) and (sniperDir == "BULL" ? c1 > priorExtreme : c1 < priorExtreme) bool penetrationInvalid = sniperMaxAdverseSeenR > sniperMaxAdverseR bool expired = elapsedMin > sniperMaxWaitMin string modeNow = sniperTimingMode == "AUTO" ? (sniperEliteContext ? "RECLAIM + BOS" : "RECLAIM HOLD") : sniperTimingMode bool triggerFast = sniperTouched and directional and bodyOK and qualityOK bool triggerBosOrReclaim = sniperTouched and directional and bodyOK and qualityOK and (microBos or reclaimWhite or microReclaim or microSweep) bool triggerHold = sniperTouched and sniperAdverseSeen and priorPending and directional and brokeReclaim and bodyOK and qualityOK bool triggerReclaimBos = sniperTouched and sniperAdverseSeen and directional and microBos and (reclaimWhite or microReclaim or priorPending) and bodyOK and qualityOK bool triggerBosOnly = sniperTouched and directional and microBos and bodyOK and qualityOK bool triggerMomentum = sniperTouched and directional and qualityOK and (microBos or bodyATR1 >= math.max(0.30, sniperBodyMinATR)) bool triggerStrict = sniperTouched and directional and microBos and (reclaimWhite or microReclaim or priorPending or microSweep) and bodyATR1 >= math.max(0.30, sniperBodyMinATR) and qualityOK bool triggerNow = switch modeNow "FAST HOLD" => triggerFast "BOS OR RECLAIM" => triggerBosOrReclaim "RECLAIM + BOS" => triggerReclaimBos "BOS" => triggerBosOnly "MOMENTUM" => triggerMomentum "STRICT" => triggerStrict => triggerHold if stopHit or penetrationInvalid or expired sniperArmed := false sniperReclaimPending := false sniperState := stopHit ? "SKIP · STRUCTURE" : penetrationInvalid ? "SKIP · PENETRATION" : "SKIP · EXPIRED" evSniperSkip := true else if triggerNow bool longTrig = sniperDir == "BULL" evSniperLong := longTrig evSniperShort := not longTrig sniperLastSignalTime := ts sniperLastSignalPrice := c1 sniperLastSignalTier := sniperEliteContext ? "ELITE" : "SNIPER" sniperState := "TRIGGERED · " + sniperLastSignalTier sniperArmed := false sniperReclaimPending := false if sniperShowMarkers string txt = (longTrig ? "AI LONG" : "AI SHORT") + " · " + sniperLastSignalTier + " ENTRY " + str.tostring(c1, format.mintick) + " CONTROL " + str.tostring(sniperWhite, format.mintick) + " · STOP " + str.tostring(sniperStructuralStop, format.mintick) color markerColor = sniperEliteContext ? c_accent : (longTrig ? c_bull : c_bear) sniperSignalLabel := label.new(ts, c1, xloc=xloc.bar_time, yloc=yloc.price, text=txt, style=longTrig ? label.style_label_up : label.style_label_down, color=markerColor, textcolor=color.white, size=size.normal) // V0.6 SHADOW guide: challenger entry is distinct from the CONTROL white line. if showChallengerLine if not na(challengerEntryLine) line.delete(challengerEntryLine) if not na(challengerEntryLabel) label.delete(challengerEntryLabel) challengerEntryLine := line.new(bar_index, c1, bar_index + challengerLineBars, c1, extend=extend.none, color=longTrig ? c_bull : c_bear, style=line.style_solid, width=3) challengerEntryLabel := label.new(bar_index + challengerLineBars, c1, "AI ENTRY @ " + str.tostring(c1, format.mintick), xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, color=longTrig ? c_bull : c_bear, textcolor=color.white, size=size.small) challengerLineExpiryBar := bar_index + challengerLineBars signalBannerTitle := longTrig ? "BLVK AI CHALLENGER · LONG" : "BLVK AI CHALLENGER · SHORT" signalBannerDetail := sniperLastSignalTier + " | ENTRY " + str.tostring(c1, format.mintick) + " | STOP " + str.tostring(sniperStructuralStop, format.mintick) + " | CONTROL " + str.tostring(sniperWhite, format.mintick) signalBannerColor := longTrig ? c_bull : c_bear signalBannerExpiryBar := bar_index + signalBannerBars else if adverseClose and priorPending sniperReclaimPending := false sniperReclaimExtreme := na sniperReclaimTime := na if reclaimWhite or microReclaim sniperReclaimPending := true sniperReclaimExtreme := sniperDir == "BULL" ? h1 : l1 sniperReclaimTime := ts sniperState := sniperReclaimPending ? "RECLAIM SEEN · WAITING CANDLE" : sniperTouched ? "WATCHING WHITE LINE" : "WAITING WHITE LINE" // V0.6 challenger guide lifecycle. It is a temporary SHADOW signal guide, never a stale instruction. if not na(challengerLineExpiryBar) and bar_index > challengerLineExpiryBar if not na(challengerEntryLine) line.delete(challengerEntryLine) challengerEntryLine := na if not na(challengerEntryLabel) label.delete(challengerEntryLabel) challengerEntryLabel := na challengerLineExpiryBar := na // High-visibility A/B markers. White diamonds = CONTROL EXEC. Large arrows = AI CHALLENGER. plotshape(showHistoricalSignals and (evLongAPlus or evLongA), title="CONTROL Long", style=shape.diamond, location=location.belowbar, color=c_text, size=size.tiny, text="C", textcolor=c_black) plotshape(showHistoricalSignals and (evShortAPlus or evShortA), title="CONTROL Short", style=shape.diamond, location=location.abovebar, color=c_text, size=size.tiny, text="C", textcolor=c_black) plotshape(evSniperLong, title="AI Challenger Long", style=shape.triangleup, location=location.belowbar, color=c_bull, size=size.large, text="AI", textcolor=c_black) plotshape(evSniperShort, title="AI Challenger Short", style=shape.triangledown, location=location.abovebar, color=c_bear, size=size.large, text="AI", textcolor=color.white) bgcolor((evSniperLong or evSniperShort) ? color.new(evSniperLong ? c_bull : c_bear, 88) : na, title="BLVK AI Signal Pulse") plotshape(sniperShowExecCompare and showHistoricalSignals and (evLongAPlus or evLongA), title="BLVK EXEC Compare Long", style=shape.circle, location=location.belowbar, color=color.new(c_text, 15), size=size.tiny, text="E") plotshape(sniperShowExecCompare and showHistoricalSignals and (evShortAPlus or evShortA), title="BLVK EXEC Compare Short", style=shape.circle, location=location.abovebar, color=color.new(c_text, 15), size=size.tiny, text="E") plotshape(sniperDebug and evSniperSkip, title="BLVK SNIPER V060 Skip", style=shape.xcross, location=location.abovebar, color=c_wait, size=size.tiny) // Keep the displayed EXEC marker pinned to the immutable numeric entry price. // TradingView may pan/zoom the viewport, but this label/line never changes tradeEntry. if showEntryLine and not na(entryLine) and not na(tradeEntry) line.set_y1(entryLine, tradeEntry) line.set_y2(entryLine, tradeEntry) if not na(entryPriceLabel) label.set_x(entryPriceLabel, bar_index + 1) label.set_y(entryPriceLabel, tradeEntry) label.set_text(entryPriceLabel, "CONTROL EXEC @ " + str.tostring(tradeEntry, format.mintick)) // ============================================================================ // 11. VISUAL UI — ideal pullback / entry zone + setup-specific FVG only // ============================================================================ if showSetupZone if not na(setupDir) and setupPullbackActive and not setupPullbackReached and not setupConfirmed and not na(pullbackZoneTop) and not na(pullbackZoneBottom) if not na(zoneBox) box.delete(zoneBox) zoneBox := box.new(setupTransitionBar, pullbackZoneTop, bar_index + 5, pullbackZoneBottom, border_color=color.new(c_wait, 60), bgcolor=color.new(c_wait, 88), border_width=1) else if not na(zoneBox) box.delete(zoneBox) zoneBox := na else if not na(zoneBox) box.delete(zoneBox) zoneBox := na if showFVG if not na(setupDir) and not setupConfirmed and setupFVGOK if setupDir == "BULL" and not na(setupFvgTop) if not na(fvgBox) box.delete(fvgBox) fvgBox := box.new(setupDisplacementBar, setupFvgTop, bar_index + 5, setupFvgBot, border_color=color.new(c_bull, 50), bgcolor=color.new(c_bull, 90), border_width=1) else if setupDir == "BEAR" and not na(setupFvgTop) if not na(fvgBox) box.delete(fvgBox) fvgBox := box.new(setupDisplacementBar, setupFvgTop, bar_index + 5, setupFvgBot, border_color=color.new(c_bear, 50), bgcolor=color.new(c_bear, 90), border_width=1) else if not na(fvgBox) box.delete(fvgBox) fvgBox := na else if not na(fvgBox) box.delete(fvgBox) fvgBox := na // ============================================================================ // DISPLAY STATE DERIVATION // ============================================================================ string curState = "SCANNING" string curReason = "" if evLongAPlus or evLongA curState := "LONG" else if evShortAPlus or evShortA curState := "SHORT" else if tradeActive curState := "MANAGE" else if not na(setupDir) if setupPullbackReached curState := "WAIT" curReason := "CONFIRMATION" else if setupPullbackActive extNow = setupDir == "BULL" ? (close - ema13) / atrSafe > maxExtensionATR : (ema13 - close) / atrSafe > maxExtensionATR curState := "WAIT" curReason := extNow ? "TOO EXTENDED" : "PULLBACK" else curState := "FORMING" curReason := "STRUCTURE" else if compressionHigh curState := "NO TRADE" curReason := "COMPRESSION" else if invalidReason != "" curState := "NO TRADE" curReason := invalidReason else curState := "SCANNING" string aiView = "Scanning market for opportunity." if curState == "LONG" aiView := tradeGradeAtEntry == "A+" ? "A+ long confirmed. Respect the stop." : "A long confirmed. Respect the stop." else if curState == "SHORT" aiView := tradeGradeAtEntry == "A+" ? "A+ short confirmed. Respect the stop." : "A short confirmed. Respect the stop." else if curState == "MANAGE" if tradeStatus == "TARGET HIT" aiView := "Target reached. Trade managed well." else if tradeStatus == "STOPPED" aiView := "Stopped out cleanly. Back to scanning." else if tradeStatus == "BREAKEVEN" aiView := "Risk-free. Letting the trade work." else aiView := "Managing active trade. Respect the stop." else if curState == "FORMING" aiView := setupDir == "BULL" ? "Bullish transition detected. Don't chase." : "Bearish transition detected. Don't chase." else if curState == "WAIT" and curReason == "PULLBACK" aiView := setupDir == "BULL" ? "Strong move. Waiting for first pullback." : "Strong move. Waiting for first pullback." else if curState == "WAIT" and curReason == "TOO EXTENDED" aiView := "Price is extended. Wait for reset." else if curState == "WAIT" and curReason == "CONFIRMATION" aiView := setupDir == "BULL" ? "Ideal pullback reached. Waiting for buyers." : "Ideal pullback reached. Waiting for sellers." else if curState == "NO TRADE" and curReason == "COMPRESSION" aiView := "Compression detected. Stay out." else if curState == "NO TRADE" and curReason == "LOW QUALITY" aiView := "Setup lacked quality. Staying out." else if curState == "NO TRADE" and curReason == "STRUCTURE BROKEN" aiView := "Setup invalidated. Back to scanning." else if curState == "NO TRADE" and curReason == "CANCELLED" aiView := "Setup went stale. Back to scanning." else if curState == "NO TRADE" and curReason == "REGIME SHIFT" aiView := "Trend flipped. Setup invalidated." else if curState == "NO TRADE" and curReason == "NO STRUCTURE BREAK" aiView := "Pullback confirmed but structure never broke. No BOS, no trade." else if curState == "NO TRADE" and curReason == "TOO EXTENDED" aiView := "Confirmation came in extended. Not chasing it." // ============================================================================ // TABLE UI // ============================================================================ if showTable and barstate.islast if not na(t) table.delete(t) tablePos = switch tablePosInput "Top Right" => position.top_right "Top Left" => position.top_left "Bottom Right" => position.bottom_right "Bottom Left" => position.bottom_left "Middle Right" => position.middle_right => position.top_right rowCount = 8 + (showDetails ? 3 : 0) + (researchMode ? 7 : 0) t := table.new(tablePos, 2, rowCount, bgcolor=c_bg, border_width=1, border_color=c_border, frame_color=c_border, frame_width=1) marketText = bullishRegime ? "BULLISH" : "BEARISH" marketColor = bullishRegime ? c_bull : c_bear stateColor = (curState == "LONG" or (curState == "MANAGE" and tradeDir == "BULL")) ? c_bull : (curState == "SHORT" or (curState == "MANAGE" and tradeDir == "BEAR")) ? c_bear : (curState == "WAIT" or curState == "FORMING") ? c_wait : c_neutral stateDisplay = curReason != "" ? curState + " · " + curReason : curState dispScore = tradeActive ? tradeQualityAtEntry : (not na(setupDir) ? setupQuality : na) dispGrade = tradeActive ? tradeGradeAtEntry : (not na(setupDir) ? setupGrade : "") qualityText = na(dispScore) ? "—" : str.tostring(math.round(dispScore), "#") + " · " + dispGrade qualityColor = dispGrade == "A+" ? c_bull : dispGrade == "A" ? c_bull : dispGrade == "B" ? c_wait : c_neutral setupText = tradeActive ? (tradeDir == "BULL" ? "LONG ACTIVE" : "SHORT ACTIVE") : not na(setupDir) ? (setupPullbackReached ? "CONFIRMATION" : setupDisplacementOK ? "FIRST PULLBACK" : "TRANSITION") : "—" entryText = tradeActive ? str.tostring(tradeEntry, format.mintick) : (curState == "WAIT" or curState == "FORMING") ? "WAITING" : "—" riskText = tradeActive ? "STOP " + str.tostring(activeStop, format.mintick) : (not na(setupDir) and setupPullbackReached) ? "CLEAN" : "—" // R is always computed against originalRiskDistance, never the (possibly breakeven) active stop. riskSafe = not na(originalRiskDistance) and originalRiskDistance > 0 ? originalRiskDistance : na curRDisplay = tradeActive and not na(riskSafe) ? (tradeDir == "BULL" ? (close - tradeEntry) / riskSafe : (tradeEntry - close) / riskSafe) : na targetText = tradeActive ? str.tostring(curRDisplay, "#.##") + "R · " + tradeStatus : str.tostring(tp1R, "#.##") + "R" table.cell(t, 0, 0, "BLVK · SNIPER V0.6", text_color=c_text, text_halign=text.align_left, text_size=size.small, bgcolor=c_bgHeader) table.cell(t, 1, 0, syminfo.ticker + " · " + timeframe.period, text_color=c_textDim, text_halign=text.align_right, text_size=size.small, bgcolor=c_bgHeader) table.cell(t, 0, 1, "MARKET", text_color=c_textDim, text_halign=text.align_left, text_size=size.small, bgcolor=c_bg) table.cell(t, 1, 1, marketText, text_color=marketColor, text_halign=text.align_right, text_size=size.small, bgcolor=c_bg) table.cell(t, 0, 2, "STATE", text_color=c_textDim, text_halign=text.align_left, text_size=size.small, bgcolor=c_bg) table.cell(t, 1, 2, stateDisplay, text_color=stateColor, text_halign=text.align_right, text_size=size.small, bgcolor=c_bg) table.cell(t, 0, 3, "QUALITY", text_color=c_textDim, text_halign=text.align_left, text_size=size.small, bgcolor=c_bg) table.cell(t, 1, 3, qualityText, text_color=qualityColor, text_halign=text.align_right, text_size=size.small, bgcolor=c_bg) controlText = tradeActive ? (tradeDir == "BULL" ? "LONG @ " : "SHORT @ ") + str.tostring(tradeEntry, format.mintick) : "SCANNING" controlColor = tradeActive ? (tradeDir == "BULL" ? c_bull : c_bear) : c_neutral challengerText = sniperArmed ? sniperState : (sniperState == "IDLE" ? "WATCHING CONTROL" : sniperState) challengerColor = str.contains(challengerText, "TRIGGERED") ? (sniperDir == "BULL" ? c_bull : c_bear) : sniperArmed ? c_wait : c_neutral table.cell(t, 0, 4, "CONTROL", text_color=c_textDim, text_halign=text.align_left, text_size=size.small, bgcolor=c_bg) table.cell(t, 1, 4, controlText, text_color=controlColor, text_halign=text.align_right, text_size=size.small, bgcolor=c_bg) table.cell(t, 0, 5, "AI CHALLENGER", text_color=c_accent, text_halign=text.align_left, text_size=size.small, bgcolor=c_bgHeader) table.cell(t, 1, 5, challengerText, text_color=challengerColor, text_halign=text.align_right, text_size=size.small, bgcolor=c_bgHeader) row = 6 if showDetails table.cell(t, 0, row, "SETUP", text_color=c_textDim, text_halign=text.align_left, text_size=size.small, bgcolor=c_bg) table.cell(t, 1, row, setupText, text_color=c_text, text_halign=text.align_right, text_size=size.small, bgcolor=c_bg) row += 1 table.cell(t, 0, row, "ENTRY", text_color=c_textDim, text_halign=text.align_left, text_size=size.small, bgcolor=c_bg) table.cell(t, 1, row, entryText, text_color=c_text, text_halign=text.align_right, text_size=size.small, bgcolor=c_bg) row += 1 if showDetails table.cell(t, 0, row, "RISK", text_color=c_textDim, text_halign=text.align_left, text_size=size.small, bgcolor=c_bg) table.cell(t, 1, row, riskText, text_color=c_text, text_halign=text.align_right, text_size=size.small, bgcolor=c_bg) row += 1 table.cell(t, 0, row, "TARGET", text_color=c_textDim, text_halign=text.align_left, text_size=size.small, bgcolor=c_bg) table.cell(t, 1, row, targetText, text_color=c_text, text_halign=text.align_right, text_size=size.small, bgcolor=c_bg) row += 1 table.cell(t, 0, row, "BLVK VIEW", text_color=c_textDim, text_halign=text.align_left, text_size=size.small, bgcolor=c_bg) table.cell(t, 1, row, aiView, text_color=c_text, text_halign=text.align_right, text_size=size.small, bgcolor=c_bg) row += 1 if researchMode barsSinceTransition = not na(setupTransitionBar) ? str.tostring(bar_index - setupTransitionBar) : "—" bosText = setupDisplacementOK ? (setupBOSOK ? "CONFIRMED" : "PENDING") : "—" fvgText = setupDisplacementOK ? (setupFVGOK ? "PRESENT" : "NONE") : "—" pbText = setupPullbackQuality != "" ? setupPullbackQuality : "—" rawScoreText = not na(setupDir) ? str.tostring(math.round(setupQuality), "#") : (tradeActive ? str.tostring(math.round(tradeQualityAtEntry), "#") : "—") table.cell(t, 0, row, "RESEARCH", text_color=c_textDim, text_halign=text.align_left, text_size=size.tiny, bgcolor=c_bgHeader) table.cell(t, 1, row, "debug", text_color=c_textDim, text_halign=text.align_right, text_size=size.tiny, bgcolor=c_bgHeader) row += 1 table.cell(t, 0, row, "COMPRESSION", text_color=c_textDim, text_halign=text.align_left, text_size=size.tiny, bgcolor=c_bg) table.cell(t, 1, row, str.tostring(math.round(compressionScore), "#") + " · " + compressionLabel, text_color=c_text, text_halign=text.align_right, text_size=size.tiny, bgcolor=c_bg) row += 1 table.cell(t, 0, row, "DISPLACEMENT", text_color=c_textDim, text_halign=text.align_left, text_size=size.tiny, bgcolor=c_bg) table.cell(t, 1, row, str.tostring(math.round(displacementStrength), "#"), text_color=c_text, text_halign=text.align_right, text_size=size.tiny, bgcolor=c_bg) row += 1 table.cell(t, 0, row, "BOS / FVG", text_color=c_textDim, text_halign=text.align_left, text_size=size.tiny, bgcolor=c_bg) table.cell(t, 1, row, bosText + " / " + fvgText, text_color=c_text, text_halign=text.align_right, text_size=size.tiny, bgcolor=c_bg) row += 1 table.cell(t, 0, row, "PULLBACK", text_color=c_textDim, text_halign=text.align_left, text_size=size.tiny, bgcolor=c_bg) table.cell(t, 1, row, pbText, text_color=c_text, text_halign=text.align_right, text_size=size.tiny, bgcolor=c_bg) row += 1 table.cell(t, 0, row, "RAW SCORE", text_color=c_textDim, text_halign=text.align_left, text_size=size.tiny, bgcolor=c_bg) table.cell(t, 1, row, rawScoreText, text_color=c_text, text_halign=text.align_right, text_size=size.tiny, bgcolor=c_bg) row += 1 table.cell(t, 0, row, "BARS SINCE", text_color=c_textDim, text_halign=text.align_left, text_size=size.tiny, bgcolor=c_bg) table.cell(t, 1, row, barsSinceTransition, text_color=c_text, text_halign=text.align_right, text_size=size.tiny, bgcolor=c_bg) // ============================================================================ // V0.6 SIGNAL POP-UP BANNER — visual only // ============================================================================ if showSignalBanner and barstate.islast if na(signalBanner) signalBanner := table.new(position.top_center, 1, 2, bgcolor=color.new(c_black, 5), border_width=1, border_color=c_border, frame_width=1, frame_color=c_border) bool bannerLive = not na(signalBannerExpiryBar) and bar_index <= signalBannerExpiryBar and signalBannerTitle != "" table.cell(signalBanner, 0, 0, bannerLive ? signalBannerTitle : "BLVK AI CHALLENGER · WATCHING", text_color=bannerLive ? color.white : c_textDim, bgcolor=bannerLive ? signalBannerColor : color.new(c_bgHeader, 25), text_size=bannerLive ? size.normal : size.small) table.cell(signalBanner, 0, 1, bannerLive ? signalBannerDetail : "CONTROL stable · challenger shadow-testing", text_color=bannerLive ? color.white : c_textDim, bgcolor=bannerLive ? color.new(signalBannerColor, 25) : color.new(c_bg, 25), text_size=size.small) // ============================================================================ // HISTORICAL ENTRY MARKERS — VISUAL ONLY // Uses the exact existing LONG/SHORT signal events. No trading logic is changed. // ============================================================================ plotshape(showHistoricalSignals and (evLongAPlus or evLongA), title="BLVK EDGE Historical Long", style=shape.labelup, location=location.belowbar, color=c_bull, text="L", textcolor=color.white, size=size.tiny) plotshape(showHistoricalSignals and (evShortAPlus or evShortA), title="BLVK EDGE Historical Short", style=shape.labeldown, location=location.abovebar, color=c_bear, text="S", textcolor=color.white, size=size.tiny) // Static alert conditions make the new challenger easy to select in TradingView's alert dialog. alertcondition(evSniperLong, title="BLVK AI CHALLENGER LONG", message="BLVK AI CHALLENGER LONG") alertcondition(evSniperShort, title="BLVK AI CHALLENGER SHORT", message="BLVK AI CHALLENGER SHORT") alertcondition(evLongAPlus or evLongA, title="BLVK CONTROL LONG", message="BLVK CONTROL LONG EXEC") alertcondition(evShortAPlus or evShortA, title="BLVK CONTROL SHORT", message="BLVK CONTROL SHORT EXEC") // ============================================================================ // BLVK SNIPER V060 ALERTS — research A/B only // Exact 1M trigger is stamped at its own timestamp. The alert itself is emitted when // the host bar confirms so original EXEC closed-bar behavior remains unchanged. // ============================================================================ if evSniperLong or evSniperShort alert("BLVK " + sniperLastSignalTier + (evSniperLong ? " LONG" : " SHORT") + " | " + syminfo.ticker + " | 1M trigger " + str.tostring(sniperLastSignalPrice, format.mintick) + " | EXEC white " + str.tostring(sniperWhite, format.mintick) + " | structural stop " + str.tostring(sniperStructuralStop, format.mintick) + " | compression " + str.tostring(sniperCompressionAtArm, "#.##") + " | max adverse " + str.tostring(sniperMaxAdverseSeenR, "#.##") + "R", alert.freq_once_per_bar_close) // ============================================================================ // 12. ALERTS // ============================================================================ // Dynamic structured alerts — direction, score, entry, stop, target, setup state. if evLongAPlus or evLongA alert("BLVK EDGE " + (evLongAPlus ? "A+" : "A") + " LONG | " + syminfo.ticker + " " + timeframe.period + " | Direction LONG | State MANAGE | Grade " + tradeGradeAtEntry + " | Score " + str.tostring(math.round(tradeQualityAtEntry), "#") + " | Entry " + str.tostring(tradeEntry, format.mintick) + " | OriginalStop " + str.tostring(originalStop, format.mintick) + " | ActiveStop " + str.tostring(activeStop, format.mintick) + " | TP1 " + str.tostring(tradeTP1, format.mintick) + " | TP2 " + str.tostring(tradeTP2, format.mintick) + " | Compression " + str.tostring(math.round(tradeCompressionAtEntry), "#") + " | Displacement " + str.tostring(math.round(tradeDispStrengthAtEntry), "#"), alert.freq_once_per_bar_close) if evShortAPlus or evShortA alert("BLVK EDGE " + (evShortAPlus ? "A+" : "A") + " SHORT | " + syminfo.ticker + " " + timeframe.period + " | Direction SHORT | State MANAGE | Grade " + tradeGradeAtEntry + " | Score " + str.tostring(math.round(tradeQualityAtEntry), "#") + " | Entry " + str.tostring(tradeEntry, format.mintick) + " | OriginalStop " + str.tostring(originalStop, format.mintick) + " | ActiveStop " + str.tostring(activeStop, format.mintick) + " | TP1 " + str.tostring(tradeTP1, format.mintick) + " | TP2 " + str.tostring(tradeTP2, format.mintick) + " | Compression " + str.tostring(math.round(tradeCompressionAtEntry), "#") + " | Displacement " + str.tostring(math.round(tradeDispStrengthAtEntry), "#"), alert.freq_once_per_bar_close) if evTPHit alert("BLVK EDGE TP Hit | " + syminfo.ticker + " " + timeframe.period + " | Status " + tradeStatus, alert.freq_once_per_bar_close) if evStopHit alert("BLVK EDGE Stop Hit | " + syminfo.ticker + " " + timeframe.period, alert.freq_once_per_bar_close) if evInvalidated alert("BLVK EDGE Invalidated (" + invalidReason + ") | " + syminfo.ticker + " " + timeframe.period, alert.freq_once_per_bar_close)