VWAP strategy in Python

VWAP is the intraday fair-value benchmark traders watch, here is a VWAP strategy in Python, backtested on intraday bars.

What is VWAP?

The volume-weighted average price (VWAP) is the average price over a period, weighted by volume. Institutions use it as an execution benchmark and a fair-value reference: trading above VWAP is read as intraday strength, below it as weakness.

A VWAP strategy uses that reference as a directional filter or a reversion anchor. Here we use it as a trend filter, holding long only while price trades above VWAP, the simplest, most common application.

The logic

Manifold-BT exposes VWAP as a built-in column, so no manual computation is needed. We compare the close to VWAP on every bar.

The rule is a clean intraday filter: long whenever the close is above VWAP, flat whenever it slips below. It keeps you on the strong side of the session and out during weakness.

VWAP in Python

Here is the full strategy in the Manifold-BT expression DSL. It imports manifoldbt, builds the signal, configures realistic execution, and runs the backtest against Rust.

vwap.py
import manifoldbt as mbt
from manifoldbt.indicators import close, vwap
from manifoldbt.helpers import time_range, Slippage, Interval

# Long while price trades above the volume-weighted average price
strategy = (
    mbt.Strategy.create("vwap_trend")
    .signal("vwap", vwap)
    .size(mbt.when(close > vwap, 1.0, 0.0))
)

start, end = time_range("2024-01-01", "2026-01-01")
config = mbt.BacktestConfig(
    universe={"binance": ["BTCUSDT"]},
    time_range_start=start,
    time_range_end=end,
    bar_interval=Interval.minutes(15),
    initial_capital=10_000,
    fees=mbt.FeeConfig.binance_perps(),
    slippage=Slippage.fixed_bps(2),
    warmup_bars=1,
)
store = mbt.ingest(provider="binance", symbol="BTCUSDT", symbol_id=1,
                   interval="15m", start="2024-01-01T00:00:00Z",
                   end="2026-01-01T00:00:00Z")
result = mbt.run(strategy, config, store)
print(result.summary())
mbt.plot.tearsheet(result, show=True)

Backtest it with Manifold-BT

VWAP strategies are intraday by nature, so we run 15-minute bars. At this frequency fees and slippage dominate the outcome, which is exactly why a realistic execution model is non-negotiable here.

Treat this as a building block: VWAP works best combined with a second signal (a momentum or volatility filter) rather than alone. Add a confirming signal and re-backtest to see the effect on the trade count and Sharpe.

Backtest configuration
Universe{"binance": ["BTCUSDT"]}
Bar interval15m
FeesFeeConfig.binance_perps()
Slippagefixed 2 bps
Warmup1 bar
Sample tearsheet (illustrative, not a forecast)
Total return+19.7%
Sharpe0.84
Max drawdown-12.1%
Win rate49%
Trades1,304

Keep reading

Run your first backtest

Install Manifold-BT and reproduce the backtest above in seconds. The Rust core runs years of bars sub-second so you can sweep parameters instead of waiting.

$pip install manifoldbt