◈   ⌬ bots · Intermediate

Market Making Bot in Python: Complete Crypto Guide

Step-by-step guide to building a Python crypto market making bot. Covers Binance API, spread logic, order placement, inventory risk, and real working code examples.

Uncle Solieditor · voc · 06.05.2026 ·views 15
◈   Contents
  1. → What Is Market Making in Crypto?
  2. → Core Components of a Market Making Bot
  3. → Setting Up Your Python Environment
  4. → Building the Core Market Making Strategy
  5. → Inventory Management and Risk Controls
  6. → Choosing the Right Exchange and Pair
  7. → Frequently Asked Questions
  8. → Conclusion

Market making is one of the oldest and most consistently profitable strategies in financial markets. Firms like Citadel and Virtu built empires on it. In crypto, the same principle applies — but the playing field is more accessible, the APIs are open, and a skilled developer with Python can build a functional market making bot in a weekend. Here is how it works and how to build one.

What Is Market Making in Crypto?

A market maker provides liquidity to an exchange by placing simultaneous buy (bid) and sell (ask) limit orders around the current mid-market price. The difference between those two prices — the spread — is where the profit comes from. Every time a market order from another trader hits your bid or ask, you capture that spread as gross profit. On Binance, for instance, the BTC/USDT spread might be just $1–$5 at any given moment. That sounds trivial, but a bot processing hundreds of fills per day compounds those gains significantly. The key insight: you are not betting on price direction. You profit from trading activity itself. The risk, however, is inventory risk. If BTC drops 3% before your ask order fills, you are sitting on an unrealized loss from the long position you accumulated on the bid side. Managing that exposure is what separates functional market making bots from strategies that blow up their accounts.

Core Components of a Market Making Bot

Before writing a single line of code, understand what your bot needs to do. A complete market making system has five moving parts that all need to work together.

Most amateur bots only implement the first three and ignore inventory and risk controls — which is exactly how they lose money. A production-grade market making bot treats inventory management as a first-class concern, not an afterthought.

Setting Up Your Python Environment

The quickest path to a working crypto market making bot in Python is the CCXT library (CryptoCurrency eXchange Trading). It provides a unified API interface for over 100 exchanges including Binance, Bybit, OKX, and Bitget — letting you write exchange-agnostic code that can be retargeted with a single config change. You'll also need python-dotenv to keep API credentials out of your source code.

# Create isolated environment and install dependencies
python3 -m venv venv
source venv/bin/activate

pip install ccxt python-dotenv pandas numpy

Generate API keys from your exchange's account settings. On Binance, go to API Management and create a key with spot trading permissions enabled. Store credentials in a .env file at the project root — never hardcode them in the script.

import ccxt
import os
from dotenv import load_dotenv

load_dotenv()

# Swap 'binance' for 'bybit', 'okx', or 'bitget' to change exchange
exchange = ccxt.binance({
    'apiKey': os.getenv('BINANCE_API_KEY'),
    'secret': os.getenv('BINANCE_SECRET'),
    'enableRateLimit': True,
    'options': {
        'defaultType': 'spot',  # use 'future' for perpetuals
    }
})

def get_order_book(symbol='BTC/USDT', depth=5):
    book = exchange.fetch_order_book(symbol, limit=depth)
    best_bid = book['bids'][0][0]   # highest buy price
    best_ask = book['asks'][0][0]   # lowest sell price
    mid_price = (best_bid + best_ask) / 2
    spread_pct = (best_ask - best_bid) / mid_price * 100

    return {
        'bid': best_bid,
        'ask': best_ask,
        'mid': mid_price,
        'spread_pct': spread_pct
    }

book = get_order_book()
print(f"Mid: {book['mid']:.2f} | Market spread: {book['spread_pct']:.4f}%")
Always enable enableRateLimit: True in CCXT. Binance will temporarily ban your IP for excessive REST requests. During active markets your bot will hit limits far faster than you expect — the library handles pacing automatically when this flag is set.

Building the Core Market Making Strategy

The core logic is straightforward: calculate where to place your bid and ask, place them, wait for fills, then cancel and reprice. The complexity is in how you calculate those prices. The most common approach is a symmetric spread around the mid-price. If ETH/USDT is trading at $3,000 and you want a 0.2% total spread, you place a bid at $2,997 and an ask at $3,003. That $6 gap is your gross profit per round trip — one buy fill and one sell fill. More sophisticated bots skew their quotes based on inventory. If you are holding too much ETH, you shade your ask price lower to sell faster and your bid price lower to slow further accumulation. This inventory-aware quoting is what professional market makers do and what separates consistently profitable bots from neutral ones that leak money in trending conditions.

