Iván González

Liquidity Contour Map: Swing Liquidity Pools Drawn as a Topographic Map

Category: Indicators By: Iván González Created: October 29, 2026, 10:09 AM
October 29, 2026, 10:09 AM
Indicators
0 Comments
Liquidity Contour Map: Swing Liquidity Pools Drawn as a Topographic Map

Introduction

 

Every swing high leaves a cluster of buy stops just above it, and every swing low a cluster of sell stops just below it. Traders call these clusters liquidity pools: levels that price is often drawn to, and that tend to produce a reaction once they are taken. Most liquidity tools mark them with a line or a box. Liquidity Contour Map by AlgoNorth draws them as a topographic map.

Each pool becomes a zone centred on its price, shaped like a stadium with a rounded nose at the swing candle, and filled with up to 14 contour rings. The height of the zone, and the colour of its inner rings, show how much liquidity the pool is likely to hold: heavy swing volume, repeated touches (equal highs and equal lows) and age make a zone taller. Pools that price has already crossed stay on the map, lower and in grey. On top of that, every live pool gets a tag with the share of comparable pools that price actually reached within the next 48 bars in the recent past.

Theory Behind the Indicator

 

Pools. A swing high is a bar whose high is the highest of the 5 bars on each side (the later of two equal highs counts as the swing); a swing low, the same with the lows. It is confirmed 5 bars later, and from then on it is a live pool. A new swing within 0.25 ATR(100) of a live pool on the same side merges into the nearest one instead of starting its own: the pool keeps its first price and candle and counts one more touch (EQH, EQL).

Score. Each pool gets a unitless score:

  • Swing volume: the volume of the swing candle divided by the average volume of the 50 bars before it, clamped between 0.5 and 2.5 (1 when there is no volume).
  • Touches: 1 for a single swing, 1.5 for two, 2 for three and 2.5 from the fourth touch.
  • Age: the score builds up in a straight line from zero at the swing candle and bends smoothly into its full height 14 bars later.

Score = volume x touches x age, from 0 to 6.25. When price crosses the pool (a high above a swing high, a low below a swing low, gaps included) the pool is swept: its score freezes at the value of the sweep bar and is multiplied by 0.35.

Zones and contours. Each pool is drawn from its swing candle (or the left edge of the 150-bar map) to 8 bars past the last closed bar; a swept pool ends 8 bars after its sweep, with a second rounded nose. Its maximum half-height is 9% of the price range of the map, scaled down for short zones. Zones that share bars are trimmed to half the distance between them, so they never touch; when two pools that share bars are closer than half of that maximum half-height, only the taller one is drawn. The contour levels are spread evenly from 5% to 90% of the tallest zone inside the map’s high-low range: a zone shows as many rings as its score reaches, and each ring is narrower and shorter than the one outside it, like the contour lines of a hill. The colours follow the same scale, from pale teal on the outer ring to deep amber at the top.

Crossing statistics. Every live pool is watched in back-to-back windows of 48 bars. A window opens only while the pool is within the relevance band (3 ATR beyond the map’s high and low), less than 8 ATR from the close and young enough to finish before 1,000 bars of age. It is filed by side (highs or lows) and by the pool’s distance from the close in ATR when it opened: under 0.5, 0.5 to 1, 1 to 2, 2 to 3, 3 to 5 and 5 to 8. It counts as crossed if price went through the pool before the window ended. Only the windows that ended in the last 3,000 bars are counted, and a band needs 30 windows before it shows a rate. The rate is a past frequency for that distance band, not a forecast for one particular level: one pool opens many windows, and a pool changes band when price moves.

Key Features at a Glance

 

  • Swing liquidity pools with equal highs and equal lows merged and counted as touches.
  • A score built from swing volume, touches and age; swept pools stay on the map, lower and in grey.
  • Contour map of up to 14 rings per zone, in a flat look or a 3D look with lit ring edges, slope shading and a ground shadow.
  • Tags on the tallest zones: pool type and price, and a 10-block meter with the past crossing rate of its distance band, or how long ago it was swept.
  • Score legend and a status panel with the crossing rates by distance and side, the tallest live zone and the pool counts.

How to Read the Indicator

 

  • Tall, amber zones are the pools with the most liquidity behind them: swings made on heavy volume, levels touched several times, pools that have had time to build. They are the most likely targets for a run on stops.
  • Low, pale zones are minor swings: they still mark liquidity, but less of it.
  • EQH and EQL tags mark equal highs and equal lows. Each touch adds stops at the same level, which makes the pool taller.
  • The meter on a live tag reads, for pools on the same side and at about the same ATR distance, how often price reached them within 48 bars in the last 3,000 bars. A full meter at a short distance means such levels are almost always taken; a short meter at a long distance means they usually survive the next 48 bars.
  • Grey zones are swept pools. The liquidity there has been taken: watch how price reacts after the sweep, either a reversal (stop hunt) or a continuation through the level.
  • The panel gives the same rates for the whole map, by band and by side, so you can compare how often highs and lows get taken at each distance.

Practical Applications

 

  • Targets: the tallest live zone in the direction of the trade is a natural target, since price tends to travel towards liquidity.
  • Stop placement: avoid placing stops just beyond a tall pool; that is where everybody else’s stops are.
  • Sweep and reversal: a tall pool swept by a wick that closes back inside the range is a classic liquidity grab. The zone turns grey at that bar and the tag says how long ago it happened.
  • Context: a side with many tall live pools close to price is a side the market has not cleaned yet.

Indicator Configuration

 

Pools

  • pivLen (default 5, 2 to 30): bars on each side that confirm a swing high or low.
  • eqTol (default 0.25, 0 to 2): a new swing within this many ATR of a live pool on the same side merges into it as an extra touch.
  • maxPools (default 60, 10 to 120): pools held in memory.
  • relBand (default 3.0, 0 to 10): pools further than this many ATR beyond the map’s high or low are not drawn and open no new statistics windows.

Score

  • riseLen (default 14, 2 to 200): bars from the swing candle to full height.
  • erode (default 0.35, 0 to 1): height multiplier of a swept pool. 0 hides swept zones.
  • sweptGrey (default 1): 1 draws swept zones in grey.
  • capLen (default 8, 1 to 60): bars a swept zone runs on past the sweep.
  • useVol (default 1): 1 scales each pool by its swing volume.

Map

  • mapW (default 150, 50 to 480): map width in bars. It also sets the price range that zone heights scale to and the length of the noses.
  • hPct (default 9.0, 2 to 30): maximum half-height of a zone, as a % of the map’s price range.
  • gapK (default 0.5, 0.25 to 3): minimum gap between two zones, in half-heights.
  • nLevels (default 14, 4 to 14): contour levels per zone.
  • lowPct (default 5) and topPct (default 90): lowest and top contour, as a % of the tallest zone.
  • extR (default 8, 0 to 50): bars the zones run past the last closed bar.
  • roundK (default 1.0, 0.3 to 3): size of the rounded noses. Raise it if they look squashed, lower it if they look stretched.
  • polyCap (default 100): budget of shapes per map. With many zones not all the contours fit, and the indicator draws the most that do, evenly spaced, always keeping the lowest and the top one; the legend and the panel show how many were drawn. Raise it to draw them all.

Look

  • look3D (default 0): 1 switches to the 3D look.
  • edgeOn, edgeW (default 1, 0.35): lit ring edges and their width, as a share of the gap between rings.
  • shadeOn, shadeK (default 1, 1.0): slope shading and its strength.
  • groundOn (default 1): soft shadow under the lowest ring.

Callouts and statistics

  • showCall (default 1), nCall (default 6, 1 to 12), tagSize (default 2: 1 small, 2 normal, 3 large): tags on the tallest zones, live ones first.
  • horizon (default 48, 5 to 500): length of each statistics window, in bars.
  • winW (default 3000, 500 to 10000): only windows that ended within this many bars are counted.
  • retireN (default 1000, 100 to 4400): a pool opens no window that would end past this age; older live pools show “no rate”.
  • minN (default 30, 10 to 500): windows a band needs before it shows a rate.

Style

  • fillT (default 40, 0 to 60): transparency of the contours. 40 lets the candles show through the map; 0 gives solid colours.
  • showLeg (default 1), showPanel (default 1), txtSize (default 2: 1 tiny, 2 small, 3 normal): legend, panel and panel text size.
  • darkChart (default 0): set it to 1 on a dark chart background to use the palette made for dark charts.

The map is built from the last closed bar, so it changes only when a bar closes. The indicator needs at least 150 bars (the map width) and 100 bars for the ATR before it draws anything; until then the panel says WARMING UP. The crossing rates need a few thousand bars of history to be meaningful.

The Code

 

//---------------------------------------------------------------
//PRC_Liquidity Contour Map
//version = 0
//22.10.2026
//Iván González @ www.prorealcode.com
//Author: AlgoNorth
//Sharing ProRealTime knowledge
//--------------------------------------------------------------------//
// Original work: "Liquidity Contour Map [AlgoNorth]" by AlgoNorth, licensed under CC BY-NC-SA 4.0
// (https://creativecommons.org/licenses/by-nc-sa/4.0/). This code is shared under the same licence.
defparam drawonlastbaronly = true

// ===================== POOLS =====================
pivLen = 5          // swing length: bars on each side that confirm a swing high / low (2 to 30)
eqTol = 0.25        // equal high / low tolerance in ATR: a new swing this close to a live pool on its side merges into it (0 to 2)
maxPools = 60       // pools held in memory (10 to 120)
relBand = 3.0       // relevance band: pools further than this many ATR beyond the map's high / low are ignored (0 to 10)

// ===================== SCORE =====================
riseLen = 14        // bars from the swing candle to full height (2 to 200)
erode = 0.35        // height multiplier once price has crossed the pool (0 = swept zones hidden)
sweptGrey = 1       // 1 = swept zones in grey
capLen = 8          // bars a swept zone runs on past the sweep (1 to 60)
useVol = 1          // 1 = scale each pool by its swing candle's volume against the 50 bars before it (0.5x to 2.5x)

