Build and inspect a daily VWAP reversion strategy
VWAP weights a chosen bar price by that bar’s reported volume over an anchor period. It is a reference average, not a fair-value estimate or a forecast that price will return. This intraday example enters when price crosses an outer band and exits on a return across VWAP, with optional fixed percentage brackets.
The anchor and band formula are separate choices
VWAP = Σ(source × volume) / Σ(volume), accumulated from the anchor. The default source here is hlc3 = (high + low + close) / 3, so this is a bar-based estimate, not an average of every underlying transaction.
The script resets when TradingView’s daily timeframe changes. That follows the chart’s trading calendar and available session bars; it is not a hardcoded New York 09:30 reset. Extended-hours settings and the feed’s volume definition affect the result.
Band width is a multiplier times the population standard deviation of source over the last 20 bars. It is neither volume-weighted nor reset with VWAP. These are custom rolling-price bands, not the built-in VWAP indicator’s anchored deviation bands, and crossing them does not imply a known reversal probability.
See TradingView’s VWAP calculation and anchor settings and strategy order and backtest documentation.
v6 Implementation
//@version=6
strategy("Pineify Daily VWAP Reversion Example", overlay=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=10, pyramiding=0, commission_type=strategy.commission.percent, commission_value=0.1, slippage=1, margin_long=100, margin_short=100)
source = input.source(hlc3, "Source price")
lookback = input.int(20, "Rolling price deviation bars", minval=2)
devMultiplier = input.float(2, "Deviation multiplier", minval=0.1, step=0.1, tooltip="Rolling price standard deviation, not anchored VWAP deviation")
useStopLoss = input.bool(true, "Use fixed stop loss")
stopLossPercent = input.float(1.5, "Stop loss (%)", minval=0.1, maxval=99, step=0.1)
useTakeProfit = input.bool(true, "Use fixed take profit")
takeProfitPercent = input.float(3, "Take profit (%)", minval=0.1, maxval=99, step=0.1)
if barstate.isfirst and not timeframe.isintraday
runtime.error("This example requires an intraday chart.")
newDay = timeframe.change("1D")
vwapValue = ta.vwap(source, newDay)
priceDeviation = ta.stdev(source, lookback)
upperBand = vwapValue + priceDeviation * devMultiplier
lowerBand = vwapValue - priceDeviation * devMultiplier
longSignal = ta.crossunder(source, lowerBand)
shortSignal = ta.crossover(source, upperBand)
longReturn = ta.crossover(source, vwapValue)
shortReturn = ta.crossunder(source, vwapValue)
validBar = source > 0 and close > 0 and not na(volume) and volume > 0 and not na(vwapValue) and not na(priceDeviation)
if barstate.isconfirmed and validBar and not newDay
if strategy.position_size == 0
if longSignal
strategy.entry("VWAP Long", strategy.long)
else if shortSignal
strategy.entry("VWAP Short", strategy.short)
if strategy.position_size > 0 and longReturn
strategy.close("VWAP Long", comment="Return across VWAP")
if strategy.position_size < 0 and shortReturn
strategy.close("VWAP Short", comment="Return across VWAP")
// These brackets are first created at the calculation after the entry fills.
if useStopLoss or useTakeProfit
if strategy.position_size > 0
sl = useStopLoss ? strategy.position_avg_price * (1 - stopLossPercent / 100) : na
tp = useTakeProfit ? strategy.position_avg_price * (1 + takeProfitPercent / 100) : na
strategy.exit("Long bracket", from_entry="VWAP Long", stop=sl, limit=tp)
if strategy.position_size < 0
sl = useStopLoss ? strategy.position_avg_price * (1 + stopLossPercent / 100) : na
tp = useTakeProfit ? strategy.position_avg_price * (1 - takeProfitPercent / 100) : na
strategy.exit("Short bracket", from_entry="VWAP Short", stop=sl, limit=tp)
plot(vwapValue, "Daily anchored VWAP", color=color.blue, linewidth=2)
p1 = plot(upperBand, "VWAP + rolling deviation", color=color.red)
p2 = plot(lowerBand, "VWAP - rolling deviation", color=color.green)
fill(p1, p2, color=color.new(color.blue, 90))Entry, reset and bracket behavior
- 1
The example requires an intraday chart and positive prices with positive reported volume on a signal bar. Missing or zero-volume bars cannot trigger entries or VWAP-cross exits. The feed may report tick volume rather than traded volume; the code cannot establish which.
- 2
Signals are computed every bar and acted on at confirmed closes. Only a flat position may enter, so the opposing signal cannot automatically reverse an open position. The first bar of each daily anchor is excluded from entries and VWAP-cross exits.
- 3
A long enters on a cross below the lower band, a short on a cross above the upper band. There is no reversal confirmation. A return across VWAP requests a market close, normally executed on the next tick.
- 4
Stop and target switches are independent: disabling one passes na for that bracket price. Brackets based on the average fill price are first submitted at the calculation after entry fills, normally the close of the entry bar. The position is unprotected by this bracket until then. Disabling both leaves only the VWAP-cross exit.
- 5
There is no forced session-end close. A position can carry overnight; the next day’s reset changes its VWAP reference. Gap losses and short-position losses can exceed a percentage stop.
Backtest Assumptions and Metrics
This tutorial supplies rules and settings, not verified returns. No reproducible dataset supports a performance figure or a claim that a filter reduces drawdown. Record the chart symbol, venue, session, timeframe, dates and all settings before comparing results.
| Metric | Value | Interpretation |
|---|---|---|
| Initial capital | 10,000 account-currency units | Confirm the strategy account currency in Properties. |
| Order size | 10% of equity | Requested position size, not a 10% loss cap; instrument rounding can prevent small orders. |
| Commission / slippage | 0.1% per fill / 1 tick | Illustrative settings; adapt fees, spread, borrowing and funding costs. |
| Default bracket | 1.5% stop / 3% target | Measured from average entry price once the bracket is submitted; not guaranteed fill prices. |
| Return and drawdown | Not established | Generate and inspect your own Strategy Tester results. |
Start with standard-price candles and check the first anchor, missing-volume bars, entry fills, the delay before bracket creation, and positions carried into the next day. Compare all four stop/target switch combinations. Bar Magnifier can add lower-timeframe fill detail where available, but does not remove execution uncertainty. Hold out a later period before choosing a band multiplier or session filter.
Review the rules and exported trades
Pineify Backtest Deep Report imports a TradingView trade-list CSV for additional trade summaries. Check that prices, quantities, currency and fees match the Strategy Tester export. Closed-trade summaries cannot reconstruct missing intrabar exposure or establish live execution quality.
The AI Coding Agent can help draft changes. Compile the resulting script and inspect its orders in TradingView before treating a change as correct.
Strategy Optimization Suggestions
Choose a session deliberately
Compare regular and extended-session data under a written rule. A chosen hour filter needs separate evaluation; there is no supported universally best trading window.
Separate a band change from an anchor change
Changing lookback alters rolling price dispersion; changing the VWAP anchor changes cumulative price-volume weighting. Test these as different hypotheses.
Inspect adverse moves
Include days where price keeps moving away from VWAP. Review exposure, costs and gaps rather than treating a deviation crossing as proof of an eventual return.
Frequently Asked Questions
Check a specific rule change
Keep the original settings, change one rule, and compare the orders and costs on a separate test period. A code draft or trade report does not establish profitability.
Disclaimer: Trading involves significant risk. The strategies and code provided are for educational purposes only and do not constitute financial advice. Past performance is not indicative of future results.
Related Strategy Tutorials
Mean Reversion Strategy Tutorial
Learn how to build, backtest, and optimize a Mean Reversion Strategy on TradingView using Bollinger Bands and RSI with Pine Script v5.
RSI Overbought Oversold Strategy Tutorial
Compare RSI threshold entries, recovery crosses and EMA trend filters. Test a Pine Script v6 strategy with explicit stops, costs and time exits on TradingView.
Bollinger Bands Breakout Strategy Tutorial
Master the Bollinger Bands Squeeze and Breakout strategy on TradingView. Learn the Pine Script v5 code and professional analysis with Pineify.
Grid Trading Strategy Tutorial
Inspect a fixed-range, long-only Pine Script v6 grid example. Check per-level sizing, limit orders, exits, fees and TradingView backtest assumptions.
SuperTrend Strategy Tutorial
Learn how to build, backtest, and optimize the SuperTrend strategy on TradingView using Pine Script v5 and Pineify Deep Report.