What it does
This indicator plots the session VWAP and three pairs of bands at 1, 2 and 3 volume-weighted standard deviations. The bands show how far price has stretched from the session's average traded price, measured in the session's own units of dispersion.
It also marks re-entries: the bar that closes back inside a chosen band after the previous bar closed outside it. That is a common way to frame a fading or mean-reversion idea, and a way to see when an extended move is losing pace.
How it works
- At the first bar of each daily session the calculation restarts.
- VWAP = cumulative (source × volume) ÷ cumulative volume since the session start.
- Standard deviation = square root of the volume-weighted variance of the source around VWAP since the session start.
- Band k = VWAP ± multiplier k × standard deviation, for the three multipliers.
- A "re-entry from above" prints when the previous close was above the selected upper band and the current close is at or below it. "Re-entry from below" is the mirror image.
- Signals are checked only on closed bars and only after the warm-up bars, because the bands start at zero width and widen as the session develops.
How to use it
In a quiet, two-sided session, moves to the 2 SD band often stall, and a close back inside can be read as a sign that the push is fading. In a strong trend day, price can ride the 1 to 2 SD band for hours. That is why the re-entry signal should be paired with context, such as whether VWAP itself is flat or sloping.
Ways traders use the bands:
- Flat VWAP plus re-entry from the 2 SD or 3 SD band: a possible fade toward VWAP.
- Sloping VWAP with price holding between VWAP and the 1 SD band: a trend; pullbacks to VWAP may be more relevant than fades.
- Band levels as reference points for take-profit targets on intraday trades. See what is a take-profit.
Settings
| Input | Default | What it changes |
|---|---|---|
| Source | hlc3 | Price averaged by volume. |
| Band 1 / 2 / 3 multiplier | 1.0 / 2.0 / 3.0 | Distance of each band. |
| Show band 3 | on | Hides the outer band if you prefer a cleaner chart. |
| Re-entry band | Band 2 | Which band the signals use. |
| Ignore first N bars of each session | 5 | Warm-up period with no signals. |
| Show re-entry markers | on | Triangles on signal bars. |
Limitations
- On spot forex, TradingView volume is typically tick volume from one feed, so VWAP and its bands are an approximation of activity.
- Fading a band in a trending session can lead to repeated losses. The tool does not detect trend days for you.
- Standard deviation bands assume nothing about future range; a news release can carry price far beyond 3 SD.
- The session boundary is the symbol's daily rollover, which may not match the session you trade.
- Requires an intraday chart and a symbol with volume data.
Pine Script source
In TradingView open Pine Editor, paste the code, click Save and then Add to chart.
FXP_VWAPDeviationBands.pine
80 lines
// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/vwap-deviation-bands/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
//@version=6
indicator("FXP VWAP Deviation Bands", shorttitle = "FXP VWAP SD", overlay = true)
// ── Inputs ─────────────────────────────────────────────────────────────
grpV = "Session VWAP"
src = input.source(hlc3, "Source", group = grpV)
grpB = "Deviation bands"
mult1 = input.float(1.0, "Band 1 multiplier", minval = 0.1, step = 0.1, group = grpB)
mult2 = input.float(2.0, "Band 2 multiplier", minval = 0.1, step = 0.1, group = grpB)
mult3 = input.float(3.0, "Band 3 multiplier", minval = 0.1, step = 0.1, group = grpB)
show3 = input.bool(true, "Show band 3", group = grpB)
grpSig = "Signals"
sigBand = input.string("Band 2", "Re-entry band", options = ["Band 1", "Band 2", "Band 3"], group = grpSig, tooltip = "A signal prints when a bar closes back inside this band after the previous bar closed outside it.")
warmup = input.int(5, "Ignore first N bars of each session", minval = 1, group = grpSig, tooltip = "Bands are very narrow right after the session opens; this avoids early noise.")
showSig = input.bool(true, "Show re-entry markers", group = grpSig)
grpVis = "Display"
vwapCol = input.color(color.orange, "VWAP color", group = grpVis)
upCol = input.color(color.red, "Upper bands color", group = grpVis)
dnCol = input.color(color.teal, "Lower bands color", group = grpVis)
// ── Calculation ────────────────────────────────────────────────────────
if timeframe.isdwm
runtime.error("Session VWAP bands need an intraday chart.")
bool newSession = timeframe.change("D")
// With a multiplier of 1, the returned band distance equals one volume-weighted standard deviation.
[sVwap, up1raw, dn1raw] = ta.vwap(src, newSession, 1.0)
float sd = up1raw - sVwap
float u1 = sVwap + mult1 * sd
float l1 = sVwap - mult1 * sd
float u2 = sVwap + mult2 * sd
float l2 = sVwap - mult2 * sd
float u3 = sVwap + mult3 * sd
float l3 = sVwap - mult3 * sd
float sigUp = sigBand == "Band 1" ? u1 : sigBand == "Band 2" ? u2 : u3
float sigDn = sigBand == "Band 1" ? l1 : sigBand == "Band 2" ? l2 : l3
// Bars elapsed since the session started (0 on the first bar).
var int sBars = 0
sBars := newSession ? 0 : sBars + 1
// Re-entry from outside the band, on closed bars, after the warm-up period.
bool ready = barstate.isconfirmed and sBars >= warmup
bool fadeFromTop = ready and close[1] > sigUp[1] and close <= sigUp
bool fadeFromBot = ready and close[1] < sigDn[1] and close >= sigDn
float cumVol = ta.cum(nz(volume))
if barstate.islast and cumVol == 0
runtime.error("This symbol provides no volume data, so VWAP cannot be calculated.")
// ── Plots ──────────────────────────────────────────────────────────────
bool hide = newSession
plot(hide ? na : sVwap, "VWAP", color = vwapCol, linewidth = 2, style = plot.style_linebr)
pU1 = plot(hide ? na : u1, "Upper 1", color = color.new(upCol, 50), style = plot.style_linebr)
pL1 = plot(hide ? na : l1, "Lower 1", color = color.new(dnCol, 50), style = plot.style_linebr)
pU2 = plot(hide ? na : u2, "Upper 2", color = color.new(upCol, 25), style = plot.style_linebr)
pL2 = plot(hide ? na : l2, "Lower 2", color = color.new(dnCol, 25), style = plot.style_linebr)
pU3 = plot(hide or not show3 ? na : u3, "Upper 3", color = upCol, style = plot.style_linebr)
pL3 = plot(hide or not show3 ? na : l3, "Lower 3", color = dnCol, style = plot.style_linebr)
fill(pU1, pU2, color = color.new(upCol, 92), title = "Upper 1-2 fill")
fill(pL1, pL2, color = color.new(dnCol, 92), title = "Lower 1-2 fill")
fill(pU2, pU3, color = color.new(upCol, 85), title = "Upper 2-3 fill")
fill(pL2, pL3, color = color.new(dnCol, 85), title = "Lower 2-3 fill")
plot(sd, "1 SD distance", display = display.data_window)
plotshape(showSig and fadeFromTop, "Re-entry from above", shape.triangledown, location.abovebar, upCol, size = size.tiny)
plotshape(showSig and fadeFromBot, "Re-entry from below", shape.triangleup, location.belowbar, dnCol, size = size.tiny)
// ── Alerts ─────────────────────────────────────────────────────────────
alertcondition(fadeFromTop, "Close back inside upper band", "FXP VWAP SD: close back inside the upper deviation band on {{ticker}} {{interval}}")
alertcondition(fadeFromBot, "Close back inside lower band", "FXP VWAP SD: close back inside the lower deviation band on {{ticker}} {{interval}}")