// ===================== MAP =====================
mapW = 150          // map width in bars (50 to 480)
hPct = 9.0          // max zone half-height, % of the map's price range (2 to 30)
gapK = 0.5          // min gap between zones, in half-heights (0.25 to 3)
nLevels = 14        // contour levels, max (4 to 14)
lowPct = 5.0        // lowest contour, % of the tallest zone (1 to 40)
topPct = 90.0       // top contour, % of the tallest zone (50 to 99)
extR = 8            // zones run up to this many bars past the last closed candle (0 to 50)
roundK = 1.0        // nose roundness (0.3 to 3)

// ===================== LOOK =====================
look3D = 0          // 0 = flat contours, 1 = 3D contours (lit ring edges and slope shadows)
edgeOn = 1          // 3D: lit ring edges
edgeW = 0.35        // 3D: edge width, share of the gap between rings (0.05 to 1)
shadeOn = 1         // 3D: slope shading
shadeK = 1.0        // 3D: shading strength (0.05 to 1.5)
groundOn = 1        // 3D: soft shadow under the lowest ring

// ===================== CALLOUTS =====================
showCall = 1        // 1 = tag the tallest zones
nCall = 6           // max callouts (1 to 12)
tagSize = 2         // tag text size: 1 = small, 2 = normal, 3 = large

// ===================== HISTORY STATS =====================
horizon = 48        // each live pool is watched in back-to-back windows of this many bars (5 to 500)
winW = 3000         // only windows that ended within this many bars are counted (500 to 10000)
retireN = 1000      // a pool opens no window that would end past this age, in bars (100 to 4400)
minN = 30           // windows a band needs before it shows a rate (10 to 500)

// ===================== STYLE =====================
fillT = 40          // contour transparency in % (0 = solid colours, up to 60): 40 keeps the candles visible through the map
showLeg = 1         // 1 = score legend (middle right)
showPanel = 1       // 1 = stats panel (top right)
txtSize = 2         // panel text size: 1 = tiny, 2 = small, 3 = normal
darkChart = 0       // 0 = palette for a light chart background, 1 = for a dark one
polyCap = 100       // shape budget: at most this many filled shapes per map, one per tag stem first. It sets how many contours fit; raise it to draw them all

// ===================== SETTINGS CHECK =====================
pLen = max(2, min(30, round(pivLen)))
eqT = max(0, min(2, eqTol))
mxP = max(10, min(120, round(maxPools)))
rBand = max(0, min(10, relBand))
rise = max(2, min(200, round(riseLen)))
ero = max(0, min(1, erode))
capB = max(1, min(60, round(capLen)))
mW = max(50, min(480, round(mapW)))
hP = max(2, min(30, hPct))
gK = max(0.25, min(3, gapK))
nLev = max(4, min(14, round(nLevels)))
loP = max(1, min(40, lowPct))
topP = max(50, min(99, topPct))
eR = max(0, min(50, round(extR)))
rndK = max(0.3, min(3, roundK))
eW = max(0.05, min(1, edgeW))
shK = max(0.05, min(1.5, shadeK))
nCl = max(1, min(12, round(nCall)))
hor = max(5, min(500, round(horizon)))
wW = max(500, min(10000, round(winW)))
retN = max(100, min(4400, round(retireN)))
mnN = max(10, min(500, round(minN)))
fT = max(0, min(60, round(fillT)))
pCap = max(1, round(polyCap))

// A pool is at least pLen bars old, so a longer horizon could never open a window
hzOK = 0
IF hor <= retN - pLen THEN
   hzOK = 1
ENDIF
// Flat look: rings share one nose centre, like contour lines. 3D: each inner nose sits further back
IF look3D = 1 THEN
   stagK = 1.5
ELSE
   stagK = 1
ENDIF
// Nose length of a full-width ring, in bars
noseR0 = rndK * hP / 100 * mW * 1.2

// ===================== SERIES =====================
// True range and ATR(100): Wilder's average seeded with a simple average
IF barindex = 0 THEN
   trv = high - low
ELSE
   trv = max(high - low, max(abs(high - close[1]), abs(low - close[1])))
ENDIF
IF barindex = 99 THEN
   atrv = summation[100](trv) / 100
ELSIF barindex > 99 THEN
   atrv = 0.01 * trv + 0.99 * atrv[1]
ELSE
   atrv = 0
ENDIF
volMA = average[50](volume)
winLo = lowest[mW](low)
winHi = highest[mW](high)

// ===================== POOL TRACKING (closed bars only) =====================
// All the state lives in arrays and each closed bar is processed exactly once: historical
// bars on themselves, and the bar that has just closed on the first tick of the next one.
// Arrays are NOT rewound between ticks the way scalars are, so nothing here may run twice.
IF barindex = 0 THEN
   $gS[0] = 0 - 1      // last bar processed
   $gS[1] = 0          // pools held
   $gS[2] = 0          // oldest window of the rolling sample (ring buffer)
   $gS[3] = 0          // windows held in the rolling sample
   $gS[4] = 0          // open windows of dropped pools
   FOR c = 0 TO 11 DO
      $rN[c] = 0       // windows per cell: highs 0-5, lows 6-11 (one cell per ATR band)
      $rH[c] = 0       // of those, crossed
   NEXT
ENDIF

