Why Is VWAP Important? A Pine Script v6 Deep Dive for Intraday Traders
VWAP (Volume-Weighted Average Price) is one of the most widely referenced benchmarks in intraday trading, used by institutional desks and algorithmic systems alike to assess whether a trade was executed at a favorable price relative to the day's volume-weighted mean. This article explains the mathematical foundation of VWAP, why it matters for intraday analysis, and how to implement a precise, session-aware VWAP indicator in Pine Script v6.
1. What Is VWAP? The Mathematical Definition
VWAP is defined as the cumulative sum of price × volume divided by the cumulative sum of volume, reset at the start of each trading session:
$$\text{VWAP}_t = \frac{\sum_{i=1}^{t} P_i \times V_i}{\sum_{i=1}^{t} V_i}$$Where:
- $P_i$ = Typical price at bar $i$, defined as $\frac{\text{High}_i + \text{Low}_i + \text{Close}_i}{3}$
- $V_i$ = Volume at bar $i$
- The summation resets at the start of each new trading session (e.g., each new day on a 1-minute chart)
This formula ensures that bars with higher volume exert a proportionally greater influence on the average price, making VWAP a volume-weighted measure rather than a simple time-weighted average.
2. Why Intraday Traders Rely on VWAP
VWAP serves several distinct analytical roles in intraday contexts:
| Role | Description |
|---|---|
| Execution Benchmark | Institutional orders are evaluated against VWAP. Buying below VWAP is considered favorable; selling above VWAP is considered favorable. |
| Dynamic Support/Resistance | Price frequently reacts at the VWAP line during the session, acting as a mean-reversion anchor. |
| Trend Confirmation | Sustained price above VWAP suggests bullish intraday sentiment; sustained price below suggests bearish sentiment. |
| Fair Value Reference | VWAP represents the average price at which all volume transacted during the session, making it a proxy for the market's consensus fair value. |
3. VWAP vs. Simple Moving Average: A Key Distinction
A common misconception is that VWAP behaves like a simple moving average (SMA). The table below clarifies the structural differences:
| Property | VWAP | SMA (e.g., 20-period) |
|---|---|---|
| Weighting | Volume-weighted | Equal-weighted |
| Session Reset | Yes — resets each session | No — rolls continuously |
| Lookback Window | Cumulative from session open | Fixed N bars |
| Volume Sensitivity | High — volume spikes shift VWAP | None |
| Primary Use Case | Intraday execution benchmark | Trend smoothing across any timeframe |
4. Logic Flow: How VWAP Is Computed Bar by Bar
The following diagram illustrates the per-bar computation logic, including the session-reset mechanism:
5. Pine Script v6 Implementation
5.1 Variable Persistence and the var Keyword
VWAP requires cumulative accumulators that grow throughout the session and reset only when a new session begins. In Pine Script v6, the var keyword controls when the initializer expression is evaluated: without var, the initializer (e.g., float x = close) is re-evaluated on every bar; with var, the initializer (e.g., var float x = close) is evaluated only once on bar 0, and the variable persists its value across all subsequent bars until explicitly reassigned with :=.
For VWAP, we declare accumulators with var so they are not re-initialized on every bar, and we manually reset them with := when a new session is detected:
var float cumPV = 0.0— cumulative price × volume, initialized to 0.0 on bar 0 onlyvar float cumVol = 0.0— cumulative volume, initialized to 0.0 on bar 0 only
When a new session starts (timeframe.change("D") returns true), both accumulators are reset to 0.0 via :=, then the current bar's contribution is added. This is the correct pattern for session-scoped cumulative calculations.
5.2 Full Indicator Code
🔽 [Click to expand] View Full Pine Script Code — Session VWAP with Standard Deviation Bands
//@version=6
indicator(
title = "Session VWAP with StdDev Bands",
shorttitle = "VWAP+Bands",
overlay = true,
max_bars_back = 500
)
// ─── INPUTS ───────────────────────────────────────────────────────────────────
int bandMult1 = input.int(1, title = "Band Multiplier 1", minval = 1, maxval = 5)
int bandMult2 = input.int(2, title = "Band Multiplier 2", minval = 1, maxval = 5)
bool showBands = input.bool(true, title = "Show Standard Deviation Bands")
// ─── SESSION DETECTION ────────────────────────────────────────────────────────
// timeframe.change("D") returns true on the first bar of each new daily session.
// This is the standard Pine v6 method for detecting session boundaries.
bool isNewSession = timeframe.change("D")
// ─── TYPICAL PRICE ────────────────────────────────────────────────────────────
// Typical price is the standard VWAP price input: (H + L + C) / 3
float typicalPrice = (high + low + close) / 3.0
// ─── CUMULATIVE ACCUMULATORS (var — initialized once on bar 0) ────────────────
// var ensures the initializer (0.0) runs only on bar 0.
// On all subsequent bars, the value persists until explicitly reassigned with :=
var float cumPV = 0.0 // cumulative sum of (typicalPrice × volume)
var float cumVol = 0.0 // cumulative sum of volume
var float cumPV2 = 0.0 // cumulative sum of (typicalPrice² × volume) for variance
// ─── SESSION RESET + ACCUMULATION ────────────────────────────────────────────
// On a new session: reset all accumulators to the current bar's contribution.
// On all other bars: add the current bar's contribution to the running totals.
if isNewSession
cumPV := typicalPrice * volume
cumPV2 := typicalPrice * typicalPrice * volume
cumVol := volume
else
cumPV := cumPV + typicalPrice * volume
cumPV2 := cumPV2 + typicalPrice * typicalPrice * volume
cumVol := cumVol + volume
// ─── VWAP CALCULATION ─────────────────────────────────────────────────────────
// Guard against zero volume (e.g., pre-market bars with no trades)
float vwap = cumVol != 0.0 ? cumPV / cumVol : na
// ─── VARIANCE AND STANDARD DEVIATION ─────────────────────────────────────────
// Variance formula: E[P²] - (E[P])²
// Where E[P²] = cumPV2 / cumVol and E[P] = vwap
// This is the volume-weighted variance of price around VWAP.
float variance = na(vwap) ? na : (cumPV2 / cumVol) - (vwap * vwap)
// Guard: variance can be slightly negative due to floating-point precision.
// Use math.max to clamp to 0.0 before taking the square root.
float stdDev = na(variance) ? na : math.sqrt(math.max(0.0, variance))
// ─── BAND LEVELS ──────────────────────────────────────────────────────────────
float upperBand1 = na(stdDev) ? na : vwap + bandMult1 * stdDev
float lowerBand1 = na(stdDev) ? na : vwap - bandMult1 * stdDev
float upperBand2 = na(stdDev) ? na : vwap + bandMult2 * stdDev
float lowerBand2 = na(stdDev) ? na : vwap - bandMult2 * stdDev
// ─── PLOTS ────────────────────────────────────────────────────────────────────
// VWAP line — primary output, always visible
plot(
vwap,
title = "VWAP",
color = color.new(color.blue, 0),
linewidth = 2,
style = plot.style_line
)
// Standard deviation bands — conditionally shown based on user input
p_upper1 = plot(
showBands ? upperBand1 : na,
title = "Upper Band 1",
color = color.new(color.orange, 30),
linewidth = 1,
style = plot.style_line
)
p_lower1 = plot(
showBands ? lowerBand1 : na,
title = "Lower Band 1",
color = color.new(color.orange, 30),
linewidth = 1,
style = plot.style_line
)
p_upper2 = plot(
showBands ? upperBand2 : na,
title = "Upper Band 2",
color = color.new(color.red, 50),
linewidth = 1,
style = plot.style_line
)
p_lower2 = plot(
showBands ? lowerBand2 : na,
title = "Lower Band 2",
color = color.new(color.red, 50),
linewidth = 1,
style = plot.style_line
)
// Fill between band pairs for visual clarity
fill(p_upper1, p_lower1, color = color.new(color.orange, 90))
fill(p_upper2, p_lower2, color = color.new(color.red, 95))
6. Dissecting the Standard Deviation Band Formula
The standard deviation bands are derived from the volume-weighted variance of price around VWAP. The formula used is the computational form of variance:
$$\sigma^2 = \frac{\sum_{i=1}^{t} P_i^2 \times V_i}{\sum_{i=1}^{t} V_i} - \left(\text{VWAP}_t\right)^2$$This is equivalent to $E[P^2] - (E[P])^2$, the standard identity for variance. It requires only one additional accumulator (cumPV2) beyond what VWAP itself needs, making it computationally efficient. The standard deviation is then:
The math.max(0.0, variance) guard is necessary because floating-point arithmetic can produce values like $-1 \times 10^{-14}$ when all prices in the session are identical, which would cause math.sqrt() to return na.
7. Important Usage Constraints
7.1 Timeframe Dependency
VWAP is an intraday indicator. It is mathematically meaningful only on intraday timeframes (e.g., 1m, 5m, 15m, 1H). On daily or weekly charts, the session-reset mechanism fires on every bar, making VWAP collapse to the typical price of each individual bar — which is not useful. The indicator should be applied exclusively to intraday charts.
7.2 Volume Data Availability
VWAP requires reliable volume data. On some instruments (e.g., certain forex pairs or synthetic indices), volume data may represent tick count rather than actual traded volume. In such cases, VWAP values are still mathematically valid but represent a tick-weighted average price rather than a true volume-weighted average.
7.3 The timeframe.change("D") Session Boundary
The expression timeframe.change("D") detects when the chart's current bar belongs to a new calendar day. This is the standard Pine Script v6 method for daily session resets. For instruments with non-standard sessions (e.g., futures with overnight sessions), a custom session string via input.session() may be more appropriate.
8. Interpreting VWAP in Practice
| Price Location | Interpretation | Band Context |
|---|---|---|
| Price > VWAP | Buyers are in control; average participant is in profit | Between VWAP and Upper Band 1: mild bullish |
| Price < VWAP | Sellers are in control; average participant is at a loss | Between VWAP and Lower Band 1: mild bearish |
| Price at Upper Band 2 | Price is 2σ above VWAP — statistically extended | Potential mean-reversion zone |
| Price at Lower Band 2 | Price is 2σ below VWAP — statistically extended | Potential mean-reversion zone |
9. Conclusion
- VWAP is a volume-weighted cumulative average that resets each session, making it fundamentally different from rolling moving averages. Its value at any point in the session reflects the true average transaction price weighted by volume, not time.
- The Pine Script v6 implementation requires
varaccumulators that persist across bars and are manually reset on session boundaries viatimeframe.change("D"). Thevarkeyword ensures the initializer expression runs only on bar 0, preventing re-initialization each bar — unlike a plain declaration such asfloat x = close, whose initializer is re-evaluated on every bar. - Standard deviation bands are derived from the volume-weighted variance identity $E[P^2] - (E[P])^2$, requiring only one additional accumulator and providing statistically grounded distance levels around VWAP.
Ideas for Further Development
- Anchored VWAP: Allow users to anchor the VWAP start to a specific bar (e.g., an earnings date or a swing low) using a
input.time()parameter and a manual reset condition, rather than resetting on every daily session boundary. - Multi-Session VWAP: Extend the script to display VWAPs for the current week and current month simultaneously, using
timeframe.change("W")andtimeframe.change("M")as additional reset conditions with separate accumulator sets.
Related Posts
- 📄 How to Use Bollinger Bands in Pine Script v6: Volatility Measurement and Breakout Detection
- 📄 Why Do Traders Use EMA? Exponential Moving Average Explained with Pine Script v6
- 📄 What Is an SMA Moving Average? Pine Script v6 Guide to Simple Moving Averages
- 📄 How Does the MACD Indicator Work? Pine Script v6 Deep Dive into Crossovers and Momentum Signals
- 📄 How to Use the RSI Indicator in Pine Script v6: Overbought, Oversold, and Signal Logic
Comments
Post a Comment