← Back to Algorithmic Glossary

Market Making in Python

A liquidity-providing strategy that simultaneously quotes bid and ask prices, profiting from the bid-ask spread while managing directional inventory risk.

Definition

Market Making is the institutional practice of continuously quoting both a bid (buy) price and an ask (sell) price for a financial instrument, providing liquidity to the market in exchange for earning the bid-ask spread. Market makers profit from the spread between what buyers pay and what sellers receive, but are exposed to adverse selection risk — the risk that informed traders (who know something the market maker does not) consistently trade against them. The optimal market-making problem, formalized by Avellaneda and Stoikov (2008), involves dynamically adjusting quote placement based on current inventory level, volatility, and remaining time horizon to balance spread capture against inventory risk.

Quantitative Formula

r=mqγσ2(Tt),δbid/ask=1γln(1+γκ)±12qγσ2(Tt)r^* = m - q \cdot \gamma \cdot \sigma^2 \cdot (T - t), \quad \delta^{bid/ask} = \frac{1}{\gamma} \ln\left(1 + \frac{\gamma}{\kappa}\right) \pm \frac{1}{2} q \gamma \sigma^2 (T-t)

From the Avellaneda-Stoikov model: rr^* is the reservation price (the market maker's indifference price), mm is the mid-price, qq is the current inventory, γ\gamma is the risk aversion coefficient, σ2\sigma^2 is variance, and (Tt)(T-t) is the remaining horizon. The optimal spread δbid/ask\delta^{bid/ask} is symmetric around rr^* and widens with volatility and inventory imbalance, ensuring the market maker skews quotes to reduce unwanted inventory.

Why It Matters in Backtesting

Backtesting market-making strategies requires tick-level or order-book-level data — daily OHLC bars are completely useless. The critical metric is not Sharpe Ratio but rather the P&L per lot traded versus the adverse selection cost. A naive backtest that assumes fills at the quoted price without modeling adverse selection will dramatically overstate profitability. In practice, approximately 30–50% of a market maker's quote volume is filled by informed traders moving against the position, and this cost must be explicitly modeled in any realistic backtest.

Python Implementation

import numpy as np
    import pandas as pd

    def avellaneda_stoikov_quotes(mid_prices: pd.Series, inventory: pd.Series,
                                  sigma: float = 0.02, gamma: float = 0.1,
                                  kappa: float = 1.5, T: float = 1.0) -> pd.DataFrame:
        """
        Computes optimal bid/ask quotes using the Avellaneda-Stoikov (2008) model.
        mid_prices: Series of mid-market prices.
        inventory: Series of current inventory (positive = long, negative = short).
        sigma: Asset volatility (daily).
        gamma: Risk aversion parameter.
        kappa: Order arrival intensity parameter.
        T: Time horizon in days.
        """
        t = np.linspace(0, T, len(mid_prices))
        time_remaining = T - t
        # Reservation price: skewed from mid based on inventory and time
        reservation_price = mid_prices - inventory * gamma * (sigma ** 2) * time_remaining
        # Optimal spread around reservation price
        base_spread = (1 / gamma) * np.log(1 + gamma / kappa)
        inventory_adjustment = 0.5 * inventory * gamma * (sigma ** 2) * time_remaining
        bid_price = reservation_price - base_spread / 2 - inventory_adjustment
        ask_price = reservation_price + base_spread / 2 - inventory_adjustment
        return pd.DataFrame({
            "mid": mid_prices.values,
            "reservation_price": reservation_price.values,
            "bid": bid_price.values,
            "ask": ask_price.values,
            "spread": (ask_price - bid_price).values
        }, index=mid_prices.index)

Test this in a live environment

Stop running Jupyter notebooks locally. Paste this Market Making code directly into Valetha's Strategy Lab and run a full historical backtest in seconds.

Open the Python Strategy Lab

Ready to find your edge ?

Start for Free