FOR pss = 1 DOWNTO 0 DO
   ofs = pss
   doIt = 0
   // the previous bar was the live one and has just closed
   IF ofs = 1 AND barindex >= 1 AND $gS[0] < barindex - 1 THEN
      doIt = 1
   ENDIF
   // a historical bar is already closed
   IF ofs = 0 AND NOT islastbarupdate AND $gS[0] < barindex THEN
      doIt = 1
   ENDIF
   IF doIt = 1 THEN
      bb = barindex - ofs
      $gS[0] = bb
      nP = $gS[1]
      nO = $gS[4]
      nF = 0
      x0b = max(0, bb - mW + 1)
      atrOKb = bb >= 99
      bandOKb = 0
      IF atrOKb AND bb >= mW - 1 THEN
         bandOKb = 1
      ENDIF
      atrE = atrv[ofs]
      bLoE = winLo[ofs] - rBand * atrE
      bHiE = winHi[ofs] + rBand * atrE
      hiE = high[ofs]
      loE = low[ofs]
      clE = close[ofs]

      // 1. Forget swept pools whose zone has left the map, and pools older than 4,900 bars
      IF nP > 0 THEN
         FOR i = nP - 1 DOWNTO 0 DO
            dropIt = 0
            IF $pTk[i] >= 0 THEN
               IF $pTk[i] + capB < x0b THEN
                  dropIt = 1
               ENDIF
            ENDIF
            IF bb - $pSt[i] > 4900 THEN
               dropIt = 1
            ENDIF
            IF dropIt = 1 THEN
               IF i < nP - 1 THEN
                  FOR k = i TO nP - 2 DO
                     $pPx[k] = $pPx[k + 1]
                     $pSt[k] = $pSt[k + 1]
                     $pSd[k] = $pSd[k + 1]
                     $pTo[k] = $pTo[k + 1]
                     $pVo[k] = $pVo[k + 1]
                     $pTk[k] = $pTk[k + 1]
                     $pWs[k] = $pWs[k + 1]
                     $pWc[k] = $pWc[k + 1]
                  NEXT
               ENDIF
               nP = nP - 1
            ENDIF
         NEXT
      ENDIF

      // 2. New swings, confirmed pLen bars after the swing candle. The swing candle may tie
      // the bars before it, not the ones after it (the later of two equal highs is the swing)
      co = ofs + pLen
      isPH = 0
      isPL = 0
      cH = 0
      cL = 0
      IF bb >= 2 * pLen THEN
         cH = high[co]
         cL = low[co]
         isPH = 1
         isPL = 1
         FOR k = 1 TO pLen DO
            IF high[co + k] > cH OR high[co - k] >= cH THEN
               isPH = 0
            ENDIF
            IF low[co + k] < cL OR low[co - k] <= cL THEN
               isPL = 0
            ENDIF
         NEXT
      ENDIF
      // Swing candle volume against the average of the 50 bars before it, clamped to 0.5-2.5
      vFa = 1
      IF useVol = 1 AND bb >= pLen + 50 THEN
         vMa = volMA[co + 1]
         IF vMa > 0 THEN
            vFa = max(0.5, min(2.5, volume[co] / vMa))
         ENDIF
      ENDIF
      FOR q = 0 TO 1 DO
         addIt = 0
         aPx = 0
         aSd = 0
         IF q = 0 AND isPH = 1 THEN
            addIt = 1
            aPx = cH
            aSd = 1
         ENDIF
         IF q = 1 AND isPL = 1 THEN
            addIt = 1
            aPx = cL
            aSd = -1
         ENDIF
         IF addIt = 1 THEN
            // An equal high / low merges into the nearest live pool on its side within the tolerance
            best = -1
            bd = 0
            IF atrOKb AND nP > 0 THEN
               tolB = eqT * atrE
               FOR i = 0 TO nP - 1 DO
                  IF $pTk[i] < 0 AND $pSd[i] = aSd THEN
                     dd = abs($pPx[i] - aPx)
                     IF dd <= tolB THEN
                        IF best < 0 OR dd < bd THEN
                           best = i
                           bd = dd
                        ENDIF
                     ENDIF
                  ENDIF
               NEXT
            ENDIF
            IF best >= 0 THEN
               $pTo[best] = $pTo[best] + 1
               IF vFa > $pVo[best] THEN
                  $pVo[best] = vFa
               ENDIF
            ELSE
               $pPx[nP] = aPx
               $pSt[nP] = bb - pLen
               $pSd[nP] = aSd
               $pTo[nP] = 1
               $pVo[nP] = vFa
               $pTk[nP] = -1      // bar price crossed it, -1 while live
               $pWs[nP] = -1      // bar its open stats window started, -1 when none is open
               $pWc[nP] = 0       // stats cell of that window
               nP = nP + 1
               // List full: drop the live pool furthest beyond the relevance band, else the oldest
               // live pool, else the oldest swept one. The pool just added is never dropped
               IF nP > mxP THEN
                  iOut = -1
                  dOut = 0
                  iOld = -1
                  iSw = -1
                  FOR i = 0 TO nP - 2 DO
                     IF $pTk[i] < 0 THEN
                        IF bandOKb = 1 THEN
                           dB = max($pPx[i] - bHiE, bLoE - $pPx[i])
                           IF dB > dOut THEN
                              iOut = i
                              dOut = dB
                           ENDIF
                        ENDIF
                        IF iOld < 0 THEN
                           iOld = i
                        ELSIF $pSt[i] < $pSt[iOld] THEN
                           iOld = i
                        ENDIF
                     ELSE
                        IF iSw < 0 THEN
                           iSw = i
                        ELSIF $pTk[i] < $pTk[iSw] THEN
                           iSw = i
                        ENDIF
                     ENDIF
                  NEXT
                  IF iOut >= 0 THEN
                     iRem = iOut
                  ELSIF iOld >= 0 THEN
                     iRem = iOld
                  ELSE
                     iRem = iSw
                  ENDIF
                  // A dropped pool's open window still runs to its end and is counted
                  IF $pWs[iRem] >= 0 THEN
                     $oPx[nO] = $pPx[iRem]
                     $oSd[nO] = $pSd[iRem]
                     $oSt[nO] = $pWs[iRem]
                     $oCe[nO] = $pWc[iRem]
                     nO = nO + 1
                  ENDIF
                  FOR k = iRem TO nP - 2 DO
                     $pPx[k] = $pPx[k + 1]
                     $pSt[k] = $pSt[k + 1]
                     $pSd[k] = $pSd[k + 1]
                     $pTo[k] = $pTo[k + 1]
                     $pVo[k] = $pVo[k + 1]
                     $pTk[k] = $pTk[k + 1]
                     $pWs[k] = $pWs[k + 1]
                     $pWc[k] = $pWc[k + 1]
                  NEXT
                  nP = nP - 1
               ENDIF
            ENDIF
         ENDIF
      NEXT

      // 3. Sweeps, and the windows behind the history stats. A live pool is watched in
      // back-to-back windows of hor bars, each filed by side and by its ATR band when it opened.
      // Finished windows are queued in $fCe / $fHt and filed below, in the same order.
      IF nP > 0 THEN
         FOR i = 0 TO nP - 1 DO
            IF $pTk[i] < 0 THEN
               pxI = $pPx[i]
               hitI = 0
               IF $pSd[i] = 1 AND hiE > pxI THEN
                  hitI = 1
               ENDIF
               IF $pSd[i] = -1 AND loE < pxI THEN
                  hitI = 1
               ENDIF
               IF hitI = 1 THEN
                  $pTk[i] = bb
                  IF $pWs[i] >= 0 THEN
                     $fCe[nF] = $pWc[i]
                     $fHt[nF] = 1
                     nF = nF + 1
                     $pWs[i] = -1
                  ENDIF
               ELSE
                  IF $pWs[i] >= 0 THEN
                     IF bb - $pWs[i] >= hor THEN
                        $fCe[nF] = $pWc[i]
                        $fHt[nF] = 0
                        nF = nF + 1
                        $pWs[i] = -1
                     ENDIF
                  ENDIF
                  // A window opens only inside the relevance band, within 8 ATR, and young enough
                  // to finish before the retire age
                  IF $pWs[i] < 0 AND bandOKb = 1 AND atrE > 0 THEN
                     IF bb - $pSt[i] + hor <= retN AND pxI >= bLoE AND pxI <= bHiE THEN
                        dA = abs(pxI - clE) / atrE
                        bk = -1
                        IF dA < 0.5 THEN
                           bk = 0
                        ELSIF dA < 1 THEN
                           bk = 1
                        ELSIF dA < 2 THEN
                           bk = 2
                        ELSIF dA < 3 THEN
                           bk = 3
                        ELSIF dA < 5 THEN
                           bk = 4
                        ELSIF dA < 8 THEN
                           bk = 5
                        ENDIF
                        IF bk >= 0 THEN
                           $pWs[i] = bb
                           IF $pSd[i] = 1 THEN
                              $pWc[i] = bk
                           ELSE
                              $pWc[i] = bk + 6
                           ENDIF
                        ENDIF
                     ENDIF
                  ENDIF
               ENDIF
            ENDIF
         NEXT
      ENDIF
      // Windows handed over by dropped pools: same crossing rule, same end
      IF nO > 0 THEN
         FOR i = nO - 1 DOWNTO 0 DO
            hitO = 0
            IF $oSd[i] = 1 AND hiE > $oPx[i] THEN
               hitO = 1
            ENDIF
            IF $oSd[i] = -1 AND loE < $oPx[i] THEN
               hitO = 1
            ENDIF
            IF hitO = 1 OR bb - $oSt[i] >= hor THEN
               $fCe[nF] = $oCe[i]
               $fHt[nF] = hitO
               nF = nF + 1
               IF i < nO - 1 THEN
                  FOR k = i TO nO - 2 DO
                     $oPx[k] = $oPx[k + 1]
                     $oSd[k] = $oSd[k + 1]
                     $oSt[k] = $oSt[k + 1]
                     $oCe[k] = $oCe[k + 1]
                  NEXT
               ENDIF
               nO = nO - 1
            ENDIF
         NEXT
      ENDIF
      // File the finished windows into the rolling sample (at most 50,000 held)
      IF nF > 0 THEN
         FOR fi = 0 TO nF - 1 DO
            IF $gS[3] = 50000 THEN
               h0 = $gS[2]
               cq = $qCe[h0]
               IF cq >= 12 THEN
                  cq = cq - 12
                  $rH[cq] = $rH[cq] - 1
               ENDIF
               $rN[cq] = $rN[cq] - 1
               $gS[2] = (h0 + 1) MOD 50000
               $gS[3] = $gS[3] - 1
            ENDIF
            ce = $fCe[fi]
            $rN[ce] = $rN[ce] + 1
            IF $fHt[fi] = 1 THEN
               $rH[ce] = $rH[ce] + 1
            ENDIF
            iq = ($gS[2] + $gS[3]) MOD 50000
            $qBar[iq] = bb
            $qCe[iq] = ce + 12 * $fHt[fi]
            $gS[3] = $gS[3] + 1
         NEXT
      ENDIF
      // Keep only the windows that ended within the last wW bars
      popOn = 1
      WHILE popOn = 1 DO
         popOn = 0
         IF $gS[3] > 0 THEN
            h0 = $gS[2]
            IF $qBar[h0] <= bb - wW THEN
               popOn = 1
               cq = $qCe[h0]
               IF cq >= 12 THEN
                  cq = cq - 12
                  $rH[cq] = $rH[cq] - 1
               ENDIF
               $rN[cq] = $rN[cq] - 1
               $gS[2] = (h0 + 1) MOD 50000
               $gS[3] = $gS[3] - 1
            ENDIF
         ENDIF
      WEND
      $gS[1] = nP
      $gS[4] = nO
   ENDIF
NEXT

