Field note No. 05 · Intermediate
Execution: TWAP, slicing and jitter
Why orders are split over time, how execution is benchmarked, what market impact costs, and why good schedules are deliberately irregular.
Educational material only. Not investment advice.
Contents
Abstract
Implementation shortfall and its parts; arrival, TWAP and VWAP benchmarks and what each rewards; the square-root law of market impact and the Almgren–Chriss trade-off between impact and timing risk; TWAP, VWAP and participation schedules; why schedules are randomised (jitter) against predatory trading and what that costs; an interactive execution simulator; and how to measure execution quality with enough orders to mean something.
Key takeaways
- Measure execution against the arrival price; TWAP and VWAP describe how an order was worked, not what it cost.
- Impact grows roughly with the square root of size relative to volume, so participation matters more than share count.
- Splitting trades impact against timing risk; TWAP is the minimum-cost schedule when risk is ignored.
- Regular schedules are easy to time; jitter makes child times much less predictable at a small expected cost, though it does not hide one-sided flow.
- Execution quality is only visible across many orders, normalised by volatility and participation.
Before you start
- Bid, ask, mid-quote and spread
- Volatility and the square root of time
- Field note 02's notation
A signal decides what to hold; execution decides what it costs to get there. For any order larger than the liquidity resting at the best prices, sending it all at once moves the price against the trader. This note covers why and how orders are split over time, how execution is benchmarked, what the market-impact literature says about cost, why schedules are deliberately randomised, and how to measure whether any of it worked.
Parent orders and implementation shortfall
Call the order the trader wants filled the parent, and the pieces actually sent to the market its children. The cost of a parent order is best measured against the price at the moment the decision was made, the arrival price, usually the mid-quote. For a buy of total size Q filled in pieces qi at prices pi:
Perold introduced implementation shortfall as the gap between a paper portfolio, traded instantly at decision prices, and the real one.1 In practice it is split into four parts: the half-spread paid to cross the quote, the market impact of the trader’s own demand, the timing risk of prices moving while the order is worked, and the opportunity cost of any part left unfilled. Splitting an order trades the second against the third: more time lowers impact and raises exposure to the market.
Arrival, TWAP and VWAP
Three benchmarks are in common use, and each rewards different behaviour. Choosing one is choosing what the execution is judged on.
| Benchmark | Definition | What it rewards | Weakness |
|---|---|---|---|
| Arrival price (implementation shortfall) | Mid-quote when the parent order arrived | Finishing fast when impact is low; the true economic cost | Noisy: dominated by market moves on any single order |
| TWAP | Time-weighted average price over the execution window | Trading evenly through the window | Can be matched exactly by trading evenly, whatever it costs |
| VWAP | Volume-weighted average price over the window | Trading in proportion to market volume | The trader's own volume is inside the benchmark; easy to match, easy to game |
VWAP was proposed as a cost benchmark by Berkowitz, Logue and Noser (1988).
VWAP became popular because it is simple to compute and hard to miss by much: an algorithm that follows the market’s volume curve will land close to it.2 That is also its weakness. A large order moves VWAP towards its own fills, so beating VWAP says little about whether the trade was cheap. Arrival-price cost is the economically honest measure, and the one this note uses; TWAP and VWAP are best treated as descriptions of how an order was worked.
Market impact and the cost of waiting
The square-root law
How much does demand move the price? Kyle’s model gives a linear answer: price moves in proportion to net order flow, with a slope that rises with the uncertainty about the asset’s value and falls with the volume of uninformed trading that hides informed orders.3 Empirically, the total impact of a large order grows more slowly than its size. Across markets and decades, a good approximation is
where σ is daily volatility, V daily volume and Y a constant of order one. Almgren, Thum, Hauptmann and Li estimated impact from large US equity orders and found temporary impact growing with the rate of trading raised to a power of about 0.6, close to the square root;4 Tóth and co-authors found the square root across many markets and argued that it reflects how thin liquidity is close to the current price.5,6 Two consequences drive everything below: the cost of an order depends on its size relative to volume more than on its size in shares, and doubling an order raises its cost per share by about forty per cent, not a hundred.
The cost of waiting
Spreading an order over time lowers impact but leaves the unfilled part exposed to price moves. Bertsimas and Lo showed that, with linear impact and no concern for risk, splitting the order evenly over the available time minimises expected cost: that even split is a TWAP.7 Almgren and Chriss added the variance of cost: a risk-averse trader trades faster at the start, and the set of schedules that minimise variance for each level of expected cost forms an efficient frontier, from TWAP at one end to immediate execution at the other.8 Obizhaeva and Wang showed that when the order book refills gradually after each trade, the optimal schedule combines discrete trades at the start and end with continuous trading in between.9
Schedules
A TWAP schedule divides a parent of size Q into N children of size Q/N, one every W/N minutes across a window W. Three numbers describe it. The participation rate, the child’s share of the market volume trading while it works, drives impact. The window drives timing risk. The child size relative to the quoted size decides whether a child can cross the spread without walking the book.
| Schedule | Child sizes follow | Best when |
|---|---|---|
| TWAP | Equal slices in time | Volume is fairly even, or predictability of volume is poor |
| VWAP | The expected intraday volume curve | Volume has a strong, stable shape (heavy at the open and close) |
| Participation (POV) | A fixed share of volume as it prints | Size is uncertain relative to the day's volume; the order must never dominate |
| Implementation shortfall | Front-loaded, per Almgren–Chriss | Timing risk is large relative to impact |
Why schedules are randomised
A perfectly regular schedule, the same size at the same interval, is a signal. Anyone watching the tape who sees clips of the same size print every few minutes can infer that a larger order is being worked, estimate its direction and remaining size, and trade ahead of it. Brunnermeier and Pedersen formalised the incentive: when a trader is known to need to buy or sell, others profit by trading in the same direction first and reversing later.10 The defence is to make the schedule harder to read. Jitter randomises each child’s time within its slot and scales its size by a random factor, while still completing the parent within the window.
def schedule(slices: int, window: int, jitter: float, rng: np.random.Generator):
"""Child times (minutes after the window opens) and sizes (fractions of the parent).
jitter = 0 is a plain TWAP: equal sizes at the start of equal intervals, so one slice is an
immediate order. jitter in (0, 1] delays each child by up to jitter x its interval and scales
each size by 1 + jitter * U(-0.5, 0.5), renormalised so the parent is filled exactly.
"""
dt = window / slices
times = (np.arange(slices) + jitter * rng.uniform(0.0, 1.0, slices)) * dt
sizes = 1.0 + jitter * rng.uniform(-0.5, 0.5, slices)
return np.sort(times), sizes / sizes.sum()In the simulator, an observer who predicts each child’s time from the previous child plus the median gap seen so far times 100% of an unjittered TWAP’s children (20 children over two hours) to within thirty seconds; at 50% jitter the figure falls to 27%, and at full jitter to 14%. The price is small but not zero: 0.4 bp of expected cost at full jitter in this model, against a timing risk of 46 bp. It arises because a child’s total impact cost grows with its size to the power 1.5, so uneven sizes cost more than even ones, and because delayed children move along the intraday volume curve. The measure is deliberately narrow: it only watches the clock. Jitter does not hide one-sided flow, which a careful observer sees in the tape whatever the timing, and it helps little when children are about a minute apart. Randomising order types and venues, and never trading the same size at the same second of the minute, follow from the same logic.
def detectability(slices: int, window: int, jitter: float, rng: np.random.Generator,
tolerance: float = 0.5) -> float:
"""Share of child orders whose time an observer predicts to within `tolerance` minutes by
adding the median of the gaps seen SO FAR to the previous child's time. 1.0 for an exact
TWAP. The observer only watches the clock; size and direction are other giveaways."""
t, _ = schedule(slices, window, jitter, rng)
gaps = np.diff(t)
if len(gaps) < 2:
return 0.0
guess = np.array([t[i - 1] + np.median(gaps[: i - 1]) for i in range(2, len(t))])
return float(np.mean(np.abs(guess - t[2:]) <= tolerance))An execution laboratory
The laboratory below simulates a buy order under the model in the code listing: a random-walk mid with the chosen daily volatility, a half-spread of 1 bp, temporary impact following the square-root law in each child’s share of the volume while it works (one minute, or its slot if shorter), linear permanent impact (small at this size, a fraction of a basis point), and a U-shaped intraday volume curve. Each child starts its slot, so a single child is an immediate order; jitter delays children within their slots. The impact coefficient (η = 0.15) applies to one minute’s participation and is chosen so that a full-day order lands near the square-root law above; γ = 0.3. Both are illustrative, not a calibration: the shapes of the results are the point, not their levels.
Each point keeps the current pace, one child every 6.0 minutes, and stretches the window. Longer windows split the order into more, smaller children, so expected cost falls; they also leave it exposed for longer, so timing risk rises. No point is better on both axes: that is the Almgren–Chriss trade-off. The current setting is marked.
Toy model: random-walk mid, half-spread 1 bp, square-root temporary impact on each child’s share of the volume while it works (one minute, or its slot if shorter), small linear permanent impact, U-shaped intraday volume. Children start their slots and jitter delays them. Coefficients are illustrative, not a calibration.
The reference grid computed by the Python simulator (4,000 paths per cell, an order of 1% of daily volume, 1.5% daily volatility) shows the two forces separately. Holding the window fixed, more children cut the cost; the timing risk depends mainly on the window:
| Window | 1 slice | 5 slices | 20 slices | 100 slices | Timing risk (st. dev.) |
|---|---|---|---|---|---|
| 30 min | 35 bp | 17 bp | 9 bp | 8 bp | 23 bp |
| 120 min | 35 bp | 19 bp | 9 bp | 6 bp | 46 bp |
| 390 min | 35 bp | 20 bp | 11 bp | 6 bp | 84 bp |
Mean implementation shortfall in basis points of the arrival mid, toy model. Timing risk shown for 20 slices; it varies little with the slice count beyond a few children.
The trade-off appears when the pace is held and the window stretched. At one child every six minutes, the Python simulator gives:
| Window | Children | Expected cost | Timing risk (st. dev.) |
|---|---|---|---|
| 6 min | 1 | 35 bp | 0 bp |
| 30 min | 5 | 17 bp | 20 bp |
| 60 min | 10 | 12 bp | 32 bp |
| 120 min | 20 | 9 bp | 46 bp |
| 240 min | 40 | 9 bp | 66 bp |
| 390 min | 65 | 5 bp | 87 bp |
Same model and assumptions as the grid above; one child every six minutes.
Stretching the window from 6 minutes to the full day cuts the expected cost from 35 bp to 5 bp and raises the timing risk from 0 bp to 87 bp, the risk growing roughly with the square root of the window. No point is better on both axes. That is the Almgren–Chriss frontier, and choosing a point on it is a statement about how much price risk a strategy is willing to carry to save impact.
def simulate(size=0.01, sigma=150.0, slices=10, window=120, jitter=0.0, spread=2.0,
eta=0.15, gamma=0.3, child_minutes=1.0, start=0, paths=2000, seed=7) -> dict:
"""Implementation shortfall (vs the arrival mid) of many paths, and its parts."""
window = min(window, MINUTES - start)
rng = np.random.default_rng(seed)
prof = volume_profile()
vol_min = sigma / np.sqrt(MINUTES)
cost = np.empty(paths)
impact = np.empty(paths)
for k in range(paths):
t, q = schedule(slices, window, jitter, rng)
minute = np.minimum(start + t.astype(int), MINUTES - 1)
steps = rng.normal(0.0, vol_min, MINUTES)
path = np.concatenate([[0.0], np.cumsum(steps)])
mid = path[minute] - path[start] # random walk from arrival
work = min(child_minutes, window / slices)
a = start + t
v = np.array([_volume(prof, x, x + work) for x in a])
child = q * size
temp = eta * sigma * np.sqrt(child / v)
perm = gamma * sigma * np.concatenate([[0.0], np.cumsum(child)[:-1]])
fills = mid + perm + temp + spread / 2
cost[k] = implementation_shortfall(fills, q, 0.0)
impact[k] = (q * (perm + temp + spread / 2)).sum()
return {"mean": cost.mean(), "sd": cost.std(ddof=1), "p05": np.percentile(cost, 5),
"p95": np.percentile(cost, 95), "expected_impact": impact.mean()}Measuring execution quality
Execution quality can only be judged in aggregate. With a timing risk of about 46 bp per order in the example above, telling apart two algorithms whose true costs differ by 5 bp needs roughly 1,351 orders for each algorithm to detect the difference reliably (a two-sided 5% test with 80% power). Removing the market’s move from each order’s cost, or alternating the two algorithms on comparable orders, cuts that number considerably. The practical consequences:
- Record everything at the child level: decision time, arrival mid, each child’s send time, fill price, size and venue, and the quote at the time.
- Measure against arrival first, then decompose: spread paid, temporary impact (the drift during the order that reverses afterwards), permanent impact (the part that persists), and timing (the market’s own moves while the order was worked, which a benchmark such as the index or the sector over the same minutes estimates).
- Normalise by volatility and participation before comparing orders: cost divided by σ√(Q/V) is comparable across days and instruments; raw basis points are not.
- Reconcile against the backtest. The backtest’s cost assumption is a hypothesis; live fills test it. Field note 04 (QuantConnect and LEAN) describes how the backtesting engine models fills, and the gap between the two is where most live underperformance starts.
def implementation_shortfall(fill_prices, fill_sizes, arrival_mid, side=1):
"""Cost of a parent order against the mid at the moment it arrived, in the price's units.
side = +1 for a buy, -1 for a sell; positive is a cost. Divide by the arrival mid (and
multiply by 1e4) for basis points when prices are levels rather than bp offsets.
"""
fill_prices, fill_sizes = np.asarray(fill_prices, float), np.asarray(fill_sizes, float)
avg = (fill_prices * fill_sizes).sum() / fill_sizes.sum()
return side * (avg - arrival_mid)References
- Perold, A. F. (1988). The implementation shortfall: Paper versus reality. Journal of Portfolio Management, 14(3), 4-9.
- Berkowitz, S. A., Logue, D. E., & Noser, E. A. (1988). The total cost of transactions on the NYSE. Journal of Finance, 43(1), 97-112.
- Kyle, A. S. (1985). Continuous auctions and insider trading. Econometrica, 53(6), 1315-1335.
- Almgren, R., Thum, C., Hauptmann, E., & Li, H. (2005). Direct estimation of equity market impact. Risk, 18(7), 58-62.
- Tóth, B., Lempérière, Y., Deremble, C., de Lataillade, J., Kockelkoren, J., & Bouchaud, J.-P. (2011). Anomalous price impact and the critical nature of liquidity in financial markets. Physical Review X, 1(2), 021006.
- Bouchaud, J.-P., Bonart, J., Donier, J., & Gould, M. (2018). Trades, quotes and prices: Financial markets under the microscope. Cambridge University Press.
- Bertsimas, D., & Lo, A. W. (1998). Optimal control of execution costs. Journal of Financial Markets, 1(1), 1-50.
- Almgren, R., & Chriss, N. (2000). Optimal execution of portfolio transactions. Journal of Risk, 3(2), 5-39.
- Obizhaeva, A. A., & Wang, J. (2013). Optimal trading strategy and supply/demand dynamics. Journal of Financial Markets, 16(1), 1-32.
- Brunnermeier, M. K., & Pedersen, L. H. (2005). Predatory trading. Journal of Finance, 60(4), 1825-1863.