An anchored VWAP answers a precise question: what is the average price, weighted by volume, that the market has paid since a given point? Anchor it at a major low and it shows the average entry of everyone who bought from there; anchor it at a high and it shows the average entry of the sellers. The difficulty is choosing the anchor, and moving it by hand every time the market turns.
The Dynamic Swing Anchored VWAP by Zeiierman does that job automatically. It detects swing highs and lows, and every time the swing structure changes direction it anchors a new VWAP at the opposite swing point and rebuilds it from there up to the current bar. Each swing is labelled HH, LH, HL or LL, so the chart shows both the market structure and the volume-weighted path that followed each turning point.
Swing points. A bar is a swing high when its high is the highest of the last Swing Period bars (50 by default), and a swing low when its low is the lowest of that window. The indicator keeps the price and the bar of the latest swing high and the latest swing low.
Direction. When the latest swing high is more recent than the latest swing low the direction is up; otherwise it is down. A change of direction is the event that re-anchors the VWAP: on a turn up the new VWAP is anchored at the latest swing low, and on a turn down at the latest swing high.
Structure labels. The anchor point gets a label that compares it with the previous swing of the same kind: HL (higher low) or LL (lower low) below the lows, HH (higher high) or LH (lower high) above the highs.
An adaptive VWAP. Instead of the classic cumulative VWAP, the line is an exponentially weighted average of price times volume divided by an exponentially weighted average of volume, both seeded at the anchor with the swing price. Its speed is set by the Adaptive Price Tracking value, a half-life in bars: with 20, a bar weighs half as much as a bar 20 bars more recent. A lower value keeps the VWAP tight to price, a higher value makes it smoother.
Volatility adaptation (optional). With the adaptation switched on, the half-life is divided by the ratio between the 50-bar ATR and its own 50-bar average, raised to the Volatility Bias. In volatile phases the VWAP reacts faster; in quiet ones it becomes smoother. The value is kept between 5 and 300 bars.
Segments. At each change of direction the running segment is frozen and a new one starts at the new anchor. Since the anchor lies in the past, consecutive segments overlap for a few bars, which is where the old VWAP and the new one can be compared.
The VWAP needs volume: on instruments without volume data the line is not drawn, while the swing labels still are.
//---------------------------------------------------------------
// PRC_Dynamic Swing VWAP
// version = 0
// 26.10.2026
// Iván González @ www.prorealcode.com
// Author: Zeiierman
// Sharing ProRealTime knowledge
//--------------------------------------------------------------------//
DEFPARAM DRAWONLASTBARONLY = true
// ===================== INPUTS =====================
// Swing points
prd = 50 // Swing Period (min 2): bars used to detect swing highs and lows
baseAPT = 20 // Adaptive Price Tracking (min 1): VWAP half-life in bars
useAdapt = 0 // 1 = adapt the tracking speed with the ATR ratio
volBias = 10 // Volatility Bias (min 0.1): strength of the ATR adaptation
// Style
lineW = 2 // VWAP line width (1 to 4)
maxLines = 100 // VWAP segments kept on the chart (the live one included)
maxLabels = 500 // Swing labels kept on the chart
// Colours: swing labels (low, high) and VWAP lines (S, R)
lblLoR = 0
lblLoG = 230
lblLoB = 118
lblHiR = 242
lblHiG = 54
lblHiB = 69
sR = 0
sG = 230
sB = 118
rR = 242
rG = 54
rB = 69
// ===================== SWING POINTS =====================
pPrd = max(2, round(prd))
hlc3V = (high + low + close) / 3
IF barindex = 0 THEN
phL = 0
plL = 0
hasPh = 0
hasPl = 0
hasPrev = 0
nEv = 0
// segment 0: the VWAP that runs from the first bar until the first swing change
$eB[0] = 0
$eX[0] = 0
$eY[0] = hlc3V
$eD[0] = -1
$eOk[0] = 1
$eT[0] = 0
ENDIF
// A bar is a swing high / low when it is the extreme of the last prd bars
IF barindex >= pPrd - 1 THEN
IF high >= highest[pPrd](high) THEN
ph = high
phL = barindex
hasPh = 1
ENDIF
IF low <= lowest[pPrd](low) THEN
pl = low
plL = barindex
hasPl = 1
ENDIF
ENDIF
IF phL > plL THEN
dir = 1
ELSE
dir = -1
ENDIF
// ===================== ADAPTIVE TRACKING =====================
atrV = averagetruerange[50](close)
IF barindex < 98 THEN
atrAvg = 0
ELSIF barindex = 98 THEN
atrAvg = average[50](atrV)
ELSE
atrAvg = (atrV + 49 * atrAvg[1]) / 50
ENDIF
IF atrAvg > 0 THEN
ratio = atrV / atrAvg
ELSE
ratio = 1
ENDIF
IF useAdapt = 1 THEN
aptRaw = baseAPT / pow(ratio, volBias)
ELSE
aptRaw = baseAPT
ENDIF
aptS = round(max(5, min(300, aptRaw)))
atr14 = averagetruerange[14](close)
// ===================== SWING CHANGE EVENTS =====================
// Every change of direction anchors a new VWAP at the opposite swing point
IF barindex > 0 AND dir <> dir[1] THEN
nEv = nEv + 1
txtCode = 0
IF dir > 0 THEN
xAnc = plL
yAnc = pl
okAnc = hasPl
IF hasPl = 1 AND hasPrev = 1 THEN
IF pl < prev THEN
txtCode = 1
ELSIF pl > prev THEN
txtCode = 2
ENDIF
ENDIF
prev = ph[1]
hasPrev = hasPh[1]
ELSE
xAnc = phL
yAnc = ph
okAnc = hasPh
IF hasPh = 1 AND hasPrev = 1 THEN
IF ph < prev THEN
txtCode = 3
ELSIF ph > prev THEN
txtCode = 4
ENDIF
ENDIF
prev = pl[1]
hasPrev = hasPl[1]
ENDIF
$eB[nEv] = barindex
$eX[nEv] = xAnc
$eY[nEv] = yAnc
$eD[nEv] = dir
$eOk[nEv] = okAnc
$eT[nEv] = txtCode
ENDIF
// ===================== DRAWING =====================
IF islastbarupdate THEN
lw = max(1, min(4, round(lineW)))
ln2 = log(2)
// ---- VWAP segments: the last maxLines ones ----
firstSeg = max(0, nEv - max(1, round(maxLines)) + 1)
FOR k = firstSeg TO nEv DO
sx = $eX[k]
IF k < nEv THEN
ex = $eB[k + 1] - 1
segDir = $eD[k + 1]
IF segDir < 0 THEN
cR = rR
cG = rG
cB = rB
ELSE
cR = sR
cG = sG
cB = sB
ENDIF
ELSE
ex = barindex
// live segment: on the change bar itself the colour rule is the opposite one
IF k > 0 AND $eB[k] = barindex THEN
IF dir < 0 THEN
cR = rR
cG = rG
cB = rB
ELSE
cR = sR
cG = sG
cB = sB
ENDIF
ELSE
IF dir > 0 THEN
cR = rR
cG = rG
cB = rB
ELSE
cR = sR
cG = sG
cB = sB
ENDIF
ENDIF
ENDIF
IF $eOk[k] = 1 THEN
off0 = barindex - sx
pAcc = $eY[k] * volume[off0]
vAcc = volume[off0]
havePt = 0
FOR t = sx TO ex DO
off = barindex - t
al = 1 - exp(0 - ln2 / max(1, aptS[off]))
pAcc = (1 - al) * pAcc + al * hlc3V[off] * volume[off]
vAcc = (1 - al) * vAcc + al * volume[off]
IF vAcc > 0 THEN
vwapV = pAcc / vAcc
IF havePt = 1 THEN
IF lw = 1 THEN
DRAWSEGMENT(t - 1, lastV, t, vwapV) COLOURED(cR, cG, cB, 255) STYLE(line, 1)
ELSIF lw = 2 THEN
DRAWSEGMENT(t - 1, lastV, t, vwapV) COLOURED(cR, cG, cB, 255) STYLE(line, 2)
ELSIF lw = 3 THEN
DRAWSEGMENT(t - 1, lastV, t, vwapV) COLOURED(cR, cG, cB, 255) STYLE(line, 3)
ELSE
DRAWSEGMENT(t - 1, lastV, t, vwapV) COLOURED(cR, cG, cB, 255) STYLE(line, 4)
ENDIF
ENDIF
lastV = vwapV
havePt = 1
ELSE
havePt = 0
ENDIF
NEXT
ENDIF
NEXT
// ---- Swing labels: the last maxLabels ones ----
firstLbl = max(1, nEv - max(1, round(maxLabels)) + 1)
FOR k = firstLbl TO nEv DO
IF $eOk[k] = 1 AND $eT[k] > 0 THEN
lx = $eX[k]
lOff = atr14[barindex - lx]
IF $eD[k] > 0 THEN
ly = $eY[k] - lOff * 0.6
IF $eT[k] = 1 THEN
DRAWTEXT("LL", lx, ly, sansserif, bold, 11) COLOURED(lblLoR, lblLoG, lblLoB, 204)
ELSE
DRAWTEXT("HL", lx, ly, sansserif, bold, 11) COLOURED(lblLoR, lblLoG, lblLoB, 204)
ENDIF
ELSE
ly = $eY[k] + lOff * 0.6
IF $eT[k] = 3 THEN
DRAWTEXT("LH", lx, ly, sansserif, bold, 11) COLOURED(lblHiR, lblHiG, lblHiB, 204)
ELSE
DRAWTEXT("HH", lx, ly, sansserif, bold, 11) COLOURED(lblHiR, lblHiG, lblHiB, 204)
ENDIF
ENDIF
ENDIF
NEXT
ENDIF
RETURN