A complete Almgren–Chriss implementation — closed-form trajectories, the cost/risk efficient frontier, Monte Carlo verification — plus the question most implementations dodge: how wrong can your impact estimate be before dumb TWAP beats "optimal"?
Almgren–Chriss (2000) is the canonical execution model and every quant-dev interview expects it. Most public implementations stop at the pretty frontier plot. But the optimal schedule depends on the temporary impact coefficient η through the urgency parameter κ ≈ √(λσ²/η) — and η is one of the hardest numbers in trading to estimate: it must be regressed out of noisy fills, it drifts with regime, and a factor-of-two error is respectable in practice. So this repo does what a practitioner actually does before trusting a model: it builds the "optimal" schedule with a wrong η, scores it under the true parameters, and compares against TWAP scored under the same truth. The result — quantified below — is that at realistic (low) urgency, AC's edge over TWAP is basis points, and a 2× underestimate of η already hands the win to TWAP. Optimality you can't parameterise isn't optimality.
- The exact discrete-time model of Almgren & Chriss (2000): arithmetic random walk, linear permanent (γ) and temporary (η) impact, fixed cost ε per share, adjusted impact η̃ = η − γτ/2, and the exact discrete κ from cosh(κτ) = 1 + ½(λσ²/η̃)τ² — not just its continuous limit.
- Closed-form optimal trajectories x_j = X·sinh(κ(T−t_j))/sinh(κT), with both sanity anchors pinned by tests: λ→0 recovers TWAP exactly, λ→∞ dumps immediately.
- E[cost] and Var[cost] for arbitrary schedules — TWAP, a stylised VWAP U-shape, front-/back-loaded power schedules and the optimal trajectory are all scored by identical formulas, so comparisons are honest by construction.
- Efficient frontier traced over risk aversion λ, with monotonicity (more λ ⇒ less variance, more expected cost) asserted in tests.
- Monte Carlo executor that simulates the actual price dynamics path by path and verifies the closed forms — the agreement is itself a test.
- The misspecification study (
optexec misspec): schedules built with η̂ from η*/5 to 5η*, scored under the truth, against the TWAP reference — table plus PNG. - Everything runs on synthetic parameters (the AC 2000 worked example is the default); no market data involved.
git clone <this-repo> && cd optimal-execution
pip install -e .
# The optimal schedule for the AC 2000 worked example (1M shares, 5 days),
# with a Monte Carlo cross-check of the closed-form E[cost] / sd:
optexec schedule --lam 1e-6 --N 10 --mc-paths 4000
# The cost/risk efficient frontier + trajectory family (PNGs to reports/):
optexec frontier
# The honesty section's numbers — how wrong can eta-hat be before TWAP wins:
optexec misspec
# Or the paper's example end to end from Python:
python examples/ac2000_example.pyReal output from optexec schedule --lam 1e-6 --N 10 --mc-paths 4000:
AC optimal schedule — X=1,000,000 sh over T=5 days, N=10, lambda=1e-06
kappa = 0.6062 / day half-life = 1.14 days
t (days) holdings trade twap holdings
0.00 1,000,000 1,000,000
0.50 737,101 262,899 900,000
1.00 542,430 194,670 800,000
1.50 397,970 144,461 700,000
2.00 290,347 107,623 600,000
2.50 209,599 80,747 500,000
3.00 148,253 61,346 400,000
3.50 100,630 47,623 300,000
4.00 62,322 38,308 200,000
4.50 29,783 32,539 100,000
5.00 0 29,783 0
AC optimal E[cost] = $ 945,216 sd = $ 723,822 U = E + lam*Var = $ 1,469,134
TWAP E[cost] = $ 675,000 sd = $ 1,134,047 U = E + lam*Var = $ 1,961,063
Monte Carlo cross-check (4000 paths, seed 0): mean = $940,159 (closed form $945,216,
stderr $11,481), sd = $726,095 (closed form $723,822)
Note the trade-off doing its job: the optimal schedule pays $270k more in expected impact
cost to cut the standard deviation of the outcome by $410k — that exchange rate is exactly
what λ prices. optexec frontier traces the whole curve and writes reports/frontier.png
and reports/trajectories.png.
The selling point. Fix the true parameters (γ*, η*, σ*), build the "optimal" schedule with an
estimated η̂ spanning η*/5 … 5η*, score it under the truth, and compare with TWAP. Real
output from optexec misspec:
lambda AC edge at truth (% of TWAP U) break-even eta_hat/eta* worst grid penalty (%)
1e-08 0.01321 0.5094 0.2374
1e-07 1.048 0.4707 8.974
1e-06 25.08 0.2062 1.033
1e-05 76.83 NaN -66.13
Three honest findings, visible in reports/misspec.png:
- At low urgency, optimality buys you almost nothing. At λ = 1e-8 the AC schedule with perfect parameters beats TWAP by 0.013% of utility. That's the entire prize for getting η exactly right — and underestimating η by barely 2× (break-even ratio 0.51) already makes the "optimal" schedule worse than TWAP. At λ = 1e-7 the prize is 1%, and the break-even is still only a 2.1× underestimate.
- At high urgency, AC's edge is real and robust. At λ = 1e-5 the edge is 77% of TWAP's utility and no η̂ in the 25× grid range loses to TWAP (break-even NaN). When variance genuinely hurts, even a badly parameterised urgency schedule beats the straight line.
- The risk is asymmetric. Over-estimating η only lowers κ and slides the schedule back toward TWAP — you converge to the benchmark from the winning side and never break even. Under-estimating η inflates κ, front-loads the program into expensive fast trading, and is the only way to lose. If you must be wrong about your impact, be wrong on the high side.
The practical reading: TWAP is not the naive baseline this literature treats it as — it is the λ→0 limit of the optimal family and nearly optimal whenever urgency is modest, at zero estimation risk. The AC machinery earns its complexity only when risk aversion is genuinely high, which is precisely when its parameter sensitivity is also safest.
model.py holds the parameters and the closed-form trajectory (with the exact discrete κ);
cost.py scores any liquidation schedule (E, Var, U = E + λVar) so every comparison in the
repo runs through identical formulas; schedules.py provides the benchmarks (TWAP, VWAP
U-shape, power-loaded); frontier.py sweeps λ; simulate.py is the Monte Carlo verifier;
misspec.py is the study; plots.py and cli.py are presentation. Tests pin every layer to
hand-computed numbers: a 2-period TWAP costed entirely on paper, the κ formula against a hand
calculation, closed-form E/Var against Monte Carlo, frontier monotonicity, and the
misspecification invariants (the true η is the grid minimum; over-estimation never loses).
This is the textbook single-asset model: linear impact, arithmetic prices, static schedules. A production build adds the pieces that don't belong in a demo — nonlinear (power-law) impact, dynamic re-optimisation as fills arrive, multi-asset portfolios with correlation, limit-order placement inside each interval, and impact estimated from your own fills.
Related repositories: walkforward-backtest is the honest strategy-evaluation harness; options-pricer cross-validates pricing models against each other; valuation-toolkit does DCF and comps.
Educational and demonstration software, run entirely on synthetic parameters. It is not investment advice, not a recommendation, and not a trading or execution system. Nothing here is warranted as fit for live trading, and the models implemented are — as the section above spells out — fragile in specific, measurable ways. Figures throughout are fictional.
Educational and analytical tooling only — not investment advice, not a trading system. Nothing here is a recommendation to buy or sell anything.
More at kayasolomon.tech.