Skip to content

Instantly share code, notes, and snippets.

@Jordan-Hall
Last active May 14, 2026 17:50
Show Gist options
  • Select an option

  • Save Jordan-Hall/3aa2e9a9f26835998de544ff9adc926b to your computer and use it in GitHub Desktop.

Select an option

Save Jordan-Hall/3aa2e9a9f26835998de544ff9adc926b to your computer and use it in GitHub Desktop.
Trading View Pinescript
//@version=6
indicator("XAUUSD Pro Scalper v5 - Fixed (Pine v6)", overlay=true, max_boxes_count=120, max_labels_count=200, max_lines_count=80)
// ============================================================================
// INPUTS
// ============================================================================
grpData = "Data Sources"
dxySymbol = input.symbol("TVC:DXY", "DXY Symbol", group=grpData)
dxyTf = input.timeframe("5", "DXY Timeframe", group=grpData)
grpMode = "Mode & Filters"
mode = input.string("Normal", "Mode", options=["Fast", "Normal", "Strict"], group=grpMode)
pauseTrading = input.bool(false, "Pause / News Mode", group=grpMode)
grpDisplay = "Display"
showDashboard = input.bool(true, "Show Dashboard", group=grpDisplay)
showLiveAdvice = input.bool(true, "Show Live Advice", group=grpDisplay)
showLivePlan = input.bool(true, "Show Live Entry / SL / TP", group=grpDisplay)
showXZones = input.bool(true, "Show Best xZones", group=grpDisplay)
showSessionLevels = input.bool(true, "Show Session Levels", group=grpDisplay)
showPdhPdl = input.bool(true, "Show PDH / PDL", group=grpDisplay)
showSR = input.bool(true, "Show Support / Resistance", group=grpDisplay)
showBeNote = input.bool(true, "Show 'Move SL to BE at TP1' note", group=grpDisplay)
showHistorical = input.bool(true, "Plot historical BUY/SELL triangles", group=grpDisplay)
colorBlindMode = input.bool(true, "Colorblind-safe palette (blue/orange, high contrast)", group=grpDisplay,
tooltip="Replaces red/green with blue (BUY) and orange (SELL). Distinguishable for deuteranopia, protanopia, and tritanopia, with extra contrast for dark-mode use.")
// ----- High-contrast palette (deuteranopia/protanopia safe by default) -----
cBuy = colorBlindMode ? color.rgb(79, 195, 247) : color.green // light blue
cSell = colorBlindMode ? color.rgb(255, 167, 38) : color.red // amber/orange
cBuyOb = colorBlindMode ? color.rgb(38, 198, 218) : color.blue // cyan
cSellOb = colorBlindMode ? color.rgb(255, 112, 67) : color.orange // deep orange
cBuySwing = colorBlindMode ? color.rgb(129, 212, 250) : color.lime // pale blue
cSellSwing = colorBlindMode ? color.rgb(255, 138, 101) : color.maroon // pale orange
cWarn = color.rgb(255, 213, 79) // gold (always readable)
cTextHi = color.white
cTextDim = color.rgb(200, 210, 220)
grpZones = "xZones"
maxZonesEachSide = input.int(4, "Best xZones Per Side", minval=1, maxval=10, group=grpZones)
zoneProjection = input.int(24, "xZone Projection Bars", minval=5, maxval=120, group=grpZones)
zoneExpiry = input.int(120, "xZone Expiry Bars", minval=10, maxval=500, group=grpZones)
removeZoneAfterTouch = input.bool(false, "Remove xZone After First Touch", group=grpZones)
removeZoneAfterMitigation = input.bool(true, "Remove xZone After Mitigation", group=grpZones)
hideFarZones = input.bool(true, "Hide Very Far Zones", group=grpZones)
maxZoneDistanceAtr = input.float(8.0, "Max Zone Distance ATR", minval=1.0, maxval=30.0, group=grpZones)
duplicateAtr = input.float(0.15, "Duplicate Zone Tolerance ATR", minval=0.05, maxval=1.0, group=grpZones)
grpSession = "Sessions"
asiaSession = input.session("0000-0700", "Asia", group=grpSession)
londonSession = input.session("0700-1200", "London", group=grpSession)
nySession = input.session("1330-2100", "New York", group=grpSession)
killzoneLondon = input.session("0700-1000", "London Killzone", group=grpSession)
killzoneNY = input.session("1330-1600", "NY Killzone", group=grpSession)
grpTech = "Indicators"
emaFastLen = input.int(20, "EMA Fast", group=grpTech)
emaSlowLen = input.int(50, "EMA Slow", group=grpTech)
atrLen = input.int(14, "ATR", group=grpTech)
atrAvgLen = input.int(50, "ATR Average", group=grpTech)
swingLen = input.int(10, "Swing Strength", minval=3, maxval=60, group=grpTech)
srLen = input.int(12, "S/R Strength", minval=3, maxval=80, group=grpTech)
grpScore = "Score Weights"
wGoldTrend = input.int(20, "Gold Trend (vwap+ema)", minval=0, maxval=50, group=grpScore)
wHtfTrend = input.int(15, "HTF Trend", minval=0, maxval=50, group=grpScore)
wDxy = input.int(25, "DXY Aligned", minval=0, maxval=50, group=grpScore)
wDxyReject = input.int(20, "DXY Reject Sweep", minval=0, maxval=50, group=grpScore)
wZone = input.int(20, "Near Zone / Level", minval=0, maxval=50, group=grpScore)
wSweep = input.int(20, "Liquidity Sweep", minval=0, maxval=50, group=grpScore)
wVwap = input.int(10, "VWAP Side", minval=0, maxval=30, group=grpScore)
wRejCandle = input.int(10, "Rejection Candle", minval=0, maxval=30, group=grpScore)
wSession = input.int(10, "Killzone / Session", minval=0, maxval=30, group=grpScore)
pChop = input.int(25, "Chop Penalty", minval=0, maxval=50, group=grpScore)
pBigCandle = input.int(15, "Big Candle Penalty", minval=0, maxval=50, group=grpScore)
grpConfirm = "Confirmation"
confirmPctBase = input.int(75, "BUY/SELL Confirm % (Normal)", minval=50, maxval=95, group=grpConfirm)
watchOffset = input.int(15, "WATCH Offset %", minval=5, maxval=30, group=grpConfirm)
minRiskPoints = input.float(2.0, "Min Risk (price units)", minval=0.1, maxval=20.0, group=grpConfirm,
tooltip="Reject signals where risk < this (in $ for XAUUSD).")
grpRisk = "Risk / Targets"
obImpulseAtr = input.float(0.75, "OB Impulse ATR", minval=0.2, maxval=3.0, group=grpRisk)
swingZoneAtr = input.float(0.35, "Swing xZone ATR Width", minval=0.05, maxval=2.0, group=grpRisk)
slBufferAtr = input.float(0.25, "SL Buffer ATR", minval=0.05, maxval=1.5, group=grpRisk)
fallbackStopAtr = input.float(1.0, "Fallback Stop ATR", minval=0.3, maxval=3.0, group=grpRisk)
tp1R = input.float(1.2, "TP1 R", minval=0.5, maxval=5.0, group=grpRisk)
tp2R = input.float(2.0, "TP2 R", minval=1.0, maxval=8.0, group=grpRisk)
structuralClampOnlyBeyond1R = input.bool(true, "Clamp TP to structure only if structure > 1R", group=grpRisk,
tooltip="Prevents tiny TPs when VWAP/PDH sits very close to entry.")
// Mode-driven tuning
confirmPct = mode == "Fast" ? confirmPctBase - 10 : mode == "Strict" ? confirmPctBase + 5 : confirmPctBase
fvgAtrMult = mode == "Fast" ? 0.08 : mode == "Normal" ? 0.14 : 0.22
obImpulseAtrM = mode == "Strict" ? obImpulseAtr * 1.3 : obImpulseAtr
// ============================================================================
// CORE
// ============================================================================
emaFast = ta.ema(close, emaFastLen)
emaSlow = ta.ema(close, emaSlowLen)
vwapValue = ta.vwap(hlc3)
atr = ta.atr(atrLen)
atrAvg = ta.sma(atr, atrAvgLen)
plot(emaFast, "EMA Fast", color=color.aqua)
plot(emaSlow, "EMA Slow", color=color.orange)
plot(vwapValue, "VWAP", linewidth=2, color=color.yellow)
pdh = request.security(syminfo.tickerid, "D", high[1], lookahead=barmerge.lookahead_off)
pdl = request.security(syminfo.tickerid, "D", low[1], lookahead=barmerge.lookahead_off)
plot(showPdhPdl ? pdh : na, "PDH", style=plot.style_linebr, linewidth=2, color=color.new(cSell, 30))
plot(showPdhPdl ? pdl : na, "PDL", style=plot.style_linebr, linewidth=2, color=color.new(cBuy, 30))
newDay = ta.change(time("D")) != 0
// ============================================================================
// SESSIONS (with prev-day NY persistence)
// ============================================================================
inAsia = not na(time(timeframe.period, asiaSession))
inLondon = not na(time(timeframe.period, londonSession))
inNY = not na(time(timeframe.period, nySession))
inLondonKZ = not na(time(timeframe.period, killzoneLondon))
inNYKZ = not na(time(timeframe.period, killzoneNY))
inKillzone = inLondonKZ or inNYKZ
var float asiaHigh = na
var float asiaLow = na
var float londonHigh = na
var float londonLow = na
var float nyHigh = na
var float nyLow = na
var float prevNyHigh = na
var float prevNyLow = na
if newDay
prevNyHigh := nyHigh
prevNyLow := nyLow
asiaHigh := na
asiaLow := na
londonHigh := na
londonLow := na
nyHigh := na
nyLow := na
if inAsia
asiaHigh := na(asiaHigh) ? high : math.max(asiaHigh, high)
asiaLow := na(asiaLow) ? low : math.min(asiaLow, low)
if inLondon
londonHigh := na(londonHigh) ? high : math.max(londonHigh, high)
londonLow := na(londonLow) ? low : math.min(londonLow, low)
if inNY
nyHigh := na(nyHigh) ? high : math.max(nyHigh, high)
nyLow := na(nyLow) ? low : math.min(nyLow, low)
plot(showSessionLevels ? asiaHigh : na, "Asia High", style=plot.style_linebr, color=color.new(color.purple, 30))
plot(showSessionLevels ? asiaLow : na, "Asia Low", style=plot.style_linebr, color=color.new(color.purple, 30))
plot(showSessionLevels ? londonHigh : na, "London High", style=plot.style_linebr, color=color.new(color.blue, 30))
plot(showSessionLevels ? londonLow : na, "London Low", style=plot.style_linebr, color=color.new(color.blue, 30))
plot(showSessionLevels ? prevNyHigh : na, "Prev NY High", style=plot.style_linebr, color=color.new(color.fuchsia, 40))
plot(showSessionLevels ? prevNyLow : na, "Prev NY Low", style=plot.style_linebr, color=color.new(color.fuchsia, 40))
// ============================================================================
// DXY (ignore_invalid_symbol + explicit lookahead)
// ============================================================================
dxyClose = request.security(dxySymbol, dxyTf, close, lookahead=barmerge.lookahead_off, ignore_invalid_symbol=true)
dxyHigh = request.security(dxySymbol, dxyTf, high, lookahead=barmerge.lookahead_off, ignore_invalid_symbol=true)
dxyLow = request.security(dxySymbol, dxyTf, low, lookahead=barmerge.lookahead_off, ignore_invalid_symbol=true)
dxyEma20 = request.security(dxySymbol, dxyTf, ta.ema(close, 20), lookahead=barmerge.lookahead_off, ignore_invalid_symbol=true)
dxyEma50 = request.security(dxySymbol, dxyTf, ta.ema(close, 50), lookahead=barmerge.lookahead_off, ignore_invalid_symbol=true)
dxyPrevHigh = request.security(dxySymbol, dxyTf, ta.highest(high[1], 12), lookahead=barmerge.lookahead_off, ignore_invalid_symbol=true)
dxyPrevLow = request.security(dxySymbol, dxyTf, ta.lowest(low[1], 12), lookahead=barmerge.lookahead_off, ignore_invalid_symbol=true)
dxyOk = not na(dxyClose)
dxyBull = dxyOk and dxyClose > dxyEma20 and dxyEma20 > dxyEma50
dxyBear = dxyOk and dxyClose < dxyEma20 and dxyEma20 < dxyEma50
dxyFlat = dxyOk and not dxyBull and not dxyBear
dxyRejectHigh = dxyOk and dxyHigh > dxyPrevHigh and dxyClose < dxyPrevHigh
dxyReclaimLow = dxyOk and dxyLow < dxyPrevLow and dxyClose > dxyPrevLow
// Stabilized DXY for exit logic (won't flip exits on a single bar)
dxyBullStable = dxyBull and dxyBull[1]
dxyBearStable = dxyBear and dxyBear[1]
// ============================================================================
// HTF
// ============================================================================
tfSeconds = timeframe.in_seconds(timeframe.period)
tfMinutes = na(tfSeconds) ? 5.0 : tfSeconds / 60.0
htfTf = tfMinutes <= 3 ? "5" : tfMinutes <= 5 ? "15" : tfMinutes <= 15 ? "60" : "240"
htfClose = request.security(syminfo.tickerid, htfTf, close, lookahead=barmerge.lookahead_off)
htfEmaFast = request.security(syminfo.tickerid, htfTf, ta.ema(close, emaFastLen), lookahead=barmerge.lookahead_off)
htfEmaSlow = request.security(syminfo.tickerid, htfTf, ta.ema(close, emaSlowLen), lookahead=barmerge.lookahead_off)
htfVwap = request.security(syminfo.tickerid, htfTf, ta.vwap(hlc3), lookahead=barmerge.lookahead_off)
htfBull = htfClose > htfVwap and htfEmaFast > htfEmaSlow
htfBear = htfClose < htfVwap and htfEmaFast < htfEmaSlow
htfRange = not htfBull and not htfBear
goldBull = close > vwapValue and emaFast > emaSlow
goldBear = close < vwapValue and emaFast < emaSlow
emaCompression = math.abs(emaFast - emaSlow) <= atr * 0.18 and math.abs(close - vwapValue) <= atr * 0.25
atrLow = atr < atrAvg * 0.65
bigCandle = math.abs(close - open) > atr * 1.8
chop = emaCompression or atrLow
sessionOk = inKillzone or (mode == "Fast" and (inLondon or inNY))
// ============================================================================
// SWEEPS (now incl. prev-day NY)
// ============================================================================
sweepAsiaLow = not na(asiaLow) and low < asiaLow and close > asiaLow
sweepAsiaHigh = not na(asiaHigh) and high > asiaHigh and close < asiaHigh
sweepLondonLow = not na(londonLow) and low < londonLow and close > londonLow
sweepLondonHigh = not na(londonHigh) and high > londonHigh and close < londonHigh
sweepPdl = low < pdl and close > pdl
sweepPdh = high > pdh and close < pdh
sweepPrevNyLow = not na(prevNyLow) and low < prevNyLow and close > prevNyLow
sweepPrevNyHigh = not na(prevNyHigh) and high > prevNyHigh and close < prevNyHigh
buySweep = sweepAsiaLow or sweepLondonLow or sweepPdl or sweepPrevNyLow
sellSweep = sweepAsiaHigh or sweepLondonHigh or sweepPdh or sweepPrevNyHigh
bullFvg = low > high[2] and low - high[2] >= atr * fvgAtrMult
bearFvg = high < low[2] and low[2] - high >= atr * fvgAtrMult
bullImpulse = close > open and close - open >= atr * obImpulseAtrM and close > emaFast and close > vwapValue
bearImpulse = close < open and open - close >= atr * obImpulseAtrM and close < emaFast and close < vwapValue
bullOb = bullImpulse and close[1] < open[1]
bearOb = bearImpulse and close[1] > open[1]
swingLow = ta.pivotlow(low, swingLen, swingLen)
swingHigh = ta.pivothigh(high, swingLen, swingLen)
srLow = ta.pivotlow(low, srLen, srLen)
srHigh = ta.pivothigh(high, srLen, srLen)
// ============================================================================
// ZONE ARRAYS (Pine v6 generic syntax)
// ============================================================================
var buyBoxes = array.new<box>()
var buyLabels = array.new<label>()
var buyTops = array.new<float>()
var buyBottoms = array.new<float>()
var buyTypes = array.new<string>()
var buyBorns = array.new<int>()
var buyTouched = array.new<bool>()
var sellBoxes = array.new<box>()
var sellLabels = array.new<label>()
var sellTops = array.new<float>()
var sellBottoms = array.new<float>()
var sellTypes = array.new<string>()
var sellBorns = array.new<int>()
var sellTouched = array.new<bool>()
// ----- Zone add helpers (fixes the sequential-overwrite bug:
// each detection now adds its OWN zone instead of clobbering the last)
addBuyZone(float top, float bottom, string ztype) =>
mid = (top + bottom) / 2
far = hideFarZones and math.abs(close - mid) > atr * maxZoneDistanceAtr
bool duplicate = false
if array.size(buyTops) > 0
for i = 0 to array.size(buyTops) - 1
oldMid = (array.get(buyTops, i) + array.get(buyBottoms, i)) / 2
if math.abs(oldMid - mid) <= atr * duplicateAtr
duplicate := true
canAdd = not far and not duplicate and showXZones
if canAdd
zColor = str.contains(ztype, "OB") ? cBuyOb : str.contains(ztype, "Swing") ? cBuySwing : cBuy
bx = box.new(bar_index - 2, top, bar_index + zoneProjection, bottom,
bgcolor=color.new(zColor, 84), border_color=color.new(zColor, 15), border_width=2)
lb = label.new(bar_index + 1, mid, ztype, style=label.style_label_left,
textcolor=color.white, color=color.new(zColor, 10))
array.push(buyBoxes, bx)
array.push(buyLabels, lb)
array.push(buyTops, top)
array.push(buyBottoms, bottom)
array.push(buyTypes, ztype)
array.push(buyBorns, bar_index)
array.push(buyTouched, false)
canAdd
addSellZone(float top, float bottom, string ztype) =>
mid = (top + bottom) / 2
far = hideFarZones and math.abs(close - mid) > atr * maxZoneDistanceAtr
bool duplicate = false
if array.size(sellTops) > 0
for i = 0 to array.size(sellTops) - 1
oldMid = (array.get(sellTops, i) + array.get(sellBottoms, i)) / 2
if math.abs(oldMid - mid) <= atr * duplicateAtr
duplicate := true
canAdd = not far and not duplicate and showXZones
if canAdd
zColor = str.contains(ztype, "OB") ? cSellOb : str.contains(ztype, "Swing") ? cSellSwing : cSell
bx = box.new(bar_index - 2, top, bar_index + zoneProjection, bottom,
bgcolor=color.new(zColor, 84), border_color=color.new(zColor, 15), border_width=2)
lb = label.new(bar_index + 1, mid, ztype, style=label.style_label_left,
textcolor=color.white, color=color.new(zColor, 10))
array.push(sellBoxes, bx)
array.push(sellLabels, lb)
array.push(sellTops, top)
array.push(sellBottoms, bottom)
array.push(sellTypes, ztype)
array.push(sellBorns, bar_index)
array.push(sellTouched, false)
canAdd
// ----- Detection: every type independently (NOT cascading)
if bullFvg
addBuyZone(low, high[2], "BUY xZone FVG")
if bullOb
addBuyZone(high[1], low[1], "BUY xZone OB")
if not na(swingLow)
addBuyZone(swingLow + atr * swingZoneAtr, swingLow - atr * swingZoneAtr, "BUY xZone Swing (confirmed)")
if bearFvg
addSellZone(low[2], high, "SELL xZone FVG")
if bearOb
addSellZone(high[1], low[1], "SELL xZone OB")
if not na(swingHigh)
addSellZone(swingHigh + atr * swingZoneAtr, swingHigh - atr * swingZoneAtr, "SELL xZone Swing (confirmed)")
// ----- Better eviction: combined distance + age score
while array.size(buyBoxes) > maxZonesEachSide
worstIndex = 0
worstScore = -1.0
for i = 0 to array.size(buyBoxes) - 1
mid = (array.get(buyTops, i) + array.get(buyBottoms, i)) / 2
dist = math.abs(close - mid) / math.max(atr, syminfo.mintick)
age = (bar_index - array.get(buyBorns, i)) / math.max(1, zoneExpiry)
sc = dist * 0.6 + age * 0.4
if sc > worstScore
worstScore := sc
worstIndex := i
box.delete(array.get(buyBoxes, worstIndex))
label.delete(array.get(buyLabels, worstIndex))
array.remove(buyBoxes, worstIndex)
array.remove(buyLabels, worstIndex)
array.remove(buyTops, worstIndex)
array.remove(buyBottoms, worstIndex)
array.remove(buyTypes, worstIndex)
array.remove(buyBorns, worstIndex)
array.remove(buyTouched, worstIndex)
while array.size(sellBoxes) > maxZonesEachSide
worstIndex = 0
worstScore = -1.0
for i = 0 to array.size(sellBoxes) - 1
mid = (array.get(sellTops, i) + array.get(sellBottoms, i)) / 2
dist = math.abs(close - mid) / math.max(atr, syminfo.mintick)
age = (bar_index - array.get(sellBorns, i)) / math.max(1, zoneExpiry)
sc = dist * 0.6 + age * 0.4
if sc > worstScore
worstScore := sc
worstIndex := i
box.delete(array.get(sellBoxes, worstIndex))
label.delete(array.get(sellLabels, worstIndex))
array.remove(sellBoxes, worstIndex)
array.remove(sellLabels, worstIndex)
array.remove(sellTops, worstIndex)
array.remove(sellBottoms, worstIndex)
array.remove(sellTypes, worstIndex)
array.remove(sellBorns, worstIndex)
array.remove(sellTouched, worstIndex)
// ----- Zone maintenance / cleanup
if array.size(buyBoxes) > 0
for i = array.size(buyBoxes) - 1 to 0
bx = array.get(buyBoxes, i)
lb = array.get(buyLabels, i)
top = array.get(buyTops, i)
bottom = array.get(buyBottoms, i)
mid = (top + bottom) / 2
born = array.get(buyBorns, i)
touchedNow = low <= top and high >= bottom
touchedEver = array.get(buyTouched, i) or touchedNow
array.set(buyTouched, i, touchedEver)
invalid = close < bottom
expired = bar_index - born > zoneExpiry
mitigated = touchedEver and close > top
far = hideFarZones and math.abs(close - mid) > atr * maxZoneDistanceAtr
remove = invalid or expired or (removeZoneAfterTouch and touchedNow) or (removeZoneAfterMitigation and mitigated) or far
if remove
box.delete(bx)
label.delete(lb)
array.remove(buyBoxes, i)
array.remove(buyLabels, i)
array.remove(buyTops, i)
array.remove(buyBottoms, i)
array.remove(buyTypes, i)
array.remove(buyBorns, i)
array.remove(buyTouched, i)
else
box.set_right(bx, bar_index + zoneProjection)
label.set_x(lb, bar_index + 1)
label.set_y(lb, mid)
if array.size(sellBoxes) > 0
for i = array.size(sellBoxes) - 1 to 0
bx = array.get(sellBoxes, i)
lb = array.get(sellLabels, i)
top = array.get(sellTops, i)
bottom = array.get(sellBottoms, i)
mid = (top + bottom) / 2
born = array.get(sellBorns, i)
touchedNow = high >= bottom and low <= top
touchedEver = array.get(sellTouched, i) or touchedNow
array.set(sellTouched, i, touchedEver)
invalid = close > top
expired = bar_index - born > zoneExpiry
mitigated = touchedEver and close < bottom
far = hideFarZones and math.abs(close - mid) > atr * maxZoneDistanceAtr
remove = invalid or expired or (removeZoneAfterTouch and touchedNow) or (removeZoneAfterMitigation and mitigated) or far
if remove
box.delete(bx)
label.delete(lb)
array.remove(sellBoxes, i)
array.remove(sellLabels, i)
array.remove(sellTops, i)
array.remove(sellBottoms, i)
array.remove(sellTypes, i)
array.remove(sellBorns, i)
array.remove(sellTouched, i)
else
box.set_right(bx, bar_index + zoneProjection)
label.set_x(lb, bar_index + 1)
label.set_y(lb, mid)
// ============================================================================
// SUPPORT / RESISTANCE
// ============================================================================
var line supportLine = na
var line resistanceLine = na
var label supportLabel = na
var label resistanceLabel= na
var float supportLevel = na
var float resistanceLevel= na
if showSR and not na(srLow)
line.delete(supportLine)
label.delete(supportLabel)
supportLevel := srLow
supportLine := line.new(bar_index - srLen, supportLevel, bar_index + zoneProjection, supportLevel,
extend=extend.right, color=color.new(cBuy, 10), width=2)
supportLabel := label.new(bar_index + 1, supportLevel, "Support",
style=label.style_label_left, textcolor=color.white, color=color.new(cBuy, 25))
if showSR and not na(srHigh)
line.delete(resistanceLine)
label.delete(resistanceLabel)
resistanceLevel := srHigh
resistanceLine := line.new(bar_index - srLen, resistanceLevel, bar_index + zoneProjection, resistanceLevel,
extend=extend.right, color=color.new(cSell, 10), width=2)
resistanceLabel := label.new(bar_index + 1, resistanceLevel, "Resistance",
style=label.style_label_left, textcolor=color.white, color=color.new(cSell, 25))
if not na(supportLine)
if close < supportLevel - atr * 0.15
line.delete(supportLine)
label.delete(supportLabel)
supportLine := na
supportLabel := na
supportLevel := na
else
label.set_x(supportLabel, bar_index + 1)
label.set_y(supportLabel, supportLevel)
if not na(resistanceLine)
if close > resistanceLevel + atr * 0.15
line.delete(resistanceLine)
label.delete(resistanceLabel)
resistanceLine := na
resistanceLabel := na
resistanceLevel := na
else
label.set_x(resistanceLabel, bar_index + 1)
label.set_y(resistanceLabel, resistanceLevel)
// ============================================================================
// ACTIVE ZONE LOOKUP
// ============================================================================
bool nearBuyZone = false
bool nearSellZone = false
float activeBuyTop = na
float activeBuyBottom = na
string activeBuyType = "Clear"
float activeSellTop = na
float activeSellBottom = na
string activeSellType = "Clear"
if array.size(buyBoxes) > 0
bestDist = 1e10
for i = 0 to array.size(buyBoxes) - 1
top = array.get(buyTops, i)
bottom = array.get(buyBottoms, i)
touching = low <= top and high >= bottom
d = math.abs(close - ((top + bottom) / 2))
if touching
nearBuyZone := true
if d < bestDist
bestDist := d
activeBuyTop := top
activeBuyBottom := bottom
activeBuyType := array.get(buyTypes, i)
if array.size(sellBoxes) > 0
bestDist = 1e10
for i = 0 to array.size(sellBoxes) - 1
top = array.get(sellTops, i)
bottom = array.get(sellBottoms, i)
touching = high >= bottom and low <= top
d = math.abs(close - ((top + bottom) / 2))
if touching
nearSellZone := true
if d < bestDist
bestDist := d
activeSellTop := top
activeSellBottom := bottom
activeSellType := array.get(sellTypes, i)
nearSupport = not na(supportLevel) and math.abs(close - supportLevel) <= atr * 0.35
nearResistance = not na(resistanceLevel) and math.abs(close - resistanceLevel) <= atr * 0.35
atPdl = low <= pdl and close >= pdl
atPdh = high >= pdh and close <= pdh
bullRejection = close > open and close > emaFast
bearRejection = close < open and close < emaFast
// ============================================================================
// SCORING (all weights configurable)
// ============================================================================
buyScore = 0
buyScore += goldBull ? wGoldTrend : 0
buyScore += htfBull ? wHtfTrend : (htfRange and mode == "Fast" ? math.round(wHtfTrend * 0.33) : 0)
buyScore += dxyBear ? wDxy : (dxyRejectHigh ? wDxyReject : (dxyFlat and mode == "Fast" ? 8 : 0))
buyScore += (nearBuyZone or atPdl or nearSupport) ? wZone : 0
buyScore += buySweep ? wSweep : 0
buyScore += close > vwapValue ? wVwap : 0
buyScore += bullRejection ? wRejCandle : 0
buyScore += sessionOk ? wSession : 0
buyScore -= chop ? pChop : 0
buyScore -= bigCandle ? pBigCandle : 0
buyScore -= dxyBull ? wDxy : 0
buyScore -= pauseTrading ? 100 : 0
sellScore = 0
sellScore += goldBear ? wGoldTrend : 0
sellScore += htfBear ? wHtfTrend : (htfRange and mode == "Fast" ? math.round(wHtfTrend * 0.33) : 0)
sellScore += dxyBull ? wDxy : (dxyReclaimLow ? wDxyReject : (dxyFlat and mode == "Fast" ? 8 : 0))
sellScore += (nearSellZone or atPdh or nearResistance) ? wZone : 0
sellScore += sellSweep ? wSweep : 0
sellScore += close < vwapValue ? wVwap : 0
sellScore += bearRejection ? wRejCandle : 0
sellScore += sessionOk ? wSession : 0
sellScore -= chop ? pChop : 0
sellScore -= bigCandle ? pBigCandle : 0
sellScore -= dxyBear ? wDxy : 0
sellScore -= pauseTrading ? 100 : 0
buyPct = math.max(0, math.min(buyScore, 100))
sellPct = math.max(0, math.min(sellScore, 100))
// Diagnostic: lifetime max of each side, so user can verify both directions fire
var int maxBuyPctEver = 0
var int maxSellPctEver = 0
maxBuyPctEver := math.max(maxBuyPctEver, buyPct)
maxSellPctEver := math.max(maxSellPctEver, sellPct)
buyWatch = buyPct >= confirmPct - watchOffset and buyPct < confirmPct and not pauseTrading
sellWatch = sellPct >= confirmPct - watchOffset and sellPct < confirmPct and not pauseTrading
buyConfirmed = barstate.isconfirmed and buyPct >= confirmPct and not pauseTrading
sellConfirmed= barstate.isconfirmed and sellPct >= confirmPct and not pauseTrading
// ============================================================================
// PLAN (entry / SL / TP) with non-destructive structural clamping
// ============================================================================
buyEntry = nearBuyZone ? activeBuyTop : close
buyStop = nearBuyZone ? (activeBuyBottom - atr * slBufferAtr) : (buySweep ? (low - atr * slBufferAtr) : (close - atr * fallbackStopAtr))
buyRisk = buyEntry - buyStop
sellEntry = nearSellZone ? activeSellBottom : close
sellStop = nearSellZone ? (activeSellTop + atr * slBufferAtr) : (sellSweep ? (high + atr * slBufferAtr) : (close + atr * fallbackStopAtr))
sellRisk = sellStop - sellEntry
float buyTp1 = na
float buyTp2 = na
float sellTp1 = na
float sellTp2 = na
if buyRisk > syminfo.mintick
buyTp1 := buyEntry + buyRisk * tp1R
buyTp2 := buyEntry + buyRisk * tp2R
structThr = structuralClampOnlyBeyond1R ? (buyEntry + buyRisk) : buyEntry
if not na(vwapValue) and vwapValue > structThr and vwapValue < buyTp1
buyTp1 := vwapValue
if not na(asiaHigh) and asiaHigh > structThr and asiaHigh < buyTp2
buyTp2 := asiaHigh
if not na(londonHigh) and londonHigh > structThr and londonHigh < buyTp2
buyTp2 := londonHigh
if pdh > structThr and pdh < buyTp2
buyTp2 := pdh
if sellRisk > syminfo.mintick
sellTp1 := sellEntry - sellRisk * tp1R
sellTp2 := sellEntry - sellRisk * tp2R
structThr = structuralClampOnlyBeyond1R ? (sellEntry - sellRisk) : sellEntry
if not na(vwapValue) and vwapValue < structThr and vwapValue > sellTp1
sellTp1 := vwapValue
if not na(asiaLow) and asiaLow < structThr and asiaLow > sellTp2
sellTp2 := asiaLow
if not na(londonLow) and londonLow < structThr and londonLow > sellTp2
sellTp2 := londonLow
if pdl < structThr and pdl > sellTp2
sellTp2 := pdl
// Reject signals with risk smaller than minRiskPoints
buyRiskOk = buyRisk >= minRiskPoints
sellRiskOk = sellRisk >= minRiskPoints
// ============================================================================
// TRADE LOCKING
// ============================================================================
var int tradeSide = 0
var int tradeBar = na
var float lockedEntry = na
var float lockedStop = na
var float lockedTp1 = na
var float lockedTp2 = na
var int lockedPct = na
var string lockedReason = "Clear"
newBuyTrade = buyConfirmed and buyRiskOk and tradeSide != 1
newSellTrade = sellConfirmed and sellRiskOk and tradeSide != -1
if newBuyTrade
tradeSide := 1
tradeBar := bar_index
lockedEntry := buyEntry
lockedStop := buyStop
lockedTp1 := buyTp1
lockedTp2 := buyTp2
lockedPct := buyPct
lockedReason := nearBuyZone ? activeBuyType : (buySweep ? "Liquidity Sweep" : (atPdl ? "PDL" : (nearSupport ? "Support" : "Momentum")))
if newSellTrade
tradeSide := -1
tradeBar := bar_index
lockedEntry := sellEntry
lockedStop := sellStop
lockedTp1 := sellTp1
lockedTp2 := sellTp2
lockedPct := sellPct
lockedReason := nearSellZone ? activeSellType : (sellSweep ? "Liquidity Sweep" : (atPdh ? "PDH" : (nearResistance ? "Resistance" : "Momentum")))
// ----- Less twitchy exits: require VWAP *and* EMA breach (was 'or'),
// and stable DXY flip across two bars instead of one
exitBuy = tradeSide == 1 and bar_index > tradeBar and (sellPct >= confirmPct or (close < vwapValue and close < emaFast) or dxyBullStable)
exitSell = tradeSide == -1 and bar_index > tradeBar and (buyPct >= confirmPct or (close > vwapValue and close > emaFast) or dxyBearStable)
if exitBuy or exitSell
tradeSide := 0
// ============================================================================
// ADVICE
// ============================================================================
advice = exitBuy ? "EXIT BUY" : exitSell ? "EXIT SELL" :
newBuyTrade ? "BUY NOW" : newSellTrade ? "SELL NOW" :
buyWatch ? "BUY WATCH" : sellWatch ? "SELL WATCH" :
tradeSide == 1 ? "HOLD BUY" : tradeSide == -1 ? "HOLD SELL" :
pauseTrading ? "PAUSED" :
buyPct > sellPct and buyPct >= 50 ? "BUY BIAS" :
sellPct > buyPct and sellPct >= 50 ? "SELL BIAS" : "WAIT"
adviceColor = str.contains(advice, "BUY") ? cBuy : str.contains(advice, "SELL") ? cSell :
str.contains(advice, "EXIT") ? cWarn : color.gray
advicePct = str.contains(advice, "BUY") ? buyPct : str.contains(advice, "SELL") ? sellPct : math.max(buyPct, sellPct)
planSide = tradeSide != 0 ? tradeSide : (str.contains(advice, "BUY") ? 1 : (str.contains(advice, "SELL") ? -1 : 0))
planEntry = tradeSide != 0 ? lockedEntry : (planSide == 1 ? buyEntry : (planSide == -1 ? sellEntry : na))
planStop = tradeSide != 0 ? lockedStop : (planSide == 1 ? buyStop : (planSide == -1 ? sellStop : na))
planTp1 = tradeSide != 0 ? lockedTp1 : (planSide == 1 ? buyTp1 : (planSide == -1 ? sellTp1 : na))
planTp2 = tradeSide != 0 ? lockedTp2 : (planSide == 1 ? buyTp2 : (planSide == -1 ? sellTp2 : na))
planPct = tradeSide != 0 ? lockedPct : (planSide == 1 ? buyPct : (planSide == -1 ? sellPct : na))
// R:R for display
float planRR = na
if planSide == 1 and not na(planEntry) and not na(planStop) and not na(planTp2)
rk = planEntry - planStop
rw = planTp2 - planEntry
if rk > 0
planRR := rw / rk
if planSide == -1 and not na(planEntry) and not na(planStop) and not na(planTp2)
rk = planStop - planEntry
rw = planEntry - planTp2
if rk > 0
planRR := rw / rk
// ============================================================================
// LIVE PLAN RENDERING
// ============================================================================
var label adviceLabel = na
var line entryLine = na
var line stopLine = na
var line tp1Line = na
var line tp2Line = na
var label entryLabel = na
var label stopLabel = na
var label tp1Label = na
var label tp2Label = na
var label beNoteLabel = na
if barstate.islast
label.delete(adviceLabel)
line.delete(entryLine)
line.delete(stopLine)
line.delete(tp1Line)
line.delete(tp2Line)
label.delete(entryLabel)
label.delete(stopLabel)
label.delete(tp1Label)
label.delete(tp2Label)
label.delete(beNoteLabel)
if showLiveAdvice
adviceY = planSide == 1 ? low - atr * 0.6 : high + atr * 0.6
adviceStyle = planSide == 1 ? label.style_label_up : label.style_label_down
adviceLabel := label.new(bar_index, adviceY, advice + " " + str.tostring(advicePct) + "%",
style=adviceStyle, textcolor=color.white, color=color.new(adviceColor, 0))
if showLivePlan and planSide != 0 and not na(planEntry) and not na(planStop) and not na(planTp1) and not na(planTp2)
planColor = planSide == 1 ? cBuy : cSell
stopColor = planSide == 1 ? cSell : cBuy
planText = (planSide == 1 ? "BUY " : "SELL ") + str.tostring(planPct) + "%"
rrText = na(planRR) ? "" : " (" + str.tostring(planRR, "#.##") + "R)"
entryLine := line.new(bar_index, planEntry, bar_index + zoneProjection, planEntry, width=2, color=color.new(planColor, 0))
stopLine := line.new(bar_index, planStop, bar_index + zoneProjection, planStop, width=2, color=color.new(stopColor, 0))
tp1Line := line.new(bar_index, planTp1, bar_index + zoneProjection, planTp1, width=2, color=color.new(planColor, 25))
tp2Line := line.new(bar_index, planTp2, bar_index + zoneProjection, planTp2, width=2, color=color.new(planColor, 45))
entryLabel := label.new(bar_index + zoneProjection, planEntry, planText + " ENTRY" + rrText, style=label.style_label_left, textcolor=color.white, color=color.new(planColor, 0))
stopLabel := label.new(bar_index + zoneProjection, planStop, "SL", style=label.style_label_left, textcolor=color.white, color=color.new(stopColor, 0))
tp1Label := label.new(bar_index + zoneProjection, planTp1, "TP1", style=label.style_label_left, textcolor=color.white, color=color.new(planColor, 25))
tp2Label := label.new(bar_index + zoneProjection, planTp2, "TP2", style=label.style_label_left, textcolor=color.white, color=color.new(planColor, 45))
if showBeNote
beY = (planTp1 + planEntry) / 2
beNoteLabel := label.new(bar_index + zoneProjection, beY, "→ Move SL to BE @ TP1",
style=label.style_label_left, textcolor=color.white, color=color.new(color.gray, 20), size=size.small)
// ============================================================================
// HISTORICAL SIGNAL MARKERS (so you can SEE every past trigger, not just live)
// ============================================================================
plotshape(showHistorical and newBuyTrade, "BUY NOW", shape.triangleup, location.belowbar, color.new(cBuy, 0), size=size.small, text="BUY", textcolor=color.white)
plotshape(showHistorical and newSellTrade, "SELL NOW", shape.triangledown, location.abovebar, color.new(cSell, 0), size=size.small, text="SELL", textcolor=color.white)
plotshape(showHistorical and buyWatch, "BUY WATCH", shape.circle, location.belowbar, color.new(cBuy, 50), size=size.tiny)
plotshape(showHistorical and sellWatch, "SELL WATCH", shape.circle, location.abovebar, color.new(cSell, 50), size=size.tiny)
plotshape(showHistorical and exitBuy, "EXIT BUY", shape.xcross, location.abovebar, color.new(cWarn, 0), size=size.tiny)
plotshape(showHistorical and exitSell, "EXIT SELL", shape.xcross, location.belowbar, color.new(cWarn, 0), size=size.tiny)
// ============================================================================
// DASHBOARD (compact 7-row layout)
// ============================================================================
zoneText = nearBuyZone ? activeBuyType : (nearSellZone ? activeSellType :
(buySweep ? "Buy Sweep" : (sellSweep ? "Sell Sweep" :
(atPdl ? "PDL" : (atPdh ? "PDH" :
(nearSupport ? "Support" : (nearResistance ? "Resistance" : "Clear")))))))
sessionText = inLondonKZ ? "Lon KZ" : (inNYKZ ? "NY KZ" :
(inAsia ? "Asia" : (inLondon ? "London" : (inNY ? "NY" : "Off"))))
filterText = pauseTrading ? "Paused" : (chop ? "Chop" :
(bigCandle ? "Extd" : (sessionOk ? "OK" : "Weak")))
// Friendly HTF label (60→1h, 240→4h)
htfDisplay = htfTf == "60" ? "1h" : htfTf == "240" ? "4h" : htfTf + "m"
// Compact merged strings
goldShort = goldBull ? "Bull" : (goldBear ? "Bear" : "Rng")
htfShort = htfBull ? "Bull" : (htfBear ? "Bear" : "Rng")
dxyShort = not dxyOk ? "$?" : (dxyBull ? "$+" : (dxyBear ? "$-" : "$="))
trendCell = goldShort + " / " + htfShort + " " + htfDisplay + " / " + dxyShort
sessCell = sessionText + " · " + filterText
zoneCell = zoneText + " (" + str.tostring(array.size(buyBoxes)) + "B/" + str.tostring(array.size(sellBoxes)) + "S)"
scoreCell = str.tostring(buyPct) + "% ▲ / " + str.tostring(sellPct) + "% ▼"
peakCell = str.tostring(maxBuyPctEver) + "% ▲ / " + str.tostring(maxSellPctEver) + "% ▼"
adviceArrow = str.contains(advice, "BUY") ? "▲ " : str.contains(advice, "SELL") ? "▼ " : str.contains(advice, "EXIT") ? "✕ " : "• "
adviceCell = adviceArrow + advice + " " + str.tostring(advicePct) + "%"
planCell = (na(planRR) ? "—" : str.tostring(planRR, "#.##") + "R") + " · " + (tradeSide == 1 ? "▲ Long" : (tradeSide == -1 ? "▼ Short" : "• Flat")) + (lockedReason != "Clear" and tradeSide != 0 ? " (" + lockedReason + ")" : "")
// Dominance tint for the scores cell — strong enough alpha to be visible in dark mode
scoreBg = buyPct > sellPct ? color.new(cBuy, 55) : (sellPct > buyPct ? color.new(cSell, 55) : color.new(color.gray, 70))
adviceBg = color.new(adviceColor, 30)
planTextColor = tradeSide == 1 ? cBuy : (tradeSide == -1 ? cSell : cTextDim)
var table dash = table.new(position.top_right, 2, 7, border_width=1, frame_color=color.new(color.gray, 40), frame_width=1)
if barstate.islast and showDashboard
// Advice (most important — biggest text, strongest highlight)
table.cell(dash, 0, 0, "Advice", text_color=cTextDim, text_size=size.small, text_halign=text.align_left)
table.cell(dash, 1, 0, adviceCell, text_color=cTextHi, text_size=size.normal, text_halign=text.align_right, bgcolor=adviceBg)
// Plan (R:R + position)
table.cell(dash, 0, 1, "Plan", text_color=cTextDim, text_size=size.small, text_halign=text.align_left)
table.cell(dash, 1, 1, planCell, text_color=planTextColor, text_size=size.small, text_halign=text.align_right)
// Scores (live)
table.cell(dash, 0, 2, "Scores", text_color=cTextDim, text_size=size.small, text_halign=text.align_left)
table.cell(dash, 1, 2, scoreCell, text_color=cTextHi, text_size=size.small, text_halign=text.align_right, bgcolor=scoreBg)
// Peak (lifetime)
table.cell(dash, 0, 3, "Peak", text_color=cTextDim, text_size=size.small, text_halign=text.align_left)
table.cell(dash, 1, 3, peakCell, text_color=cTextHi, text_size=size.small, text_halign=text.align_right)
// Zone + counts
table.cell(dash, 0, 4, "Zone", text_color=cTextDim, text_size=size.small, text_halign=text.align_left)
table.cell(dash, 1, 4, zoneCell, text_color=cTextHi, text_size=size.small, text_halign=text.align_right)
// Trend (Gold/HTF/DXY)
table.cell(dash, 0, 5, "Trend", text_color=cTextDim, text_size=size.small, text_halign=text.align_left)
table.cell(dash, 1, 5, trendCell, text_color=cTextHi, text_size=size.small, text_halign=text.align_right)
// Session + filter
table.cell(dash, 0, 6, "Session", text_color=cTextDim, text_size=size.small, text_halign=text.align_left)
table.cell(dash, 1, 6, sessCell, text_color=cTextHi, text_size=size.small, text_halign=text.align_right)
// ============================================================================
// ALERTS
// ============================================================================
alertcondition(buyWatch, "XAUUSD BUY WATCH", "BUY WATCH forming.")
alertcondition(sellWatch, "XAUUSD SELL WATCH", "SELL WATCH forming.")
alertcondition(newBuyTrade, "XAUUSD BUY NOW", "Confirmed BUY NOW.")
alertcondition(newSellTrade,"XAUUSD SELL NOW", "Confirmed SELL NOW.")
alertcondition(exitBuy, "XAUUSD EXIT BUY", "Exit BUY: conditions changed.")
alertcondition(exitSell, "XAUUSD EXIT SELL", "Exit SELL: conditions changed.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment