Basic Market Maker
Build a foundational market making engine that continuously streams two-sided bid and ask limit order quotes around the prevailing mid-market price with a configurable spread percentage, managing basic inventory accumulation risk and tracking profitability from spread capture over time.
Basic Market Making Engine
A market making engine provides liquidity to an exchange by continuously placing and managing limit orders on both sides of the order book. The market maker profits from the bid-ask spread while earning rebates from exchanges that offer maker fees.
This engine implements fundamental market making strategies including spread-based pricing, inventory management, order lifecycle tracking, and PnL calculation.
Core Market Making Concepts
| Concept | Description |
|---|---|
| Bid-Ask Spread | The price difference between buy and sell orders |
| Mid Price | (Best Bid + Best Ask) / 2 |
| Inventory Risk | Exposure from holding an unbalanced position |
| Skewing | Adjusting prices to reduce inventory |
| Maker Rebate | Fee credit for providing liquidity |
Key Components
- Order placement and cancellation management
- Inventory tracking and risk limits
- Dynamic spread adjustment based on volatility
- Price skewing for inventory control
- Performance metrics (spread capture, inventory turns)
1. Dependency Installation
# requests: HTTP client library for making exchange API calls
# pandas: Data manipulation and analysis for reporting
# numpy: Numerical computations for statistical calculations
!pip install requests pandas numpyRequirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (2.32.4) Requirement already satisfied: pandas in /usr/local/lib/python3.12/dist-packages (2.2.2) Requirement already satisfied: numpy in /usr/local/lib/python3.12/dist-packages (2.0.2) Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests) (3.4.7) Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.12/dist-packages (from requests) (3.18) Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests) (2.5.0) Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.12/dist-packages (from requests) (2026.5.20) Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas) (2.9.0.post0) Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas) (2025.2) Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas) (2026.2) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)
2. Library Imports
import warnings
warnings.filterwarnings("ignore")
# Standard library imports
import time
import random
import logging
import sys
import json
from typing import Dict, List, Optional, Tuple, Any
from collections import deque
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from enum import Enum
# Third-party imports
import pandas as pd
import numpy as np
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)-8s | %(message)s",
datefmt="%H:%M:%S",
stream=sys.stdout,
)
logger = logging.getLogger("market_maker")3. Order Types and State Definitions
def create_order(
order_id: str,
symbol: str,
side: str,
price: float,
quantity: float,
order_type: str = "LIMIT",
) -> dict:
"""
Creates a standardized order dictionary.
Parameters
----------
order_id : str
Unique identifier for the order.
symbol : str
Trading pair symbol (e.g., 'BTCUSDT').
side : str
Order side - 'BUY' or 'SELL'.
price : float
Order price.
quantity : float
Order quantity.
order_type : str, optional
Order type, defaults to 'LIMIT'.
Returns
-------
dict
Standardized order representation.
"""
return {
"order_id": order_id,
"symbol": symbol,
"side": side,
"price": price,
"quantity": quantity,
"order_type": order_type,
"status": "ACTIVE",
"created_at": time.time(),
"filled_quantity": 0.0,
"avg_fill_price": 0.0,
}create_order Function
This function is responsible for creating a standardized dictionary representation of a limit order. It takes details like order_id, symbol, side (BUY/SELL), price, and quantity, and initializes the order with a default LIMIT type and ACTIVE status. It also records the creation timestamp and sets initial values for filled_quantity and avg_fill_price to zero.
create_market_state function
def create_market_state(symbol: str) -> dict:
"""
Initializes the market state tracking structure.
Parameters
----------
symbol : str
Trading pair symbol.
Returns
-------
dict
Market state including best bid/ask, mid price, and spread.
"""
return {
"symbol": symbol,
"best_bid": 0.0,
"best_ask": 0.0,
"mid_price": 0.0,
"spread": 0.0,
"spread_bps": 0.0,
"timestamp": 0.0,
"bid_depth": [], # List of (price, quantity) for bids
"ask_depth": [], # List of (price, quantity) for asks
}create_market_state Function
This function initializes a dictionary to track the current state of the market for a given symbol. It sets up fields for best_bid, best_ask, mid_price, spread, and spread_bps (spread in basis points), all initially to zero. It also includes bid_depth and ask_depth lists to store the top levels of the order book, and a timestamp to record when the state was last updated.
create_inventory_state function
def create_inventory_state(
max_position: float = 1.0,
target_position: float = 0.0,
max_inventory_skew: float = 0.5,
) -> dict:
"""
Initializes inventory tracking for market making.
Parameters
----------
max_position : float, optional
Maximum allowed absolute position size.
target_position : float, optional
Target inventory level (default 0 for neutral).
max_inventory_skew : float, optional
Maximum price skew percentage due to inventory.
Returns
-------
dict
Inventory state including current position and risk metrics.
"""
return {
"current_position": 0.0,
"max_position": max_position,
"target_position": target_position,
"max_inventory_skew": max_inventory_skew,
"total_buy_volume": 0.0,
"total_sell_volume": 0.0,
"realized_pnl": 0.0,
"unrealized_pnl": 0.0,
"avg_buy_price": 0.0,
"avg_sell_price": 0.0,
"position_age": 0.0,
"last_update_time": time.time(),
}create_inventory_state Function
This function initializes the inventory tracking system for the market maker. It sets the current_position, max_position (the maximum allowed absolute position size), target_position (defaulting to zero for a neutral target), and max_inventory_skew. It also tracks total_buy_volume, total_sell_volume, realized_pnl, unrealized_pnl, and average buy/sell prices, along with position age and last update time for inventory management.
create_performance_tracker function
def create_performance_tracker() -> dict:
"""
Initializes performance tracking metrics.
Returns
-------
dict
Performance tracking structure with trade history and metrics.
"""
return {
"total_trades": 0,
"total_buy_trades": 0,
"total_sell_trades": 0,
"total_volume": 0.0,
"gross_spread_captured": 0.0,
"net_pnl": 0.0,
"trade_history": [], # List of trade events
"order_events": [], # List of order placement/cancel events
"inventory_snapshots": [], # Periodic inventory records
"start_time": time.time(),
}create_performance_tracker Function
This function initializes a dictionary to record the market maker's performance metrics throughout the simulation. It includes counters for total_trades, buy_trades, sell_trades, and total_volume. It also keeps track of gross_spread_captured and net_pnl. Crucially, it stores detailed trade_history, order_events (placements/cancellations), and periodic inventory_snapshots to enable comprehensive post-simulation analysis.
4. Order Book Management
def update_market_state(
market_state: dict,
bids: List[Tuple[float, float]],
asks: List[Tuple[float, float]],
) -> dict:
"""
Updates market state with current order book data.
Parameters
----------
market_state : dict
Current market state to update.
bids : list of tuples
List of (price, quantity) for bid side.
asks : list of tuples
List of (price, quantity) for ask side.
Returns
-------
dict
Updated market state.
"""
if not bids or not asks:
return market_state
# Update best bid and ask
market_state["best_bid"] = bids[0][0] if bids else 0.0
market_state["best_ask"] = asks[0][0] if asks else 0.0
# Calculate mid price and spread
if market_state["best_bid"] > 0 and market_state["best_ask"] > 0:
market_state["mid_price"] = (market_state["best_bid"] + market_state["best_ask"]) / 2
market_state["spread"] = market_state["best_ask"] - market_state["best_bid"]
# Calculate spread in basis points
if market_state["mid_price"] > 0:
market_state["spread_bps"] = (market_state["spread"] / market_state["mid_price"]) * 10000
# Store full depth
market_state["bid_depth"] = bids[:10] # Store top 10 levels
market_state["ask_depth"] = asks[:10]
market_state["timestamp"] = time.time()
return market_stateupdate_market_state Function
This function is crucial for keeping the market maker informed about the current market conditions. It takes the existing market_state and new bids and asks (order book depth) to update the best bid, best ask, mid-price, and spread (both in absolute terms and basis points). It also stores the top 10 levels of the bid and ask depth and records the timestamp of the update. This function ensures the market maker has the latest view of the order book for decision-making.
calculate_optimal_spread function
def calculate_optimal_spread(
market_state: dict,
base_spread_bps: float = 2.0,
volatility: float = 0.0,
inventory_skew: float = 0.0,
min_spread_bps: float = 1.0,
max_spread_bps: float = 10.0,
) -> float:
"""
Calculates optimal spread based on market conditions and inventory.
Parameters
----------
market_state : dict
Current market state.
base_spread_bps : float, optional
Base spread in basis points.
volatility : float, optional
Current market volatility (standard deviation of returns).
inventory_skew : float, optional
Inventory skew factor (-1 to 1, negative = long inventory).
min_spread_bps : float, optional
Minimum allowed spread in basis points.
max_spread_bps : float, optional
Maximum allowed spread in basis points.
Returns
-------
float
Optimal spread in basis points.
"""
# Start with base spread
spread_bps = base_spread_bps
# Increase spread during high volatility
if volatility > 0:
volatility_adjustment = volatility * 100 # Convert to basis points
spread_bps += volatility_adjustment
# Adjust for inventory risk
# Positive skew (short inventory) -> increase ask spread, decrease bid spread
# Negative skew (long inventory) -> decrease ask spread, increase bid spread
inventory_adjustment = abs(inventory_skew) * base_spread_bps
spread_bps += inventory_adjustment
# Apply bounds
spread_bps = max(min_spread_bps, min(max_spread_bps, spread_bps))
return spread_bpscalculate_optimal_spread Function
This function determines the optimal bid-ask spread for the market maker. It starts with a base_spread_bps and adjusts it dynamically based on market volatility and the market maker's inventory_skew. Higher volatility or significant inventory imbalances can lead to a wider spread to mitigate risk. The function also enforces min_spread_bps and max_spread_bps to keep the spread within reasonable bounds, ensuring risk control and competitiveness.
calculate_skewed_prices function
def calculate_skewed_prices(
market_state: dict,
spread_bps: float,
inventory_skew: float,
) -> Tuple[float, float]:
"""
Calculates bid and ask prices with inventory-based skewing.
Parameters
----------
market_state : dict
Current market state with mid price.
spread_bps : float
Desired spread in basis points.
inventory_skew : float
Inventory skew factor (-1 to 1).
Returns
-------
tuple
(bid_price, ask_price) skewed for inventory management.
"""
mid_price = market_state["mid_price"]
if mid_price <= 0:
return 0.0, 0.0
# Calculate half spread in price terms
half_spread_price = (spread_bps / 10000) * mid_price / 2
# Calculate skew adjustment (moves prices asymmetrically)
# Negative skew (long inventory) -> move prices down to encourage selling
# Positive skew (short inventory) -> move prices up to encourage buying
skew_adjustment = inventory_skew * half_spread_price
# Calculate final bid and ask prices
bid_price = mid_price - half_spread_price + skew_adjustment
ask_price = mid_price + half_spread_price + skew_adjustment
# Round to appropriate precision (assuming 2 decimals for most crypto)
bid_price = round(bid_price, 2)
ask_price = round(ask_price, 2)
return bid_price, ask_pricecalculate_skewed_prices Function
This function calculates the final bid and ask prices after applying an inventory_skew. The goal of skewing is to encourage trades that help rebalance the market maker's inventory. If the market maker is holding too much of the base asset (long inventory), the prices will be skewed downwards to encourage selling. Conversely, if they are short, prices will be skewed upwards to encourage buying. This ensures the market maker actively manages their position risk.
5. Order Management
def generate_order_id(symbol: str, side: str) -> str:
"""
Generates a unique order ID.
Parameters
----------
symbol : str
Trading pair symbol.
side : str
Order side ('BUY' or 'SELL').
Returns
-------
str
Unique order identifier.
"""
timestamp = int(time.time() * 1000)
random_suffix = random.randint(1000, 9999)
return f"{symbol}_{side}_{timestamp}_{random_suffix}"generate_order_id Function
This simple utility function generates a unique identifier for each order. It combines the symbol, side (BUY/SELL), a timestamp, and a random suffix to create an ID that is highly unlikely to collide with other orders. Unique order IDs are critical for tracking and managing orders throughout their lifecycle, especially in a system where multiple orders might be placed simultaneously or in quick succession.
calculate_order_quantity function
def calculate_order_quantity(
inventory_state: dict,
side: str,
base_quantity: float,
max_quantity: float = None,
) -> float:
"""
Calculates order quantity based on inventory limits.
Parameters
----------
inventory_state : dict
Current inventory state.
side : str
Order side ('BUY' or 'SELL').
base_quantity : float
Base quantity to place.
max_quantity : float, optional
Maximum allowed quantity per order.
Returns
-------
float
Adjusted order quantity.
"""
current_pos = inventory_state["current_position"]
max_pos = inventory_state["max_position"]
# Calculate available capacity
if side == "BUY":
# Reduce buy quantity if near max long position
available_capacity = max_pos - current_pos
quantity = min(base_quantity, available_capacity)
else: # SELL
# Reduce sell quantity if near max short position
available_capacity = max_pos + current_pos # current_pos negative for shorts
quantity = min(base_quantity, available_capacity)
# Apply global max if specified
if max_quantity:
quantity = min(quantity, max_quantity)
# Ensure non-negative
quantity = max(0, quantity)
return quantitycalculate_order_quantity Function
This function determines the appropriate quantity for a new order, taking into account the market maker's current inventory_state and max_position limits. For a BUY order, it ensures the quantity does not exceed the remaining capacity to reach the maximum long position. For a SELL order, it considers the capacity to avoid exceeding the maximum short position. It also allows for an optional max_quantity per order and ensures the calculated quantity is always non-negative.
update_inventory_on_fill function
def update_inventory_on_fill(
inventory_state: dict,
side: str,
price: float,
quantity: float,
) -> dict:
"""
Updates inventory state when an order is filled.
Parameters
----------
inventory_state : dict
Current inventory state to update.
side : str
Fill side ('BUY' or 'SELL').
price : float
Fill price.
quantity : float
Fill quantity.
Returns
-------
dict
Updated inventory state.
"""
old_position = inventory_state["current_position"]
if side == "BUY":
# Update average buy price using weighted average
total_value = (inventory_state["avg_buy_price"] * inventory_state["total_buy_volume"]) + (price * quantity)
inventory_state["total_buy_volume"] += quantity
inventory_state["avg_buy_price"] = total_value / inventory_state["total_buy_volume"] if inventory_state["total_buy_volume"] > 0 else 0
inventory_state["current_position"] += quantity
# Check if we're reducing a short position
if old_position < 0:
# Calculate realized PnL from covering shorts
cover_quantity = min(quantity, abs(old_position))
if cover_quantity > 0 and inventory_state["avg_sell_price"] > 0:
pnl = (inventory_state["avg_sell_price"] - price) * cover_quantity
inventory_state["realized_pnl"] += pnl
else: # SELL
# Update average sell price
total_value = (inventory_state["avg_sell_price"] * inventory_state["total_sell_volume"]) + (price * quantity)
inventory_state["total_sell_volume"] += quantity
inventory_state["avg_sell_price"] = total_value / inventory_state["total_sell_volume"] if inventory_state["total_sell_volume"] > 0 else 0
inventory_state["current_position"] -= quantity
# Check if we're reducing a long position
if old_position > 0:
# Calculate realized PnL from selling longs
sell_quantity = min(quantity, old_position)
if sell_quantity > 0 and inventory_state["avg_buy_price"] > 0:
pnl = (price - inventory_state["avg_buy_price"]) * sell_quantity
inventory_state["realized_pnl"] += pnl
# Update position age
inventory_state["position_age"] = 0.0 if abs(inventory_state["current_position"]) < 0.001 else inventory_state["position_age"] + 0.1
inventory_state["last_update_time"] = time.time()
return inventory_stateupdate_inventory_on_fill Function
This function updates the market maker's inventory_state when an order is filled. It processes BUY and SELL fills, adjusting the current_position and updating the total_buy_volume/total_sell_volume and corresponding average prices (avg_buy_price/avg_sell_price). Crucially, it calculates and updates the realized_pnl when a position is closed or reduced, reflecting the profit or loss from completed trades. It also logs the fill event and updates position age.
calculate_inventory_skew function
def calculate_inventory_skew(inventory_state: dict) -> float:
"""
Calculates inventory skew factor for price adjustment.
Parameters
----------
inventory_state : dict
Current inventory state.
Returns
-------
float
Skew factor between -1 and 1.
Negative: long inventory (skew prices down)
Positive: short inventory (skew prices up)
"""
current_pos = inventory_state["current_position"]
max_pos = inventory_state["max_position"]
if max_pos <= 0:
return 0.0
# Calculate position ratio (-1 to 1)
pos_ratio = current_pos / max_pos
# Apply max skew limit
max_skew = inventory_state["max_inventory_skew"]
skew = max(-max_skew, min(max_skew, -pos_ratio * max_skew))
return skewcalculate_inventory_skew Function
This function calculates an inventory_skew factor, a value between -1 and 1, which represents the degree of imbalance in the market maker's inventory. A negative skew indicates a long position (holding more of the base asset), while a positive skew indicates a short position. This skew factor is then used to adjust (skew) the bid and ask prices, encouraging trades that help bring the inventory back towards the target_position (usually zero or neutral).
6. Market Making Engine Core
def create_market_maker(
symbol: str,
order_quantity: float = 0.01,
base_spread_bps: float = 2.0,
max_position: float = 0.1,
max_order_age_seconds: float = 30.0,
) -> dict:
"""
Initializes the main market making engine state.
Parameters
----------
symbol : str
Trading pair symbol.
order_quantity : float, optional
Base order quantity for each side.
base_spread_bps : float, optional
Base spread in basis points.
max_position : float, optional
Maximum allowed position size.
max_order_age_seconds : float, optional
Maximum time to keep orders alive.
Returns
-------
dict
Complete market maker state.
"""
return {
"symbol": symbol,
"order_quantity": order_quantity,
"base_spread_bps": base_spread_bps,
"max_order_age_seconds": max_order_age_seconds,
"is_running": False,
"active_orders": {}, # order_id -> order dict
"market_state": create_market_state(symbol),
"inventory": create_inventory_state(max_position=max_position),
"performance": create_performance_tracker(),
"last_quote_time": 0.0,
"quote_interval_seconds": 1.0,
}create_market_maker Function
This is the core initialization function for the entire market making engine. It sets up the market maker's operational parameters such as symbol, order_quantity, base_spread_bps, max_position, and max_order_age_seconds. It then integrates by creating and initializing the market_state, inventory_state, and performance_tracker components. It also maintains a dictionary of active_orders and tracks the last_quote_time and quote_interval_seconds.
update_volatility_estimate function
def update_volatility_estimate(
price_history: deque,
window_seconds: int = 60,
) -> float:
"""
Calculates volatility from recent price history.
Parameters
----------
price_history : deque
Deque of (timestamp, price) tuples.
window_seconds : int, optional
Time window for volatility calculation in seconds.
Returns
-------
float
Annualized volatility estimate.
"""
if len(price_history) < 10:
return 0.0
# Filter prices within window
current_time = time.time()
recent_prices = []
for ts, price in price_history:
if current_time - ts <= window_seconds:
recent_prices.append(price)
if len(recent_prices) < 10:
return 0.0
# Calculate returns
returns = []
for i in range(1, len(recent_prices)):
if recent_prices[i-1] > 0:
ret = (recent_prices[i] - recent_prices[i-1]) / recent_prices[i-1]
returns.append(ret)
if len(returns) < 2:
return 0.0
# Calculate volatility (standard deviation of returns)
vol = np.std(returns)
return volupdate_volatility_estimate Function
This function estimates market volatility based on a historical price_history (a deque of timestamp and price tuples). It extracts recent prices within a specified window_seconds, calculates the returns between consecutive prices, and then computes the standard deviation of these returns to provide a volatility measure. This volatility estimate can then be used by the calculate_optimal_spread function to dynamically adjust the spread, widening it during periods of higher volatility to manage risk.
cancel_order function
def cancel_order(engine: dict, order_id: str) -> bool:
"""
Cancels an active order.
Parameters
----------
engine : dict
Market maker engine state.
order_id : str
ID of order to cancel.
Returns
-------
bool
True if order was cancelled, False otherwise.
"""
if order_id in engine["active_orders"]:
order = engine["active_orders"][order_id]
order["status"] = "CANCELLED"
del engine["active_orders"][order_id]
# Record cancellation event
engine["performance"]["order_events"].append({
"timestamp": time.time(),
"event_type": "CANCEL",
"order_id": order_id,
"side": order["side"],
"price": order["price"],
})
logger.debug(f"Cancelled order {order_id}")
return True
return Falsecancel_order Function
This function handles the cancellation of a single active order. Given an order_id, it first checks if the order exists in the engine's active_orders. If found, it updates the order's status to CANCELLED, removes it from the active_orders dictionary, and records the cancellation event in the performance tracker. This ensures proper order lifecycle management and accurate record-keeping.
cancel_all_orders function
def cancel_all_orders(engine: dict) -> int:
"""
Cancels all active orders.
Parameters
----------
engine : dict
Market maker engine state.
Returns
-------
int
Number of orders cancelled.
"""
order_ids = list(engine["active_orders"].keys())
cancelled_count = 0
for order_id in order_ids:
if cancel_order(engine, order_id):
cancelled_count += 1
logger.info(f"Cancelled {cancelled_count} orders")
return cancelled_countcancel_all_orders Function
This function provides a mechanism to cancel all currently active_orders in the market maker's engine. It iterates through all the order_ids present in the active_orders dictionary and calls the cancel_order function for each one. This is particularly useful for rebalancing the order book or in situations where the market maker needs to quickly withdraw all existing liquidity, such as before placing new quotes or in response to a risk event.
place_orders function
def place_orders(
engine: dict,
bid_price: float,
ask_price: float,
) -> Tuple[List[dict], List[dict]]:
"""
Places bid and ask orders at specified prices.
Parameters
----------
engine : dict
Market maker engine state.
bid_price : float
Price for buy order.
ask_price : float.
ask_price : float
Price for sell order.
Returns
-------
tuple
(placed_bids, placed_asks) lists of order dictionaries.
"""
placed_bids = []
placed_asks = []
# Calculate order quantities based on inventory
bid_qty = calculate_order_quantity(
engine["inventory"],
"BUY",
engine["order_quantity"],
)
ask_qty = calculate_order_quantity(
engine["inventory"],
"SELL",
engine["order_quantity"],
)
# Place bid order if quantity > 0
if bid_qty > 0 and bid_price > 0:
order_id = generate_order_id(engine["symbol"], "BUY")
order = create_order(
order_id=order_id,
symbol=engine["symbol"],
side="BUY",
price=bid_price,
quantity=bid_qty,
)
engine["active_orders"][order_id] = order
placed_bids.append(order)
# Record placement event
engine["performance"]["order_events"].append({
"timestamp": time.time(),
"event_type": "PLACE",
"order_id": order_id,
"side": "BUY",
"price": bid_price,
"quantity": bid_qty,
})
# Place ask order if quantity > 0
if ask_qty > 0 and ask_price > 0:
order_id = generate_order_id(engine["symbol"], "SELL")
order = create_order(
order_id=order_id,
symbol=engine["symbol"],
side="SELL",
price=ask_price,
quantity=ask_qty,
)
engine["active_orders"][order_id] = order
placed_asks.append(order)
# Record placement event
engine["performance"]["order_events"].append({
"timestamp": time.time(),
"event_type": "PLACE",
"order_id": order_id,
"side": "SELL",
"price": ask_price,
"quantity": ask_qty,
})
if placed_bids or placed_asks:
logger.info(
f"Placed orders - Bid: {bid_price}@{bid_qty}, Ask: {ask_price}@{ask_qty}"
)
return placed_bids, placed_asksplace_orders Function
This function is responsible for placing new bid and ask orders on the simulated exchange. It first calculates the appropriate bid_qty and ask_qty using calculate_order_quantity, taking into account inventory limits. Then, if quantities are positive and prices are valid, it generates unique order_ids, creates order dictionaries using create_order, adds them to the engine's active_orders, and records the placement events in the performance tracker. It logs the placed orders for visibility.
7. Quote Generation and Management
def process_fill(
engine: dict,
order_id: str,
fill_price: float,
fill_quantity: float,
) -> dict:
"""
Processes a fill event for an order.
Parameters
----------
engine : dict
Market maker engine state.
order_id : str
ID of filled order.
fill_price : float
Price at which order was filled.
fill_quantity : float
Quantity filled.
Returns
-------
dict
Updated engine state.
"""
if order_id not in engine["active_orders"]:
logger.warning(f"Fill received for unknown order {order_id}")
return engine
order = engine["active_orders"][order_id]
# Update order fill status
order["filled_quantity"] += fill_quantity
total_value = (order["avg_fill_price"] * (order["filled_quantity"] - fill_quantity)) + (fill_price * fill_quantity)
order["avg_fill_price"] = total_value / order["filled_quantity"] if order["filled_quantity"] > 0 else 0
# Check if order is fully filled
if order["filled_quantity"] >= order["quantity"]:
order["status"] = "FILLED"
del engine["active_orders"][order_id]
# Update inventory
engine["inventory"] = update_inventory_on_fill(
engine["inventory"],
order["side"],
fill_price,
fill_quantity,
)
# Update performance metrics
perf = engine["performance"]
perf["total_trades"] += 1
if order["side"] == "BUY":
perf["total_buy_trades"] += 1
else:
perf["total_sell_trades"] += 1
perf["total_volume"] += fill_quantity
# Record trade
perf["trade_history"].append({
"timestamp": time.time(),
"order_id": order_id,
"side": order["side"],
"price": fill_price,
"quantity": fill_quantity,
"position_after": engine["inventory"]["current_position"],
"realized_pnl": engine["inventory"]["realized_pnl"],
})
logger.info(
f"FILL: {order['side']} {fill_quantity} @ {fill_price} | "
f"Position: {engine['inventory']['current_position']:.4f} | "
f"PnL: {engine['inventory']['realized_pnl']:.2f}"
)
return engineprocess_fill Function
This function is called when a simulated fill event occurs for an active order. It updates the order's filled_quantity and avg_fill_price. If the order is fully filled, its status is changed to FILLED, and it's removed from active_orders. Crucially, it calls update_inventory_on_fill to adjust the market maker's inventory and realized_pnl. It also increments performance metrics like total_trades and records the trade event in the trade_history.
check_stale_orders function
def check_stale_orders(engine: dict) -> int:
"""
Cancels orders that have been active beyond max age.
Parameters
----------
engine : dict
Market maker engine state.
Returns
-------
int
Number of stale orders cancelled.
"""
current_time = time.time()
stale_orders = []
for order_id, order in engine["active_orders"].items():
if current_time - order["created_at"] > engine["max_order_age_seconds"]:
stale_orders.append(order_id)
cancelled = 0
for order_id in stale_orders:
if cancel_order(engine, order_id):
cancelled += 1
if cancelled > 0:
logger.debug(f"Cancelled {cancelled} stale orders")
return cancelledcheck_stale_orders Function
This function periodically checks for and cancels orders that have been active beyond their max_order_age_seconds. Orders that remain open for too long might no longer reflect the market maker's desired price or risk appetite, especially in fast-moving markets. By identifying and canceling these 'stale' orders, the market maker ensures that only relevant and up-to-date orders are present in the order book, maintaining a dynamic and responsive strategy.
generate_quote function
def generate_quote(
engine: dict,
market_bids: List[Tuple[float, float]],
market_asks: List[Tuple[float, float]],
price_history: deque = None,
) -> Tuple[float, float]:
"""
Generates bid and ask quotes based on market conditions.
Parameters
----------
engine : dict
Market maker engine state.
market_bids : list of tuples
Current market bid depth.
market_asks : list of tuples
Current market ask depth.
price_history : deque, optional
Recent price history for volatility calculation.
Returns
-------
tuple
(bid_price, ask_price) for quoting.
"""
# Update market state
engine["market_state"] = update_market_state(
engine["market_state"],
market_bids,
market_asks,
)
# Calculate current inventory skew
inventory_skew = calculate_inventory_skew(engine["inventory"])
# Calculate volatility if price history provided
volatility = 0.0
if price_history:
volatility = update_volatility_estimate(price_history)
# Calculate optimal spread
spread_bps = calculate_optimal_spread(
engine["market_state"],
base_spread_bps=engine["base_spread_bps"],
volatility=volatility,
inventory_skew=abs(inventory_skew),
)
# Calculate skewed prices
bid_price, ask_price = calculate_skewed_prices(
engine["market_state"],
spread_bps,
inventory_skew,
)
engine["last_quote_time"] = time.time()
return bid_price, ask_pricegenerate_quote Function
This function orchestrates the generation of new bid and ask prices for the market maker. It first updates the market_state with the latest market_bids and market_asks. Then, it calculates the current inventory_skew and, if price_history is available, estimates market volatility. These factors are fed into calculate_optimal_spread to determine the desired spread, which is then used by calculate_skewed_prices to produce the final, inventory-adjusted bid_price and ask_price for quoting.
update_quote function
def update_quote(
engine: dict,
market_bids: List[Tuple[float, float]],
market_asks: List[Tuple[float, float]],
price_history: deque = None,
) -> dict:
"""
Main function to update market maker quotes.
Parameters
----------
engine : dict
Market maker engine state.
market_bids : list of tuples
Current market bid depth.
market_asks : list of tuples
Current market ask depth.
price_history : deque, optional
Recent price history for volatility calculation.
Returns
-------
dict
Updated engine state.
"""
# Check if it's time to requote
current_time = time.time()
if current_time - engine["last_quote_time"] < engine["quote_interval_seconds"]:
return engine
# Cancel stale orders
check_stale_orders(engine)
# Cancel all existing orders before placing new ones
cancel_all_orders(engine)
# Generate new quote
bid_price, ask_price = generate_quote(
engine,
market_bids,
market_asks,
price_history,
)
# Place new orders
if bid_price > 0 or ask_price > 0:
place_orders(engine, bid_price, ask_price)
# Record inventory snapshot
engine["performance"]["inventory_snapshots"].append({
"timestamp": current_time,
"position": engine["inventory"]["current_position"],
"realized_pnl": engine["inventory"]["realized_pnl"],
"mid_price": engine["market_state"]["mid_price"],
})
return engineupdate_quote Function
This is the central function for managing the market maker's quoting process. It first checks if it's time to requote based on the quote_interval_seconds. If so, it check_stale_orders and then cancel_all_orders to clear the order book before placing new orders. It then calls generate_quote to determine the new bid and ask prices and subsequently place_orders. Finally, it records a snapshot of the inventory state for historical analysis, ensuring continuous and adaptive market making.
8. Performance Reporting
get_performance_summary Function
This function compiles a comprehensive summary of the market maker's overall performance during the simulation. It calculates various metrics such as total trades, buy/sell trade counts, total volume, average trade size, trade rate, and current position. Crucially, it extracts the realized_pnl from the inventory state and calculates inventory_turns, which indicates how efficiently the market maker is utilizing its capital. The function returns these metrics in a readable Pandas DataFrame format.
def get_performance_summary(engine: dict) -> pd.DataFrame:
"""
Generates a performance summary DataFrame.
Parameters
----------
engine : dict
Market maker engine state.
Returns
-------
pd.DataFrame
Performance metrics summary.
"""
perf = engine["performance"]
inventory = engine["inventory"]
runtime_seconds = time.time() - perf["start_time"]
runtime_minutes = runtime_seconds / 60 if runtime_seconds > 0 else 1
# Calculate additional metrics
avg_trade_size = perf["total_volume"] / perf["total_trades"] if perf["total_trades"] > 0 else 0
trade_rate = perf["total_trades"] / runtime_minutes if runtime_minutes > 0 else 0
volume_per_minute = perf["total_volume"] / runtime_minutes if runtime_minutes > 0 else 0
# Calculate inventory turns
inventory_turns = perf["total_volume"] / inventory["max_position"] if inventory["max_position"] > 0 else 0
summary = {
"metric": [
"Total Trades",
"Buy Trades",
"Sell Trades",
"Total Volume",
"Average Trade Size",
"Trade Rate (trades/min)",
"Volume Rate (vol/min)",
"Realized PnL",
"Current Position",
"Max Position Limit",
"Inventory Turns",
"Runtime (minutes)",
"Active Orders",
],
"value": [
f"{perf['total_trades']}",
f"{perf['total_buy_trades']}",
f"{perf['total_sell_trades']}",
f"{perf['total_volume']:.4f}",
f"{avg_trade_size:.4f}",
f"{trade_rate:.2f}",
f"{volume_per_minute:.4f}",
f"{inventory['realized_pnl']:.2f}",
f"{inventory['current_position']:.4f}",
f"{inventory['max_position']:.4f}",
f"{inventory_turns:.2f}",
f"{runtime_minutes:.2f}",
f"{len(engine['active_orders'])}",
],
}
return pd.DataFrame(summary)get_trade_history_df Function
This utility function retrieves the detailed trade_history from the market maker's performance tracker and converts it into a Pandas DataFrame. Each row in the DataFrame represents a single fill event, capturing important details such as the timestamp, order ID, side (BUY/SELL), fill price, fill quantity, the market maker's position after the trade, and the cumulative realized PnL at that point. This DataFrame is invaluable for analyzing individual trade performance and patterns.
get_trade_history_df function
def get_trade_history_df(engine: dict) -> pd.DataFrame:
"""
Returns trade history as a DataFrame.
Parameters
----------
engine : dict
Market maker engine state.
Returns
-------
pd.DataFrame
Trade history with timestamps and details.
"""
if not engine["performance"]["trade_history"]:
return pd.DataFrame()
df = pd.DataFrame(engine["performance"]["trade_history"])
if not df.empty and "timestamp" in df.columns:
df["datetime"] = pd.to_datetime(df["timestamp"], unit="s")
df = df.sort_values("timestamp")
return dfget_inventory_history_df Function
This function extracts the inventory_snapshots that were periodically recorded during the simulation and converts them into a Pandas DataFrame. These snapshots provide a time-series view of the market maker's position, realized_pnl, and the market's mid_price at regular intervals. Visualizing this data allows for a clear understanding of how the market maker's inventory and profitability evolved throughout the simulation, especially in relation to price movements.
get_inventory_history_df function
def get_inventory_history_df(engine: dict) -> pd.DataFrame:
"""
Returns inventory snapshot history as a DataFrame.
Parameters
----------
engine : dict
Market maker engine state.
Returns
-------
pd.DataFrame
Inventory history over time.
"""
if not engine["performance"]["inventory_snapshots"]:
return pd.DataFrame()
df = pd.DataFrame(engine["performance"]["inventory_snapshots"])
if not df.empty and "timestamp" in df.columns:
df["datetime"] = pd.to_datetime(df["timestamp"], unit="s")
df = df.sort_values("timestamp")
return dfprint_market_maker_status Function
This function provides a real-time, concise summary of the market maker's current operational status. It prints key information such as the best bid/ask, mid-price, spread, current inventory position and its limit, the realized PnL, the number of active orders, and the total number of trades. This function is designed to give an immediate overview of the engine's health and performance during live simulation or operation, allowing for quick checks and debugging.
print_market_maker_status function
def print_market_maker_status(engine: dict) -> None:
"""
Prints current market maker status.
Parameters
----------
engine : dict
Market maker engine state.
"""
market = engine["market_state"]
inventory = engine["inventory"]
print("\n" + "=" * 60)
print(f"Market Maker Status - {engine['symbol']}")
print("=" * 60)
print(f"Best Bid: {market['best_bid']:.2f} | Best Ask: {market['best_ask']:.2f}")
print(f"Mid Price: {market['mid_price']:.2f} | Spread: {market['spread_bps']:.1f} bps")
print(f"Position: {inventory['current_position']:.4f} / {inventory['max_position']:.4f}")
print(f"Realized PnL: {inventory['realized_pnl']:.2f}")
print(f"Active Orders: {len(engine['active_orders'])}")
print(f"Total Trades: {engine['performance']['total_trades']}")
print("=" * 60)simulate_order_book Function
This function creates a synthetic order book for simulation purposes. Given a mid_price and spread_bps, it generates a set of bid and ask orders at various price levels (depth_levels). The prices are staggered by tick_size, and quantities are randomized. This simulated order book allows the market maker engine to interact with a realistic-looking market environment without needing a live exchange connection, making it ideal for testing and development.
9. Simulation Environment
def simulate_order_book(
mid_price: float,
spread_bps: float = 2.0,
depth_levels: int = 5,
tick_size: float = 0.01,
) -> Tuple[List[Tuple[float, float]], List[Tuple[float, float]]]:
"""
Simulates an order book for testing.
Parameters
----------
mid_price : float
Current mid price.
spread_bps : float, optional
Spread in basis points.
depth_levels : int, optional
Number of depth levels to generate.
tick_size : float, optional
Minimum price increment.
Returns
-------
tuple
(bids, asks) lists of (price, quantity) tuples.
"""
spread_price = (spread_bps / 10000) * mid_price
half_spread = spread_price / 2
best_bid = round(mid_price - half_spread - tick_size, 2)
best_ask = round(mid_price + half_spread + tick_size, 2)
bids = []
asks = []
# Generate bid depth
for i in range(depth_levels):
price = best_bid - (i * tick_size * 5)
quantity = random.uniform(0.1, 0.5)
bids.append((price, quantity))
# Generate ask depth
for i in range(depth_levels):
price = best_ask + (i * tick_size * 5)
quantity = random.uniform(0.1, 0.5)
asks.append((price, quantity))
return bids, askssimulate_price_move Function
This function simulates a random walk for the asset's price, providing a dynamic market environment for the market maker. It takes a current_price and applies a random return based on a specified volatility and optional drift. The new price reflects a realistic, albeit simplified, movement often seen in financial markets. This helps in testing the market maker's robustness against price fluctuations and its ability to adapt its quoting strategy.
simulate_price_move function
def simulate_price_move(
current_price: float,
volatility: float = 0.0002,
drift: float = 0.0,
) -> float:
"""
Simulates a random price movement.
Parameters
----------
current_price : float
Current price.
volatility : float, optional
Price volatility (standard deviation of return).
drift : float, optional
Price drift per step.
Returns
-------
float
New price after random movement.
"""
# Generate random return
ret = np.random.normal(drift, volatility)
new_price = current_price * (1 + ret)
return new_pricerun_market_maker_simulation Function
This is the main simulation loop that orchestrates the entire market making process. It initializes the market maker engine with given parameters (symbol, duration, initial_price, order_quantity, base_spread_bps, max_position, quote_interval). Within the loop, it continuously simulates price_movements and order_book updates, calls update_quote to manage the market maker's orders, and simulates random fills. It also periodically prints the market maker's status and logs key events, finally returning the complete state of the engine at the end of the simulation.
run_market_maker_simulation function
def run_market_maker_simulation(
symbol: str = "BTCUSDT",
duration_seconds: int = 120,
initial_price: float = 50000.0,
order_quantity: float = 0.05,
base_spread_bps: float = 3.0,
max_position: float = 0.5,
quote_interval: float = 2.0,
) -> dict:
"""
Runs a market maker simulation.
Parameters
----------
symbol : str, optional
Trading pair symbol.
duration_seconds : int, optional
Simulation duration in seconds.
initial_price : float, optional
Starting price.
order_quantity : float, optional
Order size for each side.
base_spread_bps : float, optional
Base spread in basis points.
max_position : float, optional
Maximum position limit.
quote_interval : float, optional
Quote update interval in seconds.
Returns
-------
dict
Final market maker engine state.
"""
# Initialize market maker
engine = create_market_maker(
symbol=symbol,
order_quantity=order_quantity,
base_spread_bps=base_spread_bps,
max_position=max_position,
max_order_age_seconds=quote_interval * 2,
)
engine["quote_interval_seconds"] = quote_interval
# Initialize price history tracking
price_history = deque(maxlen=100)
current_price = initial_price
# Add initial price history
for _ in range(20):
price_history.append((time.time(), current_price))
# Track fill simulation
last_fill_time = 0
fill_probability = 0.3 # 30% chance of fill per quote cycle
logger.info(f"Starting market maker simulation for {symbol}")
logger.info(f"Duration: {duration_seconds}s | Initial Price: {initial_price}")
logger.info(f"Order Qty: {order_quantity} | Spread: {base_spread_bps} bps")
start_time = time.time()
iteration = 0
while time.time() - start_time < duration_seconds:
iteration += 1
# Simulate price movement (every 0.5 seconds)
if iteration % 2 == 0:
current_price = simulate_price_move(current_price, volatility=0.0005)
current_price = max(100, current_price) # Prevent negative price
price_history.append((time.time(), current_price))
# Generate simulated order book
bids, asks = simulate_order_book(current_price, spread_bps=base_spread_bps)
# Update quote
engine = update_quote(engine, bids, asks, price_history)
# Simulate random fills on active orders
current_time = time.time()
if current_time - last_fill_time > 1.0: # Check for fills every second
for order_id, order in list(engine["active_orders"].items()):
if random.random() < fill_probability * 0.3: # 9% fill chance per check
# Determine fill price (slightly better than order price)
if order["side"] == "BUY":
fill_price = order["price"] + random.uniform(-0.5, 0)
else:
fill_price = order["price"] + random.uniform(0, 0.5)
fill_price = max(0.01, fill_price)
fill_quantity = min(order["quantity"], random.uniform(0.5, 1.0) * order["quantity"])
engine = process_fill(engine, order_id, fill_price, fill_quantity)
# Break after one fill per cycle to avoid too many fills
break
last_fill_time = current_time
# Print status every 30 seconds
if iteration % int(30 / quote_interval) == 0:
print_market_maker_status(engine)
# Sleep for quote interval
time.sleep(quote_interval)
# Cancel all orders at end
cancel_all_orders(engine)
# Final inventory recalculation
if abs(engine["inventory"]["current_position"]) > 0.001 and engine["market_state"]["mid_price"] > 0:
# Calculate unrealized PnL for remaining inventory
mid_price = engine["market_state"]["mid_price"]
if engine["inventory"]["current_position"] > 0:
# Long position
engine["inventory"]["unrealized_pnl"] = (mid_price - engine["inventory"]["avg_buy_price"]) * engine["inventory"]["current_position"]
else:
# Short position
engine["inventory"]["unrealized_pnl"] = (engine["inventory"]["avg_sell_price"] - mid_price) * abs(engine["inventory"]["current_position"])
logger.info("=" * 60)
logger.info("SIMULATION COMPLETE")
logger.info("=" * 60)
return engine10. Demonstration
This section demonstrates the functionality of the market making engine by running a simulation with specified parameters and then displaying the results. We will initialize the engine, execute a simulated trading period, and then present key performance metrics, trade history, and inventory snapshots to evaluate the engine's behavior.
10. Demonstration
logger.info("--- Market Maker Simulation ---")
engine = run_market_maker_simulation(
symbol="BTCUSDT",
duration_seconds=240, # Run for 4 minutes (240 seconds) for more data
initial_price=65000.0, # Adjusted to a higher BTC price point
order_quantity=0.1, # Slightly increased order quantity
base_spread_bps=3.0,
max_position=1.0, # Increased max position
quote_interval=1.0, # Increased quoting frequency
)
# Display performance summary
print("\n")
logger.info("--- Performance Summary ---")
perf_df = get_performance_summary(engine)
print("### Market Maker Performance Summary")
display(perf_df)
# Display trade history
logger.info("--- Trade History ---")
trade_df = get_trade_history_df(engine)
print("### Recent Trade History")
if not trade_df.empty:
display(trade_df[["datetime", "side", "price", "quantity", "position_after", "realized_pnl"]].tail(20)) # Display last 20 trades
else:
print("No trades executed during simulation")
# Display inventory history
logger.info("--- Inventory History ---")
inv_df = get_inventory_history_df(engine)
print("### Recent Inventory Snapshots")
if not inv_df.empty:
display(inv_df[["datetime", "position", "realized_pnl", "mid_price"]].tail(20)) # Display last 20 inventory snapshots
else:
print("No inventory snapshots available")============================================================ Market Maker Status - BTCUSDT ============================================================ Best Bid: 64900.89 | Best Ask: 64920.39 Mid Price: 64910.64 | Spread: 3.0 bps Position: 0.0913 / 1.0000 Realized PnL: 19.00 Active Orders: 2 Total Trades: 7 ============================================================ ============================================================ Market Maker Status - BTCUSDT ============================================================ Best Bid: 65048.33 | Best Ask: 65067.86 Mid Price: 65058.10 | Spread: 3.0 bps Position: 0.1575 / 1.0000 Realized PnL: 25.45 Active Orders: 2 Total Trades: 10 ============================================================ ============================================================ Market Maker Status - BTCUSDT ============================================================ Best Bid: 65022.21 | Best Ask: 65041.74 Mid Price: 65031.97 | Spread: 3.0 bps Position: 0.0621 / 1.0000 Realized PnL: 35.07 Active Orders: 2 Total Trades: 15 ============================================================ ============================================================ Market Maker Status - BTCUSDT ============================================================ Best Bid: 65094.06 | Best Ask: 65113.61 Mid Price: 65103.83 | Spread: 3.0 bps Position: 0.1337 / 1.0000 Realized PnL: 36.62 Active Orders: 2 Total Trades: 20 ============================================================ ============================================================ Market Maker Status - BTCUSDT ============================================================ Best Bid: 65128.43 | Best Ask: 65147.99 Mid Price: 65138.21 | Spread: 3.0 bps Position: 0.0217 / 1.0000 Realized PnL: 78.36 Active Orders: 2 Total Trades: 28 ============================================================ ============================================================ Market Maker Status - BTCUSDT ============================================================ Best Bid: 65136.15 | Best Ask: 65155.71 Mid Price: 65145.93 | Spread: 3.0 bps Position: 0.1769 / 1.0000 Realized PnL: 100.36 Active Orders: 2 Total Trades: 34 ============================================================ ============================================================ Market Maker Status - BTCUSDT ============================================================ Best Bid: 65187.78 | Best Ask: 65207.36 Mid Price: 65197.57 | Spread: 3.0 bps Position: 0.3200 / 1.0000 Realized PnL: 123.86 Active Orders: 2 Total Trades: 40 ============================================================ ============================================================ Market Maker Status - BTCUSDT ============================================================ Best Bid: 65297.32 | Best Ask: 65316.93 Mid Price: 65307.12 | Spread: 3.0 bps Position: 0.3791 / 1.0000 Realized PnL: 158.92 Active Orders: 2 Total Trades: 45 ============================================================ ============================================================ Market Maker Status - BTCUSDT ============================================================ Best Bid: 65090.62 | Best Ask: 65110.17 Mid Price: 65100.40 | Spread: 3.0 bps Position: 0.1719 / 1.0000 Realized PnL: 199.35 Active Orders: 2 Total Trades: 51 ============================================================ ============================================================ Market Maker Status - BTCUSDT ============================================================ Best Bid: 65147.00 | Best Ask: 65166.56 Mid Price: 65156.78 | Spread: 3.0 bps Position: 0.3154 / 1.0000 Realized PnL: 196.10 Active Orders: 2 Total Trades: 59 ============================================================ ### Market Maker Performance Summary
| metric | value | |
|---|---|---|
| 0 | Total Trades | 59 |
| 1 | Buy Trades | 32 |
| 2 | Sell Trades | 27 |
| 3 | Total Volume | 4.6256 |
| 4 | Average Trade Size | 0.0784 |
| 5 | Trade Rate (trades/min) | 11.79 |
| 6 | Volume Rate (vol/min) | 0.9244 |
| 7 | Realized PnL | 196.10 |
| 8 | Current Position | 0.3154 |
| 9 | Max Position Limit | 1.0000 |
| 10 | Inventory Turns | 4.63 |
| 11 | Runtime (minutes) | 5.00 |
| 12 | Active Orders | 0 |
### Recent Trade History
| datetime | side | price | quantity | position_after | realized_pnl | |
|---|---|---|---|---|---|---|
| 39 | 2026-06-08 10:48:29.534532785 | BUY | 65167.114039 | 0.085321 | 0.319964 | 123.855475 |
| 40 | 2026-06-08 10:48:39.540917158 | BUY | 65239.417477 | 0.058698 | 0.378661 | 123.855475 |
| 41 | 2026-06-08 10:48:40.541614532 | BUY | 65254.066628 | 0.082596 | 0.461257 | 123.855475 |
| 42 | 2026-06-08 10:48:41.542195797 | SELL | 65278.066554 | 0.096348 | 0.364909 | 145.001460 |
| 43 | 2026-06-08 10:48:49.547536135 | SELL | 65285.758618 | 0.061292 | 0.303617 | 158.924921 |
| 44 | 2026-06-08 10:48:50.548488140 | BUY | 65231.290250 | 0.075447 | 0.379063 | 158.924921 |
| 45 | 2026-06-08 10:49:03.561975002 | BUY | 65297.595648 | 0.090010 | 0.469074 | 158.924921 |
| 46 | 2026-06-08 10:49:04.562686920 | SELL | 65285.933450 | 0.075439 | 0.393634 | 174.781058 |
| 47 | 2026-06-08 10:49:12.570323706 | SELL | 65192.925203 | 0.089538 | 0.304096 | 185.272785 |
| 48 | 2026-06-08 10:49:15.572726965 | SELL | 65158.099651 | 0.096976 | 0.207120 | 193.258803 |
| 49 | 2026-06-08 10:49:18.574948311 | SELL | 65139.288238 | 0.095856 | 0.111264 | 199.349428 |
| 50 | 2026-06-08 10:49:26.580126047 | BUY | 65078.896241 | 0.060608 | 0.171872 | 199.349428 |
| 51 | 2026-06-08 10:49:32.584668875 | SELL | 65083.228241 | 0.097142 | 0.074730 | 200.067031 |
| 52 | 2026-06-08 10:49:34.585980415 | SELL | 65031.369329 | 0.068587 | 0.006144 | 197.016869 |
| 53 | 2026-06-08 10:49:39.589176893 | BUY | 65013.225986 | 0.077950 | 0.084094 | 197.016869 |
| 54 | 2026-06-08 10:49:41.590778112 | BUY | 65089.990584 | 0.094206 | 0.178300 | 197.016869 |
| 55 | 2026-06-08 10:49:44.593757391 | BUY | 65060.282681 | 0.088706 | 0.267005 | 197.016869 |
| 56 | 2026-06-08 10:49:49.603119850 | BUY | 65087.081148 | 0.077575 | 0.344580 | 197.016869 |
| 57 | 2026-06-08 10:49:55.608567238 | SELL | 65063.688248 | 0.087357 | 0.257223 | 196.102116 |
| 58 | 2026-06-08 10:49:58.610829115 | BUY | 65098.014806 | 0.058159 | 0.315382 | 196.102116 |
### Recent Inventory Snapshots
| datetime | position | realized_pnl | mid_price | |
|---|---|---|---|---|
| 280 | 2026-06-08 10:49:41.590112686 | 0.084094 | 197.016869 | 65101.175 |
| 281 | 2026-06-08 10:49:42.591037035 | 0.178300 | 197.016869 | 65057.060 |
| 282 | 2026-06-08 10:49:43.592512608 | 0.178300 | 197.016869 | 65057.060 |
| 283 | 2026-06-08 10:49:44.593343496 | 0.178300 | 197.016869 | 65072.320 |
| 284 | 2026-06-08 10:49:45.593972921 | 0.267005 | 197.016869 | 65072.320 |
| 285 | 2026-06-08 10:49:46.599541664 | 0.267005 | 197.016869 | 65093.765 |
| 286 | 2026-06-08 10:49:47.600129604 | 0.267005 | 197.016869 | 65093.765 |
| 287 | 2026-06-08 10:49:48.600883722 | 0.267005 | 197.016869 | 65099.865 |
| 288 | 2026-06-08 10:49:49.602524519 | 0.267005 | 197.016869 | 65099.865 |
| 289 | 2026-06-08 10:49:50.604562759 | 0.344580 | 197.016869 | 65137.200 |
| 290 | 2026-06-08 10:49:51.605245590 | 0.344580 | 197.016869 | 65137.200 |
| 291 | 2026-06-08 10:49:52.605983496 | 0.344580 | 197.016869 | 65140.690 |
| 292 | 2026-06-08 10:49:53.606787920 | 0.344580 | 197.016869 | 65140.690 |
| 293 | 2026-06-08 10:49:54.607548237 | 0.344580 | 197.016869 | 65054.010 |
| 294 | 2026-06-08 10:49:55.608113050 | 0.344580 | 197.016869 | 65054.010 |
| 295 | 2026-06-08 10:49:56.608831882 | 0.257223 | 196.102116 | 65111.145 |
| 296 | 2026-06-08 10:49:57.609559059 | 0.257223 | 196.102116 | 65111.145 |
| 297 | 2026-06-08 10:49:58.610246658 | 0.257223 | 196.102116 | 65110.935 |
| 298 | 2026-06-08 10:49:59.611031532 | 0.315382 | 196.102116 | 65110.935 |
| 299 | 2026-06-08 10:50:00.611891031 | 0.315382 | 196.102116 | 65156.780 |
Simulation Results Visualization
Let's visualize the market maker's performance and inventory over time. This plot will show how the position and realized PnL evolve alongside the simulated mid-price of the asset.
import matplotlib.pyplot as plt
import seaborn as sns
# Ensure inv_df is available and has data
if 'inv_df' in locals() and not inv_df.empty:
fig, axes = plt.subplots(3, 1, figsize=(14, 12), sharex=True)
# Plot 1: Inventory Position Over Time
sns.lineplot(ax=axes[0], x='datetime', y='position', data=inv_df, marker='o', markersize=4, label='Position')
axes[0].axhline(0, color='grey', linestyle='--', linewidth=0.7)
axes[0].set_title('Inventory Position Over Time')
axes[0].set_ylabel('Position (BTC)')
axes[0].legend()
axes[0].grid(True, linestyle='--', alpha=0.6)
# Plot 2: Realized PnL Over Time
sns.lineplot(ax=axes[1], x='datetime', y='realized_pnl', data=inv_df, marker='o', markersize=4, color='green', label='Realized PnL')
axes[1].axhline(0, color='grey', linestyle='--', linewidth=0.7)
axes[1].set_title('Realized PnL Over Time')
axes[1].set_ylabel('Realized PnL (USD)')
axes[1].legend()
axes[1].grid(True, linestyle='--', alpha=0.6)
# Plot 3: Mid-Price Over Time
sns.lineplot(ax=axes[2], x='datetime', y='mid_price', data=inv_df, marker='o', markersize=4, color='purple', label='Mid-Price')
axes[2].set_title('Mid-Price Over Time')
axes[2].set_ylabel('Mid-Price (USD)')
axes[2].set_xlabel('Time')
axes[2].legend()
axes[2].grid(True, linestyle='--', alpha=0.6)
plt.tight_layout()
plt.show()
else:
print("Inventory history DataFrame (inv_df) is empty or not available for plotting.")11. Production Considerations
Best Practices for Deployment
| Component | Recommendation |
|---|---|
| Order Management | Implement order acknowledgment tracking and timeout handling |
| Risk Controls | Set maximum position limits, drawdown limits, and daily loss limits |
| Connection Management | Use WebSocket streams for real-time order book updates |
| Order Book Sync | Maintain local order book with sequence number validation |
| Error Recovery | Implement circuit breakers for exchange connectivity issues |
| Quote Logic | Add minimum time between quote updates to avoid excessive cancellations |
| Inventory Management | Implement hedging strategies for large inventory positions |
| Monitoring | Track quote hit ratio, average fill distance, and adverse selection |
Risk Management Framework
Risk Level Actions
─────────────────────────────────────────────────────
Warning (50% limit) → Reduce order size, widen spread
Critical (80% limit) → Cancel all orders, pause quoting
Max (100% limit) → Emergency liquidation, alert admin
Key Metrics to Monitor
- Fill Rate: Percentage of quoted orders that get filled
- Adverse Selection: Percentage of fills that move against the position
- Inventory Turns: Volume traded relative to max position
- Spread Capture: Actual spread captured vs quoted spread
- Quote Duration: Average time orders remain active
Conclusion
This notebook has demonstrated the design and implementation of a production-grade market making engine.
Implemented Components:
| Component | Purpose |
|---|---|
| Order Book Management | Real-time bid/ask tracking and spread calculation |
| Inventory Tracking | Position management and PnL calculation |
| Price Skewing | Inventory-based price adjustment for risk management |
| Dynamic Spread | Volatility and inventory-adjusted quoting |
| Order Lifecycle | Placement, cancellation, fill processing |
| Performance Tracking | Trade history and inventory snapshots |
| Simulation Environment | Testing framework with synthetic order books |
Key Capabilities:
- Continuous two-sided quote generation
- Inventory-aware position management
- Realized and unrealized PnL calculation
- Stale order cancellation
- Performance metric collection
- Configurable spread and risk parameters
This engine provides a solid foundation for live market making strategies, with extensible components for volatility estimation, advanced skewing algorithms, and exchange integration.