High-frequency market making is one of the most technically demanding trading strategies in quantitative finance. Before risking capital in live markets, you need a robust backtesting environment that can faithfully replay historical order book data with millisecond precision. In this comprehensive tutorial, I will walk you through building a complete backtesting pipeline using Tardis.dev's institutional-grade market data relay, from initial setup to running your first market making strategy simulation.

Introduction: Why Backtesting Market Making Strategies Is Different

Unlike directional trading strategies that rely on price series data, market making requires deep order book reconstruction. You need to understand not just where the price was, but who was willing to trade at each price level, how much liquidity existed, and how your orders would have interacted with the existing order book state. This is why Tardis.dev has become the preferred data provider for serious quant researchers—their normalized stream provides both raw exchange websockets and sorted, ordered book data that mirrors production infrastructure exactly.

I spent three months building our internal backtesting framework at HolySheep AI, and I can tell you that the data ingestion layer is where most teams stumble. Getting order book reconstruction right requires understanding both the data format and the replay mechanism. This tutorial will save you that three-month detour.

HolySheep AI provides free credits on registration for AI inference workloads that complement market data processing. With rates at ¥1=$1 (saving 85%+ compared to ¥7.3 industry standard) and sub-50ms latency, it's an ideal complement to your market data pipeline.

What You Will Build in This Tutorial

Prerequisites and System Requirements

Before we begin, ensure you have a working Python 3.10+ installation. For Windows users, I recommend using WSL2 (Windows Subsystem for Linux) as it provides better compatibility with the async libraries we'll be using. macOS and Linux users can proceed natively. You'll need approximately 50GB of free disk space for historical data storage, though you can start with smaller subsets while learning.

Setting Up Your Python Environment

Create a dedicated virtual environment to avoid dependency conflicts. I always use venv for market data projects since some libraries have specific version requirements that differ from other projects.

# Create and activate virtual environment
python3 -m venv backtest_env
source backtest_env/bin/activate  # Linux/macOS

backtest_env\Scripts\activate # Windows

Install core dependencies

pip install numpy pandas matplotlib pip install aiohttp asyncio_loop_aware_runner pip install tardis_client websocket-client pip install holy_sheep_ai # HolySheep AI SDK

Verify installation

python -c "import tardis; print(f'Tardis client version: {tardis.__version__}')" python -c "import holy_sheep; print('HolySheep AI SDK installed successfully')"

Screenshot hint: After running the verification commands, you should see version numbers printed without any ImportError messages. If you see red text, check the Common Errors section below.

Obtaining Tardis.dev API Credentials

Tardis.dev provides historical market data for Binance, Bybit, OKX, and Deribit with normalized formats across all exchanges. Sign up at their website to obtain your API key. The free tier includes access to sample datasets which are sufficient for learning. For production backtesting, you'll need a paid plan—typically starting around $99/month for adequate historical depth.

Store your credentials securely. Never commit API keys to version control. I use environment variables with a .env file and the python-dotenv library.

# Create .env file in your project root (DO NOT commit this to git)
cat > .env << 'EOF'
TARDIS_API_KEY=your_tardis_api_key_here
HOLYSHEEP_API_KEY=your_holysheep_key_here
EOF

Create config.py to load credentials

cat > config.py << 'EOF' import os from dotenv import load_dotenv load_dotenv() TARDIS_API_KEY = os.getenv("TARDIS_API_KEY") HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY")

HolySheep AI configuration

HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"

Model pricing (per 1M tokens) for cost estimation

MODEL_PRICING = { "gpt-4.1": 8.00, "claude-sonnet-4.5": 15.00, "gemini-2.5-flash": 2.50, "deepseek-v3.2": 0.42 } EOF

Add .env to .gitignore

echo ".env" >> .gitignore echo "__pycache__/" >> .gitignore

Downloading Historical Market Data from Tardis.dev

The Tardis.dev API provides multiple data types: trades, order book snapshots, and incremental updates. For market making backtesting, you need the order book data which includes both snapshots and diffs. Let's start with a simple data download script.

