Time-weighted average price (TWAP)

Calculate the Time-Weighted Average Price (TWAP) for execution benchmarking and price analysis. TWAP represents the average price of an instrument over a time period where each price observation is weighted by how long it persists — not by how much volume traded at that price. This makes it particularly useful for illiquid markets, algorithmic order slicing, and scenarios where volume data is unavailable or unreliable.

When to use TWAP vs VWAP

TWAPVWAP
Weights byTime (equal weight per interval)Volume
Best forIlliquid instruments, algo executionLiquid instruments, intraday benchmarking
Volume data needed?NoYes
Sensitive to volume spikes?NoYes — large prints dominate
Typical usersAlgo desks, compliance, exotic FXInstitutional execution, equity trading

Choose TWAP when volume is concentrated in a few bursts (so VWAP would be skewed), when volume data is not available, or when your execution algorithm splits an order evenly over time.

Problem: Calculate TWAP over a time window

You want to calculate the average price of a trading instrument over a time window, giving equal weight to every moment in time rather than weighting by trade volume. This is the standard benchmark for TWAP execution algorithms that slice a parent order into equal-sized child orders spread evenly over time.

Solution: Use the twap() aggregate function

QuestDB's built-in twap(price, timestamp) function computes the time-weighted average using step-function integration — each observed price is held constant until the next trade, and the result is the area under this step curve divided by the total time span.

TWAP for a single symbolDemo this query
SELECT twap(price, timestamp) AS twap
FROM trades
WHERE symbol = 'ETH-USDT'
AND timestamp IN '$yesterday';

How it works

Under the hood, twap() treats prices as a step function (also called forward-fill or last-observation-carried-forward): each price persists until a new observation arrives.

Price
| ┌─────┐
| │ │ ┌──────────┐
|─────┘ │ │ │
| └──┘ └─────
└──────────────────────────────── Time
t1 t2 t3 t4 t5

The TWAP is the total area of these rectangles divided by the time span from the first to the last observation:

TWAP=i=1n1pi(ti+1ti)tnt1\text{TWAP} = \frac{\sum_{i=1}^{n-1} p_i \cdot (t_{i+1} - t_i)}{t_n - t_1}

When all observations share the same timestamp (e.g., a single row or simultaneous prints), the function falls back to a simple arithmetic mean.

TWAP per symbol

TWAP across multiple symbolsDemo this query
SELECT symbol, twap(price, timestamp) AS twap
FROM trades
WHERE symbol IN ('BTC-USDT', 'ETH-USDT', 'SOL-USDT')
AND timestamp IN '$yesterday';

Hourly TWAP with SAMPLE BY

Use SAMPLE BY to compute TWAP over fixed intervals — useful for comparing execution prices against the benchmark in each time bucket:

Hourly TWAPDemo this query
SELECT timestamp, symbol, twap(price, timestamp) AS twap
FROM trades
WHERE symbol = 'BTC-USDT'
AND timestamp IN '$yesterday'
SAMPLE BY 1h;

TWAP vs VWAP comparison

Compare the two benchmarks side-by-side to see how volume concentration affects VWAP while leaving TWAP unchanged:

TWAP vs VWAP comparison per hourDemo this query
SELECT
timestamp,
symbol,
twap(price, timestamp) AS twap,
sum(price * amount) / sum(amount) AS vwap,
twap(price, timestamp) - sum(price * amount) / sum(amount) AS twap_vwap_diff
FROM trades
WHERE symbol = 'BTC-USDT'
AND timestamp IN '$yesterday'
SAMPLE BY 1h;
VWAP formula

This query computes tick-level VWAP directly from individual trades: sum(price * amount) / sum(amount). This is the exact VWAP. The VWAP cookbook recipe uses a different approach — the typical-price approximation (high + low + close) / 3 from OHLC candles — because it works with materialized views for better performance on large datasets. Both are valid; the tick-level formula is more accurate when individual trade data is available.

When the difference is large, it indicates that volume was concentrated at prices far from the time-weighted average — a sign of clustered liquidity or large block prints.

Execution quality analysis

Compare individual trade fill prices against the TWAP benchmark over the order's time horizon:

Fill price vs TWAP benchmarkDemo this query
WITH benchmark AS (
SELECT twap(price, timestamp) AS twap_price
FROM trades
WHERE symbol = 'ETH-USDT'
AND timestamp IN '$yesterday'
)
SELECT
t.timestamp,
t.price AS fill_price,
b.twap_price,
t.price - b.twap_price AS slippage
FROM trades t
CROSS JOIN benchmark b
WHERE t.symbol = 'ETH-USDT'
AND t.side = 'buy'
AND t.timestamp IN '$yesterday'
ORDER BY t.timestamp
LIMIT 20;

Positive slippage means the fill was worse than the benchmark (for a buy); negative means it was better.

Trading use cases
  • Compliance reporting: Some best-execution frameworks (e.g., MiFID II) use TWAP as a benchmark alongside VWAP and arrival price
  • TWAP vs VWAP divergence: Large differences signal volume concentration, which may indicate block trades or liquidity events
  • Irregular tick spacing: TWAP naturally handles unevenly spaced trades — no need to resample or fill gaps before computing the average
  • Multi-day benchmarks: Combine with SAMPLE BY and FILL(prev) to compute TWAP across sessions, carrying forward the last price over overnight gaps
Related documentation