Market Microstructure: Order Books & Execution Mechanics › Order Types
Quantitative FinanceAll chapters Market Microstructure: Order Books & Execution Mechanics › Order TypesOne section · Sections in this chapter3 / 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
Order-type selection directly affects how a trading strategy executes. Each order type serves a specific purpose and carries different execution characteristics and risks.
Market Orders
The most fundamental question a trader faces is whether to trade now or wait. Market orders represent the choice to trade immediately, accepting whatever price the market offers in exchange for certain execution. This trade-off between price and certainty lies at the heart of all order type decisions.
A market order is an instruction to buy or sell immediately at the best available price. Market orders guarantee execution (in liquid markets) but not the execution price.
Market orders are the simplest order type: you want to trade now, regardless of price. When you submit a market buy order, it executes against the best available ask prices in the order book, consuming liquidity until your order is filled. The execution process follows the order book's price levels sequentially: your order first takes all available shares at the best ask, then moves to the next price level if more shares are needed, and continues until completely filled.
The key characteristics of market orders include:
- Immediate execution: Your order fills right away against standing limit orders
- Price uncertainty: The execution price depends on available liquidity
- Liquidity consumption: You remove liquidity from the order book, typically paying a fee
- Slippage risk: Large orders may walk through multiple price levels
The slippage risk deserves particular attention. When your order size exceeds the available quantity at the best price, you experience slippage: the average execution price differs from the initial best price. This is not a market malfunction; it is the natural consequence of consuming multiple price levels to complete a large order.
Let's implement market order execution in our order book:
def execute_market_order(
book: OrderBook, side: Side, quantity: int
) -> List[Tuple[float, int]]:
"""
Execute a market order against the book.
Returns list of (price, quantity) fills.
"""
fills = []
remaining = quantity
# Determine which side of the book to hit
if side == Side.BID: # Buying - hit the asks
price_levels = sorted(book.asks.keys())
target_book = book.asks
else: # Selling - hit the bids
price_levels = sorted(book.bids.keys(), reverse=True)
target_book = book.bids
for price in price_levels:
if remaining <= 0:
break
orders_at_price = target_book[price]
i = 0
while i < len(orders_at_price) and remaining > 0:
order = orders_at_price[i]
fill_qty = min(order.quantity, remaining)
fills.append((price, fill_qty))
remaining -= fill_qty
order.quantity -= fill_qty
if order.quantity == 0:
orders_at_price.pop(i)
else:
i += 1
# Clean up empty price levels
if not orders_at_price:
del target_book[price]
return fillsLet's see how a market order executes and potentially walks through multiple price levels:
# Create a fresh order book
book = OrderBook("XYZ")
# Build the order book with known quantities
book.add_limit_order(Side.ASK, 100.00, 500)
book.add_limit_order(Side.ASK, 100.00, 300)
book.add_limit_order(Side.ASK, 100.05, 400)
book.add_limit_order(Side.ASK, 100.10, 600)
book.add_limit_order(Side.BID, 99.95, 400)
book.add_limit_order(Side.BID, 99.90, 500)
# Capture state before execution
best_ask_before = book.get_best_ask()
# Execute a large market buy order
market_buy_qty = 1500
fills = execute_market_order(book, Side.BID, market_buy_qty)
# Calculate execution statistics
total_qty = sum(qty for _, qty in fills)
total_cost = sum(price * qty for price, qty in fills)
avg_price = total_cost / total_qty if total_qty > 0 else 0Market Buy Order: 1500 shares ======================================== Execution Fills: 500 shares @ $100.00 300 shares @ $100.00 400 shares @ $100.05 300 shares @ $100.10 Total Filled: 1,500 shares Average Price: $100.0333 Best Ask Before: $100.00 Slippage: $0.0333 per share
The market order walked through three price levels to fill completely. Starting at the best ask of $100.00, it consumed 800 shares, then moved to $100.05 for another 400 shares, and finally took 300 shares at $100.10. This price impact is exactly what we analyzed in the previous chapter on transaction costs.
Limit Orders
If market orders represent the choice to trade immediately, limit orders represent the opposite choice: patience in exchange for price control. A limit order says "I am willing to trade, but only at a price I consider acceptable." This patience comes with a cost, since there is no guarantee the market will ever reach your price.
A limit order is an instruction to buy at or below a specified price (limit buy) or sell at or above a specified price (limit sell). Limit orders guarantee the execution price but not execution itself.
Limit orders provide price protection at the cost of execution certainty. A limit buy order at $99.90 will only execute at $99.90 or better (lower). If the market never trades at that price, your order never fills. This creates a fundamental asymmetry between what you request and what you receive: you know with certainty the worst price you can get, but you cannot know whether you will get any price at all.
Key characteristics of limit orders include:
- Price certainty: You know the worst price you'll receive
- Execution uncertainty: Your order may never fill if the price doesn't reach your limit
- Liquidity provision: You add liquidity to the order book, often receiving a rebate
- Queue position: Your place in line at a price level affects fill probability
The concept of queue position is particularly important and often overlooked if you are new to trading. When you place a limit order at a price level where other orders already exist, you join a queue. Your order will not execute until all orders ahead of you at that price have been filled. This means that simply having an order at the "right" price is not enough; you must also arrive early enough to be near the front of the queue.
def add_limit_order_with_crossing(
book: OrderBook, side: Side, price: float, quantity: int
) -> Tuple[Order, List[Tuple[float, int]]]:
"""
Add a limit order that may cross the spread and execute immediately.
Returns the order and any immediate fills.
"""
fills = []
remaining = quantity
# Check if limit order crosses the spread
if side == Side.BID:
best_ask = book.get_best_ask()
# Limit buy that crosses: price >= best ask
if best_ask is not None and price >= best_ask:
# Execute against asks up to limit price
ask_prices = sorted([p for p in book.asks.keys() if p <= price])
for ask_price in ask_prices:
if remaining <= 0:
break
orders = book.asks[ask_price]
i = 0
while i < len(orders) and remaining > 0:
order = orders[i]
fill_qty = min(order.quantity, remaining)
fills.append((ask_price, fill_qty))
remaining -= fill_qty
order.quantity -= fill_qty
if order.quantity == 0:
orders.pop(i)
else:
i += 1
if not orders:
del book.asks[ask_price]
else: # Sell side
best_bid = book.get_best_bid()
# Limit sell that crosses: price <= best bid
if best_bid is not None and price <= best_bid:
bid_prices = sorted(
[p for p in book.bids.keys() if p >= price], reverse=True
)
for bid_price in bid_prices:
if remaining <= 0:
break
orders = book.bids[bid_price]
i = 0
while i < len(orders) and remaining > 0:
order = orders[i]
fill_qty = min(order.quantity, remaining)
fills.append((bid_price, fill_qty))
remaining -= fill_qty
order.quantity -= fill_qty
if order.quantity == 0:
orders.pop(i)
else:
i += 1
if not orders:
del book.bids[bid_price]
# Add remaining quantity to book
new_order = None
if remaining > 0:
book.order_id_counter += 1
new_order = Order(
book.order_id_counter, side, price, remaining, book.current_time
)
book.current_time += 0.001
if side == Side.BID:
book.bids[price].append(new_order)
else:
book.asks[price].append(new_order)
return new_order, fillsStop Orders
While market and limit orders express immediate trading intentions, stop orders represent contingent instructions: "If the market moves to a certain level, then I want to trade." This conditional nature makes stop orders essential tools for risk management, allowing traders to automate their response to adverse price movements.
A stop order (or stop-loss order) becomes active only when the market price reaches a specified trigger price. Once triggered, it converts to either a market order (stop-market) or a limit order (stop-limit).
Stop orders are used primarily for risk management: to limit losses or protect profits. A stop-loss sell order at $95 for a stock you bought at $100 will trigger if the price drops to $95 potentially limiting your loss. The order remains dormant until the trigger condition is met, at which point it activates and enters the normal order flow.
The mechanics work as follows:
- Stop-market order: Trigger price reached → converts to market order → immediate execution at available prices
- Stop-limit order: Trigger price reached → converts to limit order → executes only at limit price or better
The danger with stop-market orders is that in fast-moving markets, the execution price can be significantly worse than the stop price. Consider a gap opening: if a stock closes at $97 and opens the next day at $92 due to overnight news, a stop-market order with a $95 trigger will execute at approximately $92 far below the intended protection level. Stop-limit orders provide price protection but may not execute if the market gaps through your limit, leaving you with no protection at all.
Stop-Limit Example
Consider a trader holding shares purchased at $100 who wants downside protection:
import numpy as np
import pandas as pd
## Illustrative example of stop order scenarios
scenarios = pd.DataFrame(
{
"Order Type": [
"Stop-Market Sell",
"Stop-Limit Sell",
"Stop-Market Sell",
],
"Stop Price": [95.00, 95.00, 95.00],
"Limit Price": [np.nan, 94.50, np.nan],
"Market Scenario": [
"Normal decline",
"Normal decline",
"Gap down opening",
],
"Trigger": ["Price hits $95", "Price hits $95", "Opens at $92"],
"Execution Price": [94.98, 94.52, 91.85],
"Comment": ["Small slippage", "Within limit", "Major slippage"],
}
)Stop Order Execution Scenarios ================================================================================ Stop-Market Sell @ $95.00 Scenario: Normal decline Trigger: Price hits $95 Execution: $94.98 Note: Small slippage Stop-Limit Sell @ $95.00 (limit: $94.50) Scenario: Normal decline Trigger: Price hits $95 Execution: $94.52 Note: Within limit Stop-Market Sell @ $95.00 Scenario: Gap down opening Trigger: Opens at $92 Execution: $91.85 Note: Major slippage