import os
import aiohttp
import asyncio
from tardis_client import TardisClient, Channel
from datetime import datetime, timedelta
import pandas as pd

Initialize client

tardis_client = TardisClient(TARDIS_API_KEY) async def download_btcusdt_orderbook(start_date: str, end_date: str): """ Download Binance BTC/USDT order book data for backtesting. Args: start_date: ISO format date string (e.g., '2024-01-01') end_date: ISO format date string (e.g., '2024-01-02') """ exchange = "binance" symbol = "btcusdt" # Channels specify data types: orderbook_L20 = top 20 levels channels = [ Channel.orderbook_usdt(symbol) # Binance perpetual futures format ] print(f"Downloading {symbol} orderbook from {start_date} to {end_date}") messages = [] # Replay data as iterator async for message in tardis_client.replay( exchange=exchange, from_date=start_date, to_date=end_date, channels=channels ): if message.type == "book_snapshot": messages.append({ "timestamp": message.timestamp, "asks": message.asks[:20], # Top 20 ask levels "bids": message.bids[:20], # Top 20 bid levels "local_timestamp": datetime.now() }) print(f"Downloaded {len(messages)} order book snapshots") return messages

Run download

if __name__ == "__main__": data = asyncio.run( download_btcusdt_orderbook("2024-06-01", "2024-06-02") ) # Save to parquet for fast loading df = pd.DataFrame(data) df.to_parquet("btcusdt_orderbook_2024_06.parquet") print(f"Saved {len(df)} records to btcusdt_orderbook_2024_06.parquet")

The Tardis.dev API returns data with microsecond-precision timestamps. For high-frequency backtesting, this precision is essential—two order book updates occurring within the same millisecond can have dramatically different implications for market making strategies.

Understanding Order Book Structure

An order book represents the current state of buy and sell orders for a trading pair. Each side contains price levels and the quantity available at each level. Here's how to visualize the structure:

import pandas as pd
import numpy as np

def analyze_orderbook_structure(messages_df):
    """
    Analyze the structure of order book data for market making insights.
    """
    
    # Flatten nested order book data
    flattened = []
    
    for idx, row in messages_df.head(100).iterrows():  # Sample first 100
        timestamp = row['timestamp']
        
        # Process asks (sell orders) - sorted low to high
        for price, quantity in row['asks']:
            flattened.append({
                'timestamp': timestamp,
                'side': 'ask',
                'price': float(price),
                'quantity': float(quantity),
                'level': len([p for p in row['asks'] if p[0] <= price])
            })
        
        # Process bids (buy orders) - sorted high to low
        for price, quantity in row['bids']:
            flattened.append({
                'timestamp': timestamp,
                'side': 'bid',
                'price': float(price),
                'quantity': float(quantity),
                'level': len([p for p in row['bids'] if p[0] >= price])
            })
    
    df = pd.DataFrame(flattened)
    
    # Key metrics for market making
    print("=== Order Book Analysis ===")
    print(f"Total price levels: {len(df)}")
    print(f"Spread (avg): {(df[df['side']=='ask']['price'].min() - df[df['side']=='bid']['price'].max()):.2f}")
    print(f"Avg bid size: {df[df['side']=='bid']['quantity'].mean():.4f}")
    print(f"Avg ask size: {df[df['side']=='ask']['quantity'].mean():.4f}")
    print(f"Bid depth (top 5): {df[(df['side']=='bid') & (df['level']<=5)]['quantity'].sum():.2f}")
    print(f"Ask depth (top 5): {df[(df['side']=='ask') & (df['level']<=5)]['quantity'].sum():.2f}")
    
    return df

Example usage

df = analyze_orderbook_structure(pd.read_parquet("btcusdt_orderbook_2024_06.parquet"))

Screenshot hint: When you run this analysis, you should see spread information, average order sizes, and liquidity depth metrics. The spread is your primary revenue source as a market maker—wider spreads mean more profit per trade but more inventory risk.

Building the Market Making Backtesting Engine

