Almgren-Chriss Optimal Execution
Liquidating a large position is a tradeoff between two costs that pull in opposite directions. Trade too fast and you crush the price with your own market impact; trade too slow and you expose the unexecuted shares to price risk while the market drifts. The Almgren-Chriss model, published in 2000, formalizes this as an optimization problem and produces an efficient frontier of execution: for each level of risk aversion, the trade schedule that minimizes expected cost. It is the intellectual backbone of modern implementation-shortfall algorithms and a natural complement to the schedule-based TWAP and VWAP algos. This article derives the intuition, the math, and a Python illustration.
The execution problem
You hold X shares to liquidate over a horizon of T, divided into N intervals of length τ = T/N. Let xk be the shares remaining after step k (with x0 = X, xN = 0), and nk = x{k-1} − xk the amount traded in interval k. The cost of execution, relative to the arrival (decision) price, is the implementation shortfall — the gap between the value you should have realized and what you actually got. Almgren-Chriss models this shortfall as the sum of two impact effects plus volatility risk.
Temporary vs permanent impact
Market impact splits into two distinct components, and conflating them is a common error:
- Permanent impact shifts the equilibrium price and persists for the rest of the liquidation. It is typically modeled as linear in the trade rate: each share you sell permanently depresses the price by
γper share. Permanent impact affects all remaining shares, so the total permanent cost depends only on the total quantity, not the schedule — it is, to first order, unavoidable. - Temporary impact is the transient concession you pay for demanding liquidity now — crossing the spread, walking the book. It depends on the rate of trading in that interval and dissipates before the next one. A common form is
η · (nk / τ), so trading faster (largernkper unit time) costs more per share.
Because temporary impact grows with trading speed while volatility risk grows with delay, the schedule you choose only really controls the temporary cost and the risk — and that is where the optimization bites.
| Component | Depends on | Schedule-controllable? |
|---|---|---|
| Permanent impact | Total quantity | No (first order) |
| Temporary impact | Trade rate per interval | Yes |
| Timing (volatility) risk | Time exposed × shares held | Yes |
The cost-risk tradeoff
The expected shortfall E[C] is driven up by temporary impact (worse when you trade fast). The variance of shortfall V[C] comes from price volatility acting on the shares still held — worse when you trade slow. Almgren-Chriss minimizes a mean-variance objective with a risk-aversion parameter λ:
minimize E[C] + λ · V[C]
λ → 0: a risk-neutral trader minimizes expected cost alone and trades slowly (close to a linear/TWAP-like schedule), accepting timing risk.λ → ∞: a highly risk-averse trader dumps the position quickly to eliminate exposure, paying large temporary impact.
Sweeping λ traces the efficient frontier of execution — the set of (expected cost, variance) pairs that are not dominated by any other schedule. You pick the point matching your tolerance for uncertainty, exactly analogous to the mean-variance frontier in portfolio choice.
The optimal trajectory
With linear permanent impact, linear temporary impact, and constant volatility σ, the problem has a closed form. The optimal holdings decay exponentially toward zero:
x_k = X · sinh( κ (T − t_k) ) / sinh( κ T )
where the decay rate κ satisfies 2/τ² · (cosh(κτ) − 1) = λ σ² / η. Larger risk aversion λ (or larger volatility σ) increases κ, bending the trajectory toward front-loaded trading. As λ → 0, κ → 0 and the trajectory becomes linear in time — i.e. trade equal amounts each interval, the TWAP solution. This is the satisfying punchline: TWAP is the risk-neutral special case of Almgren-Chriss.
import numpy as np
def almgren_chriss_trajectory(X, T, N, sigma, eta, lam):
"""Optimal remaining-holdings trajectory for liquidating X shares.
sigma: per-period volatility; eta: temporary impact coeff; lam: risk aversion.
Returns holdings x_k and trades n_k per interval.
"""
tau = T / N
kappa_tilde2 = lam * sigma**2 / eta
# discrete decay rate kappa from cosh relation
kappa = np.arccosh(kappa_tilde2 * tau**2 / 2 + 1) / tau
t = np.linspace(0, T, N + 1)
if kappa > 0:
x = X * np.sinh(kappa * (T - t)) / np.sinh(kappa * T)
else:
x = X * (1 - t / T) # risk-neutral -> linear (TWAP)
n = -np.diff(x) # shares traded per interval
return x, n
x, n = almgren_chriss_trajectory(X=1_000_000, T=1.0, N=20,
sigma=0.02, eta=2.5e-6, lam=2e-6)
print("front-loading ratio:", n[0] / n[-1]) # >1 means trade more early
Raise lam and the front-loading ratio climbs; drop it to zero and trades become uniform. That one knob is how production execution algos let a trader express urgency.
Practical use
- Calibrate impact coefficients (
η,γ) from your own fills via transaction cost analysis — vendor estimates rarely match your venues and order sizes. - Set
λfrom real urgency, not habit: a stale alpha signal justifies front-loading; a slow rebalance does not. - Respect capacity. If your order is large relative to daily volume, impact is nonlinear and the linear model underestimates cost — see strategy capacity and market impact.
- Layer microstructure tactics. The schedule says how much to trade each interval; smart order routing and limit-vs-market choices, informed by market microstructure, decide how to execute each slice.
Common mistakes
- Confusing temporary and permanent impact. Only temporary impact and risk are schedule-controllable; mislabeling them corrupts the optimization.
- Using textbook impact coefficients. Impact is venue-, size-, and regime-specific; calibrate from your own executions.
- Ignoring nonlinearity for large orders. The linear model breaks down when you are a big fraction of volume.
- Setting risk aversion arbitrarily.
λshould reflect signal decay and inventory risk, not a default. - Treating the schedule as the whole job. Almgren-Chriss gives a trajectory; tactical execution of each slice still matters.
Key takeaways
- Execution trades off temporary market impact (worse when fast) against timing risk (worse when slow).
- Permanent impact depends on total size and is largely unavoidable; the schedule controls temporary impact and risk.
- Minimizing
E[C] + λ·V[C]yields an efficient frontier of execution; the risk-aversionλselects your point on it. - The optimal trajectory decays exponentially (
sinhform); TWAP is the risk-neutral special case asλ → 0. - Calibrate impact from your own fills via TCA and respect capacity limits for large orders.
