VWAP Reversion Strategy
VWAP reversion is a mean-reversion strategy: when price extends far enough from the session VWAP, fade the move and target a return to VWAP. Works best on ES and NQ during non-event days, during lunch and early afternoon chop. It fails badly on strong trend days — a regime filter is mandatory.
The setup
Session VWAP (the volume-weighted average price since the session open) is the day's institutional reference. Price tends to oscillate around it. When price extends 2+ standard deviations from VWAP and the market is not trending hard, it often reverts.
Rules:
1. Regime filter. Skip days with:
- FOMC, CPI, NFP, ISM, or other major US economic releases
- ADX(14) on 5-minute > 25 (indicates strong trending regime)
- Opening-hour range > 2× 20-day average
2. Setup. Price extends 2 × session standard deviations from VWAP.
3. Trigger. Rejection candle at the extreme:
- Pin bar (long lower wick for longs, long upper wick for shorts)
- Engulfing bar in the reversion direction
- Volume spike on the rejection bar
4. Entry. Market order on the next bar open after the trigger bar closes.
5. Stop. 1× ATR beyond the trigger high/low.
6. Target. VWAP itself. Optional: half off at VWAP, trail the rest to the other side of VWAP.
Windows when it works best
- 10:00–11:30 ET — mid-morning chop after the opening drive
- 13:30–14:30 ET — early afternoon, often rotational
- Wednesday, Thursday — quiet mid-week sessions
When it doesn't work:
- 9:30–10:00 ET — too much momentum, reversion signals fail
- FOMC days (2–4pm) — news can destroy reversion setups
- Friday 2:30–4:00pm — weekly expiries and positioning create wild moves
Realistic performance
- Win rate: 55–65% (higher than trend strategies because targets are close)
- Avg winner: 0.8–1.2× avg loser (small edges per trade)
- Profit factor: 1.2–1.6
- Frequency: 2–5 trades per day on ES during good regimes, often zero on trend days
This is a "many small wins, occasional bigger losses" strategy. Commissions matter — a $2 round-trip on ES is meaningful when average winners are $75–$150.
Full Pine Script (v6)
//@version=6
strategy("VWAP Reversion", overlay=true, initial_capital=50000, default_qty_type=strategy.fixed, default_qty_value=1)
// ---------- Inputs ----------
stdDevMult = input.float(2.0, "Std Dev Multiplier")
atrLen = input.int(14, "ATR Length")
stopAtrMult = input.float(1.0, "Stop = N × ATR beyond trigger")
adxFilter = input.float(25, "Skip if ADX > X (trend filter)")
requireRejection = input.bool(true, "Require rejection candle")
// ---------- Session-based VWAP with SD bands ----------
var float sumPV = na
var float sumV = na
var float sumPPV = na
if session.isfirstbar
sumPV := 0.0
sumV := 0.0
sumPPV := 0.0
typical = hlc3
v = volume
sumPV := nz(sumPV) + typical * v
sumV := nz(sumV) + v
sumPPV := nz(sumPPV) + typical * typical * v
vwap = sumV > 0 ? sumPV / sumV : na
variance = sumV > 0 ? (sumPPV / sumV) - (vwap * vwap) : na
sd = variance > 0 ? math.sqrt(variance) : 0.0
upperBand = vwap + stdDevMult * sd
lowerBand = vwap - stdDevMult * sd
plot(vwap, color=color.orange, linewidth=2, title="VWAP")
plot(upperBand, color=color.new(color.blue, 50), title="+σ")
plot(lowerBand, color=color.new(color.blue, 50), title="−σ")
// ---------- Regime filter (ADX) ----------
[_diPlus, _diMinus, adxVal] = ta.dmi(14, 14)
goodRegime = adxVal <= adxFilter
// ---------- Candle pattern helpers ----------
body = math.abs(close - open)
range = high - low
upperWick = high - math.max(close, open)
lowerWick = math.min(close, open) - low
bullishRejection = lowerWick > 2 * body and close > open
bearishRejection = upperWick > 2 * body and close < open
// ---------- Setups ----------
atrVal = ta.atr(atrLen)
longSetup = low <= lowerBand and goodRegime and (not requireRejection or bullishRejection)
shortSetup = high >= upperBand and goodRegime and (not requireRejection or bearishRejection)
if longSetup and strategy.position_size == 0
stopPrice = low - stopAtrMult * atrVal
strategy.entry("VWAP Long", strategy.long)
strategy.exit("Long Exit", "VWAP Long", stop=stopPrice, limit=vwap)
if shortSetup and strategy.position_size == 0
stopPrice = high + stopAtrMult * atrVal
strategy.entry("VWAP Short", strategy.short)
strategy.exit("Short Exit", "VWAP Short", stop=stopPrice, limit=vwap)
// ---------- Flatten at session close ----------
if hour == 15 and minute >= 55
strategy.close_all("EOD flatten")
Automating through CrossTrade
Standard payload:
key=YOUR-SECRET-KEY;
command=place;
account=Sim101;
instrument=ES 06-26;
action={{strategy.order.action}};
qty={{strategy.order.contracts}};
order_type=market;
tif=day;
sync_strategy=true;
market_position={{strategy.market_position}};
prev_market_position={{strategy.prev_market_position}};
out_of_sync=flatten;
For a reversion strategy that fires frequently, consider using alert frequency alert.freq_once_per_bar_close to avoid intra-bar re-triggering.
Common mistakes
- Running on trend days. The ADX filter is not optional. Skipping days when trend dominates is what keeps the strategy alive.
- Widening the SD multiplier to "get more signals." Going from 2σ to 1.5σ triples signal count and halves edge. Keep it at 2σ or stricter.
- Trading the bands without a rejection trigger. Pure band-touch entries fail frequently. The rejection bar is a meaningful filter.
- Running during news. Economic releases blow up reversion setups. Use a news calendar filter.
Frequently Asked Questions
What's the win rate on a VWAP reversion strategy?
With proper filters (ADX, news, session timing), 55–65% win rate is realistic. Without filters, closer to 45% — reversion fails badly on trend days, and those failures dominate without a regime screen.
Why do I need an ADX filter for VWAP reversion?
Because mean reversion assumes the market is in a rotational, range-bound regime. When ADX is high, a trend is in place — reversion setups routinely fail as price keeps extending. The filter is what separates a tradeable strategy from one that blows up.
Does VWAP reversion work overnight?
Poorly. Overnight volume is too thin to generate reliable VWAP signals, and standard deviation bands are noisy on low-volume bars. Keep it RTH-only (9:30am–4pm ET on ES).
Can I use anchored VWAP instead of session VWAP?
Yes, for swing trading setups. Anchored from a major high or low, AVWAP can act as a slower-moving reversion reference. The strategy dynamics change — trades are multi-day, win rate shifts lower, individual R goes higher. Different strategy structure, same core idea.