Now let's implement the core backtesting logic. A market making strategy places limit orders on both sides of the spread, earning the spread while managing inventory risk. The key challenge is modeling realistic fill probabilities based on order book state.

import numpy as np
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple
from datetime import datetime, timedelta
from enum import Enum

class OrderSide(Enum):
    BUY = "buy"
    SELL = "sell"

@dataclass
class Order:
    order_id: int
    side: OrderSide
    price: float
    quantity: float
    timestamp: datetime
    filled: bool = False
    fill_price: Optional[float] = None
    fill_time: Optional[datetime] = None

@dataclass
class MarketMakingState:
    """Tracks state for market making backtesting."""
    position: float = 0.0          # Current inventory (positive = long)
    cash: float = 0.0              # Available cash
    equity: float = 100000.0        # Starting equity
    bid_orders: List[Order] = field(default_factory=list)
    ask_orders: List[Order] = field(default_factory=list)
    trades: List[Dict] = field(default_factory=list)
    inventory_pnl: float = 0.0
    realized_pnl: float = 0.0

class MarketMakingBacktester:
    """
    Backtesting engine for market making strategies.
    
    Implements:
    - Order book replay
    - Realistic fill modeling based on queue position
    - Inventory risk management
    - Fee calculation (Binance perpetual: 0.02% maker, 0.05% taker)
    """
    
    def __init__(
        self,
        maker_fee: float = 0.0002,
        taker_fee: float = 0.0005,
        starting_equity: float = 100000.0,
        max_position: float = 1.0,  # Max BTC to hold
        target_spread_bps: float = 5.0  # Target spread in basis points
    ):
        self.maker_fee = maker_fee
        self.taker_fee = taker_fee
        self.starting_equity = starting_equity
        self.max_position = max_position
        self.target_spread_bps = target_spread_bps
        
        # HolySheep AI integration for strategy optimization
        self.holysheep_client = None
        if HOLYSHEEP_API_KEY:
            self._init_holysheep()
    
    def _init_holysheep(self):
        """Initialize HolySheep AI client for strategy analysis."""
        try:
            import openai
            self.holysheep_client = openai.OpenAI(
                api_key=HOLYSHEEP_API_KEY,
                base_url=HOLYSHEEP_BASE_URL
            )
            print("HolySheep AI client initialized for strategy analysis")
        except ImportError:
            print("OpenAI SDK required for HolySheep integration")
    
    def calculate_fill_probability(
        self,
        order_price: float,
        order_side: OrderSide,
        bids: List[Tuple],
        asks: List[Tuple]
    ) -> float:
        """
        Estimate fill probability based on order book state.
        
        Higher probability if:
        - Your price is deeper in the book (more queue ahead)
        - Market is moving toward your price
        - Spread is wide (less competition)
        """
        if order_side == OrderSide.BUY:
            # For buy orders, check how much liquidity exists at higher prices
            price_levels = [float(p) for p, q in bids]
            if order_price < min(price_levels) if price_levels else 0:
                return 0.0
            
            # Queue position: more orders ahead = lower probability
            orders_ahead = sum(1 for p in price_levels if p >= order_price)
            depth_ahead = sum(q for p, q in bids if p >= order_price)
            
        else:  # SELL
            price_levels = [float(p) for p, q in asks]
            if order_price > max(price_levels) if price_levels else float('inf'):
                return 0.0
            
            orders_ahead = sum(1 for p in price_levels if p <= order_price)
            depth_ahead = sum(q for p, q in asks if p <= order_price)
        
        # Fill probability decreases with queue depth
        # This is a simplified model - real models consider time
        base_prob = 1.0 / (1 + orders_ahead * 0.1 + depth_ahead * 0.001)
        return min(0.95, max(0.0, base_prob))
    
    def execute_market_making(
        self,
        orderbook_snapshots: List[Dict],
        volatility_multiplier: float = 1.0
    ) -> MarketMakingState:
        """
        Run market making backtest over order book data.
        
        Args:
            orderbook_snapshots: List of order book snapshots from Tardis.dev
            volatility_multiplier: Adjust spread based on volatility regime
        
        Returns:
            MarketMakingState with full trading history
        """
        state = MarketMakingState(equity=self.starting_equity)
        
        for snapshot in orderbook_snapshots:
            timestamp = snapshot['timestamp']
            bids = snapshot['bids']  # [(price, quantity), ...]
            asks = snapshot['asks']
            
            if not bids or not asks:
                continue
            
            mid_price = (float(bids[0][0]) + float(asks[0][0])) / 2
            
            # Dynamic spread based on target (5 bps) and volatility
            spread = mid_price * self.target_spread_bps / 10000 * volatility_multiplier
            
            bid_price = mid_price - spread / 2
            ask_price = mid_price + spread / 2
            
            # Check for fills against existing orders
            for order in state.bid_orders[:]:
                if float(asks[0][0]) <= order.price:
                    # Price moved up, our bid would have been filled
                    fill_prob = self.calculate_fill_probability(
                        order.price, OrderSide.BUY, bids, asks
                    )
                    if np.random.random() < fill_prob:
                        # Execute fill
                        state.position += order.quantity
                        state.cash -= order.price * order.quantity
                        state.cash -= order.price * order.quantity * self.maker_fee
                        order.filled = True
                        state.bid_orders.remove(order)
                        
                        state.trades.append({
                            'timestamp': timestamp,
                            'side': 'buy',
                            'price': order.price,
                            'quantity': order.quantity,
                            'fee': order.price * order.quantity * self.maker_fee
                        })
            
            # Similar logic for ask orders being filled
            for order in state.ask_orders[:]:
                if float(bids[0][0]) >= order.price:
                    fill_prob = self.calculate_fill_probability(
                        order.price, OrderSide.SELL, bids, asks
                    )
                    if np.random.random() < fill_prob:
                        state.position -= order.quantity
                        state.cash += order.price * order.quantity
                        state.cash -= order.price * order.quantity * self.maker_fee
                        order.filled = True
                        state.ask_orders.remove(order)
                        
                        state.trades.append({
                            'timestamp': timestamp,
                            'side': 'sell',
                            'price': order.price,
                            'quantity': order.quantity,
                            'fee': order.price * order.quantity * self.maker_fee
                        })
            
            # Place new orders (simplified: just one level each side)
            if state.position < self.max_position:
                new_bid = Order(
                    order_id=len(state.trades),
                    side=OrderSide.BUY,
                    price=bid_price,
                    quantity=0.1,  # Fixed lot size for demo
                    timestamp=timestamp
                )
                state.bid_orders.append(new_bid)
            
            if state.position > -self.max_position:
                new_ask = Order(
                    order_id=len(state.trades),
                    side=OrderSide.SELL,
                    price=ask_price,
                    quantity=0.1,
                    timestamp=timestamp
                )
                state.ask_orders.append(new_ask)
            
            # Update equity
            state.equity = state.cash + state.position * mid_price
        
        return state

