//@version=6
indicator('Cnagda Liquidity Trading System', overlay=true, max_labels_count=500)
// ==== Inputs ====
swingLen = [Link](7, 'Swing Strength')
volLength = [Link](50, 'Volume Avg Period')
showSwingLabels = [Link](false, 'Show HH/HL/LH/LL Labels on Chart?')
showDashboard = [Link](true, 'Show Dashboard?')
showPools = [Link](true, 'Show Liquidity Pools (EQH/EQL)?')
useAtrTol = [Link](true, 'Use ATR-based tolerance?')
atrLenTol = [Link](14, 'ATR Length', minval=1)
atrMultTol = [Link](0.20, 'ATR Multiplier', step=0.01)
pctTol = [Link](0.10, 'Pct Tol (% of price)', step=0.01)
minBarsGap = [Link](3, 'Min bars gap between pivots', minval=1)
lookbackPivots = [Link](300, 'Max pools to remember', minval=2)
persistMitigated= [Link](false, 'Keep mitigated pools dashed/faded?')
showNearestPools= [Link](true, 'Show nearest BSL/SSL label?')
// ==== New Inputs for Dynamic Trendlines ====
showDynTL = [Link](true, 'Dynamic Trendline (on liquidity grab)?')
extendRightTL = [Link](true, 'Extend Dynamic Trendline Right?')
// ==== Imbalance Detection ====
var box[] imbalanceBoxes = array.new_box()
for i = 1 to 20
body = [Link](close[i] - open[i])
candleRange = high[i] - low[i]
wickTop = high[i] - [Link](close[i], open[i])
wickBottom = [Link](close[i], open[i]) - low[i]
bodyPerc = (body / candleRange) * 100
wickPerc = wickTop / candleRange * 100
bullishImb = close[i] > open[i] and bodyPerc > 40.0 and wickPerc > 60.0
wickPercDn = wickBottom / candleRange * 100
bearishImb = close[i] < open[i] and bodyPerc > 40.0 and wickPercDn > 60.0
boxExists = false
if [Link](imbalanceBoxes) > 0
for j = 0 to [Link](imbalanceBoxes) - 1
b = [Link](imbalanceBoxes, j)
if not na(b)
if box.get_left(b) == bar_index - i
boxExists := true
if bullishImb and not boxExists
b = [Link](bar_index - i, low[i], bar_index - (i - 1), high[i],
border_color=[Link], bgcolor=[Link]([Link], 82))
[Link](imbalanceBoxes, b)
if bearishImb and not boxExists
b = [Link](bar_index - i, low[i], bar_index - (i - 1), high[i],
border_color=[Link], bgcolor=[Link]([Link], 82))
[Link](imbalanceBoxes, b)
if [Link](imbalanceBoxes) > 0
for j = [Link](imbalanceBoxes) - 1 to 0 by -1
b = [Link](imbalanceBoxes, j)
if not na(b)
if box.get_right(b) < bar_index - 20
[Link](b)
[Link](imbalanceBoxes, j)
// ==== Swings & Colors ====
ph = [Link](high, swingLen, swingLen)
pl = [Link](low, swingLen, swingLen)
hhColor = [Link]([Link], 0)
hlColor = [Link]([Link], 0)
llColor = [Link]([Link], 0)
lhColor = [Link]([Link], 0)
var float prevHigh = na
var float prevLow = na
var int swingDir = 0
var string lastConfirmedSwing = ''
var float reversalPrice = na
var float buyOB = na
var float sellOB = na
// ===== Volume coloring =====
volAvg = [Link](volume, volLength)
volArr = array.new_float(volLength, na)
for i = 0 to volLength - 1
[Link](volArr, i, volume[i])
sortedVolArr = [Link](volArr)
[Link](sortedVolArr, [Link])
volRank = 0
for i = 0 to volLength - 1
if volume == [Link](sortedVolArr, i)
volRank := i + 1
color barCol = na
if volRank == 1
barCol := [Link]
else if volRank <= 2
barCol := [Link]
else if volRank == volLength
barCol := [Link]([Link], 0)
else if volRank == volLength - 1
barCol := [Link]([Link], 25)
else if volRank == volLength - 2
barCol := [Link]([Link], 50)
else if volRank >= volLength - 4
barCol := [Link]([Link], 75)
else
p = [Link](255 * (volRank / (volLength - 4)))
barCol := [Link]([Link], p)
insideBar = high < high[1] and low > low[1]
if insideBar
barCol := [Link]
barcolor(barCol)
// ============= Swings Detection ==============
if not na(pl)
if not na(prevLow)
if pl > prevLow
swingDir := 1
lastConfirmedSwing := 'HL'
reversalPrice := low[swingLen]
buyOB := low[swingLen]
if bar_index >= swingLen and showSwingLabels
[Link](bar_index, low, 'HL', color=hlColor,
textcolor=[Link], style=label.style_label_up)
else
if showSwingLabels
[Link](bar_index, low, 'LL', color=llColor,
textcolor=[Link], style=label.style_label_up)
swingDir := -1
lastConfirmedSwing := 'LL'
reversalPrice := low[swingLen]
buyOB := low[swingLen]
prevLow := pl
if not na(ph)
if not na(prevHigh)
if ph > prevHigh
swingDir := 1
lastConfirmedSwing := 'HH'
reversalPrice := high[swingLen]
sellOB := high[swingLen]
if bar_index >= swingLen and showSwingLabels
[Link](bar_index, high, 'HH', color=hhColor,
textcolor=[Link], style=label.style_label_down)
else
if showSwingLabels
[Link](bar_index, high, 'LH', color=lhColor,
textcolor=[Link], style=label.style_label_down)
swingDir := -1
lastConfirmedSwing := 'LH'
reversalPrice := high[swingLen]
sellOB := high[swingLen]
prevHigh := ph
//==== Advanced Liquidity Grab logic ====
liqGrabBull = not na(prevLow) and low < prevLow and close > prevLow
liqGrabBear = not na(prevHigh) and high > prevHigh and close < prevHigh
wickLargeBull = (prevLow - low) > 1.5 * [Link](open - close)
wickLargeBear = (high - prevHigh) > 1.5 * [Link](open - close)
bodySmallBull = [Link](open - close) < (high - low) * 0.35
bodySmallBear = [Link](open - close) < (high - low) * 0.35
volSpike = volume > [Link](volume, 20) * 1.7
advancedLiqGrabBull = liqGrabBull and wickLargeBull and bodySmallBull and volSpike
advancedLiqGrabBear = liqGrabBear and wickLargeBear and bodySmallBear and volSpike
var label lastAdvLiqBull = na
var label lastAdvLiqBear = na
if advancedLiqGrabBull
if not na(lastAdvLiqBull)
[Link](lastAdvLiqBull)
lastAdvLiqBull := [Link](bar_index, low - [Link](3) * 0.10, "BULLISH
LIQUIDITY\nGRAB", color=[Link], textcolor=[Link],
style=label.style_label_up, size=[Link])
if advancedLiqGrabBear
if not na(lastAdvLiqBear)
[Link](lastAdvLiqBear)
lastAdvLiqBear := [Link](bar_index, high + [Link](3) * 0.10, "BEARISH
LIQUIDITY\nGRAB", color=[Link], textcolor=[Link],
style=label.style_label_down, size=[Link])
// =========== Buy & Sell OB ===========
plot((swingDir == 1 and not na(buyOB)) ? buyOB : na, title='Buy OB',
color=[Link], linewidth=2, style=plot.style_linebr)
plot((swingDir == -1 and not na(sellOB)) ? sellOB : na, title='Sell OB',
color=[Link], linewidth=2, style=plot.style_linebr)
// ========== Candlestick Patterns with Offset ==========
isSwingLow = not na([Link](low, swingLen, swingLen))
isSwingHigh = not na([Link](high, swingLen, swingLen))
downTrendBefore = close[1] < close[14] and close[14] < close[15]
upTrendBefore = close[1] > close[14] and close[14] > close[15]
bullEngulf = close > open and close[1] < open[1] and close > open[1] and open <
close[1]
effectiveBullEngulf = bullEngulf and (isSwingLow or downTrendBefore)
bearEngulf = close < open and close[1] > open[1] and close < open[1] and open >
close[1]
effectiveBearEngulf = bearEngulf and (isSwingHigh or upTrendBefore)
bigDownMove = (high[1] - low[1]) > [Link](10)
hammer = (high - low) > 3 * [Link](open - close) and ((close - low) / (0.001 +
high - low)) > 0.6 and ((open - low) / (0.001 + high - low)) > 0.6
effectiveHammer = hammer and bigDownMove
shootingStar = (high - low) > 3 * [Link](open - close) and ((high - close) /
(0.001 + high - low)) > 0.6 and ((high - open) / (0.001 + high - low)) > 0.6
effectiveShootingStar = shootingStar and (isSwingHigh or upTrendBefore)
hangingMan = (high - low) > 3 * [Link](open - close) and ((close - low) / (0.001
+ high - low)) > 0.6 and ((open - low) / (0.001 + high - low)) > 0.6
effectiveHangingMan = hangingMan and (isSwingHigh or upTrendBefore)
doji = [Link](close - open) <= (high - low) * 0.1
highVol = volume > [Link](volume, 20) * 1.5
effectiveDoji = doji and highVol
morningStar = close[14] < open[14] and close[1] < open[1] and close > open and
close > close[1] and close > open[14] and low[1] < low[14] and low > low[1] and
close > close[1]
effectiveMorningStar = morningStar and (isSwingLow or downTrendBefore)
eveningStar = close[14] > open[14] and close[1] > open[1] and close < open and
close < close[1] and close < open[14] and high[1] > high[14] and high < high[1] and
close < close[1]
effectiveEveningStar = eveningStar and (isSwingHigh or upTrendBefore)
piercing = close < open and close[1] > open[1] and open < close and close > open[1]
and close < open
effectivePiercing = piercing and downTrendBefore
var label lastCandleLabel = na
string patternName = na
float patternY = na
color patternCol = na
color patternTxtCol = [Link]
patternStyle = label.style_label_up
float patternOffset = [Link](3)*0.10
if effectiveBullEngulf
patternName := "Bull Engulf"
patternY := low - patternOffset
patternCol := [Link]
patternStyle := label.style_label_up
if effectiveBearEngulf
patternName := "Bear Engulf"
patternY := high + patternOffset
patternCol := [Link]
patternStyle := label.style_label_down
if effectiveHammer
patternName := "Hammer"
patternY := low - patternOffset
patternCol := [Link]
patternTxtCol := [Link]
patternStyle := label.style_label_up
if effectiveShootingStar
patternName := "Shooting Star"
patternY := high + patternOffset
patternCol := [Link]
patternStyle := label.style_label_down
if effectiveHangingMan
patternName := "Hanging Man"
patternY := low - patternOffset
patternCol := [Link]([Link], 0)
patternTxtCol := [Link]
patternStyle := label.style_label_up
if effectiveDoji
patternName := "Doji"
patternY := high + patternOffset
patternCol := [Link]
patternStyle := label.style_label_down
if effectiveMorningStar
patternName := "Morning Star"
patternY := low - patternOffset
patternCol := [Link]
patternStyle := label.style_label_up
if effectiveEveningStar
patternName := "Evening Star"
patternY := high + patternOffset
patternCol := [Link]
patternStyle := label.style_label_down
if effectivePiercing
patternName := "Piercing"
patternY := low - patternOffset
patternCol := [Link]
patternStyle := label.style_label_up
if not na(patternName)
if not na(lastCandleLabel)
[Link](lastCandleLabel)
lastCandleLabel := [Link](bar_index, patternY, patternName,
color=patternCol, textcolor=patternTxtCol, style=patternStyle, size=[Link])
// ========== Liquidity Pools =============
atrVal = [Link](atrLenTol)
tol = useAtrTol ? atrVal * atrMultTol : close * pctTol * 0.01
var line[] bslLines = array.new_line()
var float[] bslPrices = array.new_float()
var int[] bslOriginBars = array.new_int()
var bool[] bslMitigated = array.new_bool()
var line[] sslLines = array.new_line()
var float[] sslPrices = array.new_float()
var int[] sslOriginBars = array.new_int()
var bool[] sslMitigated = array.new_bool()
var float prevPHPrice = na
var int prevPHBar = na
var float prevPLPrice = na
var int prevPLBar = na
f_new_pool_line(_y, _isBSL) =>
clr = _isBSL ? [Link]([Link], 0) : [Link]([Link], 0)
[Link](bar_index, _y, bar_index + 1, _y, xloc=xloc.bar_index,
extend=[Link], color=[Link](clr, 90), style=line.style_solid, width=2)
f_mark_mitigated(_line, _isBSL) =>
clrMit = _isBSL ? [Link]([Link], 60) : [Link]([Link], 60)
line.set_color(_line, [Link](clrMit, 90)), line.set_style(_line,
line.style_dashed)
f_trim_arrays(_isBSL) =>
if _isBSL
while [Link](bslLines) > lookbackPivots
[Link]([Link](bslLines)), [Link](bslPrices),
[Link](bslOriginBars), [Link](bslMitigated)
else
while [Link](sslLines) > lookbackPivots
[Link]([Link](sslLines)), [Link](sslPrices),
[Link](sslOriginBars), [Link](sslMitigated)
if showPools and not na(ph)
phIdx = bar_index - swingLen
phPrice = high[swingLen]
isEqHigh = not na(prevPHPrice) and [Link](phPrice - prevPHPrice) <= tol and
(phIdx - nz(prevPHBar)) >= minBarsGap
if isEqHigh
lvl = (phPrice + prevPHPrice) / 2.0
ln = f_new_pool_line(lvl, true)
[Link](bslLines, ln), [Link](bslPrices, lvl),
[Link](bslOriginBars, phIdx), [Link](bslMitigated, false),
f_trim_arrays(true)
prevPHPrice := phPrice, prevPHBar := phIdx
if showPools and not na(pl)
plIdx = bar_index - swingLen
plPrice = low[swingLen]
isEqLow = not na(prevPLPrice) and [Link](plPrice - prevPLPrice) <= tol and
(plIdx - nz(prevPLBar)) >= minBarsGap
if isEqLow
lvl = (plPrice + prevPLPrice) / 2.0
ln = f_new_pool_line(lvl, false)
[Link](sslLines, ln), [Link](sslPrices, lvl),
[Link](sslOriginBars, plIdx), [Link](sslMitigated, false),
f_trim_arrays(false)
prevPLPrice := plPrice, prevPLBar := plIdx
if showPools
if [Link](bslLines) > 0
for i = [Link](bslLines) - 1 to 0
if i >= 0 and i < [Link](bslLines)
prOk = i < [Link](bslPrices)
mtOk = i < [Link](bslMitigated)
if prOk and mtOk
ln = [Link](bslLines, i), lvl = [Link](bslPrices, i), mit
= [Link](bslMitigated, i)
if not mit and close > lvl + tol
if persistMitigated
f_mark_mitigated(ln, true), [Link](bslMitigated, i,
true)
else
[Link](ln), [Link](bslLines, i),
[Link](bslPrices, i), [Link](bslOriginBars, i),
[Link](bslMitigated, i)
if [Link](sslLines) > 0
for i = [Link](sslLines) - 1 to 0
if i >= 0 and i < [Link](sslLines)
prOk = i < [Link](sslPrices)
mtOk = i < [Link](sslMitigated)
if prOk and mtOk
ln = [Link](sslLines, i), lvl = [Link](sslPrices, i), mit
= [Link](sslMitigated, i)
if not mit and close < lvl - tol
if persistMitigated
f_mark_mitigated(ln, false), [Link](sslMitigated, i,
true)
else
[Link](ln), [Link](sslLines, i),
[Link](sslPrices, i), [Link](sslOriginBars, i),
[Link](sslMitigated, i)
var line dispBSL = na
var line dispSSL = na
var label lblBSL = na
var label lblSSL = na
var float nearBSL = na
var float nearSSL = na
var float dBSL = na
var float dSSL = na
nearBSL := na
nearSSL := na
dBSL := 1e10
dSSL := 1e10
bslN = [Link](bslPrices)
if bslN > 0 and [Link](bslMitigated) == bslN
for i = 0 to bslN - 1
if [Link](bslMitigated, i) == false
_d = [Link](close - [Link](bslPrices, i))
if _d < dBSL
dBSL := _d
nearBSL := [Link](bslPrices, i)
sslN = [Link](sslPrices)
if sslN > 0 and [Link](sslMitigated) == sslN
for i = 0 to sslN - 1
if [Link](sslMitigated, i) == false
_d = [Link](close - [Link](sslPrices, i))
if _d < dSSL
dSSL := _d
nearSSL := [Link](sslPrices, i)
if showPools
if not na(nearBSL)
clrB = [Link]([Link], 0)
if na(dispBSL)
dispBSL := [Link](bar_index - 1, nearBSL, bar_index + 1, nearBSL,
xloc=xloc.bar_index, extend=[Link], color=clrB, width=2)
else
line.set_y1(dispBSL, nearBSL), line.set_y2(dispBSL, nearBSL),
line.set_x2(dispBSL, bar_index + 1), line.set_color(dispBSL, clrB)
if not na(lblBSL)
[Link](lblBSL)
lblBSL := [Link](bar_index, nearBSL, "BSL " + [Link](nearBSL) + "
| Vol " + [Link](volume), style=label.style_label_right,
color=[Link]([Link], 70), textcolor=[Link])
if not na(nearSSL)
clrS = [Link]([Link], 0)
if na(dispSSL)
dispSSL := [Link](bar_index - 1, nearSSL, bar_index + 1, nearSSL,
xloc=xloc.bar_index, extend=[Link], color=clrS, width=2)
else
line.set_y1(dispSSL, nearSSL), line.set_y2(dispSSL, nearSSL),
line.set_x2(dispSSL, bar_index + 1), line.set_color(dispSSL, clrS)
if not na(lblSSL)
[Link](lblSSL)
lblSSL := [Link](bar_index, nearSSL, "SSL " + [Link](nearSSL) + "
| Vol " + [Link](volume), style=label.style_label_right,
color=[Link]([Link], 70), textcolor=[Link])
// ====== Dashboard ======
var table dash = na
shadow_bg = [Link]([Link], 90)
head_bg = [Link]([Link], 70)
if bar_index == 0 and showDashboard
dash := [Link](position.top_right, 2, 4, border_width=2, bgcolor=shadow_bg)
if showDashboard and not na(dash)
[Link](dash, 0, 0, 'Reversal Level', bgcolor=head_bg,
text_color=[Link])
[Link](dash, 1, 0, [Link](reversalPrice),
bgcolor=[Link]([Link], 0), text_color=[Link])
string volComp = na
color volCompColor = [Link]
if volume > volume[1]
volComp := 'High'
volCompColor := [Link]
else if volume < volume[1]
volComp := 'Low'
volCompColor := [Link]
else
volComp := 'Equal'
volCompColor := [Link]
[Link](dash, 0, 1, 'Volume vs Prev', bgcolor=head_bg,
text_color=[Link])
[Link](dash, 1, 1, volComp, bgcolor=[Link]([Link], 0),
text_color=volCompColor)
color volColor = close > open ? [Link] : close < open ? [Link] :
[Link]
[Link](dash, 0, 2, 'Volume', bgcolor=head_bg, text_color=[Link])
[Link](dash, 1, 2, [Link](volume), bgcolor=[Link]([Link], 0),
text_color=volColor)
string nearestTxt = "—"
if showPools and showNearestPools
float _nearestLvl = na
string _nearestSide = ""
float _minDist = 1e10
bslN = [Link](bslPrices)
if bslN > 0 and [Link](bslMitigated) == bslN
for i = 0 to bslN - 1
if [Link](bslMitigated, i) == false
d = [Link](close - [Link](bslPrices, i))
if d < _minDist
_minDist := d
_nearestLvl := [Link](bslPrices, i)
_nearestSide := "BSL"
sslN = [Link](sslPrices)
if sslN > 0 and [Link](sslMitigated) == sslN
for i = 0 to sslN - 1
if [Link](sslMitigated, i) == false
d = [Link](close - [Link](sslPrices, i))
if d < _minDist
_minDist := d
_nearestLvl := [Link](sslPrices, i)
_nearestSide := "SSL"
nearestTxt := na(_nearestLvl) ? "—" : (_nearestSide + " " +
[Link](_nearestLvl))
[Link](dash, 0, 3, 'Nearest Pool', bgcolor=head_bg, text_color=[Link])
[Link](dash, 1, 3, nearestTxt, bgcolor=[Link]([Link], 0),
text_color=[Link]([Link], 0))
// ===== New: Dynamic Trendlines on Liquidity Grab =====
var float lastLow1 = na
var float lastLow2 = na
var int lastLowIdx1 = na
var int lastLowIdx2 = na
var float lastHigh1 = na
var float lastHigh2 = na
var int lastHighIdx1 = na
var int lastHighIdx2 = na
if not na(pl)
lastLow2 := lastLow1
lastLowIdx2 := lastLowIdx1
lastLow1 := pl
lastLowIdx1 := bar_index - swingLen
if not na(ph)
lastHigh2 := lastHigh1
lastHighIdx2 := lastHighIdx1
lastHigh1 := ph
lastHighIdx1 := bar_index - swingLen
var line bullTL = na
var line bearTL = na
// extend mode as plain variable (no type keyword)
var extendMode = [Link]
extendMode := extendRightTL ? [Link] : [Link]
// Bullish grab -> connect last two lows
if showDynTL and advancedLiqGrabBull and not na(lastLow2) and not na(lastLow1)
if na(bullTL)
bullTL := [Link](lastLowIdx2, lastLow2, lastLowIdx1, lastLow1,
xloc=xloc.bar_index, color=[Link], width=2, extend=extendMode)
else
line.set_xy1(bullTL, lastLowIdx2, lastLow2)
line.set_xy2(bullTL, lastLowIdx1, lastLow1)
line.set_extend(bullTL, extendMode)
// Bearish grab -> connect last two highs
if showDynTL and advancedLiqGrabBear and not na(lastHigh2) and not na(lastHigh1)
if na(bearTL)
bearTL := [Link](lastHighIdx2, lastHigh2, lastHighIdx1, lastHigh1,
xloc=xloc.bar_index, color=[Link], width=2, extend=extendMode)
else
line.set_xy1(bearTL, lastHighIdx2, lastHigh2)
line.set_xy2(bearTL, lastHighIdx1, lastHigh1)
line.set_extend(bearTL, extendMode)