// ===================== MAP BUILD (from the last closed bar) =====================
IF islastbarupdate AND $gS[0] >= 0 THEN
   // Palettes, packed as r * 65536 + g * 256 + b: contour ramp, 3D lit edges and greys of swept zones
   IF darkChart = 1 THEN
      $rmp[0] = 730416    // #0B2530
      $rmp[1] = 930880    // #0E3440
      $rmp[2] = 1131596    // #11444C
      $rmp[3] = 1332563    // #145553
      $rmp[4] = 1533526    // #176656
      $rmp[5] = 2062935    // #1F7A57
      $rmp[6] = 3051093    // #2E8E55
      $rmp[7] = 4891474    // #4AA352
      $rmp[8] = 7583308    // #73B64C
      $rmp[9] = 10732615    // #A3C447
      $rmp[10] = 13617989    // #CFCB45
      $rmp[11] = 15780159    // #F0C93F
      $rmp[12] = 16767334    // #FFD966
      $rmp[13] = 16773570    // #FFF1C2
      $edg[0] = 2774374    // #2A5566
      $edg[1] = 3105651    // #2F6373
      $edg[2] = 3502973    // #35737D
      $edg[3] = 3966594    // #3C8682
      $edg[4] = 4561540    // #459A84
      $edg[5] = 5549957    // #54AF85
      $edg[6] = 6865797    // #68C385
      $edg[7] = 9098628    // #8AD584
      $edg[8] = 11723391    // #B2E27F
      $edg[9] = 14281599    // #D9EB7F
      $edg[10] = 16117643    // #F5EF8B
      $edg[11] = 16771476    // #FFE994
      $edg[12] = 16773565    // #FFF1BD
      $edg[13] = 16776695    // #FFFDF7
      $gry[0] = 1974566    // #1E2126
      $gry[1] = 2632753    // #282C31
      $gry[2] = 3356220    // #33363C
      $gry[3] = 4014406    // #3D4146
      $gry[4] = 4672337    // #474B51
      $gry[5] = 5396060    // #52565C
      $gry[6] = 6053991    // #5C6067
      $gry[7] = 6712177    // #666B71
      $gry[8] = 7370108    // #70757C
      $gry[9] = 8093831    // #7B8087
      $gry[10] = 8751762    // #858A92
      $gry[11] = 9409948    // #8F959C
      $gry[12] = 10133415    // #9A9FA7
      $gry[13] = 10791602    // #A4AAB2
      txR = 215
      txG = 220
      txB = 228
      dmR = 142
      dmG = 151
      dmB = 166
      bgR = 10
      bgG = 13
      bgB = 19
      bgA = 235
      acR = 255
      acG = 197
      acB = 61
      hbR = 255
      hbG = 241
      hbB = 194
      htR = 18
      htG = 12
      htB = 0
   ELSE
      $rmp[0] = 14873071    // #E2F1EF
      $rmp[1] = 12903904    // #C4E5E0
      $rmp[2] = 10934737    // #A6D9D1
      $rmp[3] = 8570813    // #82C7BD
      $rmp[4] = 6141353    // #5DB5A9
      $rmp[5] = 4630162    // #46A692
      $rmp[6] = 3118715    // #2F967B
      $rmp[7] = 4102741    // #3E9A55
      $rmp[8] = 6202440    // #5EA448
      $rmp[9] = 8367676    // #7FAE3C
      $rmp[10] = 10531379    // #A0B233
      $rmp[11] = 12760362    // #C2B52A
      $rmp[12] = 14786068    // #E19E14
      $rmp[13] = 12152842    // #B9700A
      $edg[0] = 15726582    // #EFF7F6
      $edg[1] = 14676462    // #DFF1EE
      $edg[2] = 13560550    // #CEEAE6
      $edg[3] = 12247259    // #BAE0DB
      $edg[4] = 10933968    // #A6D6D0
      $edg[5] = 10079939    // #99CEC3
      $edg[6] = 9291190    // #8DC5B6
      $edg[7] = 9815970    // #95C7A2
      $edg[8] = 10931610    // #A6CD9A
      $edg[9] = 12178068    // #B9D294
      $edg[10] = 13358479    // #CBD58F
      $edg[11] = 14538378    // #DDD68A
      $edg[12] = 15649406    // #EECA7E
      $edg[13] = 14200952    // #D8B078
      $gry[0] = 12830926    // #C3C8CE
      $gry[1] = 12107204    // #B8BDC4
      $gry[2] = 11449273    // #AEB3B9
      $gry[3] = 10725551    // #A3A8AF
      $gry[4] = 10067620    // #999EA4
      $gry[5] = 9343898    // #8E939A
      $gry[6] = 8685967    // #84898F
      $gry[7] = 7962245    // #797E85
      $gry[8] = 7304314    // #6F747A
      $gry[9] = 6580592    // #646970
      $gry[10] = 5922661    // #5A5F65
      $gry[11] = 5198939    // #4F545B
      $gry[12] = 4541008    // #454A50
      $gry[13] = 3817286    // #3A3F46
      txR = 26
      txG = 31
      txB = 41
      dmR = 90
      dmG = 98
      dmB = 112
      bgR = 255
      bgG = 255
      bgB = 255
      bgA = 242
      acR = 122
      acG = 79
      acB = 0
      hbR = 122
      hbG = 79
      hbB = 0
      htR = 255
      htG = 241
      htB = 194
   ENDIF

   aFill = round(255 * (100 - fT) / 100)
   bC = $gS[0]
   oB = barindex - bC
   clC = close[oB]
   atrC = atrv[oB]
   atrOK = bC >= 99
   winOK = bC >= mW - 1
   wLo = winLo[oB]
   wHi = winHi[oB]
   bandOK = 0
   IF atrOK AND winOK THEN
      bandOK = 1
   ENDIF
   bLoC = wLo - rBand * atrC
   bHiC = wHi + rBand * atrC
   x0 = max(0, bC - mW + 1)            // map's left edge
   xEnd = bC + eR                       // zones run to here (swept ones end earlier)
   rng = wHi - wLo
   sg = hP / 100 * rng                  // max zone half-height
   dG = (topP - loP) / 100 / (nLev - 1)  // contour spacing, share of the peak
   nP = $gS[1]
   IF look3D = 1 THEN
      nLay0 = groundOn + edgeOn + 1
      nCol = edgeOn + 1
   ELSE
      nLay0 = 1
      nCol = 1
   ENDIF

   // Pools in view: everything inside the relevance band
   nV = 0
   nSwept = 0
   IF bandOK = 1 AND nP > 0 THEN
      FOR i = 0 TO nP - 1 DO
         IF $pPx[i] >= bLoC AND $pPx[i] <= bHiC THEN
            $vP[nV] = i
            nV = nV + 1
            IF $pTk[i] >= 0 THEN
               nSwept = nSwept + 1
            ENDIF
         ENDIF
      NEXT
   ENDIF

   // Each pool is its own zone. Pools claim their space in order of pre-sweep height; a pool is
   // hidden only when it sits within the min zone gap of a taller one sharing bars, its peak is
   // below the lowest contour, or its outer ring has too few bars for a smooth outline
   nZ = 0
   nZS = 0
   fTop = 0
   IF winOK AND sg > 0 AND nV > 0 THEN
      pkAll = 0
      pkMap = 0
      FOR v = 0 TO nV - 1 DO
         i = $vP[v]
         zs = max($pSt[i], x0)
         IF $pTk[i] < 0 THEN
            ze = xEnd
            xq = bC
         ELSE
            ze = min(xEnd, $pTk[i] + capB)
            xq = $pTk[i]
         ENDIF
         // Age envelope: a straight build-up from the swing candle that bends into full height
         // at rise bars (a live pool peaks now, a swept one keeps the height of its sweep bar)
         uu = 1.25 * (xq - $pSt[i]) / rise
         tt = 1.25 - uu
         IF uu <= 0 THEN
            env = 0
         ELSIF tt >= 0.5 THEN
            env = uu
         ELSIF tt <= 0 THEN
            env = 1
         ELSE
            env = 1 - tt * tt / 1
         ENDIF
         // Score: swing volume x touches (up to 2.5 at the 4th touch) x age envelope
         rkV = $pVo[i] * min(2.5, 1 + 0.5 * ($pTo[i] - 1)) * env
         $vS[v] = zs
         $vE[v] = ze
         $vRk[v] = rkV
         IF $pTk[i] < 0 THEN
            pkV = rkV
         ELSE
            pkV = rkV * ero
         ENDIF
         $vPk[v] = pkV
         pkAll = max(pkAll, pkV)
         IF $pPx[i] >= wLo AND $pPx[i] <= wHi THEN
            pkMap = max(pkMap, pkV)
         ENDIF
      NEXT
      // Colours and contours scale to the tallest zone inside the map's high-low range
      IF pkMap > 0 THEN
         gate = max(0.01, pkMap * loP / 100)
      ELSE
         gate = max(0.01, pkAll * loP / 100)
      ENDIF
      // Order by pre-sweep score, tallest first (stable: ties keep the list order)
      FOR v = 0 TO nV - 1 DO
         $ord[v] = v
      NEXT
      IF nV > 1 THEN
         FOR v = 1 TO nV - 1 DO
            cur = $ord[v]
            rkCur = $vRk[cur]
            w = v - 1
            moving = 1
            WHILE moving = 1 DO
               IF w < 0 THEN
                  moving = 0
               ELSE
                  ow = $ord[w]
                  IF $vRk[ow] < rkCur THEN
                     $ord[w + 1] = ow
                     w = w - 1
                  ELSE
                     moving = 0
                  ENDIF
               ENDIF
            WEND
            $ord[w + 1] = cur
         NEXT
      ENDIF
      nK = 0
      ptAcc = 0
      fMap = 0
      FOR oo = 0 TO nV - 1 DO
         k = $ord[oo]
         i = $vP[k]
         zs = $vS[k]
         ze = $vE[k]
         pkV = $vPk[k]
         pt = (2 * (ze - zs) + 5) * nLay0
         ok = 0
         IF pkV > gate AND ptAcc + pt <= 150000 THEN
            IF look3D = 1 OR nK < pCap - showCall * nCl THEN
               ok = 1
            ENDIF
         ENDIF
         IF ok = 1 THEN
            // its outer ring needs room for a smooth outline, checked at the gate
            heK = 0
            IF $pTk[i] >= 0 AND ze < xEnd THEN
               heK = 1
            ENDIF
            ff = 1 - gate / pkV
            IF heK = 1 THEN
               rz0 = min(noseR0, (ze - zs) / 2 / stagK)
            ELSE
               rz0 = min(noseR0, (ze - zs) / stagK)
            ENDIF
            ld = stagK * rz0 * (1 - ff)
            xa = floor(zs + ld + 0.5)
            IF heK = 1 THEN
               xl = floor(ze - ld + 0.5) - 1
            ELSE
               xl = ze
            ENDIF
            IF xl < xa + 4 THEN
               ok = 0
            ENDIF
         ENDIF
         IF ok = 1 AND nK > 0 THEN
            pxK = $pPx[i]
            FOR mm = 0 TO nK - 1 DO
               m = $kp[mm]
               im = $vP[m]
               IF $vS[m] <= ze AND zs <= $vE[m] AND abs($pPx[im] - pxK) < gK * sg THEN
                  ok = 0
               ENDIF
            NEXT
         ENDIF
         IF ok = 1 THEN
            $kp[nK] = k
            nK = nK + 1
            ptAcc = ptAcc + pt
            fTop = max(fTop, pkV)
            IF $pPx[i] >= wLo AND $pPx[i] <= wHi THEN
               fMap = max(fMap, pkV)
            ENDIF
         ENDIF
      NEXT
      IF fMap > 0 THEN
         fTop = fMap
      ENDIF
      // Zones that share bars are trimmed to half the gap between them, so no two touch.
      // A zone too short for full noses is scaled down whole, so it keeps the same shape
      IF nK > 0 THEN
         FOR kk = 0 TO nK - 1 DO
            k = $kp[kk]
            i = $vP[k]
            pxK = $pPx[i]
            gg = 1000000000000
            FOR mm = 0 TO nK - 1 DO
               m = $kp[mm]
               IF m <> k AND $vS[m] <= $vE[k] AND $vS[k] <= $vE[m] THEN
                  im = $vP[m]
                  gg = min(gg, abs($pPx[im] - pxK))
               ENDIF
            NEXT
            zs = $vS[k]
            ze = $vE[k]
            heK = 0
            IF $pTk[i] >= 0 AND ze < xEnd THEN
               heK = 1
            ENDIF
            IF heK = 1 THEN
               rz0 = min(noseR0, (ze - zs) / 2 / stagK)
            ELSE
               rz0 = min(noseR0, (ze - zs) / stagK)
            ENDIF
            sI = min(sg * rz0 / noseR0, 0.5 * gg - 0.06 * sg)
            $zPl[nZ] = i
            $zSg[nZ] = sI
            $zAm[nZ] = $vPk[k]
            $zXs[nZ] = zs
            $zXe[nZ] = ze
            $zHe[nZ] = heK
            $zGp[nZ] = sI * fTop / $vPk[k] * dG
            $zGr[nZ] = 0
            IF sweptGrey = 1 AND $pTk[i] >= 0 THEN
               $zGr[nZ] = 1
               nZS = nZS + 1
            ENDIF
            nZ = nZ + 1
         NEXT
      ENDIF
   ENDIF

   // Contours: geometry of every ring, then the most contours that fit the shape budget
   // (the original draws at most 100 polylines, one per tag stem first)
   nTagOK = 0
   IF nZ > 0 THEN
      FOR z = 0 TO nZ - 1 DO
         pl = $zPl[z]
         IF $pSt[pl] >= x0 THEN
            nTagOK = nTagOK + 1
         ENDIF
      NEXT
   ENDIF
   nTag = 0
   IF showCall = 1 THEN
      nTag = min(nCl, nTagOK)
   ENDIF
   budget = pCap - nTag
   nSel = 0
   need = 0
   nPoly = 0
   dropped = 0
   IF nZ > 0 THEN
      FOR i = 0 TO nLev - 1 DO
         lvI = fTop * (loP / 100 + i * dG)
         $lv[i] = lvI
         $cix[i] = floor(13 * i / (nLev - 1) + 0.5)
         cntP = 0
         cntQ = 0
         FOR z = 0 TO nZ - 1 DO
            gi = i * 120 + z
            am = $zAm[z]
            zs = $zXs[z]
            ze = $zXe[z]
            heZ = $zHe[z]
            ff = 1 - lvI / am
            IF heZ = 1 THEN
               rz0 = min(noseR0, (ze - zs) / 2 / stagK)
            ELSE
               rz0 = min(noseR0, (ze - zs) / stagK)
            ENDIF
            ld = stagK * rz0 * (1 - ff)
            rr = max(2, rz0 * ff)
            xa = floor(zs + ld + 0.5)
            IF heZ = 1 THEN
               xb = floor(ze - ld + 0.5)
               xl = xb - 1
            ELSE
               xb = ze
               xl = ze
            ENDIF
            nn = 0
            IF lvI < am AND xl >= xa + 4 THEN
               nn = 1 + 2 * (xl - xa) + heZ
            ENDIF
            $gN[gi] = nn
            $gXa[gi] = xa
            $gXb[gi] = xb
            $gXl[gi] = xl
            $gRl[gi] = zs + ld + rr - xa
            $gRr[gi] = xb - (ze - ld - rr)
            $gF[gi] = ff
            IF nn > 0 THEN
               cntP = cntP + 1
               cntQ = cntQ + nn
            ENDIF
         NEXT
         IF look3D = 0 THEN
            // flat: one polyline per ring
            $cP[i] = cntP
            $cPt[i] = cntQ
         ELSE
            // 3D: each layer joins every zone into one polyline, cut every 9,500 points
            FOR sb = 0 TO 2 DO
               lcP = 0
               lcQ = 0
               acc = 0
               nA = 0
               FOR z = 0 TO nZ - 1 DO
                  inSb = 0
                  IF sb = 0 THEN
                     inSb = 1
                  ELSIF sb = 1 AND $zGr[z] = 0 THEN
                     inSb = 1
                  ELSIF sb = 2 AND $zGr[z] = 1 THEN
                     inSb = 1
                  ENDIF
                  IF inSb = 1 THEN
                     IF acc > 0 AND acc + 2 * ($zXe[z] - $zXs[z]) + 8 > 9500 THEN
                        lcP = lcP + 1
                        lcQ = lcQ + acc + nA - 1
                        acc = 0
                        nA = 0
                     ENDIF
                     nn = $gN[i * 120 + z]
                     IF nn > 0 THEN
                        acc = acc + nn + 1
                        nA = nA + 1
                     ENDIF
                  ENDIF
               NEXT
               IF acc > 0 THEN
                  lcP = lcP + 1
                  lcQ = lcQ + acc + nA - 1
               ENDIF
               $lyP[sb] = lcP
               $lyQ[sb] = lcQ
            NEXT
            IF i = 0 THEN
               nSh = groundOn
            ELSIF shadeOn = 1 THEN
               nSh = 2
            ELSE
               nSh = 0
            ENDIF
            $cP[i] = nSh * $lyP[0] + nCol * ($lyP[1] + $lyP[2])
            $cPt[i] = nSh * $lyQ[0] + nCol * ($lyQ[1] + $lyQ[2])
         ENDIF
         need = need + $cP[i]
      NEXT
      // The most contours that fit: evenly spaced, the lowest and the top kept whenever two fit
      kS = nLev
      WHILE nSel = 0 AND kS >= 1 DO
         sumP = 0
         sumQ = 0
         FOR j = 0 TO kS - 1 DO
            IF kS = 1 THEN
               ii = 0
            ELSE
               ii = floor(j * (nLev - 1) / (kS - 1) + 0.5)
            ENDIF
            $sTry[j] = ii
            sumP = sumP + $cP[ii]
            sumQ = sumQ + $cPt[ii]
         NEXT
         IF sumP <= budget AND sumQ <= 150000 THEN
            nSel = kS
            FOR j = 0 TO kS - 1 DO
               $sel[j] = $sTry[j]
               nPoly = nPoly + $cP[$sel[j] + 0]
            NEXT
         ENDIF
         kS = kS - 1
      WEND
      dropped = need - nPoly
      // 3D on a thinned ladder: edges, cores and ground shadows scale with the ring spacing drawn
      IF look3D = 1 AND nSel > 1 AND nSel < nLev THEN
         FOR z = 0 TO nZ - 1 DO
            $zGp[z] = $zGp[z] * (nLev - 1) / (nSel - 1)
         NEXT
      ENDIF

      // ===================== DRAW THE MAP =====================
      // Every ring is a closed outline: a pointed nose at the swing candle (and at the end of a
      // swept zone) and a flat run in between, filled column by column with triangles (a flat run
      // is one block). Flat look: each ring is drawn as the band between it and the next contour
      // of its zone, so colours never pile up and the candles show through evenly; with no
      // transparency it is exactly the stacked picture of the original. 3D look: every layer is
      // drawn whole and on top of the previous one, as in the original.
      op = 1 - fT / 100
      IF darkChart = 1 THEN
         grT = round(100 - 45 * op)
      ELSE
         grT = round(100 - 15 * op)
      ENDIF
      aGround = round(255 * (100 - grT) / 100)
      coreT = round(100 - 100 * shK * 0.32 * op)
      aCore = round(255 * (100 - coreT) / 100)
      IF nSel > 0 THEN
         FOR jj = 0 TO nSel - 1 DO
            i = $sel[jj]
            lvI = $lv[i]
            ci = $cix[i]
            iNx = -1
            IF look3D = 0 AND jj < nSel - 1 THEN
               iNx = $sel[jj + 1]
            ENDIF
            FOR ps = 0 TO 4 DO
               psOn = 0
               IF look3D = 0 THEN
                  IF ps = 4 THEN
                     psOn = 1
                  ENDIF
               ELSE
                  IF ps = 0 AND i = 0 AND groundOn = 1 THEN
                     psOn = 1
                  ENDIF
                  IF (ps = 1 OR ps = 2) AND i > 0 AND shadeOn = 1 THEN
                     psOn = 1
                  ENDIF
                  IF ps = 3 AND edgeOn = 1 THEN
                     psOn = 1
                  ENDIF
                  IF ps = 4 THEN
                     psOn = 1
                  ENDIF
               ENDIF
               IF psOn = 1 THEN
                  // Layer: 0 ground shadow, 1 shadow cast down to the ring outside, 2 dark core
                  // under the ring, 3 lit edge (ring widened), 4 face
                  kd = 0
                  eT = 0
                  eB = 0
                  lvP = lvI
                  aSh = 255
                  IF ps = 0 THEN
                     kd = 3
                     aSh = aGround
                  ELSIF ps = 1 THEN
                     kd = 1
                     jp = $sel[jj - 1]
                     lvP = $lv[jp]
                     aL = shK * (0.62 - 0.48 * (i - 1) / (nLev - 2)) * op
                     shT = round(100 - 100 * aL)
                     aSh = round(255 * (100 - shT) / 100)
                  ELSIF ps = 2 THEN
                     kd = 2
                     aSh = aCore
                  ELSIF ps = 3 THEN
                     eT = eW
                     eB = eW * 0.5
                  ENDIF
                  FOR z = 0 TO nZ - 1 DO
                     gi = i * 120 + z
                     IF $gN[gi] > 0 THEN
                        // colour of this zone on this layer
                        IF ps <= 2 THEN
                           cR = 0
                           cG = 0
                           cB = 0
                           cA = aSh
                        ELSE
                           IF ps = 3 THEN
                              IF $zGr[z] = 1 THEN
                                 IF darkChart = 1 THEN
                                    cv = $gry[min(13, ci + 2)]
                                 ELSE
                                    cv = $gry[max(0, ci - 2)]
                                 ENDIF
                              ELSE
                                 cv = $edg[ci]
                              ENDIF
                           ELSE
                              IF $zGr[z] = 1 THEN
                                 cv = $gry[ci]
                              ELSE
                                 cv = $rmp[ci]
                              ENDIF
                           ENDIF
                           cR = floor(cv / 65536)
                           cG = floor(cv / 256) - cR * 256
                           cB = cv - floor(cv / 256) * 256
                           cA = aFill
                        ENDIF
                        // no outline: an outline in the fill colour shows every seam between triangles
                        oA = 0
                        pl = $zPl[z]
                        pr = $pPx[pl]
                        sz = $zSg[z]
                        am = $zAm[z]
                        gp = $zGp[z]
                        heZ = $zHe[z]
                        wRing = sz * $gF[gi]
                        dW = 0
                        IF kd = 1 THEN
                           dW = sz * (lvI - lvP) / am
                        ELSIF kd = 2 THEN
                           dW = 0.6 * gp
                        ELSIF kd = 3 THEN
                           dW = 1.2 * gp
                        ENDIF
                        wT = min(sz, wRing + eT * gp)
                        IF kd = 0 THEN
                           wB = min(sz, wRing + eB * gp)
                        ELSE
                           wB = min(sz, wRing + dW)
                        ENDIF
                        xa = $gXa[gi]
                        xb = $gXb[gi]
                        xl = $gXl[gi]
                        rlN = $gRl[gi]
                        rrN = $gRr[gi]
                        IF heZ = 1 THEN
                           xEo = xb
                        ELSE
                           xEo = xl
                        ENDIF
                        // the next contour of this zone is cut out of this ring (flat look)
                        inOn = 0
                        wI = 0
                        IF iNx >= 0 THEN
                           gn = iNx * 120 + z
                           IF $gN[gn] > 0 THEN
                              inOn = 1
                              xaI = $gXa[gn]
                              xbI = $gXb[gn]
                              xlI = $gXl[gn]
                              rlI = $gRl[gn]
                              rrI = $gRr[gn]
                              wI = sz * $gF[gn]
                              IF heZ = 1 THEN
                                 xEi = xbI
                              ELSE
                                 xEi = xlI
                              ENDIF
                           ENDIF
                        ENDIF
                        x1 = xa
                        fo1 = 0
                        fn1 = 0
                        runOn = 0
                        xRun = xa
                        runIn = 0
                        // one column per step; the extra last step only closes a pending flat block
                        FOR xx = xa + 1 TO xEo + 1 DO
                           drawNow = 0
                           flushNow = 0
                           newRun = 0
                           fo2 = 0
                           fn2 = 0
                           inCol = 0
                           IF xx <= xEo THEN
                              // ring profile at xx: 0 at the tips, 1 on the flat run
                              IF xx <= xl THEN
                                 cc = min(1, (xx - xa) / rlN)
                                 fo2 = POW(cc * (2 - cc), 0.7)
                                 IF heZ = 1 THEN
                                    cc = min(1, (xb - xx) / rrN)
                                    fo2 = fo2 * POW(cc * (2 - cc), 0.7)
                                 ENDIF
                              ENDIF
                              IF inOn = 1 THEN
                                 IF x1 >= xaI AND xx <= xEi THEN
                                    inCol = 1
                                    IF xx <= xlI THEN
                                       cc = min(1, (xx - xaI) / rlI)
                                       fn2 = POW(cc * (2 - cc), 0.7)
                                       IF heZ = 1 THEN
                                          cc = min(1, (xbI - xx) / rrI)
                                          fn2 = fn2 * POW(cc * (2 - cc), 0.7)
                                       ENDIF
                                    ENDIF
                                 ENDIF
                              ENDIF
                              flatC = 0
                              IF fo1 = 1 AND fo2 = 1 THEN
                                 IF inCol = 0 THEN
                                    flatC = 1
                                 ELSIF fn1 = 1 AND fn2 = 1 THEN
                                    flatC = 1
                                 ENDIF
                              ENDIF
                              IF flatC = 1 AND runOn = 1 AND runIn = inCol THEN
                                 drawNow = 0
                              ELSE
                                 IF runOn = 1 THEN
                                    flushNow = 1
                                 ENDIF
                                 IF flatC = 1 THEN
                                    newRun = 1
                                 ELSE
                                    drawNow = 1
                                 ENDIF
                              ENDIF
                           ELSIF runOn = 1 THEN
                              flushNow = 1
                           ENDIF
                           // emit the pending flat block, then this column
                           FOR em = 0 TO 1 DO
                              doEm = 0
                              IF em = 0 AND flushNow = 1 THEN
                                 doEm = 1
                                 exL = xRun
                                 exR = x1
                                 uTa = pr + wT
                                 uTb = uTa
                                 uBa = pr - wB
                                 uBb = uBa
                                 hasIn = runIn
                                 vTa = pr + wI
                                 vTb = vTa
                                 vBa = pr - wI
                                 vBb = vBa
                                 runOn = 0
                              ENDIF
                              IF em = 1 AND drawNow = 1 THEN
                                 doEm = 1
                                 exL = x1
                                 exR = xx
                                 uTa = pr + wT * fo1
                                 uTb = pr + wT * fo2
                                 uBa = pr - wB * fo1
                                 uBb = pr - wB * fo2
                                 hasIn = inCol
                                 vTa = pr + wI * fn1
                                 vTb = pr + wI * fn2
                                 vBa = pr - wI * fn1
                                 vBb = pr - wI * fn2
                              ENDIF
                              IF doEm = 1 THEN
                                 FOR pc = 0 TO hasIn DO
                                    IF hasIn = 0 THEN
                                       pa1 = uTa
                                       pa2 = uTb
                                       pb1 = uBa
                                       pb2 = uBb
                                    ELSIF pc = 0 THEN
                                       pa1 = uTa
                                       pa2 = uTb
                                       pb1 = vTa
                                       pb2 = vTb
                                    ELSE
                                       pa1 = vBa
                                       pa2 = vBb
                                       pb1 = uBa
                                       pb2 = uBb
                                    ENDIF
                                    // a vertical-sided quad as two triangles (a tip leaves one)
                                    IF pa2 <> pb2 THEN
                                       DRAWTRIANGLE(exL, pa1, exR, pa2, exR, pb2) COLOURED(cR, cG, cB, oA) FILLCOLOR(cR, cG, cB, cA)
                                    ENDIF
                                    IF pa1 <> pb1 THEN
                                       DRAWTRIANGLE(exL, pa1, exR, pb2, exL, pb1) COLOURED(cR, cG, cB, oA) FILLCOLOR(cR, cG, cB, cA)
                                    ENDIF
                                 NEXT
                              ENDIF
                           NEXT
                           IF newRun = 1 THEN
                              runOn = 1
                              xRun = x1
                              runIn = inCol
                           ENDIF
                           x1 = xx
                           fo1 = fo2
                           fn1 = fn2
                        NEXT
                     ENDIF
                  NEXT
               ENDIF
            NEXT
         NEXT
      ENDIF
   ENDIF
   // ===================== CALLOUTS =====================
   // Live zones first, then swept ones, each tallest first. A tag sits beyond its own zone and is
   // pushed past any zone or earlier tag in its way; a dotted stem joins it to the swing candle
   IF nTag > 0 THEN
      IF tagSize = 1 THEN
         tgK = 0.8
         tz = 10
      ELSIF tagSize = 3 THEN
         tgK = 1.3
         tz = 15
      ELSE
         tgK = 1
         tz = 12
      ENDIF
      tagH = 0.075 * tgK * rng
      tagW = 0.13 * tgK * mW
      mg = 0.3 * sg
      nT = 0
      FOR pz = 0 TO 1 DO
         FOR z = 0 TO nZ - 1 DO
            pl = $zPl[z]
            isLive = 0
            IF $pTk[pl] < 0 THEN
               isLive = 1
            ENDIF
            okT = 0
            IF nT < nTag AND $pSt[pl] >= x0 THEN
               IF (pz = 0 AND isLive = 1) OR (pz = 1 AND isLive = 0) THEN
                  okT = 1
               ENDIF
            ENDIF
            IF okT = 1 THEN
               xT = $pSt[pl]
               prT = $pPx[pl]
               upT = 0
               IF $pSd[pl] = 1 THEN
                  upT = 1
                  yT = prT + $zSg[z] + mg
               ELSE
                  yT = prT - $zSg[z] - mg
               ENDIF
               // obstacles: every zone and earlier tag in the tag's columns
               nOb = 0
               FOR o = 0 TO nZ - 1 DO
                  IF $zXs[o] <= xT + tagW / 2 AND xT - tagW / 2 <= $zXe[o] THEN
                     po = $zPl[o]
                     $obL[nOb] = $pPx[po] - $zSg[o] - mg
                     $obH[nOb] = $pPx[po] + $zSg[o] + mg
                     nOb = nOb + 1
                  ENDIF
               NEXT
               IF nT > 0 THEN
                  FOR tq = 0 TO nT - 1 DO
                     IF abs($tX[tq] - xT) < tagW THEN
                        $obL[nOb] = $tLo[tq] - mg
                        $obH[nOb] = $tHi[tq] + mg
                        nOb = nOb + 1
                     ENDIF
                  NEXT
               ENDIF
               // one sweep outwards in price order clears them all
               IF nOb > 0 THEN
                  FOR v = 0 TO nOb - 1 DO
                     IF upT = 1 THEN
                        $obK[v] = $obL[v]
                     ELSE
                        $obK[v] = 0 - $obH[v]
                     ENDIF
                     $obO[v] = v
                  NEXT
                  IF nOb > 1 THEN
                     FOR v = 1 TO nOb - 1 DO
                        cur = $obO[v]
                        kCur = $obK[cur]
                        w = v - 1
                        moving = 1
                        WHILE moving = 1 DO
                           IF w < 0 THEN
                              moving = 0
                           ELSE
                              ow = $obO[w]
                              IF $obK[ow] > kCur THEN
                                 $obO[w + 1] = ow
                                 w = w - 1
                              ELSE
                                 moving = 0
                              ENDIF
                           ENDIF
                        WEND
                        $obO[w + 1] = cur
                     NEXT
                  ENDIF
                  FOR v = 0 TO nOb - 1 DO
                     jo = $obO[v]
                     IF upT = 1 THEN
                        IF yT < $obH[jo] AND yT + tagH > $obL[jo] THEN
                           yT = $obH[jo]
                        ENDIF
                     ELSE
                        IF yT > $obL[jo] AND yT - tagH < $obH[jo] THEN
                           yT = $obL[jo]
                        ENDIF
                     ENDIF
                  NEXT
               ENDIF
               $tX[nT] = xT
               IF upT = 1 THEN
                  $tLo[nT] = yT
                  $tHi[nT] = yT + tagH
                  y1 = yT + 0.68 * tagH
                  y2 = yT + 0.32 * tagH
               ELSE
                  $tLo[nT] = yT - tagH
                  $tHi[nT] = yT
                  y1 = yT - 0.32 * tagH
                  y2 = yT - 0.68 * tagH
               ENDIF
               nT = nT + 1

               // Tag colour: live tags in the text colour (light chart) or the lit edge of their height (dark chart)
               IF isLive = 1 THEN
                  IF darkChart = 1 THEN
                     ce2 = max(6, min(13, floor(13 * $zAm[z] / fTop + 0.5)))
                     cv = $edg[ce2]
                     tcR = floor(cv / 65536)
                     tcG = floor(cv / 256) - tcR * 256
                     tcB = cv - floor(cv / 256) * 256
                  ELSE
                     tcR = txR
                     tcG = txG
                     tcB = txB
                  ENDIF
               ELSE
                  tcR = dmR
                  tcG = dmG
                  tcB = dmB
               ENDIF
               IF darkChart = 1 THEN
                  skR = tcR
                  skG = tcG
                  skB = tcB
               ELSE
                  skR = txR
                  skG = txG
                  skB = txB
               ENDIF
               DRAWSEGMENT(xT, prT, xT, yT) COLOURED(skR, skG, skB, 217) STYLE(dottedline, 1)

               // Line 1: pool type and price
               tch = $pTo[pl]
               IF upT = 1 THEN
                  IF tch > 1 THEN
                     DRAWTEXT("EQH×#tch#  #prT#", xT, y1, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                  ELSE
                     DRAWTEXT("HIGH  #prT#", xT, y1, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                  ENDIF
               ELSE
                  IF tch > 1 THEN
                     DRAWTEXT("EQL×#tch#  #prT#", xT, y1, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                  ELSE
                     DRAWTEXT("LOW  #prT#", xT, y1, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                  ENDIF
               ENDIF

               // Line 2, live pool: share of past windows in this pool's band (same side) that price
               // crossed within the horizon. Swept pool: when it was swept
               IF isLive = 1 THEN
                  ageT = bC - $pSt[pl]
                  bkT = -1
                  IF atrC > 0 THEN
                     dAt = abs(prT - clC) / atrC
                     IF dAt < 0.5 THEN
                        bkT = 0
                     ELSIF dAt < 1 THEN
                        bkT = 1
                     ELSIF dAt < 2 THEN
                        bkT = 2
                     ELSIF dAt < 3 THEN
                        bkT = 3
                     ELSIF dAt < 5 THEN
                        bkT = 4
                     ELSIF dAt < 8 THEN
                        bkT = 5
                     ENDIF
                  ENDIF
                  IF hzOK = 0 THEN
                     DRAWTEXT("no rate · horizon too long", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                  ELSIF ageT > retN THEN
                     DRAWTEXT("no rate · >#retN#b", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                  ELSIF bkT < 0 THEN
                     DRAWTEXT("beyond 8 ATR", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                  ELSE
                     cT = bkT
                     IF upT = 0 THEN
                        cT = bkT + 6
                     ENDIF
                     nW = $rN[cT]
                     hW = $rH[cT]
                     IF nW = 0 THEN
                        DRAWTEXT("no windows yet", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                     ELSIF nW < mnN THEN
                        DRAWTEXT("n #nW# · too few", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                     ELSE
                        pI = floor(100 * hW / nW + 0.5)
                        IF pI <= 0 THEN
                           nFill = 0
                        ELSIF pI >= 100 THEN
                           nFill = 10
                        ELSE
                           nFill = max(1, min(9, floor(pI / 10 + 0.5)))
                        ENDIF
                        // 10-block meter: 0% shows no block, a full meter needs 99.5% or more
                        IF nFill = 0 THEN
                           IF hW > 0 THEN
                              DRAWTEXT("▱▱▱▱▱▱▱▱▱▱ <1%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                           ELSE
                              DRAWTEXT("▱▱▱▱▱▱▱▱▱▱ 0%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                           ENDIF
                        ELSIF nFill = 1 THEN
                           DRAWTEXT("▰▱▱▱▱▱▱▱▱▱ #pI#%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                        ELSIF nFill = 2 THEN
                           DRAWTEXT("▰▰▱▱▱▱▱▱▱▱ #pI#%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                        ELSIF nFill = 3 THEN
                           DRAWTEXT("▰▰▰▱▱▱▱▱▱▱ #pI#%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                        ELSIF nFill = 4 THEN
                           DRAWTEXT("▰▰▰▰▱▱▱▱▱▱ #pI#%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                        ELSIF nFill = 5 THEN
                           DRAWTEXT("▰▰▰▰▰▱▱▱▱▱ #pI#%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                        ELSIF nFill = 6 THEN
                           DRAWTEXT("▰▰▰▰▰▰▱▱▱▱ #pI#%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                        ELSIF nFill = 7 THEN
                           DRAWTEXT("▰▰▰▰▰▰▰▱▱▱ #pI#%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                        ELSIF nFill = 8 THEN
                           DRAWTEXT("▰▰▰▰▰▰▰▰▱▱ #pI#%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                        ELSIF nFill = 9 THEN
                           DRAWTEXT("▰▰▰▰▰▰▰▰▰▱ #pI#%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                        ELSE
                           IF hW < nW THEN
                              DRAWTEXT("▰▰▰▰▰▰▰▰▰▰ >99%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                           ELSE
                              DRAWTEXT("▰▰▰▰▰▰▰▰▰▰ 100%", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                           ENDIF
                        ENDIF
                     ENDIF
                  ENDIF
               ELSE
                  agoT = bC - $pTk[pl]
                  IF agoT = 0 THEN
                     DRAWTEXT("SWEPT this bar", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                  ELSIF agoT = 1 THEN
                     DRAWTEXT("SWEPT 1 bar ago", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                  ELSE
                     DRAWTEXT("SWEPT #agoT# bars ago", xT, y2, monospaced, bold, tz) COLOURED(tcR, tcG, tcB, 255)
                  ENDIF
               ENDIF
            ENDIF
         NEXT
      NEXT
   ENDIF

   // ===================== LEGEND (middle right) =====================
   // Drawn contours only, top contour first, as % of the tallest zone; a grey row for swept zones
   IF showLeg = 1 THEN
      nRow = 0
      IF nSel > 0 THEN
         FOR jj = nSel - 1 DOWNTO 0 DO
            i = $sel[jj]
            IF $cP[i] > 0 THEN
               $lgI[nRow] = i
               nRow = nRow + 1
            ENDIF
         NEXT
      ENDIF
      nLg = nRow
      IF nZS > 0 THEN
         nLg = nLg + 1
      ENDIF
      IF nLg > 0 THEN
         lgH = 18
         lgTop = nLg * lgH / 2
         lgR = 0 - 90
         lgL = 0 - 160
         DRAWRECTANGLE(lgL, lgTop + 3, lgR, lgTop - nLg * lgH - 3) ANCHOR(RIGHT, XSHIFT, YSHIFT) COLOURED(bgR, bgG, bgB, bgA) FILLCOLOR(bgR, bgG, bgB, bgA)
         IF nRow > 0 THEN
            FOR rw = 0 TO nRow - 1 DO
               i = $lgI[rw]
               ci = $cix[i]
               cv = $rmp[ci]
               cR = floor(cv / 65536)
               cG = floor(cv / 256) - cR * 256
               cB = cv - floor(cv / 256) * 256
               yC = lgTop - (rw + 0.5) * lgH
               DRAWRECTANGLE(lgL + 6, yC + 7, lgL + 30, yC - 7) ANCHOR(RIGHT, XSHIFT, YSHIFT) COLOURED(cR, cG, cB, aFill) FILLCOLOR(cR, cG, cB, aFill)
               pcL = round(loP + i * (topP - loP) / (nLev - 1))
               DRAWTEXT("#pcL#%", lgL + 50, yC, monospaced, bold, 10) ANCHOR(RIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
            NEXT
         ENDIF
         IF nZS > 0 THEN
            cv = $gry[5]
            cR = floor(cv / 65536)
            cG = floor(cv / 256) - cR * 256
            cB = cv - floor(cv / 256) * 256
            yC = lgTop - (nRow + 0.5) * lgH
            DRAWRECTANGLE(lgL + 6, yC + 7, lgL + 30, yC - 7) ANCHOR(RIGHT, XSHIFT, YSHIFT) COLOURED(cR, cG, cB, aFill) FILLCOLOR(cR, cG, cB, aFill)
            DRAWTEXT("swept", lgL + 50, yC, monospaced, bold, 10) ANCHOR(RIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ENDIF
   ENDIF

   // ===================== PANEL (top right) =====================
   // Live settings, past crossing rates by band and side, the tallest live zone, pool counts and
   // the last bar used. Texts are centred in their column
   IF showPanel = 1 THEN
      IF txtSize = 1 THEN
         fz = 8
         fzs = 8
      ELSIF txtSize = 3 THEN
         fz = 12
         fzs = 10
      ELSE
         fz = 10
         fzs = 10
      ENDIF
      cw = 0.62 * fz
      rhM = fz + 8
      rhS = fzs + 6
      w0 = 32 * cw
      w1 = 16 * cw
      w2 = 16 * cw
      pnR = 0 - 90
      pnL = pnR - w0 - w1 - w2
      c0 = pnL + w0 / 2
      c1 = pnL + w0 + w1 / 2
      c2 = pnR - w2 / 2
      c01 = pnL + (w0 + w1) / 2
      c12 = pnL + w0 + (w1 + w2) / 2
      cMid = (pnL + pnR) / 2
      nSet = 5 + look3D
      pTop = 0 - 8
      pH = 8 * rhM + (nSet + 1) * rhS
      DRAWRECTANGLE(pnL, pTop, pnR, pTop - pH) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(125, 125, 125, 255) FILLCOLOR(bgR, bgG, bgB, bgA)
      DRAWRECTANGLE(pnL, pTop, pnR, pTop - rhM) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(hbR, hbG, hbB, 255) FILLCOLOR(hbR, hbG, hbB, 255)

      // Header
      yy = pTop - rhM / 2
      DRAWTEXT("LIQUIDITY CONTOUR MAP", c01, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(htR, htG, htB, 255)
      IF atrOK = 0 OR winOK = 0 THEN
         wu1 = bC + 1
         wu2 = max(100, mW)
         DRAWTEXT("WARMING UP #wu1#/#wu2#", c2, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(htR, htG, htB, 255)
      ELSIF nZ = 1 THEN
         DRAWTEXT("1 ZONE", c2, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(htR, htG, htB, 255)
      ELSE
         DRAWTEXT("#nZ# ZONES", c2, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(htR, htG, htB, 255)
      ENDIF

      // Settings
      yy = pTop - rhM - rhS / 2
      loPi = round(loP)
      toPi = round(topP)
      DRAWTEXT("swing #pLen# · map #mW#b +#eR# · contours #nSel# of #nLev# (#loPi#–#toPi#%)", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      yy = yy - rhS
      hPr = round(hP * 10) / 10
      gKr = round(gK * 100) / 100
      rKr = round(rndK * 10) / 10
      IF look3D = 0 THEN
         DRAWTEXT("height #hPr#% · gap #gKr# · rise #rise#b · nose #rKr# · flat", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      ELSE
         DRAWTEXT("height #hPr#% · gap #gKr# · rise #rise#b · nose #rKr# · 3D", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         yy = yy - rhS
         eWr = round(eW * 100) / 100
         shKr = round(shK * 100) / 100
         IF edgeOn = 1 AND shadeOn = 1 AND groundOn = 1 THEN
            DRAWTEXT("edges #eWr# · shade #shKr# · ground on", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSIF edgeOn = 1 AND shadeOn = 1 THEN
            DRAWTEXT("edges #eWr# · shade #shKr# · ground off", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSIF edgeOn = 1 AND groundOn = 1 THEN
            DRAWTEXT("edges #eWr# · shade off · ground on", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSIF edgeOn = 1 THEN
            DRAWTEXT("edges #eWr# · shade off · ground off", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSIF shadeOn = 1 AND groundOn = 1 THEN
            DRAWTEXT("edges off · shade #shKr# · ground on", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSIF shadeOn = 1 THEN
            DRAWTEXT("edges off · shade #shKr# · ground off", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSIF groundOn = 1 THEN
            DRAWTEXT("edges off · shade off · ground on", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("edges off · shade off · ground off", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ENDIF
      yy = yy - rhS
      eqr = round(eqT * 100) / 100
      IF useVol = 0 THEN
         DRAWTEXT("eq #eqr# ATR · pools #mxP# · vol off", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      ELSIF bC < 49 OR volMA[oB] <= 0 THEN
         DRAWTEXT("eq #eqr# ATR · pools #mxP# · vol on · no data", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      ELSE
         DRAWTEXT("eq #eqr# ATR · pools #mxP# · vol on", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      ENDIF
      yy = yy - rhS
      DRAWTEXT("horizon #hor#b · window #wW#b · retire #retN#b · min n #mnN#", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      yy = yy - rhS
      rBr = round(rBand * 10) / 10
      eror = round(ero * 100) / 100
      IF sweptGrey = 1 THEN
         DRAWTEXT("band #rBr# ATR · swept ×#eror# / #capB#b grey", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      ELSE
         DRAWTEXT("band #rBr# ATR · swept ×#eror# / #capB#b", cMid, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      ENDIF

      // Crossing rates by distance band and side
      yy = yy - rhS / 2 - rhM / 2
      IF hzOK = 1 THEN
         DRAWTEXT("Crossed ≤#hor# bars", c0, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         DRAWTEXT("HIGHS", c1, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         DRAWTEXT("LOWS", c2, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      ELSE
         DRAWTEXT("STATS OFF · horizon > retire - swing", c01, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(acR, acG, acB, 255)
      ENDIF
      FOR rr = 0 TO 3 DO
         yy = yy - rhM
         IF rr = 0 THEN
            b0 = 0
            b1 = 1
            DRAWTEXT("< 1 ATR", c0, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSIF rr = 1 THEN
            b0 = 2
            b1 = 3
            DRAWTEXT("1–3 ATR", c0, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSIF rr = 2 THEN
            b0 = 4
            b1 = 4
            DRAWTEXT("3–5 ATR", c0, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            b0 = 5
            b1 = 5
            DRAWTEXT("5–8 ATR", c0, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
         FOR sdI = 0 TO 1 DO
            nS = 0
            hS = 0
            FOR bq = b0 TO b1 DO
               cq = bq + 6 * sdI
               nS = nS + $rN[cq]
               hS = hS + $rH[cq]
            NEXT
            IF sdI = 0 THEN
               xC = c1
            ELSE
               xC = c2
            ENDIF
            IF nS = 0 THEN
               DRAWTEXT("-", xC, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(txR, txG, txB, 255)
            ELSIF nS < mnN THEN
               DRAWTEXT("- · n #nS#", xC, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(txR, txG, txB, 255)
            ELSE
               pcS = floor(100 * hS / nS + 0.5)
               IF pcS = 0 AND hS > 0 THEN
                  DRAWTEXT("<1% · n #nS#", xC, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(txR, txG, txB, 255)
               ELSIF pcS = 100 AND hS < nS THEN
                  DRAWTEXT(">99% · n #nS#", xC, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(txR, txG, txB, 255)
               ELSE
                  DRAWTEXT("#pcS#% · n #nS#", xC, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(txR, txG, txB, 255)
               ENDIF
            ENDIF
         NEXT
      NEXT

      // Tallest live zone (zones are ordered by height)
      yy = yy - rhM
      DRAWTEXT("Tallest live zone", c0, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      tl = -1
      IF nZ > 0 THEN
         FOR z = nZ - 1 DOWNTO 0 DO
            pl = $zPl[z]
            IF $pTk[pl] < 0 THEN
               tl = z
            ENDIF
         NEXT
      ENDIF
      IF tl < 0 THEN
         DRAWTEXT("-", c12, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(txR, txG, txB, 255)
      ELSE
         pl = $zPl[tl]
         tpx = $pPx[pl]
         tsc = round($zAm[tl] * 100) / 100
         dz = 0
         IF atrC > 0 THEN
            dz = (tpx - clC) / atrC
         ENDIF
         dzr = round(abs(dz) * 10) / 10
         IF dz > 0 THEN
            DRAWTEXT("#tpx# · score #tsc# · ▲ #dzr# ATR", c12, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(txR, txG, txB, 255)
         ELSIF dz < 0 THEN
            DRAWTEXT("#tpx# · score #tsc# · ▼ #dzr# ATR", c12, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(txR, txG, txB, 255)
         ELSE
            DRAWTEXT("#tpx# · score #tsc# · = 0.0 ATR", c12, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(txR, txG, txB, 255)
         ENDIF
      ENDIF

      // Pool counts and shapes drawn against the budget
      yy = yy - rhM
      nLive = nV - nSwept
      DRAWTEXT("Pools live · swept · polylines", c0, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      IF dropped > 0 THEN
         DRAWTEXT("#nLive# · #nSwept# · #nPoly#/#budget# · #dropped# short", c12, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(acR, acG, acB, 255)
      ELSE
         DRAWTEXT("#nLive# · #nSwept# · #nPoly#/#budget#", c12, yy, monospaced, bold, fz) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(txR, txG, txB, 255)
      ENDIF

      // Last bar used (close time, the instrument's time zone)
      yy = yy - rhM / 2 - rhS / 2
      dtC = date[oB]
      tmC = time[oB]
      yr2 = floor(dtC / 10000) - 2000
      mo = floor(dtC / 100) - floor(dtC / 10000) * 100
      dy = dtC - floor(dtC / 100) * 100
      hh = floor(tmC / 10000)
      mi = floor(tmC / 100) - hh * 100
      IF mo = 1 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Jan 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Jan 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 2 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Feb 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Feb 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 3 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Mar 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Mar 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 4 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Apr 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Apr 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 5 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# May 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# May 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 6 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Jun 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Jun 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 7 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Jul 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Jul 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 8 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Aug 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Aug 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 9 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Sep 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Sep 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 10 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Oct 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Oct 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 11 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Nov 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Nov 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ELSIF mo = 12 THEN
         IF mi < 10 THEN
            DRAWTEXT("Last bar #dy# Dec 20#yr2# · #hh#h0#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ELSE
            DRAWTEXT("Last bar #dy# Dec 20#yr2# · #hh#h#mi#", c01, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
         ENDIF
      ENDIF
      nBar = bC + 1
      IF nBar = 1 THEN
         DRAWTEXT("1 bar", c2, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      ELSE
         DRAWTEXT("#nBar# bars", c2, yy, monospaced, bold, fzs) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(dmR, dmG, dmB, 255)
      ENDIF
   ENDIF
ENDIF

RETURN

Download
Filename: PRC_Liquidity-contour-map.itf
Downloads: 0
Iván González
Iván González Legend
Currently debugging life, so my bio is on hold. Check back after the next commit for an update.
Author’s Profile

Comments

ProRealCode ProRealCode
Loading...