Run backtest

if __name__ == "__main__": # Load downloaded data df = pd.read_parquet("btcusdt_orderbook_2024_06.parquet") # Initialize backtester backtester = MarketMakingBacktester( maker_fee=0.0002, taker_fee=0.0005, starting_equity=100000.0, target_spread_bps=5.0 ) # Run backtest state = backtester.execute_market_making(df.to_dict('records')) print(f"=== Backtest Results ===") print(f"Total Trades: {len(state.trades)}") print(f"Final Position: {state.position:.4f} BTC") print(f"Final Equity: ${state.equity:,.2f}") print(f"Return: {(state.equity/backtester.starting_equity - 1)*100:.2f}%")

Performance Analysis and Visualization

After running the backtest, you need to analyze performance with realistic metrics including slippage, fees, and inventory risk. HolySheep AI's low-cost inference (DeepSeek V3.2 at $0.42/MTok) makes it practical to run complex optimization loops that would be prohibitively expensive elsewhere.

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

def analyze_performance(trades: List[Dict], initial_equity: float):
    """
    Generate comprehensive performance analytics.
    """
    
    if not trades:
        print("No trades to analyze")
        return
    
    df = pd.DataFrame(trades)
    df['timestamp'] = pd.to_datetime(df['timestamp'])
    df = df.sort_values('timestamp')
    
    # Calculate cumulative metrics
    df['cumulative_volume'] = df['quantity'].cumsum()
    df['cumulative_fees'] = df['fee'].cumsum()
    
    # Running PnL (assuming mid-price revaluation)
    df['mid_price'] = df['price']  # Simplified
    df['realized_pnl'] = 0.0
    
    position = 0
    avg_price = 0
    
    for i, row in df.iterrows():
        if row['side'] == 'buy':
            position += row['quantity']
            avg_price = (avg_price * (position - row['quantity']) + row['price'] * row['quantity']) / position if position > 0 else 0
        else:
            pnl = (row['price'] - avg_price) * row['quantity']
            df.loc[i, 'realized_pnl'] = pnl
            position -= row['quantity']
    
    df['cumulative_pnl'] = df['realized_pnl'].cumsum()
    
    # Key metrics
    print("=== Performance Metrics ===")
    print(f"Total Volume: {df['quantity'].sum():.2f} BTC")
    print(f"Total Fees: ${df['cumulative_fees'].iloc[-1]:.2f}")
    print(f"Realized PnL: ${df['cumulative_pnl'].iloc[-1]:.2f}")
    print(f"Trades: {len(df)}")
    print(f"Avg Trade Size: {df['quantity'].mean():.4f} BTC")
    print(f"Win Rate: {(df['realized_pnl'] > 0).mean()*100:.1f}%")
    
    # Visualization
    fig, axes = plt.subplots(3, 1, figsize=(12, 10))
    
    # PnL over time
    axes[0].plot(df['timestamp'], df['cumulative_pnl'], label='Cumulative PnL')
    axes[0].set_ylabel('Cumulative PnL ($)')
    axes[0].set_title('Market Making Strategy Performance')
    axes[0].legend()
    axes[0].grid(True, alpha=0.3)
    
    # Trade volume
    axes[1].bar(df['timestamp'], df['quantity'], alpha=0.7, color=['green' if s=='buy' else 'red' for s in df['side']])
    axes[1].set_ylabel('Trade Size (BTC)')
    axes[1].set_title('Trade Volume by Side')
    axes[1].grid(True, alpha=0.3)
    
    # Cumulative fees
    axes[2].plot(df['timestamp'], df['cumulative_fees'], color='orange')
    axes[2].set_ylabel('Cumulative Fees ($)')
    axes[2].set_xlabel('Time')
    axes[2].set_title('Fee Accumulation')
    axes[2].grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.savefig('backtest_results.png', dpi=150)
    print("Saved visualization to backtest_results.png")
    
    return df

