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
- A complete Python development environment for market data analysis
- Historical market data retrieval from Tardis.dev API
- Order book state reconstruction and visualization
- A functional market making backtesting engine
- Performance analytics with realistic slippage modeling
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:
- Quantitative researchers building market making strategies from scratch who need realistic backtesting
- Individual traders with Python experience looking to understand order book dynamics
- Hedge fund analysts evaluating data providers for backtesting infrastructure
- Students learning quantitative finance with limited budget (Tardis.dev free tier works for learning)
- Developers building trading infrastructure who need to understand market data formats
This Tutorial Is NOT For:
- Traders with no programming experience — you need Python familiarity
- Those seeking trading signals — this is infrastructure, not strategy tips
- Users needing real-time trading — this covers backtesting only
- People requiring crypto data outside supported exchanges — Binance/Bybit/OKX/Deribit only
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:
- Cost Efficiency: At ¥1=$1 (85%+ savings vs. ¥7.3 standard pricing), HolySheep AI makes extensive strategy optimization affordable. Running 1000 optimization iterations at 10K tokens each costs only $4.20 with DeepSeek V3.2.
- Low Latency: Sub-50ms inference latency ensures your optimization loops don't become bottlenecks in rapid iteration cycles.
- Multi-Model Flexibility: Access GPT-4.1 ($8/MTok), Claude Sonnet 4.5 ($15/MTok), Gemini 2.5 Flash ($2.50/MTok), and DeepSeek V3.2 ($0.42/MTok) through a single API with unified interface.
- Payment Flexibility: Support for WeChat Pay and Alipay alongside international options removes friction for users in Asia-Pacific markets.
- Free Credits: New registrations receive complimentary credits to evaluate the service before commitment.