Resting orders are not placed at random. Stops sit just beyond the last swing high and just below the last swing low, and the orders that stopped a push leave a long wick behind them. None of this shows up in an order book you can see from a chart, but all of it leaves a trace in the candles.
Liquidity Master by SimpleAlgoAI reads those traces and turns them into a depth ladder drawn beside price. Every confirmed swing adds a pool of stops past it, every rejection wick adds the orders that absorbed it, and each pool stays on the ladder until price trades through it or it fades with age. What remains is stacked into rows: bids below price in one colour, offers above price in another, and the longer the row, the more liquidity is estimated to be resting there. The biggest row on each side becomes a named wall with a dotted line back across the chart, and a panel keeps score of how the walls on this chart have behaved when price reached them.
Two kinds of pool. When a swing high is confirmed (a bar higher than the bars on each side of it, 4 by default), a pool of buy stops is added in the band from the swing high to three quarters of an ATR above it; a swing low adds a pool of sell stops in the band below it. A rejection wick at least 0.45 ATR long becomes a pool too: the wick itself is where the orders that absorbed the push were resting. Swing pools weigh 1.6; wick pools weigh 0.5 times the volume of the bar relative to its 50-bar average (capped at 4), or 0.5 on instruments without volume.
Spent as price trades through. On every closed bar each pool above price is eaten into up to that bar’s high, and each pool below price down to that bar’s low. Only the part of the zone that price has not reached is still counted, and once price has crossed the whole zone the pool is gone. A level that price has already visited is no longer drawn as liquidity.
Ageing. Orders get pulled. A pool loses half of its weight every 300 bars and is dropped after four half-lives. At most 400 pools are kept; when a new one arrives with the list full, the oldest goes.
The ladder. The ladder covers the price range of the last 300 bars plus a margin, cut into 80 rows on a fixed price grid (the rows do not slide with every tick). Each pool spreads its remaining weight over the rows it overlaps, in proportion to the overlap. The rows are smoothed once with their neighbours and scaled so that the biggest row has full width. The ladder is relative: it shows where liquidity is thick and where it is thin, not how many contracts are there.
The walls. The biggest row at least 0.3 ATR above the close is the sell wall, and the biggest row at least 0.3 ATR below it is the buy wall. The nearest row holding at least 30% of the biggest one is the first pool in the way on each side, which is not always the wall.
The record. Every 10 bars the walls are noted (a wall must hold at least half of the biggest row to count). When price reaches a noted wall a test starts. A close half an ATR through the wall means it broke; a close half an ATR back from it means it held; a test without a verdict after 60 bars is dropped. The last 10 verdicts are kept. The rule is symmetric, so on a chart where walls mean nothing the score drifts around 5 out of 10.
Everything is built from closed bars only: the bar in progress spends and adds nothing until it closes. On the live bar the ladder and the walls follow the close, which decides which rows are above and below price, and the record changes only when a bar closes.
Liquidity:
Ladder:
The rules behind the pools and the record are fixed in the code: the stop zone of three quarters of an ATR, the weights, the refresh every 10 bars, the half-ATR verdicts and the 60-bar limit on a test.
On a dark chart background the default colours work as they are; on a white background the Custom theme with darker colours makes the rows easier to read. The wall names take a darker shade of their side’s colour whenever it is too light to read on white, so they read on both backgrounds. The panel draws its own solid dark background with bold text, so it reads on both too.
Alerts. ProRealTime alerts can only read the values an indicator returns, and Liquidity Master only draws. For alerts, add the second indicator, Liquidity Master Alerts, in its own panel below the chart, with the same pivIn, wickIn, halfIn, lookIn, spanIn and rowsIn as Liquidity Master, so both map the same walls. It returns four bars: Sell wall reached, Buy wall reached, Wall held and Wall broke, worth 1 on the bar right after the event and 0 otherwise. Create the alert on the one you want with the condition “crosses over 0.5”. The bars only mark the events while the chart stays open: when the chart is reloaded, the past values are 0.
//-----------------------------------------------------------------------
//PRC_Liquidity Master
//version = 0
//22.10.2026
//Iván González @ www.prorealcode.com
//Author: SimpleAlgoAI
//Sharing ProRealTime knowledge
//-----------------------------------------------------------------------
defparam drawonlastbaronly = true
// ===================== INPUTS =====================
// --- Liquidity ---
pivIn = 4 // swing strength: a swing needs this many lower highs / higher lows on each side (2 to 20)
wickIn = 0.45 // wick rejection: a wick at least this many ATRs long becomes a pool (0.2 to 2)
halfIn = 300 // liquidity ages out: half of a pool is gone after this many bars (50 to 3000)
lookIn = 1500 // bars to compute: the engine runs on the most recent bars only (300 to 4000)
// --- Ladder ---
posIn = 0 // ladder sits: 0 = over the last bars, 1 = right of price (needs right margin on the chart)
widthIn = 30 // ladder width, in bars (10 to 100)
spanIn = 300 // the ladder covers the price range of this many recent bars, plus a margin (50 to 2000)
rowsIn = 80 // rows (20 to 100)
themeIn = 1 // colours: 1 = Mint and Pink, 2 = Ice, 3 = Custom (the six values below)
cBidR = 0
cBidG = 255
cBidB = 163
cAskR = 255
cAskG = 61
cAskB = 113
bright = 3 // brightness (1 to 5)
showWalls = 1 // 1 = wall lines
showNames = 1 // 1 = wall names
showPrice = 1 // 1 = price tag (only shown when the ladder sits right of price)
showPanel = 1 // 1 = panel
panelPos = 2 // panel position: 1 = top left, 2 = top right, 3 = bottom left, 4 = bottom right
panelSize = 2 // panel size: 1 = small, 2 = normal, 3 = large
nameOff = 20 // wall names: the text is centred this many bars left of the ladder's widest row
// ===================== FIXED RULES =====================
maxEv = 400 // pools kept at once
maxRows = 100 // ladder rows available
zoneATR = 0.75 // stops sit within three quarters of an ATR past a swing
pivW = 1.6 // weight of the stops past a swing
wickW = 0.5 // weight of a rejection wick, times its relative volume
refreshEvery = 10 // bars between wall refreshes for the record
keepRec = 10 // wall tests remembered
brokeATR = 0.5 // a close this far past a wall breaks it
heldATR = 0.5 // a close this far back from a wall means it held
testBars = 60 // a test is dropped after this many bars without a verdict
lineBack = 150 // how far the wall lines reach back
wallAt = 0.5 // a wall must hold at least this share of the biggest row to be tracked
pivN = max(2, min(20, round(pivIn)))
wickK = max(0.2, min(2, wickIn))
halfN = max(50, min(3000, round(halfIn)))
lookN = max(300, min(4000, round(lookIn)))
widN = max(10, min(100, round(widthIn)))
spanN = max(50, min(2000, round(spanIn)))
nrow = min(max(20, min(100, round(rowsIn))), maxRows)
brightN = max(1, min(5, round(bright)))
brightMul = 0.4 + 0.2 * brightN
// Colours: bids (below price) and offers (above price)
IF themeIn = 2 THEN
bidR = 125
bidG = 211
bidB = 252
askR = 196
askG = 181
askB = 253
ELSIF themeIn = 3 THEN
bidR = cBidR
bidG = cBidG
bidB = cBidB
askR = cAskR
askG = cAskG
askB = cAskB
ELSE
bidR = 0
bidG = 255
bidB = 163
askR = 255
askG = 61
askB = 113
ENDIF
// Wall names: they sit straight on the chart, with no box behind them. A side colour too light to
// read on a white background (the mint, the Ice theme) is darkened just enough, to a contrast of
// 4 to 1 against white; a colour that already reads is kept
lumB = 0.2126 * pow(bidR / 255, 2.2) + 0.7152 * pow(bidG / 255, 2.2) + 0.0722 * pow(bidB / 255, 2.2)
kB = 1
IF lumB > 0.2125 THEN
kB = pow(0.2125 / lumB, 1 / 2.2)
ENDIF
nbR = round(bidR * kB)
nbG = round(bidG * kB)
nbB = round(bidB * kB)
lumA = 0.2126 * pow(askR / 255, 2.2) + 0.7152 * pow(askG / 255, 2.2) + 0.0722 * pow(askB / 255, 2.2)
kA = 1
IF lumA > 0.2125 THEN
kA = pow(0.2125 / lumA, 1 / 2.2)
ENDIF
naR = round(askR * kA)
naG = round(askG * kA)
naB = round(askB * kA)
// Panel ink: light text, drawn on the panel's own dark background
inkR = 209
inkG = 212
inkB = 220
// ===================== INPUTS TO THE ENGINE (every bar) =====================
// ATR(14) and the slow ATR(50) for the ladder geometry; the bar range until they exist
atr14 = averagetruerange[14](close)
IF barindex >= 13 THEN
atrV = atr14
ELSE
atrV = high - low
ENDIF
atrV = max(atrV, ticksize)
atr50 = averagetruerange[50](close)
IF barindex >= 49 THEN
atrGV = atr50
ELSE
atrGV = atrV
ENDIF
atrGV = max(atrGV, ticksize)
// Volume against its 50-bar average (1 when the instrument has no volume)
vAvg = average[50](volume)
vRel = 1
IF barindex >= 49 THEN
IF vAvg > 0 THEN
vRel = min(volume / vAvg, 4)
ENDIF
ENDIF
// Swing high / low confirmed this bar: the bar pivN bars back, higher (lower) than the pivN bars
// on its right and at least as high (low) as the pivN bars on its left (on a flat top the most
// recent bar of the plateau is the swing)
hhR = highest[pivN](high)
llR = lowest[pivN](low)
phF = 0
phP = 0
plF = 0
plP = 0
IF barindex >= 2 * pivN THEN
candH = high[pivN]
IF candH > hhR AND candH >= hhR[pivN + 1] THEN
phF = 1
phP = candH
ENDIF
candL = low[pivN]
IF candL < llR AND candL <= llR[pivN + 1] THEN
plF = 1
plP = candL
ENDIF
ENDIF
// Ladder grid: the range of the last spanN bars plus a margin, on rows of a fixed price step
hhSp = highest[spanN](high)
llSp = lowest[spanN](low)
spanOk = 0
IF barindex >= spanN - 1 THEN
spanOk = 1
ENDIF
marg = max(1.5 * atrGV, 0.08 * (hhSp - llSp))
stepV = max(round(((hhSp - llSp + 2 * marg) / nrow) / ticksize) * ticksize, ticksize)
baseV = floor((llSp - marg) / stepV) * stepV
// ===================== THE ENGINE (last bar) =====================
// The pools, the walls and the record are rebuilt from the last lookN bars, once per new bar and
// only from CLOSED bars: the live bar spends and adds nothing until it closes. The result is kept
// in arrays, because arrays are not rewound between ticks the way scalar variables are.
IF barindex = 0 THEN
$runAt[0] = 0 - 1
ENDIF
IF islastbarupdate THEN
doEng = 0
IF $runAt[0] <> barindex THEN
doEng = 1
ENDIF
IF doEng = 1 THEN
nEv = 0
tSide = 0
tP = 0
tBar = 0
wUpOk = 0
wUpV = 0
wDnOk = 0
wDnV = 0
nRec = 0
$eng[0] = 0
$eng[1] = 0
$eng[2] = 0
jStart = min(lookN - 1, barindex)
ELSE
nEv = $eng[0]
jStart = 0
ENDIF
FOR j = jStart DOWNTO 0 DO
bi = barindex - j
hj = high[j]
lj = low[j]
cj = close[j]
aj = atrV[j]
bStep = stepV[j]
bBase = baseV[j]
bOk = spanOk[j]
doBuild = 0
IF j >= 1 THEN
// 1. price trading through a pool spends it, and old pools are gone
IF nEv > 0 THEN
k = 0
FOR i = 0 TO nEv - 1 DO
gone = 0
IF bi - $evBar[i] > 4 * halfN THEN
gone = 1
ENDIF
IF $evSide[i] = 1 THEN
ext = max($evExt[i], hj)
IF ext >= $evP1[i] THEN
gone = 1
ENDIF
ELSE
ext = min($evExt[i], lj)
IF ext <= $evP0[i] THEN
gone = 1
ENDIF
ENDIF
IF gone = 0 THEN
$evSide[k] = $evSide[i]
$evP0[k] = $evP0[i]
$evP1[k] = $evP1[i]
$evW[k] = $evW[i]
$evBar[k] = $evBar[i]
$evExt[k] = ext
k = k + 1
ENDIF
NEXT
nEv = k
ENDIF
// 2. new pools: the stops past a swing that just confirmed, and the orders in a rejection wick
oj = open[j]
FOR q = 1 TO 4 DO
qOk = 0
IF q = 1 THEN
IF phF[j] = 1 THEN
IF hhR[j] < phP[j] + zoneATR * aj THEN
qOk = 1
qSide = 1
qP0 = phP[j]
qP1 = phP[j] + zoneATR * aj
qW = pivW
qBar = bi - pivN
qExt = max(hhR[j], phP[j])
ENDIF
ENDIF
ELSIF q = 2 THEN
IF plF[j] = 1 THEN
IF llR[j] > plP[j] - zoneATR * aj THEN
qOk = 1
qSide = -1
qP0 = plP[j] - zoneATR * aj
qP1 = plP[j]
qW = pivW
qBar = bi - pivN
qExt = min(llR[j], plP[j])
ENDIF
ENDIF
ELSIF q = 3 THEN
IF hj - max(oj, cj) >= wickK * aj THEN
qOk = 1
qSide = 1
qP0 = max(oj, cj)
qP1 = hj
qW = wickW * vRel[j]
qBar = bi
qExt = max(oj, cj)
ENDIF
ELSE
IF min(oj, cj) - lj >= wickK * aj THEN
qOk = 1
qSide = -1
qP0 = lj
qP1 = min(oj, cj)
qW = wickW * vRel[j]
qBar = bi
qExt = min(oj, cj)
ENDIF
ENDIF
IF qOk = 1 THEN
// at most maxEv pools: the oldest one goes
IF nEv >= maxEv THEN
FOR i = 1 TO nEv - 1 DO
$evSide[i - 1] = $evSide[i]
$evP0[i - 1] = $evP0[i]
$evP1[i - 1] = $evP1[i]
$evW[i - 1] = $evW[i]
$evBar[i - 1] = $evBar[i]
$evExt[i - 1] = $evExt[i]
NEXT
nEv = nEv - 1
ENDIF
$evSide[nEv] = qSide
$evP0[nEv] = qP0
$evP1[nEv] = qP1
$evW[nEv] = qW
$evBar[nEv] = qBar
$evExt[nEv] = qExt
nEv = nEv + 1
ENDIF
NEXT
// 3. every refreshEvery bars, name the walls for the record
IF (bi MOD refreshEvery) = 0 AND nEv >= 15 THEN
doBuild = 1
ENDIF
ELSE
// the live bar: the ladder that is drawn
doBuild = 1
ENDIF
IF doBuild = 1 THEN
// Resting liquidity per row: each pool's remaining zone spread over the rows it overlaps,
// weighted by its age; then smoothed once with the neighbours
FOR r = 0 TO nrow - 1 DO
$rowS[r] = 0
NEXT
IF nEv > 0 AND bOk = 1 THEN
FOR i = 0 TO nEv - 1 DO
IF $evSide[i] = 1 THEN
a0 = max($evP0[i], $evExt[i])
a1 = $evP1[i]
ELSE
a0 = $evP0[i]
a1 = min($evP1[i], $evExt[i])
ENDIF
IF a1 > a0 THEN
wq = $evW[i] * pow(0.5, (bi - $evBar[i]) / halfN)
r0 = max(floor((a0 - bBase) / bStep), 0)
r1 = min(floor((a1 - bBase) / bStep), nrow - 1)
IF r1 >= r0 THEN
FOR r = r0 TO r1 DO
lo = bBase + r * bStep
ov = min(a1, lo + bStep) - max(a0, lo)
IF ov > 0 THEN
$rowS[r] = $rowS[r] + wq * ov / (a1 - a0)
ENDIF
NEXT
ENDIF
ENDIF
NEXT
ENDIF
peak = 0
prv = $rowS[0]
FOR r = 0 TO nrow - 1 DO
cur = $rowS[r]
IF r < nrow - 1 THEN
nxt = $rowS[r + 1]
ELSE
nxt = cur
ENDIF
sm = 0.25 * prv + 0.5 * cur + 0.25 * nxt
prv = cur
$rowS[r] = sm
peak = max(peak, sm)
NEXT
// The walls: the biggest row on each side at least 0.3 ATR from price; also the nearest
// real pool on each side and the liquidity on each side
upOk = 0
upP = 0
upT = 0
dnOk = 0
dnP = 0
dnT = 0
nUpOk = 0
nUpP = 0
nDnOk = 0
nDnP = 0
sumUp = 0
sumDn = 0
IF peak > 0 THEN
FOR r = 0 TO nrow - 1 DO
mid = bBase + (r + 0.5) * bStep
tq = $rowS[r] / peak
IF mid >= cj + 0.3 * aj THEN
sumUp = sumUp + tq
IF tq > upT THEN
upT = tq
upP = mid
upOk = 1
ENDIF
IF tq >= 0.3 AND nUpOk = 0 THEN
nUpOk = 1
nUpP = mid
ENDIF
ELSIF mid <= cj - 0.3 * aj THEN
sumDn = sumDn + tq
IF tq > dnT THEN
dnT = tq
dnP = mid
dnOk = 1
ENDIF
IF tq >= 0.3 THEN
nDnOk = 1
nDnP = mid
ENDIF
ENDIF
NEXT
ENDIF
ENDIF
IF j >= 1 THEN
IF doBuild = 1 THEN
wUpOk = 0
IF upOk = 1 AND upT >= wallAt THEN
wUpOk = 1
wUpV = upP
ENDIF
wDnOk = 0
IF dnOk = 1 AND dnT >= wallAt THEN
wDnOk = 1
wDnV = dnP
ENDIF
ENDIF
// 4. a wall gets tested when price reaches it: it held if price backs off, it broke if
// price closes through
IF tSide = 0 THEN
IF wUpOk = 1 AND hj >= wUpV - bStep / 2 THEN
tSide = 1
tP = wUpV
tBar = bi
wUpOk = 0
ELSIF wDnOk = 1 AND lj <= wDnV + bStep / 2 THEN
tSide = -1
tP = wDnV
tBar = bi
wDnOk = 0
ENDIF
ELSE
brk = 0
hld = 0
IF tSide = 1 THEN
IF cj > tP + brokeATR * aj THEN
brk = 1
ENDIF
IF cj <= tP - heldATR * aj THEN
hld = 1
ENDIF
ELSE
IF cj < tP - brokeATR * aj THEN
brk = 1
ENDIF
IF cj >= tP + heldATR * aj THEN
hld = 1
ENDIF
ENDIF
IF brk = 1 OR hld = 1 THEN
// the last keepRec verdicts: 1 the wall held, 0 it broke
IF nRec >= keepRec THEN
FOR i = 1 TO nRec - 1 DO
$rec[i - 1] = $rec[i]
NEXT
nRec = nRec - 1
ENDIF
IF brk = 1 THEN
$rec[nRec] = 0
ELSE
$rec[nRec] = 1
ENDIF
nRec = nRec + 1
tSide = 0
ELSIF bi - tBar > testBars THEN
tSide = 0
ENDIF
ENDIF
IF j = 1 THEN
$eng[0] = nEv
$eng[1] = tSide
$eng[2] = nRec
ENDIF
ENDIF
NEXT
$runAt[0] = barindex
// ===================== THE LADDER =====================
IF posIn = 1 THEN
xR = barindex + 2 + widN
ELSE
xR = barindex
ENDIF
xL = xR - widN
IF peak > 0 THEN
FOR r = 0 TO nrow - 1 DO
tq = $rowS[r] / peak
y0 = bBase + r * bStep
y1 = y0 + bStep
IF (y0 + y1) / 2 >= close THEN
cR = askR
cG = askG
cB = askB
ELSE
cR = bidR
cG = bidG
cB = bidB
ENDIF
wd = max(round(widN * tq), 1)
IF tq >= 0.03 THEN
trp = round(82 - 65 * pow(tq, 1.2) * brightMul)
IF posIn = 1 THEN
trp = min(max(trp, 10), 90)
ELSE
trp = min(max(trp, 32), 90)
ENDIF
alphaV = round(255 * (100 - trp) / 100)
DRAWRECTANGLE(xR - wd, y1 - bStep * 0.07, xR, y0 + bStep * 0.07) COLOURED(cR, cG, cB, 0) FILLCOLOR(cR, cG, cB, alphaV)
ENDIF
// a soft glow behind the rows that hold more than half of the biggest one
IF tq > 0.5 THEN
trp = round(100 - 14 * tq * brightMul)
alphaV = round(255 * (100 - trp) / 100)
DRAWRECTANGLE(xR - wd - 1, y1 + bStep * 0.2, xR, y0 - bStep * 0.2) COLOURED(cR, cG, cB, 0) FILLCOLOR(cR, cG, cB, alphaV)
ENDIF
NEXT
ENDIF
IF bOk = 1 THEN
DRAWSEGMENT(xR + 1, bBase, xR + 1, bBase + nrow * bStep) COLOURED(42, 46, 57, 255) STYLE(line, 1)
ENDIF
// Prices as the chart shows them
IF ticksize >= 1 THEN
pxU = round(upP)
pxD = round(dnP)
pxC = round(close)
ELSIF ticksize >= 0.01 THEN
pxU = round(upP * 100) / 100
pxD = round(dnP * 100) / 100
pxC = round(close * 100) / 100
ELSE
pxU = round(upP / ticksize) * ticksize
pxD = round(dnP / ticksize) * ticksize
pxC = round(close / ticksize) * ticksize
ENDIF
// The walls: a dotted line back across the chart, with a soft halo
xW = max(0, barindex - lineBack)
IF showWalls = 1 AND upOk = 1 THEN
DRAWSEGMENT(xW, upP, xR, upP) COLOURED(askR, askG, askB, 255) STYLE(dottedline, 1)
DRAWSEGMENT(xW, upP, xR, upP) COLOURED(askR, askG, askB, 71) STYLE(line, 3)
DRAWSEGMENT(xW, upP, xR, upP) COLOURED(askR, askG, askB, 36) STYLE(line, 5)
ENDIF
IF showWalls = 1 AND dnOk = 1 THEN
DRAWSEGMENT(xW, dnP, xR, dnP) COLOURED(bidR, bidG, bidB, 255) STYLE(dottedline, 1)
DRAWSEGMENT(xW, dnP, xR, dnP) COLOURED(bidR, bidG, bidB, 71) STYLE(line, 3)
DRAWSEGMENT(xW, dnP, xR, dnP) COLOURED(bidR, bidG, bidB, 36) STYLE(line, 5)
ENDIF
// The names sit by the wall lines, left of the ladder's widest row and just off the line
// (above the sell wall, below the buy wall): with no box behind them they would not read
// over the rows or across the dots
xN = xL - 1 - nameOff
yN = nrow * bStep * 0.016
IF showNames = 1 AND upOk = 1 THEN
DRAWTEXT("sell wall #pxU#", xN, upP + yN, SansSerif, standard, 12) COLOURED(naR, naG, naB, 255)
ENDIF
IF showNames = 1 AND dnOk = 1 THEN
DRAWTEXT("buy wall #pxD#", xN, dnP - yN, SansSerif, standard, 12) COLOURED(nbR, nbG, nbB, 255)
ENDIF
IF showPrice = 1 AND posIn = 1 THEN
DRAWTEXT("#pxC#", xR + 6, close, SansSerif, standard, 10) COLOURED(42, 46, 57, 255)
ENDIF
// ===================== PANEL =====================
IF showPanel = 1 THEN
// Distances in ATRs, one decimal
dUpT = 0
dDnT = 0
IF upOk = 1 THEN
dUpT = round((upP - close) / aj * 10) / 10
ENDIF
IF dnOk = 1 THEN
dDnT = round((close - dnP) / aj * 10) / 10
ENDIF
nUpD = 0
nDnD = 0
IF nUpOk = 1 THEN
nUpD = (nUpP - close) / aj
ENDIF
IF nDnOk = 1 THEN
nDnD = (close - nDnP) / aj
ENDIF
nUpT = round(nUpD * 10) / 10
nDnT = round(nDnD * 10) / 10
// Balance: share of the resting liquidity below price
total = sumUp + sumDn
IF total > 0 THEN
shDn = sumDn / total * 100
ELSE
shDn = 50
ENDIF
shDnT = round(shDn)
shUpT = round(100 - shDn)
// The record
nRecP = $eng[2]
heldP = 0
IF nRecP > 0 THEN
FOR i = 0 TO nRecP - 1 DO
heldP = heldP + $rec[i]
NEXT
ENDIF
// Which text each row reads
IF upOk = 1 THEN
IF upT >= 0.999 THEN
v1 = 2
ELSE
v1 = 1
ENDIF
ELSE
v1 = 0
ENDIF
IF dnOk = 1 THEN
IF dnT >= 0.999 THEN
v2 = 2
ELSE
v2 = 1
ENDIF
ELSE
v2 = 0
ENDIF
IF total = 0 THEN
v3 = 0
ELSE
v3 = 1
ENDIF
IF nRecP = 0 THEN
v4 = 0
ELSIF nRecP = 1 THEN
v4 = 1
ELSE
v4 = 2
ENDIF
nearUp = 0
IF nUpOk = 1 THEN
IF nDnOk = 0 OR nUpD <= nDnD THEN
nearUp = 1
ENDIF
ENDIF
nearWall = 0
IF nearUp = 1 THEN
IF upOk = 1 AND abs(nUpP - upP) < bStep THEN
nearWall = 1
ENDIF
ELSE
IF dnOk = 1 AND nDnOk = 1 AND abs(nDnP - dnP) < bStep THEN
nearWall = 1
ENDIF
ENDIF
thick = 0
IF shDn >= 58 THEN
thick = 1
ELSIF shDn <= 42 THEN
thick = 2
ENDIF
IF $eng[1] = 1 THEN
v5 = 1
ELSIF $eng[1] = -1 THEN
v5 = 2
ELSIF total = 0 THEN
v5 = 3
ELSIF nUpOk = 0 AND nDnOk = 0 THEN
v5 = 4
ELSIF nearUp = 1 THEN
v5 = 6 - nearWall
ELSE
v5 = 8 - nearWall
ENDIF
// Colours of each row
IF v1 = 0 THEN
c1R = inkR
c1G = inkG
c1B = inkB
ELSE
c1R = askR
c1G = askG
c1B = askB
ENDIF
IF v2 = 0 THEN
c2R = inkR
c2G = inkG
c2B = inkB
ELSE
c2R = bidR
c2G = bidG
c2B = bidB
ENDIF
IF shDn >= 58 THEN
c3R = bidR
c3G = bidG
c3B = bidB
ELSIF shDn <= 42 THEN
c3R = askR
c3G = askG
c3B = askB
ELSE
c3R = inkR
c3G = inkG
c3B = inkB
ENDIF
IF nearUp = 1 THEN
c5R = askR
c5G = askG
c5B = askB
ELSE
c5R = bidR
c5G = bidG
c5B = bidB
ENDIF
// Text size and geometry: a monospaced font, so every character is chW pixels wide
IF panelSize = 1 THEN
pz = 10
ELSIF panelSize = 3 THEN
pz = 15
ELSE
pz = 12
ENDIF
chW = pz * 0.62
rowH = pz + 8
padX = 8
padY = 4
// Decimals the chart shows for a price
IF ticksize >= 1 THEN
pDec = 0
ELSIF ticksize >= 0.01 THEN
pDec = 3
ELSE
tkq = ticksize
pDec = 1
WHILE tkq < 0.9999999 AND pDec < 11 DO
tkq = tkq * 10
pDec = pDec + 1
WEND
ENDIF
// How many characters each number takes on screen (digits, thousands separators, decimals and
// the space ProRealTime prints after an interpolated number)
$nv[0] = pxC
$nv[1] = pxU
$nv[2] = pxD
$nv[3] = dUpT
$nv[4] = dDnT
$nv[5] = nUpT
$nv[6] = nDnT
$nv[7] = shDnT
$nv[8] = shUpT
$nv[9] = heldP
$nv[10] = nRecP
FOR q = 0 TO 10 DO
vq = abs($nv[q])
dq = 1
WHILE vq >= 10 DO
vq = vq / 10
dq = dq + 1
WEND
lq = dq + floor((dq - 1) / 3) + 1
IF $nv[q] < 0 THEN
lq = lq + 1
ENDIF
IF q <= 2 THEN
lq = lq + pDec
ELSIF q <= 6 THEN
lq = lq + 2
ENDIF
$nl[q] = lq
NEXT
// Length of the text each row reads
len0 = 0 + $nl[0]
IF v1 = 0 THEN
len1 = 21
ELSIF v1 = 1 THEN
len1 = 11 + $nl[1] + $nl[3]
ELSIF v1 = 2 THEN
len1 = 43 + $nl[1] + $nl[3]
ENDIF
IF v2 = 0 THEN
len2 = 21
ELSIF v2 = 1 THEN
len2 = 11 + $nl[2] + $nl[4]
ELSIF v2 = 2 THEN
len2 = 43 + $nl[2] + $nl[4]
ENDIF
IF v3 = 0 THEN
len3 = 18
ELSIF v3 = 1 THEN
len3 = 52 + $nl[7] + $nl[8]
ENDIF
IF v4 = 0 THEN
len4 = 18
ELSIF v4 = 1 THEN
len4 = 27 + $nl[9] + $nl[10]
ELSIF v4 = 2 THEN
len4 = 28 + $nl[9] + $nl[10]
ENDIF
IF v5 = 1 THEN
len5 = 63
ELSIF v5 = 2 THEN
len5 = 62
ELSIF v5 = 3 THEN
len5 = 25
ELSIF v5 = 4 THEN
len5 = 38
ELSIF v5 = 5 AND thick = 0 THEN
len5 = 49 + $nl[5]
ELSIF v5 = 5 AND thick = 1 THEN
len5 = 76 + $nl[5]
ELSIF v5 = 5 AND thick = 2 THEN
len5 = 76 + $nl[5]
ELSIF v5 = 6 AND thick = 0 THEN
len5 = 72 + $nl[5] + $nl[3]
ELSIF v5 = 6 AND thick = 1 THEN
len5 = 99 + $nl[5] + $nl[3]
ELSIF v5 = 6 AND thick = 2 THEN
len5 = 99 + $nl[5] + $nl[3]
ELSIF v5 = 7 AND thick = 0 THEN
len5 = 48 + $nl[6]
ELSIF v5 = 7 AND thick = 1 THEN
len5 = 75 + $nl[6]
ELSIF v5 = 7 AND thick = 2 THEN
len5 = 75 + $nl[6]
ELSIF v5 = 8 AND thick = 0 THEN
len5 = 71 + $nl[6] + $nl[4]
ELSIF v5 = 8 AND thick = 1 THEN
len5 = 98 + $nl[6] + $nl[4]
ELSIF v5 = 8 AND thick = 2 THEN
len5 = 98 + $nl[6] + $nl[4]
ENDIF
maxL = max(max(max(len0, len1), max(len2, len3)), max(len4, len5))
pW = 16 * chW + maxL * chW + 2 * chW + 4 * padX
pH = 6 * rowH + 2 * padY
// The panel sits in its corner, clear of the price scale on the right and, on the left, of the
// chart's legend bar (top) and of the platform's mark and news icons (bottom)
IF panelPos = 1 THEN
pL = 10
pT = 0 - 40
ELSIF panelPos = 3 THEN
pL = 10
pT = 40 + pH
ELSIF panelPos = 4 THEN
pL = 0 - 85 - pW
pT = 12 + pH
ELSE
pL = 0 - 85 - pW
pT = 0 - 12
ENDIF
// Names aligned left, values aligned right
yR0 = pT - padY - rowH * 0.5
xV0 = pL + pW - padX - len0 * chW / 2
yR1 = pT - padY - rowH * 1.5
xV1 = pL + pW - padX - len1 * chW / 2
yR2 = pT - padY - rowH * 2.5
xV2 = pL + pW - padX - len2 * chW / 2
yR3 = pT - padY - rowH * 3.5
xV3 = pL + pW - padX - len3 * chW / 2
yR4 = pT - padY - rowH * 4.5
xV4 = pL + pW - padX - len4 * chW / 2
yR5 = pT - padY - rowH * 5.5
xV5 = pL + pW - padX - len5 * chW / 2
// The panel, drawn in the chosen corner
IF panelPos = 1 THEN
DRAWRECTANGLE(pL, pT, pL + pW, pT - pH) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(38, 43, 56, 255) FILLCOLOR(38, 43, 56, 255)
DRAWTEXT("LIQUIDITY MASTER", pL + padX + 8 * chW, yR0, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
DRAWTEXT("#pxC#", xV0, yR0, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
DRAWTEXT("Sell wall", pL + padX + 4.5 * chW, yR1, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v1 = 0 THEN
DRAWTEXT("No wall on the ladder", xV1, yR1, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ELSIF v1 = 1 THEN
DRAWTEXT("#pxU#· #dUpT#ATR above", xV1, yR1, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ELSIF v1 = 2 THEN
DRAWTEXT("#pxU#· #dUpT#ATR above, the biggest pool on the ladder", xV1, yR1, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ENDIF
DRAWTEXT("Buy wall", pL + padX + 4 * chW, yR2, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v2 = 0 THEN
DRAWTEXT("No wall on the ladder", xV2, yR2, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ELSIF v2 = 1 THEN
DRAWTEXT("#pxD#· #dDnT#ATR below", xV2, yR2, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ELSIF v2 = 2 THEN
DRAWTEXT("#pxD#· #dDnT#ATR below, the biggest pool on the ladder", xV2, yR2, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ENDIF
DRAWTEXT("Balance", pL + padX + 3.5 * chW, yR3, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v3 = 0 THEN
DRAWTEXT("Nothing mapped yet", xV3, yR3, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c3R, c3G, c3B, 255)
ELSIF v3 = 1 THEN
DRAWTEXT("#shDnT#% of the resting liquidity sits below price, #shUpT#% above", xV3, yR3, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c3R, c3G, c3B, 255)
ENDIF
DRAWTEXT("Walls here", pL + padX + 5 * chW, yR4, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v4 = 0 THEN
DRAWTEXT("No wall tested yet", xV4, yR4, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ELSIF v4 = 1 THEN
DRAWTEXT("Walls held #heldP#of the last #nRecP#test", xV4, yR4, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ELSIF v4 = 2 THEN
DRAWTEXT("Walls held #heldP#of the last #nRecP#tests", xV4, yR4, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ENDIF
DRAWTEXT("Read", pL + padX + 2 * chW, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v5 = 1 THEN
DRAWTEXT("Testing the sell wall now, a close 0.5 ATR through it breaks it", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(255, 255, 255, 255)
ELSIF v5 = 2 THEN
DRAWTEXT("Testing the buy wall now, a close 0.5 ATR through it breaks it", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(255, 255, 255, 255)
ELSIF v5 = 3 THEN
DRAWTEXT("Waiting for swings to map", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 4 THEN
DRAWTEXT("Thin on both sides, nothing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 0 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 1 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 2 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 0 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 1 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 2 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 0 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 1 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 2 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 0 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 1 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 2 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ENDIF
ELSIF panelPos = 3 THEN
DRAWRECTANGLE(pL, pT, pL + pW, pT - pH) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(38, 43, 56, 255) FILLCOLOR(38, 43, 56, 255)
DRAWTEXT("LIQUIDITY MASTER", pL + padX + 8 * chW, yR0, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
DRAWTEXT("#pxC#", xV0, yR0, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
DRAWTEXT("Sell wall", pL + padX + 4.5 * chW, yR1, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v1 = 0 THEN
DRAWTEXT("No wall on the ladder", xV1, yR1, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ELSIF v1 = 1 THEN
DRAWTEXT("#pxU#· #dUpT#ATR above", xV1, yR1, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ELSIF v1 = 2 THEN
DRAWTEXT("#pxU#· #dUpT#ATR above, the biggest pool on the ladder", xV1, yR1, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ENDIF
DRAWTEXT("Buy wall", pL + padX + 4 * chW, yR2, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v2 = 0 THEN
DRAWTEXT("No wall on the ladder", xV2, yR2, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ELSIF v2 = 1 THEN
DRAWTEXT("#pxD#· #dDnT#ATR below", xV2, yR2, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ELSIF v2 = 2 THEN
DRAWTEXT("#pxD#· #dDnT#ATR below, the biggest pool on the ladder", xV2, yR2, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ENDIF
DRAWTEXT("Balance", pL + padX + 3.5 * chW, yR3, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v3 = 0 THEN
DRAWTEXT("Nothing mapped yet", xV3, yR3, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c3R, c3G, c3B, 255)
ELSIF v3 = 1 THEN
DRAWTEXT("#shDnT#% of the resting liquidity sits below price, #shUpT#% above", xV3, yR3, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c3R, c3G, c3B, 255)
ENDIF
DRAWTEXT("Walls here", pL + padX + 5 * chW, yR4, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v4 = 0 THEN
DRAWTEXT("No wall tested yet", xV4, yR4, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ELSIF v4 = 1 THEN
DRAWTEXT("Walls held #heldP#of the last #nRecP#test", xV4, yR4, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ELSIF v4 = 2 THEN
DRAWTEXT("Walls held #heldP#of the last #nRecP#tests", xV4, yR4, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ENDIF
DRAWTEXT("Read", pL + padX + 2 * chW, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v5 = 1 THEN
DRAWTEXT("Testing the sell wall now, a close 0.5 ATR through it breaks it", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(255, 255, 255, 255)
ELSIF v5 = 2 THEN
DRAWTEXT("Testing the buy wall now, a close 0.5 ATR through it breaks it", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(255, 255, 255, 255)
ELSIF v5 = 3 THEN
DRAWTEXT("Waiting for swings to map", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 4 THEN
DRAWTEXT("Thin on both sides, nothing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 0 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 1 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 2 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 0 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 1 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 2 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 0 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 1 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 2 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 0 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 1 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 2 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMLEFT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ENDIF
ELSIF panelPos = 4 THEN
DRAWRECTANGLE(pL, pT, pL + pW, pT - pH) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(38, 43, 56, 255) FILLCOLOR(38, 43, 56, 255)
DRAWTEXT("LIQUIDITY MASTER", pL + padX + 8 * chW, yR0, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
DRAWTEXT("#pxC#", xV0, yR0, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
DRAWTEXT("Sell wall", pL + padX + 4.5 * chW, yR1, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v1 = 0 THEN
DRAWTEXT("No wall on the ladder", xV1, yR1, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ELSIF v1 = 1 THEN
DRAWTEXT("#pxU#· #dUpT#ATR above", xV1, yR1, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ELSIF v1 = 2 THEN
DRAWTEXT("#pxU#· #dUpT#ATR above, the biggest pool on the ladder", xV1, yR1, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ENDIF
DRAWTEXT("Buy wall", pL + padX + 4 * chW, yR2, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v2 = 0 THEN
DRAWTEXT("No wall on the ladder", xV2, yR2, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ELSIF v2 = 1 THEN
DRAWTEXT("#pxD#· #dDnT#ATR below", xV2, yR2, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ELSIF v2 = 2 THEN
DRAWTEXT("#pxD#· #dDnT#ATR below, the biggest pool on the ladder", xV2, yR2, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ENDIF
DRAWTEXT("Balance", pL + padX + 3.5 * chW, yR3, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v3 = 0 THEN
DRAWTEXT("Nothing mapped yet", xV3, yR3, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c3R, c3G, c3B, 255)
ELSIF v3 = 1 THEN
DRAWTEXT("#shDnT#% of the resting liquidity sits below price, #shUpT#% above", xV3, yR3, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c3R, c3G, c3B, 255)
ENDIF
DRAWTEXT("Walls here", pL + padX + 5 * chW, yR4, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v4 = 0 THEN
DRAWTEXT("No wall tested yet", xV4, yR4, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ELSIF v4 = 1 THEN
DRAWTEXT("Walls held #heldP#of the last #nRecP#test", xV4, yR4, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ELSIF v4 = 2 THEN
DRAWTEXT("Walls held #heldP#of the last #nRecP#tests", xV4, yR4, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ENDIF
DRAWTEXT("Read", pL + padX + 2 * chW, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v5 = 1 THEN
DRAWTEXT("Testing the sell wall now, a close 0.5 ATR through it breaks it", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255, 255)
ELSIF v5 = 2 THEN
DRAWTEXT("Testing the buy wall now, a close 0.5 ATR through it breaks it", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255, 255)
ELSIF v5 = 3 THEN
DRAWTEXT("Waiting for swings to map", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 4 THEN
DRAWTEXT("Thin on both sides, nothing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 0 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 1 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 2 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 0 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 1 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 2 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 0 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 1 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 2 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 0 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 1 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 2 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ENDIF
ELSE
DRAWRECTANGLE(pL, pT, pL + pW, pT - pH) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(38, 43, 56, 255) FILLCOLOR(38, 43, 56, 255)
DRAWTEXT("LIQUIDITY MASTER", pL + padX + 8 * chW, yR0, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
DRAWTEXT("#pxC#", xV0, yR0, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
DRAWTEXT("Sell wall", pL + padX + 4.5 * chW, yR1, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v1 = 0 THEN
DRAWTEXT("No wall on the ladder", xV1, yR1, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ELSIF v1 = 1 THEN
DRAWTEXT("#pxU#· #dUpT#ATR above", xV1, yR1, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ELSIF v1 = 2 THEN
DRAWTEXT("#pxU#· #dUpT#ATR above, the biggest pool on the ladder", xV1, yR1, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c1R, c1G, c1B, 255)
ENDIF
DRAWTEXT("Buy wall", pL + padX + 4 * chW, yR2, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v2 = 0 THEN
DRAWTEXT("No wall on the ladder", xV2, yR2, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ELSIF v2 = 1 THEN
DRAWTEXT("#pxD#· #dDnT#ATR below", xV2, yR2, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ELSIF v2 = 2 THEN
DRAWTEXT("#pxD#· #dDnT#ATR below, the biggest pool on the ladder", xV2, yR2, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c2R, c2G, c2B, 255)
ENDIF
DRAWTEXT("Balance", pL + padX + 3.5 * chW, yR3, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v3 = 0 THEN
DRAWTEXT("Nothing mapped yet", xV3, yR3, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c3R, c3G, c3B, 255)
ELSIF v3 = 1 THEN
DRAWTEXT("#shDnT#% of the resting liquidity sits below price, #shUpT#% above", xV3, yR3, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c3R, c3G, c3B, 255)
ENDIF
DRAWTEXT("Walls here", pL + padX + 5 * chW, yR4, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v4 = 0 THEN
DRAWTEXT("No wall tested yet", xV4, yR4, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ELSIF v4 = 1 THEN
DRAWTEXT("Walls held #heldP#of the last #nRecP#test", xV4, yR4, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ELSIF v4 = 2 THEN
DRAWTEXT("Walls held #heldP#of the last #nRecP#tests", xV4, yR4, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 255)
ENDIF
DRAWTEXT("Read", pL + padX + 2 * chW, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(209, 212, 220, 153)
IF v5 = 1 THEN
DRAWTEXT("Testing the sell wall now, a close 0.5 ATR through it breaks it", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255, 255)
ELSIF v5 = 2 THEN
DRAWTEXT("Testing the buy wall now, a close 0.5 ATR through it breaks it", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255, 255)
ELSIF v5 = 3 THEN
DRAWTEXT("Waiting for swings to map", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 4 THEN
DRAWTEXT("Thin on both sides, nothing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 0 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 1 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 5 AND thick = 2 THEN
DRAWTEXT("Sell wall #nUpT#ATR above is the first thing in the way, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 0 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 1 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 6 AND thick = 2 THEN
DRAWTEXT("Offers #nUpT#ATR above are the first thing in the way, the sell wall is #dUpT#ATR up, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 0 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 1 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 7 AND thick = 2 THEN
DRAWTEXT("Buy wall #nDnT#ATR below is the first thing in the way, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 0 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 1 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down, the thicker side is below", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ELSIF v5 = 8 AND thick = 2 THEN
DRAWTEXT("Bids #nDnT#ATR below are the first thing in the way, the buy wall is #dDnT#ATR down, the thicker side is above", xV5, yR5, monospaced, bold, pz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(c5R, c5G, c5B, 255)
ENDIF
ENDIF
ENDIF
ENDIF
RETURN
//-----------------------------------------------------------------------
//PRC_Liquidity Master Alerts
//version = 0
//22.10.2026
//Iván González @ www.prorealcode.com
//Author: SimpleAlgoAI
//Sharing ProRealTime knowledge
//-----------------------------------------------------------------------
// ===================== INPUTS =====================
// --- Liquidity ---
pivIn = 4 // swing strength: a swing needs this many lower highs / higher lows on each side (2 to 20)
wickIn = 0.45 // wick rejection: a wick at least this many ATRs long becomes a pool (0.2 to 2)
halfIn = 300 // liquidity ages out: half of a pool is gone after this many bars (50 to 3000)
lookIn = 1500 // bars to compute: the engine runs on the most recent bars only (300 to 4000)
// --- Ladder ---
spanIn = 300 // the ladder covers the price range of this many recent bars, plus a margin (50 to 2000)
rowsIn = 80 // rows (20 to 100)
// ===================== FIXED RULES =====================
maxEv = 400 // pools kept at once
maxRows = 100 // ladder rows available
zoneATR = 0.75 // stops sit within three quarters of an ATR past a swing
pivW = 1.6 // weight of the stops past a swing
wickW = 0.5 // weight of a rejection wick, times its relative volume
refreshEvery = 10 // bars between wall refreshes for the record
keepRec = 10 // wall tests remembered
brokeATR = 0.5 // a close this far past a wall breaks it
heldATR = 0.5 // a close this far back from a wall means it held
testBars = 60 // a test is dropped after this many bars without a verdict
wallAt = 0.5 // a wall must hold at least this share of the biggest row to be tracked
pivN = max(2, min(20, round(pivIn)))
wickK = max(0.2, min(2, wickIn))
halfN = max(50, min(3000, round(halfIn)))
lookN = max(300, min(4000, round(lookIn)))
spanN = max(50, min(2000, round(spanIn)))
nrow = min(max(20, min(100, round(rowsIn))), maxRows)
// ===================== INPUTS TO THE ENGINE (every bar) =====================
// ATR(14) and the slow ATR(50) for the ladder geometry; the bar range until they exist
atr14 = averagetruerange[14](close)
IF barindex >= 13 THEN
atrV = atr14
ELSE
atrV = high - low
ENDIF
atrV = max(atrV, ticksize)
atr50 = averagetruerange[50](close)
IF barindex >= 49 THEN
atrGV = atr50
ELSE
atrGV = atrV
ENDIF
atrGV = max(atrGV, ticksize)
// Volume against its 50-bar average (1 when the instrument has no volume)
vAvg = average[50](volume)
vRel = 1
IF barindex >= 49 THEN
IF vAvg > 0 THEN
vRel = min(volume / vAvg, 4)
ENDIF
ENDIF
// Swing high / low confirmed this bar: the bar pivN bars back, higher (lower) than the pivN bars
// on its right and at least as high (low) as the pivN bars on its left (on a flat top the most
// recent bar of the plateau is the swing)
hhR = highest[pivN](high)
llR = lowest[pivN](low)
phF = 0
phP = 0
plF = 0
plP = 0
IF barindex >= 2 * pivN THEN
candH = high[pivN]
IF candH > hhR AND candH >= hhR[pivN + 1] THEN
phF = 1
phP = candH
ENDIF
candL = low[pivN]
IF candL < llR AND candL <= llR[pivN + 1] THEN
plF = 1
plP = candL
ENDIF
ENDIF
// Ladder grid: the range of the last spanN bars plus a margin, on rows of a fixed price step
hhSp = highest[spanN](high)
llSp = lowest[spanN](low)
spanOk = 0
IF barindex >= spanN - 1 THEN
spanOk = 1
ENDIF
marg = max(1.5 * atrGV, 0.08 * (hhSp - llSp))
stepV = max(round(((hhSp - llSp + 2 * marg) / nrow) / ticksize) * ticksize, ticksize)
baseV = floor((llSp - marg) / stepV) * stepV
// ===================== THE ENGINE (last bar) =====================
// The pools, the walls and the record are rebuilt from the last lookN bars, once per new bar and
// only from CLOSED bars: the live bar spends and adds nothing until it closes. The result is kept
// in arrays, because arrays are not rewound between ticks the way scalar variables are.
IF barindex = 0 THEN
$runAt[0] = 0 - 1
ENDIF
IF islastbarupdate THEN
doEng = 0
IF $runAt[0] <> barindex THEN
doEng = 1
ENDIF
IF doEng = 1 THEN
nEv = 0
tSide = 0
tP = 0
tBar = 0
wUpOk = 0
wUpV = 0
wDnOk = 0
wDnV = 0
nRec = 0
$eng[0] = 0
$eng[1] = 0
$eng[2] = 0
$eng[3] = 0
$eng[4] = 0
$eng[5] = 0
$eng[6] = 0
jStart = min(lookN - 1, barindex)
ELSE
nEv = $eng[0]
jStart = 0
ENDIF
FOR j = jStart DOWNTO 0 DO
bi = barindex - j
hj = high[j]
lj = low[j]
cj = close[j]
aj = atrV[j]
bStep = stepV[j]
bBase = baseV[j]
bOk = spanOk[j]
doBuild = 0
evRU = 0
evRD = 0
evWH = 0
evWB = 0
IF j >= 1 THEN
// 1. price trading through a pool spends it, and old pools are gone
IF nEv > 0 THEN
k = 0
FOR i = 0 TO nEv - 1 DO
gone = 0
IF bi - $evBar[i] > 4 * halfN THEN
gone = 1
ENDIF
IF $evSide[i] = 1 THEN
ext = max($evExt[i], hj)
IF ext >= $evP1[i] THEN
gone = 1
ENDIF
ELSE
ext = min($evExt[i], lj)
IF ext <= $evP0[i] THEN
gone = 1
ENDIF
ENDIF
IF gone = 0 THEN
$evSide[k] = $evSide[i]
$evP0[k] = $evP0[i]
$evP1[k] = $evP1[i]
$evW[k] = $evW[i]
$evBar[k] = $evBar[i]
$evExt[k] = ext
k = k + 1
ENDIF
NEXT
nEv = k
ENDIF
// 2. new pools: the stops past a swing that just confirmed, and the orders in a rejection wick
oj = open[j]
FOR q = 1 TO 4 DO
qOk = 0
IF q = 1 THEN
IF phF[j] = 1 THEN
IF hhR[j] < phP[j] + zoneATR * aj THEN
qOk = 1
qSide = 1
qP0 = phP[j]
qP1 = phP[j] + zoneATR * aj
qW = pivW
qBar = bi - pivN
qExt = max(hhR[j], phP[j])
ENDIF
ENDIF
ELSIF q = 2 THEN
IF plF[j] = 1 THEN
IF llR[j] > plP[j] - zoneATR * aj THEN
qOk = 1
qSide = -1
qP0 = plP[j] - zoneATR * aj
qP1 = plP[j]
qW = pivW
qBar = bi - pivN
qExt = min(llR[j], plP[j])
ENDIF
ENDIF
ELSIF q = 3 THEN
IF hj - max(oj, cj) >= wickK * aj THEN
qOk = 1
qSide = 1
qP0 = max(oj, cj)
qP1 = hj
qW = wickW * vRel[j]
qBar = bi
qExt = max(oj, cj)
ENDIF
ELSE
IF min(oj, cj) - lj >= wickK * aj THEN
qOk = 1
qSide = -1
qP0 = lj
qP1 = min(oj, cj)
qW = wickW * vRel[j]
qBar = bi
qExt = min(oj, cj)
ENDIF
ENDIF
IF qOk = 1 THEN
// at most maxEv pools: the oldest one goes
IF nEv >= maxEv THEN
FOR i = 1 TO nEv - 1 DO
$evSide[i - 1] = $evSide[i]
$evP0[i - 1] = $evP0[i]
$evP1[i - 1] = $evP1[i]
$evW[i - 1] = $evW[i]
$evBar[i - 1] = $evBar[i]
$evExt[i - 1] = $evExt[i]
NEXT
nEv = nEv - 1
ENDIF
$evSide[nEv] = qSide
$evP0[nEv] = qP0
$evP1[nEv] = qP1
$evW[nEv] = qW
$evBar[nEv] = qBar
$evExt[nEv] = qExt
nEv = nEv + 1
ENDIF
NEXT
// 3. every refreshEvery bars, name the walls for the record
IF (bi MOD refreshEvery) = 0 AND nEv >= 15 THEN
doBuild = 1
ENDIF
ELSE
// the live bar: the ladder that is drawn
doBuild = 1
ENDIF
IF doBuild = 1 THEN
// Resting liquidity per row: each pool's remaining zone spread over the rows it overlaps,
// weighted by its age; then smoothed once with the neighbours
FOR r = 0 TO nrow - 1 DO
$rowS[r] = 0
NEXT
IF nEv > 0 AND bOk = 1 THEN
FOR i = 0 TO nEv - 1 DO
IF $evSide[i] = 1 THEN
a0 = max($evP0[i], $evExt[i])
a1 = $evP1[i]
ELSE
a0 = $evP0[i]
a1 = min($evP1[i], $evExt[i])
ENDIF
IF a1 > a0 THEN
wq = $evW[i] * pow(0.5, (bi - $evBar[i]) / halfN)
r0 = max(floor((a0 - bBase) / bStep), 0)
r1 = min(floor((a1 - bBase) / bStep), nrow - 1)
IF r1 >= r0 THEN
FOR r = r0 TO r1 DO
lo = bBase + r * bStep
ov = min(a1, lo + bStep) - max(a0, lo)
IF ov > 0 THEN
$rowS[r] = $rowS[r] + wq * ov / (a1 - a0)
ENDIF
NEXT
ENDIF
ENDIF
NEXT
ENDIF
peak = 0
prv = $rowS[0]
FOR r = 0 TO nrow - 1 DO
cur = $rowS[r]
IF r < nrow - 1 THEN
nxt = $rowS[r + 1]
ELSE
nxt = cur
ENDIF
sm = 0.25 * prv + 0.5 * cur + 0.25 * nxt
prv = cur
$rowS[r] = sm
peak = max(peak, sm)
NEXT
// The walls: the biggest row on each side at least 0.3 ATR from price; also the nearest
// real pool on each side and the liquidity on each side
upOk = 0
upP = 0
upT = 0
dnOk = 0
dnP = 0
dnT = 0
nUpOk = 0
nUpP = 0
nDnOk = 0
nDnP = 0
sumUp = 0
sumDn = 0
IF peak > 0 THEN
FOR r = 0 TO nrow - 1 DO
mid = bBase + (r + 0.5) * bStep
tq = $rowS[r] / peak
IF mid >= cj + 0.3 * aj THEN
sumUp = sumUp + tq
IF tq > upT THEN
upT = tq
upP = mid
upOk = 1
ENDIF
IF tq >= 0.3 AND nUpOk = 0 THEN
nUpOk = 1
nUpP = mid
ENDIF
ELSIF mid <= cj - 0.3 * aj THEN
sumDn = sumDn + tq
IF tq > dnT THEN
dnT = tq
dnP = mid
dnOk = 1
ENDIF
IF tq >= 0.3 THEN
nDnOk = 1
nDnP = mid
ENDIF
ENDIF
NEXT
ENDIF
ENDIF
IF j >= 1 THEN
IF doBuild = 1 THEN
wUpOk = 0
IF upOk = 1 AND upT >= wallAt THEN
wUpOk = 1
wUpV = upP
ENDIF
wDnOk = 0
IF dnOk = 1 AND dnT >= wallAt THEN
wDnOk = 1
wDnV = dnP
ENDIF
ENDIF
// 4. a wall gets tested when price reaches it: it held if price backs off, it broke if
// price closes through
IF tSide = 0 THEN
IF wUpOk = 1 AND hj >= wUpV - bStep / 2 THEN
tSide = 1
tP = wUpV
tBar = bi
wUpOk = 0
evRU = 1
ELSIF wDnOk = 1 AND lj <= wDnV + bStep / 2 THEN
tSide = -1
tP = wDnV
tBar = bi
wDnOk = 0
evRD = 1
ENDIF
ELSE
brk = 0
hld = 0
IF tSide = 1 THEN
IF cj > tP + brokeATR * aj THEN
brk = 1
ENDIF
IF cj <= tP - heldATR * aj THEN
hld = 1
ENDIF
ELSE
IF cj < tP - brokeATR * aj THEN
brk = 1
ENDIF
IF cj >= tP + heldATR * aj THEN
hld = 1
ENDIF
ENDIF
IF brk = 1 OR hld = 1 THEN
// the last keepRec verdicts: 1 the wall held, 0 it broke
IF nRec >= keepRec THEN
FOR i = 1 TO nRec - 1 DO
$rec[i - 1] = $rec[i]
NEXT
nRec = nRec - 1
ENDIF
IF brk = 1 THEN
$rec[nRec] = 0
evWB = 1
ELSE
$rec[nRec] = 1
evWH = 1
ENDIF
nRec = nRec + 1
tSide = 0
ELSIF bi - tBar > testBars THEN
tSide = 0
ENDIF
ENDIF
IF j = 1 THEN
$eng[0] = nEv
$eng[1] = tSide
$eng[2] = nRec
$eng[3] = evRU
$eng[4] = evRD
$eng[5] = evWH
$eng[6] = evWB
ENDIF
ENDIF
NEXT
$runAt[0] = barindex
ENDIF
// ===================== ALERTS =====================
// Add this indicator in its own panel, under a chart with Liquidity Master, and give it the same
// pivIn, wickIn, halfIn, lookIn, spanIn and rowsIn, so both map the same walls. An alert can only
// read the series returned below: they carry the four events of the bar that just closed
// (1 = it happened), read on the live bar; 0 on every other bar.
aRU = 0
aRD = 0
aWH = 0
aWB = 0
IF islastbarupdate THEN
aRU = $eng[3]
aRD = $eng[4]
aWH = $eng[5]
aWB = $eng[6]
ENDIF
RETURN aRU COLOURED(255, 61, 113) STYLE(histogram) AS "Sell wall reached", aRD COLOURED(0, 255, 163) STYLE(histogram) AS "Buy wall reached", aWH COLOURED(125, 211, 252) STYLE(histogram) AS "Wall held", aWB COLOURED(255, 180, 60) STYLE(histogram) AS "Wall broke"