Analyze results

performance_df = analyze_performance(state.trades, backtester.starting_equity)

Screenshot hint: The generated PNG should show three charts: cumulative PnL line chart (should generally trend upward for profitable strategies), volume bars colored by side (green buys, red sells), and fee accumulation curve (typically linear or slightly increasing with volatility).

Integrating HolySheep AI for Strategy Optimization

One powerful application of AI in market making is optimizing strategy parameters based on backtest results. HolySheep AI provides cost-effective inference for running optimization loops that would cost hundreds of dollars with other providers. With DeepSeek V3.2 at $0.42 per million tokens, you can iterate extensively without budget concerns.

from typing import Dict, List, Optional
import json

class StrategyOptimizer:
    """
    Uses HolySheep AI to optimize market making parameters.
    
    Analyzes backtest results and suggests parameter adjustments.
    """
    
    def __init__(self, api_key: str, base_url: str = "https://api.holysheep.ai/v1"):
        try:
            from openai import OpenAI
            self.client = OpenAI(api_key=api_key, base_url=base_url)
            self.model = "deepseek-v3.2"  # Cost-effective model for optimization
        except ImportError:
            print("Install openai package: pip install openai")
            self.client = None
    
    def analyze_and_suggest(
        self,
        backtest_results: Dict,
        current_params: Dict
    ) -> Optional[Dict]:
        """
        Analyze backtest results and get AI-powered optimization suggestions.
        
        Args:
            backtest_results: Dict containing performance metrics
            current_params: Current strategy parameters
        
        Returns:
            Suggested parameter adjustments
        """
        if not self.client:
            return None
        
        prompt = f"""Analyze this market making backtest and suggest parameter optimizations.

Current Parameters:
{json.dumps(current_params, indent=2)}

Backtest Results:
{json.dumps(backtest_results, indent=2)}

Provide specific, actionable parameter adjustments for:
1. Optimal spread (in basis points)
2. Order sizing strategy
3. Inventory risk limits
4. Any observed inefficiencies

Be concise and specific with numerical recommendations."""

        try:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=[
                    {"role": "system", "content": "You are an expert quantitative trading advisor specializing in market making strategies."},
                    {"role": "user", "content": prompt}
                ],
                temperature=0.3,  # Low temperature for consistent recommendations
                max_tokens=500
            )
            
            suggestions = response.choices[0].message.content
            print("=== AI Strategy Suggestions ===")
            print(suggestions)
            
            return {"suggestions": suggestions, "model_used": self.model}
            
        except Exception as e:
            print(f"Optimization error: {e}")
            return None