import time

CONFIG = {
    'symbol': 'ETH/USDT',
    'spread_pct': 0.002,       # 0.2% total spread
    'order_size': 0.05,        # ETH per side
    'max_inventory': 0.5,      # max ETH to hold long
    'inventory_skew': 0.5,     # quote skew aggressiveness (0-1)
    'refresh_interval': 5,     # seconds between requotes
}

def calculate_quotes(mid_price, inventory, config):
    half_spread = mid_price * config['spread_pct'] / 2

    # Positive inventory → skew both quotes down to sell faster
    max_inv = config['max_inventory']
    skew = (inventory / max_inv) * config['inventory_skew'] * half_spread

    bid = round(mid_price - half_spread - skew, 2)
    ask = round(mid_price + half_spread - skew, 2)
    return bid, ask

def run_market_maker():
    open_orders = []
    inventory = 0.0

    while True:
        try:
            # Cancel existing quotes before repricing
            for order in open_orders:
                try:
                    exchange.cancel_order(order['id'], CONFIG['symbol'])
                except Exception:
                    pass
            open_orders = []

            book = get_order_book(CONFIG['symbol'])
            bid_price, ask_price = calculate_quotes(
                book['mid'], inventory, CONFIG
            )

            bid_order = exchange.create_limit_buy_order(
                CONFIG['symbol'], CONFIG['order_size'], bid_price
            )
            ask_order = exchange.create_limit_sell_order(
                CONFIG['symbol'], CONFIG['order_size'], ask_price
            )
            open_orders = [bid_order, ask_order]

            print(
                f"Bid: {bid_price} | Ask: {ask_price} "
                f"| Spread: {ask_price - bid_price:.2f} "
                f"| Inv: {inventory:.4f}"
            )
            time.sleep(CONFIG['refresh_interval'])

        except KeyboardInterrupt:
            print("Shutting down — cancelling open orders...")
            for order in open_orders:
                exchange.cancel_order(order['id'], CONFIG['symbol'])
            break
        except Exception as e:
            print(f"Error: {e} — retrying in 10s")
            time.sleep(10)

# run_market_maker()  # Uncomment to start

This bot cancels and replaces orders every 5 seconds. That is a reasonable default for most liquid pairs on Binance or OKX — frequent enough to stay competitive, slow enough to avoid rate limit bans. On perpetual futures on Bybit, you can tighten the refresh to 1–2 seconds and switch to WebSocket feeds instead of REST polling for lower latency order book data.

Inventory Management and Risk Controls

Inventory management determines whether your market making bot is profitable or just busy. The danger is one-sided fills: price drops sharply, your bids fill but your asks do not, and now you are holding a losing long position with no automated response. There are two complementary approaches. Hard limits stop placing bids when inventory exceeds a threshold, preventing further accumulation. Dynamic skewing, already shown above, continuously adjusts quote prices to incentivize fills on the heavy side before limits are hit. For real capital you need both. Hard limits prevent catastrophic exposure. Skewing reduces average inventory and improves fill balance under normal conditions.

def get_inventory_status(symbol, config):
    """Fetch live balance and compute inventory exposure fraction."""
    balance = exchange.fetch_balance()
    base_asset = symbol.split('/')[0]   # 'ETH' from 'ETH/USDT'

    base_free = balance[base_asset]['free']
    inv_pct = base_free / config['max_inventory']

    return {
        'base': base_free,
        'inv_pct': inv_pct,
        'at_limit': inv_pct >= 1.0,
        'nearly_empty': base_free < 0.001,
    }

def should_quote(side, inv):
    """Returns False if this side should be suppressed."""
    if side == 'buy' and inv['at_limit']:
        return False   # maxed out — stop accumulating
    if side == 'sell' and inv['nearly_empty']:
        return False   # nothing to sell
    return True

# In the main loop, replace hardcoded order placement with:
# inv = get_inventory_status(CONFIG['symbol'], CONFIG)
# if should_quote('buy', inv):
#     bid_order = exchange.create_limit_buy_order(...)
# if should_quote('sell', inv):
#     ask_order = exchange.create_limit_sell_order(...)
Add a daily loss limit kill switch. Check cumulative P&L at the top of every loop iteration and halt the bot if losses exceed your threshold — for example 2% of allocated capital. A malfunctioning bot in a fast market can lose more in minutes than you expect.

