// ============================================================================ // © algonexta // This script is for education and research; it does not guarantee results. // Verify this source in TradingView before use. // ============================================================================ //@version=6 indicator("ICT Gold Engine — A+ Setup Scanner", shorttitle="ICTGoldA+", overlay=true, max_boxes_count=100, max_lines_count=100, max_labels_count=150) //────────────────────────────────────────────────────────────────────────────── // Inputs — Gold defaults //────────────────────────────────────────────────────────────────────────────── groupContext = "1. Context and Structure" biasMode = input.string("Structural HTF Bias", "HTF bias mode", options=["Structural HTF Bias", "EMA Bias", "Bias Filter Off"], group=groupContext) htf = input.timeframe("60", "Higher timeframe", group=groupContext) swingLength = input.int(5, "Confirmed swing length", minval=2, maxval=20, group=groupContext) atrLength = input.int(14, "ATR length", minval=2, group=groupContext) wickBreakMode = input.bool(false, "Allow wick structure breaks", group=groupContext) showStructure = input.bool(false, "Show MSS / BOS", group=groupContext) groupLiquidity = "2. Liquidity" equalTolerance = input.float(0.12, "EQH/EQL tolerance (ATR)", minval=0.01, step=0.01, group=groupLiquidity) sweepMemory = input.int(12, "Sweep memory (bars)", minval=1, maxval=50, group=groupLiquidity) preferEqual = input.bool(false, "Prefer EQH/EQL sweep", group=groupLiquidity, tooltip="Adds a quality gate: the swept pool must have been classified as equal liquidity.") showLiquidity = input.bool(false, "Show active liquidity", group=groupLiquidity) groupSequence = "3. Sequence" signalProfile = input.string("Balanced", "Signal profile", options=["Balanced", "Selective A+"], group=groupSequence) structureConfirmationMode = input.string("MSS or BOS", "Structure confirmation", options=["MSS or BOS", "MSS only"], group=groupSequence) bodyFactor = input.float(0.75, "Displacement body (ATR)", minval=0.1, step=0.05, group=groupSequence) rangeFactor = input.float(1.20, "Displacement range (ATR)", minval=0.1, step=0.05, group=groupSequence) requireCloseExpansion = input.bool(true, "Displacement closes beyond prior candle", group=groupSequence) fvgMinimum = input.float(0.10, "Minimum FVG size (ATR)", minval=0.0, step=0.01, group=groupSequence) fvgMemory = input.int(10, "Bars allowed from MSS to FVG", minval=0, maxval=50, group=groupSequence) entryMode = input.string("50% FVG midpoint", "Retracement entry", options=["Touch FVG", "50% FVG midpoint", "Deep FVG retracement"], group=groupSequence) maxSetupAge = input.int(60, "Maximum setup age (bars)", minval=5, maxval=500, group=groupSequence) signalCooldown = input.int(20, "Minimum bars between arrows", minval=1, maxval=100, group=groupSequence) showFvg = input.bool(false, "Show active setup FVG", group=groupSequence) groupSessions = "4. Sessions" sessionFilter = input.bool(true, "Session filter", group=groupSessions) londonEnabled = input.bool(true, "London enabled", inline="L", group=groupSessions) londonSession = input.session("0200-0500", "", inline="L", group=groupSessions) newYorkEnabled = input.bool(true, "New York enabled", inline="N", group=groupSessions) newYorkSession = input.session("0700-1000", "", inline="N", group=groupSessions) shadeSessions = input.bool(false, "Shade enabled sessions", group=groupSessions) groupFilters = "5. Quality Gates" requireBias = input.bool(true, "Require aligned HTF bias", group=groupFilters) requireSweep = input.bool(true, "Require liquidity sweep", group=groupFilters) requireDisplacement = input.bool(true, "Require displacement", group=groupFilters) requireFvg = input.bool(true, "Require linked FVG", group=groupFilters) requirePd = input.bool(false, "Require premium/discount alignment", group=groupFilters) requireSession = input.bool(true, "Require active session", group=groupFilters) minimumGrade = input.string("A", "Minimum displayed grade", options=["B", "A", "A+"], group=groupFilters) minimumScore = input.int(70, "Minimum score", minval=0, maxval=100, group=groupFilters) gradeB = input.int(60, "B threshold", minval=0, maxval=100, group=groupFilters) gradeA = input.int(70, "A threshold", minval=0, maxval=100, group=groupFilters) gradeAPlus = input.int(85, "A+ threshold", minval=0, maxval=100, group=groupFilters) groupVisual = "6. Display" cleanSignalMode = input.bool(true, "Clean signal mode — arrows only", group=groupVisual) signalMarkerSize = input.string("Large", "Signal marker size", options=["Normal", "Large", "Huge"], group=groupVisual) showEquilibrium = input.bool(false, "Show structural equilibrium", group=groupVisual) showDashboard = input.bool(false, "Show dashboard", group=groupVisual) showSignalDetails = input.bool(false, "Show grade and score beside arrow", group=groupVisual) groupTradePlan = "7. Visual Risk / Reward" showTradePlan = input.bool(true, "Show Entry / SL / TP plan", group=groupTradePlan) atrStopMultiple = input.float(1.5, "Stop distance (ATR)", minval=0.25, maxval=10.0, step=0.25, group=groupTradePlan) targetR1 = input.float(1.0, "TP1 (R)", minval=0.25, maxval=10.0, step=0.25, group=groupTradePlan) targetR2 = input.float(2.0, "TP2 (R)", minval=0.5, maxval=15.0, step=0.25, group=groupTradePlan) targetR3 = input.float(3.0, "TP3 (R)", minval=0.75, maxval=20.0, step=0.25, group=groupTradePlan) planProjectionBars = input.int(16, "Plan width (bars)", minval=3, maxval=100, group=groupTradePlan) rrHistoryCount = input.int(15, "Signal RR plans to keep", minval=1, maxval=20, group=groupTradePlan) //────────────────────────────────────────────────────────────────────────────── // Helpers //────────────────────────────────────────────────────────────────────────────── f_structuralBias(int len) => float ph = ta.pivothigh(high, len, len) float pl = ta.pivotlow(low, len, len) var float lastH = na var float lastL = na var int direction = 0 if not na(ph) lastH := ph if not na(pl) lastL := pl if not na(lastH) and close > lastH direction := 1 if not na(lastL) and close < lastL direction := -1 direction f_grade(int score) => score >= gradeAPlus ? "A+" : score >= gradeA ? "A" : score >= gradeB ? "B" : "—" f_gradeRank(string grade) => grade == "A+" ? 3 : grade == "A" ? 2 : grade == "B" ? 1 : 0 f_stateName(int state) => state == 0 ? "IDLE" : state == 1 ? "SWEEP" : state == 2 ? "DISPLACEMENT" : state == 3 ? "MSS" : state == 4 ? "WAITING FOR RETRACEMENT" : state == 5 ? "READY" : "INVALIDATED" // HTF calculations execute in the HTF context; lookahead is explicitly off. htfStructuralBias = request.security(syminfo.tickerid, htf, f_structuralBias(swingLength), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off) htfClose = request.security(syminfo.tickerid, htf, close, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off) htfEma = request.security(syminfo.tickerid, htf, ta.ema(close, 50), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off) int htfBias = biasMode == "Bias Filter Off" ? 0 : biasMode == "EMA Bias" ? (htfClose > htfEma ? 1 : htfClose < htfEma ? -1 : 0) : htfStructuralBias float atr = ta.atr(atrLength) float pivotHigh = ta.pivothigh(high, swingLength, swingLength) float pivotLow = ta.pivotlow(low, swingLength, swingLength) var float lastSwingHigh = na var float priorSwingHigh = na var float lastSwingLow = na var float priorSwingLow = na var bool lastHighEqual = false var bool lastLowEqual = false var bool highLiquidityAvailable = false var bool lowLiquidityAvailable = false var bool highStructureAvailable = false var bool lowStructureAvailable = false bool newEqh = false bool newEql = false if not na(pivotHigh) priorSwingHigh := lastSwingHigh lastSwingHigh := pivotHigh lastHighEqual := not na(priorSwingHigh) and math.abs(lastSwingHigh - priorSwingHigh) <= atr[swingLength] * equalTolerance highLiquidityAvailable := true highStructureAvailable := true newEqh := lastHighEqual if not na(pivotLow) priorSwingLow := lastSwingLow lastSwingLow := pivotLow lastLowEqual := not na(priorSwingLow) and math.abs(lastSwingLow - priorSwingLow) <= atr[swingLength] * equalTolerance lowLiquidityAvailable := true lowStructureAvailable := true newEql := lastLowEqual // Structure is based on confirmed swing levels and close breaks by default. float bullBreakPrice = wickBreakMode ? high : close float bearBreakPrice = wickBreakMode ? low : close bool breakHigh = highStructureAvailable and not na(lastSwingHigh) and bullBreakPrice > lastSwingHigh bool breakLow = lowStructureAvailable and not na(lastSwingLow) and bearBreakPrice < lastSwingLow var int structureDirection = 0 bool bullMss = breakHigh and structureDirection <= -1 bool bearMss = breakLow and structureDirection >= 1 bool bullBos = breakHigh and structureDirection == 1 bool bearBos = breakLow and structureDirection == -1 if breakHigh structureDirection := 1 highStructureAvailable := false if breakLow structureDirection := -1 lowStructureAvailable := false // Structural dealing range: most recently confirmed swing low and high. float equilibrium = not na(lastSwingHigh) and not na(lastSwingLow) ? (lastSwingHigh + lastSwingLow) * 0.5 : na bool inDiscount = not na(equilibrium) and close <= equilibrium bool inPremium = not na(equilibrium) and close >= equilibrium bool inLondon = londonEnabled and not na(time(timeframe.period, londonSession, "America/New_York")) bool inNewYork = newYorkEnabled and not na(time(timeframe.period, newYorkSession, "America/New_York")) bool inKillZone = inLondon or inNewYork string sessionName = inLondon ? "LONDON" : inNewYork ? "NEW YORK" : "OFF SESSION" // True rejection: trade through confirmed liquidity, then close back inside. bool sslSweep = barstate.isconfirmed and lowLiquidityAvailable and not na(lastSwingLow) and low < lastSwingLow and close > lastSwingLow bool bslSweep = barstate.isconfirmed and highLiquidityAvailable and not na(lastSwingHigh) and high > lastSwingHigh and close < lastSwingHigh bool bullDisplacement = barstate.isconfirmed and close > open and math.abs(close-open) >= atr*bodyFactor and (high-low) >= atr*rangeFactor and (not requireCloseExpansion or close > high[1]) bool bearDisplacement = barstate.isconfirmed and close < open and math.abs(close-open) >= atr*bodyFactor and (high-low) >= atr*rangeFactor and (not requireCloseExpansion or close < low[1]) bool bullFvgNow = barstate.isconfirmed and low > high[2] and (low-high[2]) >= atr*fvgMinimum bool bearFvgNow = barstate.isconfirmed and high < low[2] and (low[2]-high) >= atr*fvgMinimum //────────────────────────────────────────────────────────────────────────────── // Sequence state. 0 idle, 1 sweep, 2 displacement, 3 MSS, 4 wait, 5 ready. //────────────────────────────────────────────────────────────────────────────── var int bullState = 0 var int bearState = 0 var int bullStart = na var int bearStart = na var int bullStateBar = na var int bearStateBar = na var float bullFvgTop = na var float bullFvgBottom = na var float bearFvgTop = na var float bearFvgBottom = na var int bullFvgBar = na var int bearFvgBar = na var bool bullSweptEqual = false var bool bearSweptEqual = false var box bullBox = na var box bearBox = na var string lastLiquidityEvent = "NONE" if sslSweep lowLiquidityAvailable := false bullState := 1 bullStart := bar_index bullStateBar := bar_index bullSweptEqual := lastLowEqual lastLiquidityEvent := "SSL SWEEP" bearState := 0 if not na(bearBox) box.delete(bearBox) bearBox := na if bslSweep highLiquidityAvailable := false bearState := 1 bearStart := bar_index bearStateBar := bar_index bearSweptEqual := lastHighEqual lastLiquidityEvent := "BSL SWEEP" bullState := 0 if not na(bullBox) box.delete(bullBox) bullBox := na if bullState == 1 and bar_index - bullStateBar <= sweepMemory and bullDisplacement bullState := 2 bullStateBar := bar_index if bearState == 1 and bar_index - bearStateBar <= sweepMemory and bearDisplacement bearState := 2 bearStateBar := bar_index // The imbalance commonly forms on the displacement candle before structure is // confirmed. Preserve that setup-owned FVG instead of searching for a random one. if bullState == 2 and bullFvgNow bullFvgTop := low bullFvgBottom := high[2] bullFvgBar := bar_index if bearState == 2 and bearFvgNow bearFvgTop := low[2] bearFvgBottom := high bearFvgBar := bar_index bool bullStructureConfirmed = bullMss or (structureConfirmationMode == "MSS or BOS" and breakHigh) bool bearStructureConfirmed = bearMss or (structureConfirmationMode == "MSS or BOS" and breakLow) if bullState == 2 and bullStructureConfirmed bullState := not na(bullFvgBar) and bullFvgBar >= bullStart ? 4 : 3 bullStateBar := bar_index if bearState == 2 and bearStructureConfirmed bearState := not na(bearFvgBar) and bearFvgBar >= bearStart ? 4 : 3 bearStateBar := bar_index // Only an FVG formed after the sequence MSS owns the setup. if bullState == 3 and bar_index - bullStateBar <= fvgMemory and bullFvgNow bullFvgTop := low bullFvgBottom := high[2] bullFvgBar := bar_index bullState := 4 if not na(bullBox) box.delete(bullBox) bullBox := box.new(bar_index-2, bullFvgTop, bar_index+1, bullFvgBottom, border_color=cleanSignalMode ? color.new(color.teal, 100) : color.new(color.teal, 45), bgcolor=showFvg and not cleanSignalMode ? color.new(color.teal, 87) : color.new(color.teal, 100), extend=extend.right) if bearState == 3 and bar_index - bearStateBar <= fvgMemory and bearFvgNow bearFvgTop := low[2] bearFvgBottom := high bearFvgBar := bar_index bearState := 4 if not na(bearBox) box.delete(bearBox) bearBox := box.new(bar_index-2, bearFvgTop, bar_index+1, bearFvgBottom, border_color=cleanSignalMode ? color.new(color.orange, 100) : color.new(color.orange, 45), bgcolor=showFvg and not cleanSignalMode ? color.new(color.orange, 87) : color.new(color.orange, 100), extend=extend.right) float bullEntryLevel = entryMode == "Touch FVG" ? bullFvgTop : entryMode == "50% FVG midpoint" ? (bullFvgTop+bullFvgBottom)*0.5 : bullFvgBottom + (bullFvgTop-bullFvgBottom)*0.25 float bearEntryLevel = entryMode == "Touch FVG" ? bearFvgBottom : entryMode == "50% FVG midpoint" ? (bearFvgTop+bearFvgBottom)*0.5 : bearFvgTop - (bearFvgTop-bearFvgBottom)*0.25 bool bullRetrace = bullState == 4 and bar_index > bullFvgBar and low <= bullEntryLevel and high >= bullFvgBottom bool bearRetrace = bearState == 4 and bar_index > bearFvgBar and high >= bearEntryLevel and low <= bearFvgTop // Balanced public-facing path: impulse imbalance, then a real pullback into it. // It remains confirmed-bar and HTF-filtered, but does not require every strict // ICT event to occur in one exact order. var float balancedBullTop = na var float balancedBullBottom = na var float balancedBearTop = na var float balancedBearBottom = na var int balancedBullBar = na var int balancedBearBar = na var bool balancedBullActive = false var bool balancedBearActive = false var int lastBuyArrowBar = na var int lastSellArrowBar = na var int lastAnyArrowBar = na var int lastSignalDirection = 0 var line[] rrLines = array.new_line() var label[] rrLabels = array.new_label() int barsSinceBullDisplacement = nz(ta.barssince(bullDisplacement), 10000) int barsSinceBearDisplacement = nz(ta.barssince(bearDisplacement), 10000) // Balanced mode uses the stable HTF EMA direction so a slowly-confirming swing // bias cannot suppress an entire trend. Selective A+ retains structural bias. bool balancedBullBias = biasMode == "Bias Filter Off" or htfClose >= htfEma bool balancedBearBias = biasMode == "Bias Filter Off" or htfClose <= htfEma if signalProfile == "Balanced" and not balancedBullActive and bullFvgNow and barsSinceBullDisplacement <= 2 and balancedBullBias balancedBullTop := low balancedBullBottom := high[2] balancedBullBar := bar_index balancedBullActive := true if signalProfile == "Balanced" and not balancedBearActive and bearFvgNow and barsSinceBearDisplacement <= 2 and balancedBearBias balancedBearTop := low[2] balancedBearBottom := high balancedBearBar := bar_index balancedBearActive := true float balancedBullEntry = entryMode == "Touch FVG" ? balancedBullTop : entryMode == "50% FVG midpoint" ? (balancedBullTop+balancedBullBottom)*0.5 : balancedBullBottom + (balancedBullTop-balancedBullBottom)*0.25 float balancedBearEntry = entryMode == "Touch FVG" ? balancedBearBottom : entryMode == "50% FVG midpoint" ? (balancedBearTop+balancedBearBottom)*0.5 : balancedBearTop - (balancedBearTop-balancedBearBottom)*0.25 bool balancedBuyRetrace = balancedBullActive and bar_index > balancedBullBar and low <= balancedBullEntry and high >= balancedBullBottom bool balancedSellRetrace = balancedBearActive and bar_index > balancedBearBar and high >= balancedBearEntry and low <= balancedBearTop bool globalCooldownReady = na(lastAnyArrowBar) or bar_index-lastAnyArrowBar >= signalCooldown bool buyCooldownReady = globalCooldownReady and lastSignalDirection != 1 and (na(lastBuyArrowBar) or bar_index-lastBuyArrowBar >= signalCooldown) bool sellCooldownReady = globalCooldownReady and lastSignalDirection != -1 and (na(lastSellArrowBar) or bar_index-lastSellArrowBar >= signalCooldown) bool balancedBuySignal = barstate.isconfirmed and balancedBuyRetrace and balancedBullBias and buyCooldownReady bool balancedSellSignal = barstate.isconfirmed and balancedSellRetrace and balancedBearBias and sellCooldownReady int recentSslBars = nz(ta.barssince(sslSweep), 10000) int recentBslBars = nz(ta.barssince(bslSweep), 10000) int balancedBullScore = math.min(100, 70 + (htfBias == 1 ? 10 : 0) + (recentSslBars <= 20 ? 10 : 0) + (inDiscount ? 5 : 0) + (inKillZone ? 5 : 0)) int balancedBearScore = math.min(100, 70 + (htfBias == -1 ? 10 : 0) + (recentBslBars <= 20 ? 10 : 0) + (inPremium ? 5 : 0) + (inKillZone ? 5 : 0)) // Score is transparent and event-based. Sequence prerequisites are retained in state. int bullScore = (htfBias == 1 ? 15 : 0) + (bullState >= 1 ? 20 : 0) + (bullState >= 2 ? 15 : 0) + (bullState >= 3 ? 15 : 0) + (bullState >= 4 ? 15 : 0) + (bullRetrace ? 10 : 0) + (inDiscount ? 5 : 0) + (inKillZone ? 5 : 0) int bearScore = (htfBias == -1 ? 15 : 0) + (bearState >= 1 ? 20 : 0) + (bearState >= 2 ? 15 : 0) + (bearState >= 3 ? 15 : 0) + (bearState >= 4 ? 15 : 0) + (bearRetrace ? 10 : 0) + (inPremium ? 5 : 0) + (inKillZone ? 5 : 0) string bullGrade = f_grade(bullScore) string bearGrade = f_grade(bearScore) int requiredRank = f_gradeRank(minimumGrade) bool effectiveSessionRequired = signalProfile == "Selective A+" and requireSession bool bullGates = (biasMode == "Bias Filter Off" or not requireBias or htfBias == 1) and (not requireSweep or bullState >= 1) and (not requireDisplacement or bullState >= 2) and (not requireFvg or bullState >= 4) and (not requirePd or inDiscount) and (not sessionFilter or not effectiveSessionRequired or inKillZone) and (not preferEqual or bullSweptEqual) bool bearGates = (biasMode == "Bias Filter Off" or not requireBias or htfBias == -1) and (not requireSweep or bearState >= 1) and (not requireDisplacement or bearState >= 2) and (not requireFvg or bearState >= 4) and (not requirePd or inPremium) and (not sessionFilter or not effectiveSessionRequired or inKillZone) and (not preferEqual or bearSweptEqual) bool strictBuySignal = barstate.isconfirmed and bullRetrace and bullGates and bullScore >= minimumScore and f_gradeRank(bullGrade) >= requiredRank bool strictSellSignal = barstate.isconfirmed and bearRetrace and bearGates and bearScore >= minimumScore and f_gradeRank(bearGrade) >= requiredRank bool buySignal = signalProfile == "Balanced" ? balancedBuySignal : strictBuySignal bool sellSignal = signalProfile == "Balanced" ? balancedSellSignal : strictSellSignal float signalBuyLevel = signalProfile == "Balanced" ? balancedBullEntry : bullEntryLevel float signalSellLevel = signalProfile == "Balanced" ? balancedBearEntry : bearEntryLevel int signalBullScore = signalProfile == "Balanced" ? balancedBullScore : bullScore int signalBearScore = signalProfile == "Balanced" ? balancedBearScore : bearScore string signalBullGrade = f_grade(signalBullScore) string signalBearGrade = f_grade(signalBearScore) if buySignal lastBuyArrowBar := bar_index lastAnyArrowBar := bar_index lastSignalDirection := 1 balancedBullActive := false bullState := 5 bullStateBar := bar_index string buyMarkerText = signalMarkerSize == "Huge" ? "▲\nBUY" : "BUY" label.new(bar_index, signalBuyLevel, buyMarkerText, style=label.style_label_up, color=color.rgb(0, 200, 83), textcolor=color.white, size=signalMarkerSize == "Normal" ? size.normal : signalMarkerSize == "Large" ? size.large : size.huge, tooltip="BUY entry · " + signalBullGrade + " · Score " + str.tostring(signalBullScore)) if showTradePlan float buyRisk = atr*atrStopMultiple float buyStop = signalBuyLevel-buyRisk float buyTp1 = signalBuyLevel+buyRisk*targetR1 float buyTp2 = signalBuyLevel+buyRisk*targetR2 float buyTp3 = signalBuyLevel+buyRisk*targetR3 line buyEntryLine = line.new(bar_index, signalBuyLevel, bar_index+planProjectionBars, signalBuyLevel, color=color.new(color.white, 15), style=line.style_dashed, width=1) line buySlLine = line.new(bar_index, buyStop, bar_index+planProjectionBars, buyStop, color=color.rgb(255, 45, 85), width=2) line buyTp1Line = line.new(bar_index, buyTp1, bar_index+planProjectionBars, buyTp1, color=color.new(color.lime, 35), width=1) line buyTp2Line = line.new(bar_index, buyTp2, bar_index+planProjectionBars, buyTp2, color=color.new(color.lime, 15), width=2) line buyTp3Line = line.new(bar_index, buyTp3, bar_index+planProjectionBars, buyTp3, color=color.rgb(0, 230, 118), width=2) label buySlLabel = label.new(bar_index+planProjectionBars, buyStop, "SL", style=label.style_label_left, color=color.new(color.red, 10), textcolor=color.white, size=size.small) label buyTp1Label = label.new(bar_index+planProjectionBars, buyTp1, "TP1 · " + str.tostring(targetR1, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 35), textcolor=color.white, size=size.tiny) label buyTp2Label = label.new(bar_index+planProjectionBars, buyTp2, "TP2 · " + str.tostring(targetR2, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 20), textcolor=color.white, size=size.tiny) label buyTp3Label = label.new(bar_index+planProjectionBars, buyTp3, "TP3 · " + str.tostring(targetR3, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 5), textcolor=color.white, size=size.tiny) array.push(rrLines, buyEntryLine) array.push(rrLines, buySlLine) array.push(rrLines, buyTp1Line) array.push(rrLines, buyTp2Line) array.push(rrLines, buyTp3Line) array.push(rrLabels, buySlLabel) array.push(rrLabels, buyTp1Label) array.push(rrLabels, buyTp2Label) array.push(rrLabels, buyTp3Label) if showSignalDetails and not cleanSignalMode label.new(bar_index, signalBuyLevel, signalBullGrade + " · " + str.tostring(signalBullScore), style=label.style_label_up, color=color.new(color.green, 15), textcolor=color.white, size=size.small) if not na(bullBox) box.set_extend(bullBox, extend.none) box.set_right(bullBox, bar_index) if sellSignal lastSellArrowBar := bar_index lastAnyArrowBar := bar_index lastSignalDirection := -1 balancedBearActive := false bearState := 5 bearStateBar := bar_index string sellMarkerText = signalMarkerSize == "Huge" ? "SELL\n▼" : "SELL" label.new(bar_index, signalSellLevel, sellMarkerText, style=label.style_label_down, color=color.rgb(255, 45, 85), textcolor=color.white, size=signalMarkerSize == "Normal" ? size.normal : signalMarkerSize == "Large" ? size.large : size.huge, tooltip="SELL entry · " + signalBearGrade + " · Score " + str.tostring(signalBearScore)) if showTradePlan float sellRisk = atr*atrStopMultiple float sellStop = signalSellLevel+sellRisk float sellTp1 = signalSellLevel-sellRisk*targetR1 float sellTp2 = signalSellLevel-sellRisk*targetR2 float sellTp3 = signalSellLevel-sellRisk*targetR3 line sellEntryLine = line.new(bar_index, signalSellLevel, bar_index+planProjectionBars, signalSellLevel, color=color.new(color.white, 15), style=line.style_dashed, width=1) line sellSlLine = line.new(bar_index, sellStop, bar_index+planProjectionBars, sellStop, color=color.rgb(255, 45, 85), width=2) line sellTp1Line = line.new(bar_index, sellTp1, bar_index+planProjectionBars, sellTp1, color=color.new(color.lime, 35), width=1) line sellTp2Line = line.new(bar_index, sellTp2, bar_index+planProjectionBars, sellTp2, color=color.new(color.lime, 15), width=2) line sellTp3Line = line.new(bar_index, sellTp3, bar_index+planProjectionBars, sellTp3, color=color.rgb(0, 230, 118), width=2) label sellSlLabel = label.new(bar_index+planProjectionBars, sellStop, "SL", style=label.style_label_left, color=color.new(color.red, 10), textcolor=color.white, size=size.small) label sellTp1Label = label.new(bar_index+planProjectionBars, sellTp1, "TP1 · " + str.tostring(targetR1, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 35), textcolor=color.white, size=size.tiny) label sellTp2Label = label.new(bar_index+planProjectionBars, sellTp2, "TP2 · " + str.tostring(targetR2, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 20), textcolor=color.white, size=size.tiny) label sellTp3Label = label.new(bar_index+planProjectionBars, sellTp3, "TP3 · " + str.tostring(targetR3, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 5), textcolor=color.white, size=size.tiny) array.push(rrLines, sellEntryLine) array.push(rrLines, sellSlLine) array.push(rrLines, sellTp1Line) array.push(rrLines, sellTp2Line) array.push(rrLines, sellTp3Line) array.push(rrLabels, sellSlLabel) array.push(rrLabels, sellTp1Label) array.push(rrLabels, sellTp2Label) array.push(rrLabels, sellTp3Label) if showSignalDetails and not cleanSignalMode label.new(bar_index, signalSellLevel, signalBearGrade + " · " + str.tostring(signalBearScore), style=label.style_label_down, color=color.new(color.red, 15), textcolor=color.white, size=size.small) if not na(bearBox) box.set_extend(bearBox, extend.none) box.set_right(bearBox, bar_index) // Invalidation: expiry, opposite structure, or close through the far FVG boundary. bool bullInvalid = bullState > 0 and bullState < 5 and ((bar_index-bullStart > maxSetupAge) or (bullState == 1 and bar_index-bullStateBar > sweepMemory) or (bullState == 3 and bar_index-bullStateBar > fvgMemory) or bearMss or (bullState >= 4 and close < bullFvgBottom)) bool bearInvalid = bearState > 0 and bearState < 5 and ((bar_index-bearStart > maxSetupAge) or (bearState == 1 and bar_index-bearStateBar > sweepMemory) or (bearState == 3 and bar_index-bearStateBar > fvgMemory) or bullMss or (bearState >= 4 and close > bearFvgTop)) if bullInvalid bullState := 6 bullStateBar := bar_index if not na(bullBox) box.set_extend(bullBox, extend.none) box.set_right(bullBox, bar_index) box.set_bgcolor(bullBox, color.new(color.gray, 94)) if bearInvalid bearState := 6 bearStateBar := bar_index if not na(bearBox) box.set_extend(bearBox, extend.none) box.set_right(bearBox, bar_index) box.set_bgcolor(bearBox, color.new(color.gray, 94)) if balancedBullActive and (bar_index-balancedBullBar > maxSetupAge or close < balancedBullBottom) balancedBullActive := false if balancedBearActive and (bar_index-balancedBearBar > maxSetupAge or close > balancedBearTop) balancedBearActive := false if (bullState == 5 or bullState == 6) and bar_index > bullStateBar bullState := 0 if (bearState == 5 or bearState == 6) and bar_index > bearStateBar bearState := 0 // Recycle the oldest visual plans to keep the chart clean and object use bounded. while array.size(rrLines) > rrHistoryCount*5 line.delete(array.shift(rrLines)) while array.size(rrLabels) > rrHistoryCount*4 label.delete(array.shift(rrLabels)) //────────────────────────────────────────────────────────────────────────────── // Visuals //────────────────────────────────────────────────────────────────────────────── // BUY/SELL labels above are anchored by their arrow tip to the selected FVG entry. plot(showLiquidity and not cleanSignalMode ? lastSwingHigh : na, "BSL", color=color.new(color.red, 55), linewidth=1, style=plot.style_stepline) plot(showLiquidity and not cleanSignalMode ? lastSwingLow : na, "SSL", color=color.new(color.aqua, 55), linewidth=1, style=plot.style_stepline) plot(showEquilibrium and not cleanSignalMode ? equilibrium : na, "Equilibrium", color=color.new(color.silver, 55), linewidth=1, style=plot.style_stepline) plotshape(showLiquidity and not cleanSignalMode and newEqh, title="Equal highs confirmed", text="EQH", style=shape.labeldown, location=location.abovebar, color=color.new(color.red, 70), textcolor=color.white, size=size.tiny) plotshape(showLiquidity and not cleanSignalMode and newEql, title="Equal lows confirmed", text="EQL", style=shape.labelup, location=location.belowbar, color=color.new(color.teal, 70), textcolor=color.white, size=size.tiny) plotshape(showStructure and not cleanSignalMode and bullMss, title="Bullish MSS", text="MSS", style=shape.labelup, location=location.belowbar, color=color.new(color.teal, 55), textcolor=color.white, size=size.tiny) plotshape(showStructure and not cleanSignalMode and bearMss, title="Bearish MSS", text="MSS", style=shape.labeldown, location=location.abovebar, color=color.new(color.orange, 55), textcolor=color.white, size=size.tiny) plotshape(showStructure and not cleanSignalMode and bullBos, title="Bullish BOS", text="BOS", style=shape.labelup, location=location.belowbar, color=color.new(color.blue, 70), textcolor=color.white, size=size.tiny) plotshape(showStructure and not cleanSignalMode and bearBos, title="Bearish BOS", text="BOS", style=shape.labeldown, location=location.abovebar, color=color.new(color.purple, 70), textcolor=color.white, size=size.tiny) plotshape(showLiquidity and not cleanSignalMode and sslSweep, title="SSL Sweep", text="SSL Sweep", style=shape.labelup, location=location.belowbar, color=color.new(color.aqua, 55), textcolor=color.black, size=size.tiny) plotshape(showLiquidity and not cleanSignalMode and bslSweep, title="BSL Sweep", text="BSL Sweep", style=shape.labeldown, location=location.abovebar, color=color.new(color.red, 55), textcolor=color.white, size=size.tiny) bgcolor(not cleanSignalMode and shadeSessions and inKillZone ? color.new(inLondon ? color.blue : color.purple, 93) : na) // Hidden plots provide dynamic values to alert placeholders. plot(buySignal or sellSignal ? (buySignal ? signalBullScore : signalBearScore) : na, "Signal Score", display=display.none) bool aBuy = buySignal and signalBullGrade == "A" bool aSell = sellSignal and signalBearGrade == "A" bool apBuy = buySignal and signalBullGrade == "A+" bool apSell = sellSignal and signalBearGrade == "A+" alertcondition(aBuy, "A BUY", "{{ticker}} {{interval}} BUY A | Score {{plot(\"Signal Score\")}}") alertcondition(aSell, "A SELL", "{{ticker}} {{interval}} SELL A | Score {{plot(\"Signal Score\")}}") alertcondition(apBuy, "A+ BUY", "{{ticker}} {{interval}} BUY A+ | Score {{plot(\"Signal Score\")}}") alertcondition(apSell, "A+ SELL", "{{ticker}} {{interval}} SELL A+ | Score {{plot(\"Signal Score\")}}") alertcondition(buySignal or sellSignal, "Any qualified setup", "{{ticker}} {{interval}} qualified ICT setup | Score {{plot(\"Signal Score\")}}") var table dashboard = table.new(position.top_right, 2, 6, bgcolor=color.new(color.black, 18), border_color=color.new(color.gray, 65), border_width=1) if barstate.islast and showDashboard and not cleanSignalMode int activeDirection = bullState > 0 ? 1 : bearState > 0 ? -1 : 0 int activeState = activeDirection == 1 ? bullState : activeDirection == -1 ? bearState : 0 int activeScore = activeDirection == 1 ? bullScore : activeDirection == -1 ? bearScore : 0 string activeGrade = activeDirection == 1 ? bullGrade : activeDirection == -1 ? bearGrade : "—" table.cell(dashboard, 0, 0, "HTF Bias", text_color=color.silver) table.cell(dashboard, 1, 0, htfBias == 1 ? "BULLISH" : htfBias == -1 ? "BEARISH" : "NEUTRAL/OFF", text_color=htfBias == 1 ? color.lime : htfBias == -1 ? color.red : color.silver) table.cell(dashboard, 0, 1, "Session", text_color=color.silver) table.cell(dashboard, 1, 1, sessionName, text_color=inKillZone ? color.yellow : color.gray) table.cell(dashboard, 0, 2, "Liquidity", text_color=color.silver) table.cell(dashboard, 1, 2, lastLiquidityEvent, text_color=color.white) table.cell(dashboard, 0, 3, "Structure", text_color=color.silver) table.cell(dashboard, 1, 3, structureDirection == 1 ? "BULLISH" : structureDirection == -1 ? "BEARISH" : "UNSET", text_color=color.white) table.cell(dashboard, 0, 4, "Setup", text_color=color.silver) table.cell(dashboard, 1, 4, (activeDirection == 1 ? "BUY · " : activeDirection == -1 ? "SELL · " : "") + f_stateName(activeState), text_color=color.white) table.cell(dashboard, 0, 5, "Grade / Score", text_color=color.silver) table.cell(dashboard, 1, 5, activeGrade + " / " + str.tostring(activeScore), text_color=activeGrade == "A+" ? color.lime : color.white)