Example usage

if __name__ == "__main__": optimizer = StrategyOptimizer( api_key=HOLYSHEEP_API_KEY, base_url=HOLYSHEEP_BASE_URL ) sample_results = { "total_trades": 1523, "realized_pnl": 2340.50, "max_drawdown": -850.00, "avg_slippage_bps": 2.3, "inventory_skew": 0.15, # Position bias "win_rate": 0.72 } current_params = { "target_spread_bps": 5.0, "order_size": 0.1, "max_position": 1.0, "rebalance_threshold": 0.5 } suggestions = optimizer.analyze_and_suggest(sample_results, current_params)

Tardis.dev vs Alternative Data Sources

When building a market making backtesting infrastructure, you have several data provider options. Here's how Tardis.dev compares to the alternatives:

Feature Tardis.dev CCXT Pro Exchange Native APIs Alternative Providers
Historical Depth 2+ years Limited (exchange-dependent) 7-30 days typically Varies widely
Order Book Snapshots Full L20+ support Partial Requires polling Usually top 10
Normalization Unified format across exchanges Partial Exchange-specific Usually normalized
Replay Capability Native async replay Manual implementation Not available Usually limited
Latency (Live) <10ms <50ms <20ms 20-100ms
Starting Price $99/month $30/month Free (rate limits) $200+/month
Supported Exchanges Binance, Bybit, OKX, Deribit, +8 40+ exchanges 1 exchange Varies
Python SDK First-class support Official Community Variable

Who This Tutorial Is For

This Tutorial Is For:

This Tutorial Is NOT For:

Pricing and ROI Analysis

Building a market making backtesting system involves several cost components. Here's a realistic budget breakdown:

Component Free Tier Starter ($99/mo) Professional ($499/mo) HolySheep AI Add-on
Tardis.dev Data Limited historical 6 months depth 2+ years depth N/A
AI Optimization N/A N/A N/A $0.42/MTok (DeepSeek)
Compute (EC2 t3.medium) $0.04/hr $0.04/hr $0.04/hr Same
Storage (100GB) $10/month $10/month $10/month Same
Monthly Total (est) $15-30 $115-150 $515-600 +$20-50 for AI

ROI Considerations: A profitable market making strategy trading $1M notional daily with 5 bps spread earns approximately $500/day or $150,000 annually. The backtesting infrastructure cost ($115-600/month) represents less than 0.5% of potential revenue. HolySheep AI's optimization suggestions at $0.42/MTok enable iteration cycles that would cost $15+/MTok with OpenAI—saving 97%+ on strategy research.

Why Choose HolySheep AI for Your Market Data Pipeline

While this tutorial focuses on Tardis.dev for market data, HolySheep AI complements your infrastructure in several ways:

Common Errors and Fixes

Related Resources

Related Articles