Skip to content
ArthaQuant

TWAP, VWAP and beyond

Published
Updated
Topic
Execution
Reading time
6 min read

How TWAP, VWAP, percentage-of-volume and implementation-shortfall algorithms work, and how to measure what your execution really costs.

A strategy's signals decide what to trade. Execution decides what it actually costs. For many systematic strategies, the gap between the backtest price and the real fill is the difference between an edge and a loss.

This guide explains the main execution algorithms, TWAP, VWAP, percentage of volume and implementation shortfall, what each one optimises, and how to measure whether your execution is working.

What execution costs

Every order pays some combination of:

  • Explicit costs: commission, exchange and regulatory fees, and taxes. In India, for example, securities transaction tax and stamp duty apply on top of brokerage.
  • Spread: crossing from the mid-price to the bid or ask.
  • Market impact: your own order moving the price against you, temporarily while you trade and sometimes permanently.
  • Timing risk: the price drifting while you wait to complete the order.

Impact and timing risk pull in opposite directions. Trade fast and you pay more impact. Trade slowly and you are exposed to the price moving away. Every execution algorithm is a way of choosing a point on that trade-off.

A widely used rule of thumb for impact is the square-root law: the cost of a large order grows roughly with the square root of its size relative to daily volume.

impact ≈ k × σ_daily × sqrt(order size / daily volume)

Here k is a constant of order one that varies by market. Trading 1% of daily volume in a stock with 2% daily volatility costs very roughly 0.02 × √0.01 = 0.2% in impact. Doubling the order does not double the cost per share; it raises it by about 41%.

TWAP: time-weighted average price

TWAP splits an order into equal slices spread evenly over a period: buy 10,000 shares over 50 minutes as 1,000 shares every five minutes.

import numpy as np

def twap_schedule(total_qty: int, n_slices: int) -> list[int]:
    base, extra = divmod(total_qty, n_slices)
    return [base + (1 if i < extra else 0) for i in range(n_slices)]

print(twap_schedule(10_000, 7))  # [1429, 1429, 1429, 1429, 1428, 1428, 1428]

Good for: simplicity, predictability and instruments with fairly even volume through the day. Weakness: it ignores volume, so it trades the same amount in the quiet lunch hour as in the busy open, and its regular pattern can be spotted by other participants. Practical versions randomise slice sizes and timing slightly.

VWAP: volume-weighted average price

VWAP follows the market's typical volume profile, trading more when the market usually trades more. Volume in many equity markets follows a U shape, heavy at the open and close and lighter in the middle of the day.

def vwap_schedule(total_qty: int, volume_profile: np.ndarray) -> np.ndarray:
    """Split an order in proportion to the expected share of volume in each interval."""
    weights = volume_profile / volume_profile.sum()
    slices = np.floor(weights * total_qty).astype(int)
    slices[np.argmax(weights)] += total_qty - slices.sum()  # put the rounding remainder in the busiest bucket
    return slices

# Average share of daily volume per half-hour bucket, estimated from recent history
profile = np.array([14, 9, 7, 6, 5, 5, 5, 6, 7, 9, 12, 15], dtype=float)
print(vwap_schedule(10_000, profile))

Good for: benchmark-sensitive orders, since many institutions measure execution against the day's VWAP, and for hiding an order inside normal market activity. Weakness: it relies on a forecast of volume. On unusual days, such as results, expiry or index rebalances, the profile can be badly wrong, and the algorithm keeps buying into a falling or rising price because the schedule says so.

POV: percentage of volume

A percentage-of-volume (or participation) algorithm trades a fixed share of actual market volume as it happens: "be 10% of whatever trades". It speeds up when the market is busy and slows down when it is quiet.

Good for: keeping impact under control in uncertain volume conditions. Weakness: completion time is uncertain. On a quiet day the order may not finish, and participation rates that are too high make you the market.

Implementation shortfall

André Perold (1988) defined implementation shortfall as the difference between the price when you decided to trade (the arrival or decision price) and the average price you actually got, including the cost of anything you failed to fill. It is the most honest measure of execution, because it charges you for both impact and delay.

Implementation-shortfall algorithms, often built on the framework of Almgren and Chriss (2000), choose a trading schedule that balances expected impact against the risk of the price moving. A risk-averse setting trades quickly at the start (front-loading); a patient one spreads the order out more evenly, much like TWAP.

def implementation_shortfall_bps(side: int, decision_price: float, fills: list[tuple[float, int]]) -> float:
    """side = +1 for buys, -1 for sells. fills = [(price, quantity), ...]."""
    qty = sum(q for _, q in fills)
    avg = sum(p * q for p, q in fills) / qty
    return side * (avg - decision_price) / decision_price * 10_000

print(round(implementation_shortfall_bps(+1, 100.00, [(100.05, 400), (100.12, 600)]), 1))  # 9.2

Execution for smaller systematic traders

Most retail and small-fund strategies do not move markets, but execution still matters:

  • Model costs in the backtest the way they will occur. Use the actual fee schedule, a realistic spread for the time of day you trade, and slippage estimated from real fills rather than a hopeful constant.
  • Watch the open and the close. Spreads are often widest just after the open. Signals computed on the closing price cannot usually be filled at that exact price.
  • Respect exchange limits. Exchanges cap the size of a single order in some contracts (on NSE, for example, derivatives have a "freeze quantity"), so larger orders must be split into several.
  • Use limit orders with care. A limit order avoids paying the spread but may not fill. Strategies that only fill when the price moves against them suffer adverse selection: the missed trades are often the best ones.
  • Mind liquidity in options. Far-from-the-money strikes and far expiries can have wide spreads that dwarf any modelled edge.

Measuring your execution

You cannot improve what you do not measure. For every live order, record:

  1. The decision time and price (when the signal fired).
  2. Each fill with its time, price and quantity.
  3. The benchmark prices: arrival mid, interval VWAP and close.

From these, track average slippage versus the decision price in basis points, broken down by instrument, time of day and order size. Compare it with the costs assumed in the backtest. A persistent gap means the backtest is overstating performance, and it usually grows as the strategy scales up.

Summary

TWAP spreads an order evenly in time, VWAP follows expected volume, POV follows actual volume, and implementation-shortfall algorithms balance impact against timing risk. Which one fits depends on order size, urgency and the instrument. For most systematic traders, the bigger win is simpler: model costs realistically in the backtest, measure slippage on every live trade, and make sure the two agree.

This article is for education only and is not investment advice.

Read next

All writing