Market Making·Market Making Fundamentals·Intermediate

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.

market-makingmarket-making-fundamentals

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

ConceptDescription
Bid-Ask SpreadThe price difference between buy and sell orders
Mid Price(Best Bid + Best Ask) / 2
Inventory RiskExposure from holding an unbalanced position
SkewingAdjusting prices to reduce inventory
Maker RebateFee 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 numpy
Requirement 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_state

update_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_bps

calculate_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_price

calculate_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 quantity

calculate_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_state

update_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 skew

calculate_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 vol

update_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 False

cancel_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_count

cancel_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_asks

place_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 engine

process_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 cancelled

check_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_price

generate_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 engine

update_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 df

get_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 df

print_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, asks

simulate_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_price

run_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 engine

10. 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.")
cell output

11. Production Considerations

Best Practices for Deployment

ComponentRecommendation
Order ManagementImplement order acknowledgment tracking and timeout handling
Risk ControlsSet maximum position limits, drawdown limits, and daily loss limits
Connection ManagementUse WebSocket streams for real-time order book updates
Order Book SyncMaintain local order book with sequence number validation
Error RecoveryImplement circuit breakers for exchange connectivity issues
Quote LogicAdd minimum time between quote updates to avoid excessive cancellations
Inventory ManagementImplement hedging strategies for large inventory positions
MonitoringTrack 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:

ComponentPurpose
Order Book ManagementReal-time bid/ask tracking and spread calculation
Inventory TrackingPosition management and PnL calculation
Price SkewingInventory-based price adjustment for risk management
Dynamic SpreadVolatility and inventory-adjusted quoting
Order LifecyclePlacement, cancellation, fill processing
Performance TrackingTrade history and inventory snapshots
Simulation EnvironmentTesting 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.

Basic Market Maker · BitPredict