Avellaneda-Stoikov Market Making: The Reservation Price and Inventory Risk
Market making looks simple: quote both sides, capture the spread. The hard part is that inventory accumulates, prices move, and tight quotes get run over. Avellaneda-Stoikov (2008) solves the trade-off with two formulas: the reservation price and the optimal spread.
Market making is the cleanest strategy in finance in theory and the hardest in practice. The idea is simple: post bids and asks, capture the spread, manage inventory. The reality: your fills are random, the price moves, and inventory piles up in exactly the direction you don’t want. Every second, a market maker is answering two questions — where to center the quotes and how wide to open the spread — and the two answers are tangled together.
The modern mathematical treatment is Avellaneda-Stoikov (2008). Its elegance is that it cleanly decouples those two questions: the quote center comes from one formula — the reservation price — and the spread from another, each with its own economics. It’s the standard reference for academic market making and the skeleton of many quantitative market-making strategies. Harris covers market making in Ch. 13, with extensive discussion of dealer behavior. Below we derive the model and both formulas, then get it to the trading floor.
The Market Maker’s Problem
You make markets in asset . You post a bid and an ask , with midprice and half-spread .
Flow is price-sensitive: buyers arrive at rate (the less aggressive your bid, the longer you wait) and sellers at rate (likewise). When someone hits your bid, you buy and inventory rises; when someone lifts your ask, you sell and inventory falls. You hold inventory (positive = long, negative = short). In the simplest version the midprice is a driftless Brownian motion:
The objective is to maximize expected utility of terminal wealth. Avellaneda-Stoikov use exponential utility:
where is terminal cash, is the terminal inventory marked at mid, and is risk aversion. Exponential utility isn’t a casual choice: it makes the optimal policy independent of the wealth level, which is what makes the problem tractable. Variants add a running inventory penalty; the structure survives.
Note what’s not in this setup: informed traders. The price is exogenous, and flow responds only to your quote distance. That absence will matter when we compare with Glosten-Milgrom below.
The Avellaneda-Stoikov Solution
Solving the HJB equation for the value function — the optimal continuation value at inventory , midprice , time — gives quotes with the structure:
The quotes no longer straddle the midprice; they straddle a new center, — the reservation price. That is the decoupling promised above: first choose the center, then the width.
The reservation price is the price at which the market maker is indifferent between buying and selling:
where is current inventory, risk aversion, midprice volatility, and time remaining. Read its direction: when you’re long (), the reservation price sits below the mid — you’ll sell a little cheap (you want the inventory gone) and demand an even lower price to buy more. Short, the reverse. The shift scales with volatility and time remaining: the harder the position can hurt you and the longer you’d have to carry it, the further you drag the whole quote structure.
The width half of the answer is the optimal half-spread:
where is the elasticity of arrival rates to quote distance (the in the flow model ). In the limit (a very liquid market with highly price-sensitive flow):
The two terms carry different economics and are worth reading separately.
The first, , is the price of inventory risk. It widens with risk aversion, with volatility, and with time remaining. The logic: holding inventory in a volatile market is risky, and the spread is the compensation you charge for it. Widen the spread and your fill probability drops — less flow — but each fill earns more. The formula sits at the optimum of exactly that trade-off.
The second, , is the base spread: the width you’d quote even at zero inventory. It’s priced by flow elasticity — large (flow highly price-sensitive, liquid markets) means you can’t extract much and the base spread is tight; small (inelastic flow) lets you charge more. Empirically is larger in liquid markets; crypto majors have higher than small-caps. One thing worth being precise about: inside the AS model this term comes from flow elasticity, not adverse selection — there are no informed traders in the model at all. Adverse selection is real, but it’s an account you settle outside the model; we come back to it below.
Putting It Together
Assembling reservation price and half-spread, the optimal quotes are:
With the quotes are symmetric around the mid. With (long), the whole structure shifts down: both bid and ask sit lower, and you’re coaxing the market to buy from you and discouraging it from selling to you. With it shifts up. Pile on enough inventory and your ask drops below the midprice — not a bug, but the model forcing you out of the position.
A Worked Example (with a Units Trap)
Step on the trap first. Take parameters that merely “look reasonable”: , . Base spread = — sixty-five cents per side? On a $100 stock that's a 130 bp full spread, absurdly wide. The arithmetic is fine; the mistake is treating $\gamma\kappa$as dimensionless. Both carry units (1/price) and must be consistent with$\sigma^2$(price²/time) and$T-t$ (time). With mismatched units this model’s quotes can be off by orders of magnitude — that’s check number one whenever you use AS.
Now with units squared away. Stock at , daily vol 2% (so dollars/√day, ), remaining horizon days (about 40 minutes), (per dollar), (per dollar), inventory shares long.
Reservation shift: dollars, so .
Half-spread: the inventory component is negligible; the base component dollars. Half-spread .
Quotes: bid , ask .
Look at what happened: your ask of $99.90 sits below the $100.00 midprice. Five hundred shares of long inventory make the model prefer “losing” 10 cents to the mid on the way out over holding the position and betting on direction. That is the entire point of the reservation-price mechanism — the quote center follows inventory, the spread only sets width. For calibration feel: once (the per-share quote-shift slope) is set sensibly, active market makers’ total spreads typically land between 1 bp and 100 bps, depending on the asset’s volatility, the desk’s risk tolerance, and the horizon.
Where Glosten-Milgrom and Ho-Stoll Fit
The modern view splits market making into two orthogonal problems: adverse selection (does my counterparty know more than I do?) and inventory management (how do I control position risk?). Glosten-Milgrom (article 4) is the canonical model of the first: a dealer quotes static bid and ask, the spread purely compensates for being picked off by informed traders, and inventory doesn’t exist. Ho-Stoll (1981) and Avellaneda-Stoikov are models of the second: spread and quote skew manage inventory risk, and informed traders don’t exist.
Set in AS and every inventory term vanishes, leaving only the base spread — structurally echoing Glosten-Milgrom’s central insight that a spread survives even at zero inventory, though the economics differ: GM’s spread is an information rent, AS’s base spread is a flow-elasticity rent. A real market maker collects both rents: AS hands you the formula for the inventory half, and the adverse-selection half has to be patched in from GM’s logic outside the model. The two models aren’t competitors; they’re two lines of the same bill.
Add Almgren-Chriss (article 9) and the picture completes: optimal execution is to a one-sided parent order what Avellaneda-Stoikov is to two-sided quoting — down to the parallel role of the risk-aversion parameter. Understand all three and you have the skeleton of modern microstructure.
From Model to Production: The Missing Pieces
The AS quotes are a starting point, not a destination. A real high-frequency market maker has to settle at least these additional accounts on top of it.
Queue position. The model assumes posting means filling; in reality you’re at the back of the queue, so the good fills don’t reach you and the bad ones (price about to trade through you) can’t be escaped. Harris Ch. 13 and article 1 cover this. Quote sniping. HFTs detect your stale quotes and trade against them — not “informed” in any fundamental sense, just statistical arbitrage on quote changes, but to your ledger it looks exactly like adverse selection. Latency. The model is continuous-time; you have millisecond-scale latency and your cancels are always half a beat late. The tick grid. Model prices are continuous; real quotes must land on discrete ticks (one cent in US equities; in crypto it varies by venue and pair), and the rounding itself changes your queue fate. Borrow costs. Short inventory pays borrow fees, which belongs in the quotes on the negative-inventory side. Fees and rebates. Maker-taker schedules add to or subtract from every fill.
A production market-making system stacks hundreds of such adjustments on the AS skeleton. The core formulas give you the structure; the rest is engineering.
Calibration and Implementation
Three parameters need estimating to run the model. : realized volatility of the midprice, on a 1–5 minute rolling window. : from the relationship between quote distance and fill intensity (regressions on quote-update data). cannot be estimated from data — it is your risk preference, set by the trading mandate, commonly ranging from 1 to in model units. Set it too small and your quotes are tight enough to get run over; too large and you’re too wide to see flow. In volatile regimes, either let the estimate catch up fast or raise outright — don’t fight a crash with calm-market parameters, because every input to this model is a function of the regime.
The core logic fits in a few lines:
import numpy as np
def avellaneda_stoikov_quotes(mid, inventory, sigma, T_remaining,
gamma=1.0, kappa=1.0):
"""
Compute optimal bid/ask quotes per Avellaneda-Stoikov.
mid: current midprice
inventory: current inventory (positive = long)
sigma: per-period volatility of midprice
T_remaining: time remaining in horizon (in same units as sigma)
gamma: risk aversion
kappa: arrival rate elasticity
"""
reservation = mid - inventory * gamma * sigma**2 * T_remaining
half_spread = (gamma * sigma**2 * T_remaining) / 2 + \
(1 / gamma) * np.log(1 + gamma / kappa)
return reservation - half_spread, reservation + half_spread
Real implementations add tick rounding, queue-position adjustments, and inventory limits — that last one deserves emphasis: the model does not constrain inventory, it only nudges you gently back toward . A real desk’s hard limits must be bolted on outside the model; don’t expect the skew term to stop a one-sided pile-up in an extreme tape.
Arithmion packages this pipeline. arithmion.marketmaking.AvellanedaStoikov provides real-time quote computation with calibrated parameters, inventory tracking and skew, per-fill P&L attribution, backtesting on real L2 data, and extensions for multi-asset books, jump-diffusion midprices, and borrow costs; the MarketMaker module handles calibration — you supply historical L2 data, it estimates and and helps you choose . The whole chain plugs into arithmion.execution and arithmion.tca for full performance analysis.
What to Actually Watch When Trading It
A few field notes worth taping to the monitor when the model becomes a daily tool. The reservation price is your anchor: when inventory is heavy, the quote center should move, and resisting the skew is betting against the model on direction. The half-spread is your risk budget: a tick tighter means more fills and more risk. If you follow the model honestly, inventory mean-reverts toward zero — and conversely, inventory that won’t return to zero means something outside the model (latency, toxic flow, parameter drift) is leaking. The most important note: AS does not detect adverse selection. Even with the right spread posted, you will still lose to informed traders; monitor your realized spread at multiple horizons, and if it’s negative you have a toxicity problem that needs a signal from outside the model (and the discipline to stop quoting when flow turns toxic).
Harris Ch. 13 has the full practitioner view, the Avellaneda-Stoikov paper is the formal reference, and Cartea-Jaimungal-Penalva (2015) organizes the model and its many extensions into textbook form.
References
- Harris, L. Trading and Exchanges: Market Microstructure for Practitioners. Oxford University Press, 2003. Ch. 13 (dealers and market makers). ISBN: 978-0-19-514470-3.
- Avellaneda, M., Stoikov, S. “High-Frequency Trading in a Limit Order Book.” Quantitative Finance, 2008.
- Glosten, L., Milgrom, P. “Bid, Ask and Transaction Prices in a Specialist Market with Heterogeneously Informed Traders.” Journal of Financial Economics, 1985.
- Ho, T., Stoll, H. “Optimal Dealer Pricing Under Transactions and Return Uncertainty.” Journal of Financial Economics, 1981.
- Cartea, A., Jaimungal, S., Penalva, J. Algorithmic and High-Frequency Trading. Cambridge University Press, 2015.