Indicator TutorialPine Script 5

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.
ParameterDefaultDescription
Sourcehlc3Typical price is common for VWAP; some traders use close or ohlc4 for sensitivity tests.
AnchorSessionSession VWAP resets each trading day (per chart session). Custom anchors need manual cum math.
Band widthMAD or stdev multipleBands are not standardized like Bollinger sigma—define a spread model (MAD, stdev proxy) and validate visually.
Timezone / sessionChart settingsVWAP 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.

Pine Script 5
//@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.

FeatureManual Pine ScriptPineify Visual Editor
Session reset logicHand-roll cum sums and session detection; easy to desync timezones.Reuse session-aware blocks aligned with chart templates.
BandsImplement dispersion math and tune on every symbol.Clone band presets and adjust multiples from a UI.
Multi-anchor VWAPTrack state variables for each anchor manually.Template alternate anchors without rewriting core cum logic.
ReadabilityLong scripts obscure VWAP vs band sections.Modular generation keeps plotting and alerts grouped.
Iteration speedSlow 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

1

Create overlay indicator

Start a chart-overlaid study so VWAP prints on price.

2

Insert ta.vwap

Bind `ta.vwap(hlc3)` (or your chosen source) to an input.

3

Add band channel

Compute a dispersion series around VWAP and scale by a multiplier.

4

Style by slope or regime

Color VWAP or background when slope aligns with your playbook.

5

Wire alerts

Alert on VWAP crosses or band tags with debounce if needed.

6

Validate session

Compare against TradingView built-in VWAP on the same symbol.

7

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.