How to Create VWAP on TradingView with Pine Script
Plot session-anchored VWAP, optional band channels, and stats traders use for intraday bias.
VWAP summarizes where volume transacted relative to price for the current session (or custom anchor). It is a cornerstone execution benchmark for intraday traders and desk workflows. This tutorial explains cumulative VWAP math, why resets matter, and how to implement VWAP plus practical band channels in Pine Script v5 using ta.vwap() and transparent inputs you can extend into strategies.
What is VWAP?
Volume Weighted Average Price (VWAP) is the cumulative sum of typical price times volume, divided by cumulative volume, from an anchor point (usually the session open).
Unlike a moving average, VWAP is not a fixed-length smoother—it “remembers” the entire session unless you reset the anchor. Heavier volume at a price level pulls VWAP toward that level.
On TradingView, ta.vwap(source) computes session VWAP when used on intraday charts with default session rules, using the chart’s timezone and session template.
Why Traders Use This Indicator
- Judge whether fills are better or worse than the volume-weighted consensus.
- Treat VWAP slope and position as intraday trend and mean-reversion context.
- Combine VWAP with deviation bands to spot stretched prices that may revert.
- Align short-term long bias above VWAP and short bias below VWAP in some systematic playbooks.
| Parameter | Default | Description |
|---|---|---|
| Source | hlc3 | Typical price is common for VWAP; some traders use close or ohlc4 for sensitivity tests. |
| Anchor | Session | Session VWAP resets each trading day (per chart session). Custom anchors need manual cum math. |
| Band width | MAD or stdev multiple | Bands are not standardized like Bollinger sigma—define a spread model (MAD, stdev proxy) and validate visually. |
| Timezone / session | Chart settings | VWAP resets follow the chart session; mismatched exchange hours produce different curves. |
How VWAP is calculated
For bar i within the anchor window, let P be price (commonly hlc3) and V volume:
PV = cum(P × V) and CV = cum(V) from the anchor.
VWAP = PV / CV
TradingView’s ta.vwap(source) handles cumulative logic and session resets consistently with platform data. Band variants often use rolling dispersion of price around VWAP, such as mean absolute deviation, as a lightweight alternative to full variance formulas.
Signal Interpretation
Price above VWAP: Frequently interpreted as buyers willing to pay a premium versus the volume-weighted average—some desks use it as a bullish intraday filter.
Price below VWAP: Mirrors the above for discounted trading versus VWAP.
Mean reversion: Tags of the upper or lower band may coincide with short-term exhaustion if liquidity is two-sided; in one-way trends price can ride a band.
End-of-day: VWAP becomes smoother as cumulative volume grows; interpret band width relative to recent ATR.
Combining with Other Indicators
Pair VWAP with volume histograms or cumulative delta proxies to confirm initiative flow. Add ATR for stop placement independent of VWAP noise. Use opening range or initial balance markers alongside VWAP for session structure strategies.
The Hard Way: Writing Pine Script Manually
Challenges of Manual Coding
Implement session VWAP manually with ta.cum() and compare to ta.vwap().
Build anchored VWAP from a user-selected start bar index.
Swap MAD bands for a rolling standard deviation of (source - VWAP) and compare whipsaw rates.
Add a label that prints distance from close to VWAP in ticks for futures contracts.
//@version=5
indicator("VWAP Tutorial (ta.vwap + bands)", shorttitle="VWAP+", overlay=true, max_lines_count=50, max_labels_count=50)
// -----------------------------------------------------------------------------
// Inputs
// -----------------------------------------------------------------------------
src = input.source(hlc3, "VWAP source", tooltip="Typical price is a common default")
showBands = input.bool(true, "Show bands")
bandLen = input.int(30, "Dispersion lookback", minval=5, tooltip="Rolling window for spread estimate")
bandMult = input.float(1.5, "Band multiplier", step=0.1)
showPrev = input.bool(true, "Plot prior close horizontal (style aid)")
showSlope = input.bool(true, "Color VWAP by slope")
// -----------------------------------------------------------------------------
// Core VWAP (session-anchored per chart session rules)
// -----------------------------------------------------------------------------
vwapLine = ta.vwap(src)
// -----------------------------------------------------------------------------
// Band proxy: mean absolute deviation from VWAP, smoothed
// -----------------------------------------------------------------------------
dev = ta.sma(math.abs(src - vwapLine), bandLen)
upper = vwapLine + bandMult * dev
lower = vwapLine - bandMult * dev
// -----------------------------------------------------------------------------
// Styling
// -----------------------------------------------------------------------------
slopeUp = vwapLine > vwapLine[1]
vwapColor = showSlope ? (slopeUp ? color.new(color.teal, 0) : color.new(color.orange, 0)) : color.new(color.purple, 0)
plot(vwapLine, title="VWAP", color=vwapColor, linewidth=2)
plot(showBands ? upper : na, title="Upper band", color=color.new(color.red, 35), linewidth=1)
plot(showBands ? lower : na, title="Lower band", color=color.new(color.green, 35), linewidth=1)
// Mid-session reference: previous close
plot(showPrev ? close[1] : na, title="Prev close (ghost)", color=color.new(color.gray, 60), linewidth=1, style=plot.style_circles)
// -----------------------------------------------------------------------------
// Fill between bands
// -----------------------------------------------------------------------------
fillColor = color.new(color.blue, 92)
pU = plot(showBands ? upper : na, display=display.none)
pL = plot(showBands ? lower : na, display=display.none)
fill(pU, pL, color=showBands ? fillColor : na)
// -----------------------------------------------------------------------------
// Distance metrics
// -----------------------------------------------------------------------------
distTicks = (close - vwapLine) / syminfo.mintick
stretch = dev > 0 ? math.abs(close - vwapLine) / dev : na
// -----------------------------------------------------------------------------
// Alerts
// -----------------------------------------------------------------------------
touchUpper = showBands and ta.cross(close, upper)
touchLower = showBands and ta.cross(close, lower)
crossVwap = ta.cross(close, vwapLine)
alertcondition(crossVwap, title="Price crossed VWAP", message="Close crossed session VWAP")
alertcondition(touchUpper, title="Tagged upper VWAP band", message="Price touched upper deviation band")
alertcondition(touchLower, title="Tagged lower VWAP band", message="Price touched lower deviation band")
// -----------------------------------------------------------------------------
// Status table
// -----------------------------------------------------------------------------
var table tb = table.new(position.bottom_right, 2, 5, border_width=1)
if barstate.islast
table.cell(tb, 0, 0, "VWAP", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 0, str.tostring(vwapLine, format.mintick), text_color=color.white)
table.cell(tb, 0, 1, "Close - VWAP", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 1, str.tostring(close - vwapLine, format.mintick), text_color=color.white)
table.cell(tb, 0, 2, "Dist (ticks)", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 2, str.tostring(distTicks, "#.##"), text_color=color.white)
table.cell(tb, 0, 3, "|stretch| (vs MAD)", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 3, str.tostring(stretch, "#.##"), text_color=color.white)
table.cell(tb, 0, 4, "Band width", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 4, str.tostring(upper - lower, format.mintick), text_color=color.white)
// -----------------------------------------------------------------------------
// Notes for learners
// -----------------------------------------------------------------------------
// 1) Session definition follows chart settings (exchange timezone).
// 2) Bands here are educational proxies—not identical to broker-specific sigma VWAP models.
// 3) For strategies, gate entries with barstate.isconfirmed to avoid intrabar repaints on some feeds.Maintenance Note: If you need exact broker VWAP band math, validate against your execution platform—sigma definitions differ. For strategies, confirm whether `ta.vwap` behavior matches your intended session on higher timeframes (e.g., daily bars).
The Easy Way: Build with Pineify Visual Editor
What if you could create the same indicator without writing a single line of code? Pineify is a visual editor designed for TradingView users who want professional-grade indicators through an intuitive interface.
| Feature | Manual Pine Script | Pineify Visual Editor |
|---|---|---|
| Session reset logic | Hand-roll cum sums and session detection; easy to desync timezones. | Reuse session-aware blocks aligned with chart templates. |
| Bands | Implement dispersion math and tune on every symbol. | Clone band presets and adjust multiples from a UI. |
| Multi-anchor VWAP | Track state variables for each anchor manually. | Template alternate anchors without rewriting core cum logic. |
| Readability | Long scripts obscure VWAP vs band sections. | Modular generation keeps plotting and alerts grouped. |
| Iteration speed | Slow to test bandLen and bandMult across tickers. | Faster sweeps before exporting final Pine. |
Quick UI iteration on sources, band length, and multipliers.
Less risk of timezone bugs when reusing standard session templates.
Cleaner exports for teams mixing indicators and strategies.
Step-by-Step Tutorial
Create overlay indicator
Start a chart-overlaid study so VWAP prints on price.
Insert ta.vwap
Bind `ta.vwap(hlc3)` (or your chosen source) to an input.
Add band channel
Compute a dispersion series around VWAP and scale by a multiplier.
Style by slope or regime
Color VWAP or background when slope aligns with your playbook.
Wire alerts
Alert on VWAP crosses or band tags with debounce if needed.
Validate session
Compare against TradingView built-in VWAP on the same symbol.
Export Pine v5
Save the script and document inputs for collaborators.
Trading Strategies & Pro Tips
VWAP mean reversion
Fade moves to the outer band when higher timeframe bias is neutral and liquidity is thick.
Pro Tip: Skip fading when trend tools show a one-way institutional drive day.
VWAP trend hold
Stay long while price holds above VWAP and VWAP slopes up; exit on sustained breaks below.
Band breakout continuation
Enter in trend direction when price closes outside a band with expanding volume.
Pair with opening range
Use OR high/low as structure filters; only take VWAP pullbacks that respect those levels.
Common Mistakes to Avoid
- Applying intraday VWAP rules to daily charts without understanding reset behavior.
- Treating band touches as guaranteed reversals during momentum auctions.
- Ignoring timezone and session settings when comparing VWAP across brokers.
- Using tiny dispersion lookbacks that make bands hyperactive and noisy.
- Confusing anchored VWAP with session VWAP when reading third-party scripts.
Frequently Asked Questions
Ship VWAP indicators faster with Pineify
- Prototype VWAP, bands, and alerts without rewriting cumulative math.
- Keep session and source inputs consistent across your team’s scripts.
- Export production-ready Pine Script v5 in fewer edit cycles.
Disclaimer: Trading involves significant risk. The indicators, code, and strategies provided are for educational purposes only and do not constitute financial advice. Past performance is not indicative of future results.