For broader market context while your bot is running, VoiceOfChain provides real-time sentiment signals and price alerts that tell you when a trending move is underway. Market making works best in sideways, range-bound conditions. Pausing your bot during a confirmed breakout or high-volatility event prevents accumulating inventory into a move that will not reverse quickly.

Choosing the Right Exchange and Pair

Not every exchange and pair is a good candidate for a Python market making bot. You want sufficient volume so orders fill regularly, but not so much that you are competing against professional HFT firms with latency and colocation advantages you cannot match. Binance spot is the deepest market for BTC/USDT and ETH/USDT, but the competition is intense. More accessible targets for a retail Python bot are mid-tier pairs on Binance — SOL/USDT, AVAX/USDT — or moving to Gate.io and Bitget where smaller pairs have meaningful volume with far less sophisticated competition. Fee structure matters enormously. Most exchanges use a maker-taker model where limit orders that add liquidity pay a lower maker fee. On Bybit and OKX perpetuals, maker fees are actually negative — the exchange pays you a rebate for providing liquidity. Running a maker-only strategy on those platforms means collecting the spread plus a fee rebate on every single fill. That fundamentally changes the profitability math compared to spot markets.

Maker Fee Comparison Across Major Exchanges
ExchangeSpot Maker FeePerp Maker FeeNotes
Binance0.10%0.02%Lower with BNB discount
Bybit0.10%-0.01%Rebate on perp makers
OKX0.08%-0.02%Rebate on perp makers
Gate.io0.20%0.015%Good mid-cap spot pairs
Bitget0.10%-0.016%Rebate on futures makers

If your bot targets perpetual futures, Bybit and OKX are hard to beat on fee structure alone. A maker-only strategy collecting both the spread and a rebate means your break-even threshold is dramatically lower than on spot markets with positive taker fees on both sides.

Frequently Asked Questions

How much capital do I need to run a crypto market making bot?
You can start with $500–$1,000, but the math works much better at $5,000 or more. Smaller capital forces smaller order sizes that may hit exchange minimums, and fees consume a larger percentage of the spread captured. Most serious retail market makers allocate at least $10,000 per pair.
Is Python fast enough for crypto market making?
For retail market making on moderate-frequency pairs — refreshing quotes every 1–10 seconds — Python is completely adequate. If you are competing in high-frequency environments where being first by milliseconds matters, you would need C++ or Rust. The vast majority of profitable retail market making bots run in Python with no performance issues.
What is the biggest risk with a market making bot?
Inventory risk during trending markets. When price moves sharply in one direction, orders on the wrong side fill repeatedly while the other side sits untouched, leaving you with a directional position and growing unrealized losses. Strict hard inventory limits and a volatility-based pause mechanism are essential safeguards.
Can I test this bot on Binance without real money?
Yes, and you absolutely should. Binance provides a testnet at testnet.binance.vision with paper trading. In CCXT, set 'test': True in the exchange options and configure the testnet URLs. Run your bot there for at least one week — edge cases you did not anticipate will surface before they cost you real capital.
Do I need to keep the bot running 24/7?
Yes. Your limit orders sit on the exchange book between refresh cycles and can fill at any time. If the bot is offline when a fill happens, stale quotes on the other side remain unhedged and inventory goes unmanaged. Run it on a VPS or cloud instance with a process manager like supervisord or systemd to auto-restart on crashes.
How do I accurately track P&L for a market making bot?
Use fetch_my_trades() in CCXT to pull trade history and reconcile fills. The key metrics are not just total profit — track spread captured per fill, inventory turns per day, and time spent at the inventory limit. These tell you whether the strategy is working or just generating fees for the exchange.

Conclusion

A Python crypto market making bot is one of the most teachable algorithmic trading strategies — the mechanics are clear, the code is accessible, and the profitability logic is transparent. The hard part is not writing the code. It is the operational discipline: running it reliably around the clock, managing inventory under pressure, choosing the right pairs on the right exchanges, and knowing exactly when to shut it down. Start on Binance testnet. Study your fill patterns and inventory curves for at least a week before deploying real capital. Layer in exchange-specific fee optimizations — maker rebates on Bybit or OKX can turn a marginally profitable strategy into a solid one. Use market signal platforms like VoiceOfChain to stay aware of macro conditions so you are not quoting into a trending breakout when you should be sitting flat. Market making rewards patience and iteration over cleverness. Ship the simplest working version first, measure it, and improve from real data.

◈   more on this topic
⌘ api Kraken API Documentation for Crypto Traders: Essentials and Examples ◉ basics Mastering the ccxt library documentation for crypto traders