VWAP is the volume-weighted average price, the level large players benchmark fills against. A plain cumulative VWAP drifts: it averages from wherever the chart happened to start loading, so the line you see depends on how much history your browser fetched. This recipe pins VWAP to real calendar sessions instead. The weekly line resets every Monday at 00:00 UTC and the daily line resets every midnight, so two traders looking at the same symbol see the same level. It adds percentage bands and a "stretch" number that tells you how far price has pulled from fair value.
//@version=2
// ============================================================================
// WEEKLY ANCHORED VWAP + DAILY SESSIONS (v3.2 only)
// v2's vwap was a single cumulative line from wherever the chart happened to
// start loading: it drifted as more history loaded. v3 anchors to real UTC
// sessions: the weekly VWAP resets every Monday 00:00 UTC and the daily one
// every midnight, regardless of how much data the browser fetched.
// ============================================================================
define(title="Anchored VWAP (week + day)", position="onchart", axis=false);
var weekCol = input(name="weekCol", type="color", defaultValue="#f5a623", label="Weekly VWAP");
var dayCol = input(name="dayCol", type="color", defaultValue="#4a90d9", label="Daily VWAP");
var bandPct = input(name="bandPct", type="slider", defaultValue=0.5, label="Band %", constraints={min: 0.1, max: 3, step: 0.1});
timeseries d = ohlcv(symbol=currentSymbol, exchange=currentExchange);
// Session-anchored: na before the first complete session, exact after.
timeseries wVwap = vwap(anchor="week", source=d);
timeseries dVwap = vwap(anchor="day", source=d);
timeseries wUpper = wVwap * (1 + bandPct / 100);
timeseries wLower = wVwap * (1 - bandPct / 100);
plotLine(wVwap, colors=[weekCol], width=2, label=["Weekly VWAP"], desc=["Volume weighted average price anchored to the UTC week"]);
plotLine(dVwap, colors=[dayCol], width=1, label=["Daily VWAP"], desc=["Volume weighted average price anchored to the UTC day"]);
plotLine(wUpper, colors=[opacity(weekCol, 30)], width=1, label=["Week Upper"], desc=["Weekly VWAP plus band percent"]);
plotLine(wLower, colors=[opacity(weekCol, 30)], width=1, label=["Week Lower"], desc=["Weekly VWAP minus band percent"]);
fillBetween(wUpper, wLower, weekCol, 8);
// Price stretched from the weekly anchor = mean-reversion context.
var stretch = isnum(wVwap[0]) && wVwap[0] > 0 ? (d.close[0] - wVwap[0]) / wVwap[0] * 100 : 0;
if (isLastBar) {
plotTable(
data=[["Anchored VWAP", ""], ["Weekly", isnum(wVwap[0]) ? "".concat(math.round(wVwap[0] * 100) / 100) : "warming"], ["Stretch %", "".concat(math.round(stretch * 100) / 100)]],
position="bottom_right", headerRow=true, backgroundColor="#0d1117", textColor="#e6edf3", fontSize=11
);
}How it works
The anchor is the whole trick. vwap(anchor="week", source=d) accumulates price-times-volume from the start of the current UTC week and resets the moment a new week begins. The daily line does the same on a midnight boundary. The reset is tied to the calendar, not to your scroll position or how much history loaded, which is exactly why the level is stable and shared.
Warm-up is honest. An anchored VWAP is na until its session actually starts on screen. The daily line fills in within the first day of loaded data, the weekly line needs roughly a week. Every place the script reads the VWAP it checks isnum(...) first, so nothing breaks during warm-up. The table even prints "warming" instead of a fake number while it waits.
The bands frame the move. wUpper and wLower are just the weekly VWAP scaled by a percentage. fillBetween shades the channel between them. When price rides the upper band it is stretched rich versus the week's fair value, when it sags to the lower band it is cheap. The bands are drawn faint with opacity() so the VWAP line itself stays the star.
The stretch readout. The most actionable number is how far price has pulled from the anchor, expressed as a percent: (close - wVwap) / wVwap * 100. A large positive stretch is an overextension signal for mean-reversion traders. It is printed once, on the last bar, in a small plotTable so you get a live readout without cluttering history.
Customize it
- Band width. The
bandPctslider sets how far the bands sit from VWAP. Tighten it toward0.2on calm majors, widen it toward2on volatile names so the bands actually contain the noise. - Anchor period. Swap
"week"for"month"to track a longer-horizon fair value, or"day"for an intraday anchor. Monthly needs about a month of loaded history before it shows anything, so load enough bars. See special indicators for the full anchor rules. - Band the daily line too. The bands here wrap the weekly VWAP. Duplicate the
wUpper/wLower/fillBetweenblock againstdVwapif you trade the intraday session instead. - Stretch as a signal. Right now stretch is just displayed. Add a
plotShapewhenstretchexceeds a threshold to mark genuine overextensions, or wire it into analert(). - Colors.
weekColanddayColare inputs. Theopacity()calls derive the faint band tints from them automatically, so changing the base color restyles the whole set.
Concepts used
- Special indicators for anchored
vwapand its session-reset behavior - Plotting for
plotLine,fillBetween,hline, andplotTable - Color functions for
opacityband tints - na and scalar types for the warm-up guards