Market making in Python

Market making earns the spread by providing liquidity, here is a simplified market-making strategy in Python, backtested on the Rust core.

What is market making?

A market maker continuously quotes both a bid and an ask, earning the spread when both fill while managing the inventory it accumulates. It is fundamentally a mean-reversion-around-fair-value business: buy a touch below fair, sell a touch above, repeat.

True market making is a low-latency, order-book game. A bar-level backtest cannot model queue position or live quoting, but it can model the inventory logic, fade small deviations from fair value, which is the strategic core.

The logic

We approximate micro fair value with a 20-bar moving average and measure the fractional deviation of price from it. Small deviations are the maker's edge.

When price dips a tenth of a percent below fair value we go long (simulating a passive buy fill); when it pops above, we go short; otherwise flat. That captures the inventory-cycling behaviour of a maker at bar resolution.

market making in Python

Here is the full strategy in the Manifold-BT expression DSL. It imports manifoldbt, builds the signal, configures realistic execution, and runs the backtest against Rust.

market_making.py
import manifoldbt as mbt
from manifoldbt.indicators import close
from manifoldbt.helpers import time_range, Slippage, Interval

# Micro fair value and the fractional deviation of price from it
fair = close.rolling_mean(20)
edge = (close - fair) / fair

# Fade small deviations: buy below fair, sell above, capturing the spread
strategy = (
    mbt.Strategy.create("market_making")
    .signal("edge", edge)
    .size(mbt.when(edge < -0.001, 1.0, mbt.when(edge > 0.001, -1.0, 0.0)))
)

start, end = time_range("2024-01-01", "2026-01-01")
config = mbt.BacktestConfig(
    universe={"binance": ["BTCUSDT"]},
    time_range_start=start,
    time_range_end=end,
    bar_interval=Interval.minutes(1),
    initial_capital=10_000,
    fees=mbt.FeeConfig.binance_perps(),
    slippage=Slippage.fixed_bps(1),
    warmup_bars=20,
)
store = mbt.ingest(provider="binance", symbol="BTCUSDT", symbol_id=1,
                   interval="1m", start="2024-01-01T00:00:00Z",
                   end="2026-01-01T00:00:00Z")
result = mbt.run(strategy, config, store)
print(result.summary())
mbt.plot.tearsheet(result, show=True)

Backtest it with Manifold-BT

At one-minute resolution fees are the whole game: a maker lives and dies on the difference between the spread captured and the costs paid. The Binance perp fee model here makes that trade-off explicit.

Treat the numbers as directional, not literal, a real maker would post passive orders and earn maker rebates this backtest does not capture. Use it to study the inventory logic, then validate with higher-resolution data via the MCP server or the Pro tier.

Backtest configuration
Universe{"binance": ["BTCUSDT"]}
Bar interval1m
FeesFeeConfig.binance_perps()
Slippagefixed 1 bps
Warmup20 bars
Sample tearsheet (illustrative, not a forecast)
Total return+16.3%
Sharpe1.42
Max drawdown-7.8%
Win rate63%
Trades9,820

Keep reading

Run your first backtest

Install Manifold-BT and reproduce the backtest above in seconds. The Rust core runs years of bars sub-second so you can sweep parameters instead of waiting.

$pip install manifoldbt