Market Microstructure: Order Books & Execution Mechanics › Bid-Ask Spread and Liquidity
Quantitative FinanceAll chapters Market Microstructure: Order Books & Execution Mechanics › Bid-Ask Spread and LiquidityOne section · Sections in this chapter6 / 13 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
The bid-ask spread is perhaps the most fundamental indicator of market liquidity. It represents the cost of immediacy: the price you pay for instant execution versus patient limit orders.
Components of the Spread
Why does the spread exist at all? Why don't market makers simply quote the same price on both sides? The answer lies in the risks and costs that market makers face. They must be compensated for maintaining operations, holding inventory that may lose value, and trading against participants who possess superior information. The spread is this compensation.
The bid-ask spread compensates market makers for several costs and risks:
- Order processing costs: The fixed costs of maintaining market-making operations
- Inventory risk: The risk that prices move against the market maker's accumulated position
- Adverse selection: The risk of trading against informed traders who have superior information
Mathematically, we can decompose the total spread into these three components:
This decomposition provides insight into why spreads vary across different securities and market conditions. Let us examine each component:
- : total bid-ask spread, the observable difference between the best ask and best bid prices
- : order processing component (fixed costs). This includes the infrastructure costs of running a trading operation: technology, connectivity, regulatory compliance, and personnel. For highly automated market makers, this component is relatively small and stable.
- : inventory risk component, proportional to volatility and position size. When a market maker buys shares from a seller, they hold inventory that may decline in value before they can sell it. Higher volatility increases this risk, requiring wider spreads as compensation.
- : adverse selection component, which reflects the probability of trading against better-informed participants. If some traders have superior information about future prices, the market maker loses money on average when trading with them. To break even, the market maker must recover these losses from uninformed traders through wider spreads.
def decompose_spread_simple(
spread: float, volatility: float, volume: float, tick_size: float
) -> dict:
"""
Simplified spread decomposition model.
Based on Huang and Stoll (1997) approach.
"""
# Minimum spread is the tick size
min_spread = tick_size
# Adverse selection component increases with information asymmetry
# Proxy: higher for volatile, lower-volume stocks
adverse_selection = spread * 0.4 # typically 30-50% of spread
# Inventory component increases with volatility
inventory = spread * 0.3 * (volatility / 0.02) # scaled by typical vol
# Order processing is the residual
order_processing = max(min_spread, spread - adverse_selection - inventory)
return {
"total_spread": spread,
"adverse_selection": adverse_selection,
"inventory": inventory,
"order_processing": order_processing,
}
# Example for different stocks
stocks = [
{
"name": "Large Cap Tech",
"spread": 0.01,
"vol": 0.25,
"volume": 10_000_000,
},
{
"name": "Mid Cap Industrial",
"spread": 0.05,
"vol": 0.30,
"volume": 500_000,
},
{
"name": "Small Cap Biotech",
"spread": 0.15,
"vol": 0.50,
"volume": 100_000,
},
]
# Calculate decompositions
decompositions = []
tick_size = 0.01
for stock in stocks:
decomp = decompose_spread_simple(
stock["spread"], stock["vol"], stock["volume"], tick_size
)
decompositions.append((stock, decomp))Spread Decomposition by Stock Type ============================================================ Large Cap Tech Total Spread: $0.010 Adverse Selection: $0.004 (40%) Inventory Risk: $0.037 (375%) Order Processing: $0.010 (100%) Mid Cap Industrial Total Spread: $0.050 Adverse Selection: $0.020 (40%) Inventory Risk: $0.225 (450%) Order Processing: $0.010 (20%) Small Cap Biotech Total Spread: $0.150 Adverse Selection: $0.060 (40%) Inventory Risk: $1.125 (750%) Order Processing: $0.010 (7%)
The results show how market characteristics drive spread composition. For the Small Cap Biotech stock, adverse selection and inventory risks constitute a much larger portion of the spread compared to the Large Cap Tech stock, which reflects higher volatility and lower liquidity.
The adverse selection component is particularly important for you. When you have better information than the market, you benefit from the adverse selection cost that market makers embed in spreads. Conversely, when you're trading without an edge, you're paying this cost to informed traders.
Key Parameters
The key parameters for the spread decomposition model are:
- spread: The observed bid-ask spread in the market. This is the starting point for decomposition and represents the total cost of crossing from bid to ask.
- volatility: The volatility of the asset, which increases inventory risk. Higher volatility means greater potential for adverse price movements while the market maker holds inventory.
- volume: Trading volume, used here as a proxy for liquidity and adverse selection risk. Higher volume typically correlates with lower adverse selection since uninformed traders constitute a larger share of the flow.
- tick_size: The minimum price increment, acting as a lower bound for the spread. Regulatory requirements and exchange rules establish this minimum, which constrains how narrow spreads can become even for the most liquid securities.
Order Book Depth and Liquidity
Beyond the spread, the depth of the order book at various price levels indicates how much liquidity is available before prices move:
def calculate_liquidity_metrics(book: OrderBook, max_levels: int = 10) -> dict:
"""Calculate various liquidity metrics from an order book."""
bid_levels, ask_levels = book.get_book_snapshot(max_levels)
# Depth at best prices
best_bid_depth = bid_levels[0][1] if bid_levels else 0
best_ask_depth = ask_levels[0][1] if ask_levels else 0
# Total visible depth
total_bid_depth = sum(qty for _, qty in bid_levels)
total_ask_depth = sum(qty for _, qty in ask_levels)
# Book imbalance
total_depth = total_bid_depth + total_ask_depth
imbalance = (
(total_bid_depth - total_ask_depth) / total_depth
if total_depth > 0
else 0
)
# Depth-weighted spread
spread = book.get_spread()
weighted_spread = (
spread * (best_bid_depth + best_ask_depth) if spread else 0
)
return {
"spread": spread,
"best_bid_depth": best_bid_depth,
"best_ask_depth": best_ask_depth,
"total_bid_depth": total_bid_depth,
"total_ask_depth": total_ask_depth,
"book_imbalance": imbalance,
"midprice": book.get_midprice(),
}
# Create a more realistic order book
np.random.seed(123)
realistic_book = OrderBook("AAPL")
# Build with realistic depth profile (more at worse prices)
for i, price in enumerate([199.95, 199.90, 199.85, 199.80, 199.75]):
depth = int(np.random.normal(1000 + i * 500, 200))
for _ in range(np.random.randint(3, 8)):
realistic_book.add_limit_order(Side.BID, price, depth // 5)
for i, price in enumerate([200.00, 200.05, 200.10, 200.15, 200.20]):
depth = int(np.random.normal(1000 + i * 500, 200))
for _ in range(np.random.randint(3, 8)):
realistic_book.add_limit_order(Side.ASK, price, depth // 5)
metrics = calculate_liquidity_metrics(realistic_book)Order Book Liquidity Metrics ============================================= Midprice: $199.97 Spread: $0.05 (2.5 bps) Depth Analysis: Best Bid Depth: 1,092 shares Best Ask Depth: 728 shares Total Bid Depth (5 levels): 9,171 shares Total Ask Depth (5 levels): 9,438 shares Book Imbalance: -1.43%
Book imbalance (or Order Book Imbalance, OBI) is a commonly studied signal in market microstructure research. The positive imbalance calculated above indicates stronger buying pressure in our simulated book, as bid volume exceeds ask volume. This metric quantifies the disparity between buying and selling pressure in the order book and can be expressed mathematically as:
Understanding this formula requires examining what each term represents and why the specific mathematical form was chosen:
- : order book imbalance, ranging from -1 to +1. The Greek letter rho is commonly used to denote this imbalance measure. A value of +1 would indicate all visible liquidity is on the bid side (maximum buying pressure), while -1 indicates all liquidity is on the ask side (maximum selling pressure). A value of zero indicates perfect balance.
- : total volume at the best bid (or top levels). This measures the depth of buying interest in the market. Larger values suggest substantial demand at current prices.
- : total volume at the best ask (or top levels). This measures the depth of selling interest. Larger values suggest substantial supply at current prices.
The formula uses a normalized difference (dividing by the sum) rather than a simple difference because this produces a bounded measure that is comparable across securities with different typical order sizes. A stock with 10,000 shares on the bid and 8,000 on the ask has the same imbalance as a stock with 100 shares on the bid and 80 on the ask: both have .
A positive indicates stronger buying pressure (more bids), while a negative value suggests selling pressure. Empirically, order book imbalance tends to predict short-term price movements: an excess of bids relative to asks often precedes price increases.