A 10%-participation buy on a mid-cap costs 12.59 bps to execute, enough to eat most of any alpha under 25 bps. For a 50,000-share buy at 500,000 ADV, 12 bps spread, 1.8% daily vol, $45 reference price, and 50 ms latency with 20 ms jitter, the Execution Simulator returns permanent impact 5.69 bps, temporary impact 0.90 bps, half-spread 6.00 bps, total cost 12.59 bps, $2,833 on a $2.25M notional over a 390-minute participation window. Latency drift is 0.26 bps (worst-case 0.35 bps). The cure for that 12.59-bps drag is splitting the order across days, not lowering participation within the day.
TL;DR
Almgren-Chriss decomposition for the canonical order:
| Cost component | Bps | Dollar (on $2.25M) |
|---|---|---|
| Permanent impact (Almgren) | 5.69 | $1,281 |
| Temporary impact (linear) | 0.90 | $203 |
| Half-spread | 6.00 | $1,350 |
| Total | 12.59 | $2,833 |
| Latency drift (expected) | 0.26 | $59 |
| Latency drift (worst) | 0.35 | $79 |
Duration: 390 minutes (the full US trading session at 10% participation). The order is large enough to require the entire day to execute without paying premium-VWAP slippage above 10% participation. A 25-bps gross alpha on this trade nets to 25 − 12.59 = 12.41 bps after slippage, survivable but uncomfortable. A 15-bps gross alpha nets to 2.41 bps, which is below the noise floor of most measurement.
The square-root impact model
Almgren-Chriss permanent impact for a buy order:
permanent_bps ≈ k_perm × σ_daily × sqrt(order_size / ADV)
For the canonical input (σ_daily = 1.8%, order_size/ADV = 0.10):
sqrt(0.10) = 0.3162
permanent_bps = k_perm × 1.8% × 0.3162 = k_perm × 56.9 bps
The engine returns 5.69 bps, implying k_perm = 0.10 in this configuration. That is the canonical empirical Almgren-Chriss value for US large-cap names; mid-caps can run 0.12–0.18, which would lift the permanent impact to 6.8–10.2 bps. The engine uses a fixed k_perm by default; production calibration should fit k_perm to the buyer's own historical fills.
The square-root scaling is the load-bearing intuition. Doubling order size (from 50k to 100k shares at the same ADV) lifts permanent impact by √2 ≈ 1.41×, not 2×. Halving participation rate (from 10% to 5%) takes √0.5 ≈ 0.71× the impact, a 29% reduction for halving the rate. This sub-linearity is why splitting orders across days is more cost-effective than splitting across hours within a day.
The temporary-impact contribution
Temporary impact is linear in participation rate and represents the spread-widening effect of the buyer's own pressure on the offer side of the book. The engine returns 0.90 bps for 10% participation; at 5% participation it would drop to 0.45 bps; at 20% it would rise to 1.80 bps.
The linear scaling is fast, and small in absolute magnitude, relative to permanent impact. For a 10% participation order, temporary impact contributes only 7% of the total cost (0.90 of 12.59 bps). For 30%+ participation, temporary impact becomes the dominant non-spread cost.
For LLM-driven retail strategies that trade at sub-5% participation, temporary impact is negligible (~0.3-0.5 bps). At 10%+ participation it becomes material; at 30%+ it dominates. The defensive participation cap for retail is 5%; the defensive cap for institutional desks is 15-20%.
The half-spread floor
The half-spread (6.00 bps for a 12-bps quoted spread) is the unavoidable cost of crossing the spread on every fill. It is independent of order size and participation rate, every share crosses the spread once. For mid-cap names quoted at 10-15 bps, half-spread contributes ~50% of total cost.
The half-spread can be reduced (not eliminated) by:
- Passive execution, sit on the bid for buys, on the offer for sells. Adverse selection bites when the market moves against the passive order, but for non-event-driven flows it cuts the half-spread by 50-70%.
- Tighter venues, IEX, dark pools, or mid-point matching reduce effective spread on liquid names. Not all of a 50k order will route to the tighter venue; expect 30-50% of fills at midpoint.
- Order routing, broker smart order routers split between lit and dark; the dark fills get midpoint and the lit fills cross half-spread. The blended cost can be 60-70% of pure half-spread.
The engine assumes lit-market crossing on every fill (worst-case for half-spread). Production routing typically beats the engine's half-spread by 20-40%; the engine's 12.59 bps is a conservative upper bound.
The latency-drift contribution
50 ms median latency with 20 ms jitter contributes 0.26 bps expected drift (worst case 0.35 bps). For a 0.5-second slippage tolerance window the latency drift is roughly 0.5% of the daily-vol-in-window, small in absolute terms but non-trivial when the order is repeatedly chasing the offer through a trending session.
The engine returns latencyDriftBps as a probabilistic expectation across the participation period. The worst-case (latencyDriftWorstBps) is the 95th percentile under the engine's latency model. For a real-time strategy, this is the slippage the operator has to budget against; for an end-of-day strategy with batch-mode execution, latency drift is irrelevant.
The latency contribution is far smaller than the impact contribution at this order size. For larger orders (1M+ shares) at the same participation, latency drift can compound to 1-3 bps because the participation window stretches longer; the engine accommodates that scaling but the canonical 50k-share order has a 390-minute window that is short enough for latency to remain small.
The split-across-days arithmetic
If the gross alpha is 15 bps and the single-day cost is 12.59 bps, splitting the 50,000-share order across five days (10,000 shares/day, 2% participation per day):
new_participation = 2% per day
new_size_relative = 0.02
sqrt(0.02) = 0.1414
permanent_per_day = 0.10 × 1.8% × 0.1414 = 2.55 bps
permanent_5_days = 2.55 × 5 (linear in execution count, NOT in size)
= 12.74 bps (modestly worse than single-day!)
Wait, that arithmetic looks wrong. The split-across-days arithmetic depends on whether the impact is treated as a fixed cost per slice or scaled by total volume across the day. The standard model: the permanent impact of N independent slices of size S/N on independent days is the impact of a single slice times N, only if the market completely "resets" between days. In practice impact decays with a half-life on the order of 30-60 minutes; overnight effectively does reset, so the multi-day estimate sums per-day impact contributions.
But each per-day order is sqrt(size/ADV) = sqrt(0.02) = 0.141, vs single-day 0.316. So per-day impact is 0.141/0.316 = 45% of single-day. Five days × 45% = 225% of a single-day's permanent impact. The split is worse, not better. The right strategy is the opposite: concentrate the order on a high-liquidity day (earnings reaction, index rebalance day) when ADV is 2-3× normal, dropping the size-relative ratio and the impact accordingly.
This counter-intuitive result is the classic Almgren-Chriss finding: when impact is permanent and recovers slowly, splitting across days saves nothing. The optimization is to find high-ADV windows (event days, market-on-close cross days) and route execution to those windows. For retail LLM-driven strategies this is impractical at small sizes; for institutional desks the alpha is in the routing schedule, not the prediction model.
Where the engine breaks
The engine assumes constant ADV across the participation window. Real intraday volume profiles are U-shaped (heavy at open and close, light midday). Executing 50k shares at 10% participation across the full session means the morning slice has high participation share of low volume (heavy impact) and the midday slice has low participation share of low volume (lower impact than the engine's average). The engine's flat assumption over-states midday cost and under-states morning/afternoon cost.
The engine also does not model adverse selection, the systematic drift of the price against a passive order. For passive execution this can add 1-3 bps to the half-spread savings; for aggressive execution it is built into the temporary-impact term. Production calibration should fit the engine's k_perm to the buyer's blended (passive + aggressive) fill history.
The engine assumes the order is the buyer's only flow that day. For a multi-name basket that creates incidental liquidity in correlated names, the impact for any single line is overstated. Multi-line baskets get a 10-20% impact discount in production estimators; the engine does not model this.
Connects to
- Execution Simulation: Slippage and Impact — the broader Almgren-Chriss treatment with the math derivation.
- Retail PnL vs Backtest: Eight Mechanisms — slippage is the largest of the eight backtest-vs-live gaps.
- Real-Time vs End-of-Day Trading Systems — when latency drift matters and when it doesn't.
- Execution Simulator — engine endpoint.
- Order Book Replay — companion for tick-level fill-quality analysis.
- Statistical Arbitrage Capacity — capacity calculation that consumes the impact output.
References
- Almgren, R., & Chriss, N. (2001). "Optimal Execution of Portfolio Transactions." Journal of Risk 3(2), 5–39. The canonical impact-cost model implemented by the engine. https://www.smallake.kr/wp-content/uploads/2016/03/optliq.pdf
- Almgren, R. (2003). "Optimal Execution with Nonlinear Impact Functions and Trading-Enhanced Risk." Applied Mathematical Finance 10(1), 1–18.
- Lillo, F., Farmer, J. D., & Mantegna, R. N. (2003). "Master Curve for Price-Impact Function." Nature 421, 129–130. https://www.nature.com/articles/421129a. Empirical evidence for the square-root law.
- Kissell, R. (2013). The Science of Algorithmic Trading and Portfolio Management. Academic Press. Practitioner-oriented treatment of impact modeling and calibration.
- Bouchaud, J. P., Bonart, J., Donier, J., & Gould, M. (2018). Trades, Quotes and Prices: Financial Markets Under the Microscope. Cambridge UP. The contemporary survey of market microstructure including impact-decay dynamics.
Verified engine output
Show the recompute-verified inputs and outputs
| side | buy |
|---|---|
| order_size | 50000 |
| adv | 500000 |
| spread_bps | 12 |
| daily_vol_pct | 1.8 |
| participation_pct | 10 |
| latency_ms | 50 |
| latency_jitter_ms | 20 |
| ref_price | 45 |
| side | buy |
|---|---|
| permanent bps | 5.692099788303084 |
| temporary bps | 0.9000000000000002 |
| half spread bps | 6 |
| total bps | 12.592099788303084 |
| total dollars | 2833.222452368194 |
| notional usd | 2250000 |
| duration minutes | 390 |
| latency drift bps | 0.2631174057921088 |
| latency drift worst bps | 0.3530090432487313 |
Computed live at build time.
Frequently asked questions
- Why does splitting across days make permanent impact worse?
- Almgren-Chriss impact is sub-linear in size (square root) but linear in execution events. A 5-day split has 5× the events with 45% impact each = 225% of single-day impact. Find high-ADV days instead.
- Is the 12.59-bps total cost typical for mid-caps?
- Yes — for 12-bps spread, 1.8% daily vol, 10% participation. Tight-spread large-caps run 6-8 bps total; illiquid small-caps run 35-50 bps.
- What participation rate is safe for retail?
- 5% is the conservative floor; 1-2% is the institutional VWAP-quality target. Above 10%, market-making counter-flow widens effective spread.
- Should I net latency drift against permanent impact?
- No, they are independent contributors. Latency drift is timing slippage during the execution window; permanent impact is the market re-rate from the order.
- Can the engine model a sell order?
- Yes via the side parameter. The math is symmetric in expectation; small differences arise from spread asymmetry and adverse-selection direction.