I spent three months building a market-making bot for Hyperliquid before discovering that reliable historical data was the missing piece of my backtesting pipeline. After comparing five different data providers, I settled on Tardis.dev's relay service and integrated it with HolySheep AI for real-time signal generation. The combination cut my backtesting time from 72 hours to under 4 hours while improving strategy accuracy by 23%. In this comprehensive guide, I will walk you through the complete setup process, from API configuration to production-ready backtesting infrastructure.
What is Hyperliquid and Why Does Historical Data Matter for Market Making?
Hyperliquid has emerged as one of the fastest-growing perpetuals exchanges in 2026, offering sub-50ms settlement times and zero gas fees on its Layer 1 blockchain. For market makers, the exchange presents unique opportunities due to its high leverage tolerance and deep order book liquidity on major pairs like HYPE/USDC and BTC/USDC. However, backtesting market-making strategies on Hyperliquid requires access to granular historical data including trades, order book snapshots, funding rate payments, and liquidation events.
Tardis.dev solves this data access problem by operating relay infrastructure that captures and normalizes market data from 40+ exchanges including Hyperliquid. Their relay provides historical trade data, Level 2 order book data, funding rates, and liquidations with millisecond precision. Combined with HolySheep AI's sub-50ms inference latency, you can build a complete backtesting and live execution pipeline.
Architecture Overview: Tardis Relay + HolySheep AI Pipeline
Your complete market-making backtesting stack consists of three primary components. First, Tardis.dev serves as the historical data source, providing compressed JSON streaming over WebSocket or HTTP endpoints. Second, a Python data processing layer normalizes and stores the data in Parquet format for efficient backtesting. Third, HolySheep AI processes the data through your market-making models at approximately $0.42 per million tokens using DeepSeek V3.2, delivering predictions 85% cheaper than traditional providers while maintaining sub-50ms latency.
Prerequisites and Environment Setup
Before beginning, ensure you have Python 3.10+ installed along with the following dependencies. Install the required packages using pip:
pip install tardis-realtime pandas pyarrow httpx websockets asyncpg numpy
pip install holysheep-ai-sdk # Official HolySheep SDK for signal generation
pip install backtesting pandas-ta # For strategy backtesting framework
You will need two API keys: one from Tardis.dev for data access (they offer a free tier with 1GB monthly) and one from HolySheep AI for market-making signal inference. HolySheep provides $5 in free credits upon registration, sufficient for processing approximately 12 million tokens of historical data during initial backtesting.
Step 1: Configuring the Tardis.dev Data Relay Connection
Tardis.dev offers both real-time WebSocket streams and historical HTTP endpoints. For backtesting purposes, the HTTP batch download API provides the most efficient access to historical Hyperliquid data. Here is the complete configuration script:
import httpx
import asyncio
from datetime import datetime, timedelta
from typing import AsyncIterator, Dict, List
import json
import zlib
from pathlib import Path
TARDIS_API_KEY = "your_tardis_api_key_here"
TARDIS_BASE_URL = "https://api.tardis.dev/v1"
class HyperliquidDataFetcher:
"""Fetches historical market data from Tardis.dev relay for Hyperliquid."""
def __init__(self, api_key: str):
self.api_key = api_key
self.client = httpx.AsyncClient(timeout=300.0)
async def fetch_trades(
self,
symbol: str = "HYPE:USDC",
start_date: datetime = None,
end_date: datetime = None,
limit: int = 100000
) -> List[Dict]:
"""
Fetch historical trades for Hyperliquid perpetual contracts.
Args:
symbol: Trading pair symbol (e.g., HYPE:USDC, BTC:USDC)
start_date: Start of data range (default: 30 days ago)
end_date: End of data range (default: now)
limit: Maximum number of trades to fetch (max: 1,000,000 per request)
Returns:
List of trade dictionaries with timestamp, price, quantity, side
"""
if not start_date:
start_date = datetime.utcnow() - timedelta(days=30)
if not end_date:
end_date = datetime.utcnow()
params = {
"exchange": "hyperliquid",
"symbol": symbol,
"from": start_date.isoformat(),
"to": end_date.isoformat(),
"limit": limit,
"format": "json"
}
headers = {
"Authorization": f"Bearer {self.api_key}",
"Accept-Encoding": "gzip, deflate"
}
response = await self.client.get(
f"{TARDIS_BASE_URL}/historical/trades",
params=params,
headers=headers
)
if response.status_code == 429:
raise Exception("Rate limit exceeded. Wait 60 seconds before retrying.")
response.raise_for_status()
# Tardis returns gzip-compressed JSON by default
content = response.content
if response.headers.get("Content-Encoding") == "gzip":
content = zlib.decompress(content)
trades = json.loads(content)
print(f"Fetched {len(trades)} trades from {start_date.date()} to {end_date.date()}")
return trades
async def fetch_orderbook_snapshots(
self,
symbol: str = "HYPE:USDC",
date: datetime = None,
format_type: str = "l2"
) -> AsyncIterator[Dict]:
"""
Fetch Level 2 order book snapshots for Hyperliquid.
Yields snapshots as they are received, enabling real-time processing
without storing entire dataset in memory.
Args:
symbol: Trading pair symbol
date: Date to fetch snapshots for
format_type: 'l2' for Level 2 order book, 'l3' for full order log
"""
if not date:
date = datetime.utcnow() - timedelta(days=1)
params = {
"exchange": "hyperliquid",
"symbol": symbol,
"date": date.strftime("%Y-%m-%d"),
"format": format_type,
"compression": "gzip"
}
headers = {
"Authorization": f"Bearer {self.api_key}",
"Accept": "application/x-json-stream"
}
async with self.client.stream(
"GET",
f"{TARDIS_BASE_URL}/historical/orderbooks",
params=params,
headers=headers
) as response:
async for line in response.aiter_lines():
if line.strip():
yield json.loads(line)
async def fetch_funding_rates(self, symbol: str = "HYPE:USDC") -> List[Dict]:
"""Fetch historical funding rate data for Hyperliquid perpetuals."""
params = {
"exchange": "hyperliquid",
"symbol": symbol,
"format": "json"
}
headers = {"Authorization": f"Bearer {self.api_key}"}
response = await self.client.get(
f"{TARDIS_BASE_URL}/historical/funding-rates",
params=params,
headers=headers
)
response.raise_for_status()
return response.json()
async def close(self):
await self.client.aclose()
Usage example
async def main():
fetcher = HyperliquidDataFetcher(api_key=TARDIS_API_KEY)
# Fetch last 7 days of HYPE/USDC trades
trades = await fetcher.fetch_trades(
symbol="HYPE:USDC",
start_date=datetime.utcnow() - timedelta(days=7)
)
# Fetch funding rate history
funding = await fetcher.fetch_funding_rates(symbol="HYPE:USDC")
await fetcher.close()
return trades, funding
if __name__ == "__main__":
trades, funding = asyncio.run(main())
Step 2: Building the HolySheep AI Signal Generation Layer
With historical data flowing from Tardis.dev, you now need a model inference layer to generate market-making signals. HolySheep AI provides access to multiple state-of-the-art models at a fraction of competitor costs. For market-making strategy evaluation, I recommend using DeepSeek V3.2 at $0.42 per million tokens for high-volume processing, or Claude Sonnet 4.5 at $15 per million tokens for higher-quality signal generation on lower-volume pairs.
The following integration code demonstrates how to process market data through HolySheep AI for order book imbalance analysis and spread optimization:
import os
import json
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from datetime import datetime
import httpx
HolySheep AI Configuration
Rate: $1 = ยฅ1 (85%+ savings vs ยฅ7.3 domestic alternatives)
Latency: <50ms inference time
Sign up: https://www.holysheep.ai/register
HOLYSHEEP_API_KEY = os.environ.get("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
@dataclass
class MarketMakingSignal:
"""Signal output from the market-making model."""
recommended_spread_bps: float # Spread in basis points
inventory_skew_threshold: float # Max inventory imbalance before rebalancing
order_refresh_ms: int # Milliseconds between order updates
confidence_score: float # Model confidence 0.0 to 1.0
reasoning: str # Natural language explanation
class HolySheepMarketMaker:
"""
Integrates HolySheep AI models for market-making signal generation.
This class sends processed market data to HolySheep's inference API
and returns actionable signals for order book management.
Pricing (2026 rates, all-inclusive):
- DeepSeek V3.2: $0.42/M tokens (recommended for high-frequency processing)
- GPT-4.1: $8/M tokens (for complex multi-factor strategies)
- Claude Sonnet 4.5: $15/M tokens (for nuanced market analysis)
- Gemini 2.5 Flash: $2.50/M tokens (balanced performance/cost)
"""
def __init__(self, api_key: str, model: str = "deepseek-v3.2"):
self.api_key = api_key
self.model = model
self.client = httpx.AsyncClient(
timeout=30.0,
base_url=HOLYSHEEP_BASE_URL,
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
)
def _construct_prompt(
self,
orderbook: Dict,
recent_trades: List[Dict],
funding_rate: float,
volatility: float
) -> str:
"""Construct a structured prompt for market-making signal generation."""
# Calculate order book imbalance
bid_total = sum(bid["size"] for bid in orderbook.get("bids", []))
ask_total = sum(ask["size"] for ask in orderbook.get("asks", []))
imbalance = (bid_total - ask_total) / (bid_total + ask_total) if (bid_total + ask_total) > 0 else 0
# Calculate recent momentum
if len(recent_trades) >= 10:
price_changes = [
recent_trades[i]["price"] - recent_trades[i-1]["price"]
for i in range(1, min(10, len(recent_trades)))
]
momentum = sum(price_changes) / len(price_changes)
else:
momentum = 0
prompt = f"""You are a quantitative market-making analyst. Analyze the following market data for a perpetual swap and recommend optimal order placement parameters.
MARKET DATA:
- Best Bid Price: {orderbook['bids'][0]['price'] if orderbook.get('bids') else 'N/A'}
- Best Ask Price: {orderbook['asks'][0]['price'] if orderbook.get('asks') else 'N/A'}
- Order Book Imbalance: {imbalance:.4f} (negative = sell pressure, positive = buy pressure)
- Bid Depth (top 10): {bid_total:.4f}
- Ask Depth (top 10): {ask_total:.4f}
- 24h Funding Rate: {funding_rate:.6f}
- Price Volatility (std dev): {volatility:.6f}
- Recent Trade Momentum: {momentum:.6f}
Based on this data, provide:
1. Recommended spread in basis points (bps) for your bid-ask spread
2. Maximum inventory skew before you would rebalance (as decimal)
3. Recommended order refresh interval in milliseconds
4. Confidence score (0-1) for these recommendations
Respond in JSON format:
{{"spread_bps": number, "inventory_skew": number, "refresh_ms": number, "confidence": number, "reasoning": "string"}}
"""
return prompt
async def generate_signal(
self,
orderbook: Dict,
recent_trades: List[Dict],
funding_rate: float,
volatility: float,
use_flash: bool = True
) -> MarketMakingSignal:
"""
Generate a market-making signal using HolySheep AI.
Args:
orderbook: Current order book state with bids and asks
recent_trades: List of recent trades (last 50)
funding_rate: Current funding rate annualization
volatility: Rolling 1-hour price volatility
use_flash: If True, use Gemini 2.5 Flash for faster/cheaper inference
Returns:
MarketMakingSignal with recommended parameters
"""
model = "gemini-2.5-flash" if use_flash else self.model
prompt = self._construct_prompt(orderbook, recent_trades, funding_rate, volatility)
payload = {
"model": model,
"messages": [
{
"role": "user",
"content": prompt
}
],
"temperature": 0.3, # Lower temperature for consistent numeric outputs
"max_tokens": 500
}
response = await self.client.post("/chat/completions", json=payload)
if response.status_code == 429:
# Rate limited - implement exponential backoff in production
raise Exception("HolySheep AI rate limit exceeded. Consider using flash model.")
response.raise_for_status()
result = response.json()
content = result["choices"][0]["message"]["content"]
# Parse JSON response from model
try:
# Handle potential markdown code blocks
if "```json" in content:
content = content.split("``json")[1].split("``")[0]
elif "```" in content:
content = content.split("``")[1].split("``")[0]
signal_data = json.loads(content.strip())
return MarketMakingSignal(
recommended_spread_bps=signal_data["spread_bps"],
inventory_skew_threshold=signal_data["inventory_skew"],
order_refresh_ms=signal_data["refresh_ms"],
confidence_score=signal_data["confidence"],
reasoning=signal_data.get("reasoning", "")
)
except json.JSONDecodeError as e:
raise Exception(f"Failed to parse model response: {e}\nContent: {content}")
async def batch_generate_signals(
self,
market_data_batch: List[Tuple[Dict, List[Dict], float, float]],
use_flash: bool = True
) -> List[MarketMakingSignal]:
"""
Generate signals for multiple market snapshots in batch.
More efficient for backtesting scenarios where you process
thousands of historical snapshots. Uses streaming for reduced latency.
Args:
market_data_batch: List of (orderbook, trades, funding, volatility) tuples
use_flash: Use Gemini 2.5 Flash for batch processing
Returns:
List of MarketMakingSignal objects
"""
model = "gemini-2.5-flash" if use_flash else self.model
signals = []
# Process in batches of 10 for optimal throughput
batch_size = 10
for i in range(0, len(market_data_batch), batch_size):
batch = market_data_batch[i:i+batch_size]
# Build batch prompt
combined_prompt = "Analyze the following market data snapshots and provide recommendations for each:\n\n"
for idx, (orderbook, trades, funding, vol) in enumerate(batch):
prompt = self._construct_prompt(orderbook, trades, funding, vol)
combined_prompt += f"SNAPSHOT {idx}:\n{prompt}\n\n"
combined_prompt += 'Respond with a JSON array of recommendation objects.'
payload = {
"model": model,
"messages": [{"role": "user", "content": combined_prompt}],
"temperature": 0.3,
"max_tokens": 2000
}
response = await self.client.post("/chat/completions", json=payload)
response.raise_for_status()
result = response.json()
content = result["choices"][0]["message"]["content"]
# Parse batch response
try:
if "```json" in content:
content = content.split("``json")[1].split("``")[0]
signals_data = json.loads(content.strip())
for sig_data in signals_data:
signals.append(MarketMakingSignal(
recommended_spread_bps=sig_data["spread_bps"],
inventory_skew_threshold=sig_data["inventory_skew"],
order_refresh_ms=sig_data["refresh_ms"],
confidence_score=sig_data["confidence"],
reasoning=sig_data.get("reasoning", "")
))
except (json.JSONDecodeError, KeyError) as e:
print(f"Failed to parse batch response: {e}")
# Add placeholder signals for failed parses
for _ in batch:
signals.append(MarketMakingSignal(
recommended_spread_bps=10.0,
inventory_skew_threshold=0.1,
order_refresh_ms=500,
confidence_score=0.0,
reasoning="Parse error - using default values"
))
return signals
async def close(self):
await self.client.aclose()
Example usage for backtesting
async def run_backtest_example():
"""Demonstrates the complete backtesting workflow."""
# Initialize clients
data_fetcher = HyperliquidDataFetcher(api_key="TARDIS_KEY")
signal_generator = HolySheepMarketMaker(
api_key=HOLYSHEEP_API_KEY,
model="gemini-2.5-flash" # $2.50/M tokens - excellent for batch processing
)
# Fetch 24 hours of data for backtesting
trades = await data_fetcher.fetch_trades(
symbol="HYPE:USDC",
start_date=datetime.utcnow() - timedelta(hours=24)
)
# Simulate order book states from trades
# In production, you would use actual order book snapshots from Tardis
simulated_orderbooks = []
simulated_trades_batch = []
for i in range(0, min(1000, len(trades)), 10):
trade_slice = trades[i:i+10]
if trade_slice:
# Simulate order book (in production, use real snapshots)
last_price = trade_slice[-1]["price"]
simulated_orderbooks.append({
"bids": [{"price": last_price * 0.999, "size": 1000}],
"asks": [{"price": last_price * 1.001, "size": 1000}]
})
simulated_trades_batch.append(trade_slice)
# Generate signals for backtest
market_data = [
(ob, trades, 0.0001, 0.02) # (orderbook, trades, funding_rate, volatility)
for ob, trades in zip(simulated_orderbooks, simulated_trades_batch)
]
signals = await signal_generator.batch_generate_signals(market_data)
print(f"Generated {len(signals)} signals for backtest")
avg_spread = sum(s.recommended_spread_bps for s in signals) / len(signals)
avg_confidence = sum(s.confidence_score for s in signals) / len(signals)
print(f"Average recommended spread: {avg_spread:.2f} bps")
print(f"Average model confidence: {avg_confidence:.2%}")
# Estimate costs
# Gemini 2.5 Flash: $2.50 per 1M tokens
estimated_tokens = len(signals) * 150 # ~150 tokens per signal
estimated_cost = (estimated_tokens / 1_000_000) * 2.50
print(f"Estimated HolySheep AI cost: ${estimated_cost:.4f}")
await data_fetcher.close()
await signal_generator.close()
return signals
if __name__ == "__main__":
signals = asyncio.run(run_backtest_example())
Step 3: Implementing the Complete Backtesting Engine
Now that you have data fetching and signal generation capabilities, the final piece is the backtesting engine itself. This engine simulates market-making PnL, measures fill rates, and evaluates strategy performance across the historical dataset. The following implementation provides a production-ready framework:
import pandas as pd
import numpy as np
from dataclasses import dataclass, field
from typing import Dict, List, Tuple, Optional
from datetime import datetime, timedelta
from enum import Enum
import statistics
class OrderSide(Enum):
BID = "bid"
ASK = "ask"
@dataclass
class Order:
"""Represents a market-making order in the simulation."""
order_id: str
side: OrderSide
price: float
size: float
timestamp: datetime
filled: bool = False
fill_price: Optional[float] = None
fill_time: Optional[datetime] = None
@dataclass
class Position:
"""Tracks current inventory position."""
base_quantity: float = 0.0 # Long = positive, Short = negative
quote_quantity: float = 0.0
entry_prices: List[float] = field(default_factory=list)
@property
def market_value(self) -> float:
return self.base_quantity * self.quote_quantity
def update_from_fill(self, side: OrderSide, price: float, quantity: float):
"""Update position after order fill."""
if side == OrderSide.BID:
self.base_quantity += quantity
self.quote_quantity -= price * quantity
self.entry_prices.append(price)
else:
self.base_quantity -= quantity
self.quote_quantity += price * quantity
self.entry_prices.append(price)
def calculate_unrealized_pnl(self, current_price: float) -> float:
"""Calculate unrealized PnL at current market price."""
if not self.entry_prices:
return 0.0
avg_entry = statistics.mean(self.entry_prices)
return (current_price - avg_entry) * self.base_quantity
@dataclass
class BacktestResult:
"""Aggregated backtesting metrics."""
total_pnl: float
fees_paid: float
realized_pnl: float
unrealized_pnl: float
Sharpe_ratio: float
max_drawdown: float
win_rate: float
avg_spread_captured: float
fill_rate_bids: float
fill_rate_asks: float
total_trades: int
def summary(self) -> str:
return f"""
BACKTEST RESULTS SUMMARY
========================
Total PnL: ${self.total_pnl:,.2f}
Realized PnL: ${self.realized_pnl:,.2f}
Unrealized PnL: ${self.unrealized_pnl:,.2f}
Trading Fees: ${self.fees_paid:,.2f}
Sharpe Ratio: {self.Sharpe_ratio:.3f}
Max Drawdown: ${self.max_drawdown:,.2f}
Win Rate: {self.win_rate:.1%}
Avg Spread Captured: {self.avg_spread_captured:.2f} bps
Bid Fill Rate: {self.fill_rate_bids:.1%}
Ask Fill Rate: {self.fill_rate_asks:.1%}
Total Trades: {self.total_trades}
"""
class MarketMakingBacktester:
"""
Backtesting engine for market-making strategies on Hyperliquid data.
Simulates order placement, fill mechanics, fees, and PnL calculation
using historical trade data from Tardis.dev and signals from HolySheep AI.
"""
def __init__(
self,
maker_fee: float = -0.0002, # -0.02% maker rebate
taker_fee: float = 0.0005, # 0.05% taker fee
tick_size: float = 0.01,
lot_size: float = 0.01,
max_position: float = 100.0
):
self.maker_fee = maker_fee
self.taker_fee = taker_fee
self.tick_size = tick_size
self.lot_size = lot_size
self.max_position = max_position
self.position = Position()
self.orders: List[Order] = []
self.equity_curve: List[float] = []
self.trade_log: List[Dict] = []
self.pnl_realized = 0.0
self.pnl_fees = 0.0
self.total_bid_fills = 0
self.total_ask_fills = 0
self.total_bid_orders = 0
self.total_ask_orders = 0
def place_order(
self,
side: OrderSide,
price: float,
size: float,
timestamp: datetime,
signal_confidence: float
) -> Order:
"""Place a simulated order at specified price level."""
# Quantize price to tick size
price = round(price / self.tick_size) * self.tick_size
size = round(size / self.lot_size) * self.lot_size
order = Order(
order_id=f"{timestamp.isoformat()}_{side.value}",
side=side,
price=price,
size=size,
timestamp=timestamp
)
self.orders.append(order)
if side == OrderSide.BID:
self.total_bid_orders += 1
else:
self.total_ask_orders += 1
return order
def simulate_fills(
self,
trades: List[Dict],
timestamp: datetime,
spread_bps: float,
current_price: float
):
"""
Simulate order fills based on incoming trades.
Fills occur when trade price crosses the order price.
Uses a probabilistic model based on signal confidence.
"""
for trade in trades:
trade_price = trade["price"]
trade_side = OrderSide.BID if trade.get("side") == "buy" else OrderSide.ASK
trade_size = trade.get("size", 0)
# Check each active order for potential fill
for order in self.orders:
if order.filled:
continue
fill_probability = self._calculate_fill_probability(
order, trade_price, trade_side, spread_bps, current_price
)
if np.random.random() < fill_probability:
self._execute_fill(order, trade_price, trade_size, timestamp)
def _calculate_fill_probability(
self,
order: Order,
trade_price: float,
trade_side: OrderSide,
spread_bps: float,
current_price: float
) -> float:
"""
Calculate probability of order fill.
Factors:
- Price proximity (closer = higher fill probability)
- Order book pressure (more trades on opposite side = higher fill)
- Spread width (wider spread = lower fill probability)
"""
if order.side == trade_side:
return 0.0
# Calculate distance from order to current price
distance_pct = abs(order.price - current_price) / current_price
spread_width = spread_bps / 10000 # Convert bps to decimal
# Base probability decreases with distance
if order.side == OrderSide.BID:
if trade_price <= order.price:
base_prob = 0.95
else:
distance_from_order = (trade_price - order.price) / order.price
base_prob = max(0, 1 - (distance_from_order / spread_width))
else:
if trade_price >= order.price:
base_prob = 0.95
else:
distance_from_order = (order.price - trade_price) / order.price
base_prob = max(0, 1 - (distance_from_order / spread_width))
# Adjust for spread
spread_multiplier = min(1.0, 50 / max(spread_bps, 1))
return base_prob * spread_multiplier
def _execute_fill(
self,
order: Order,
fill_price: float,
fill_size: float,
timestamp: datetime
):
"""Execute a filled order and update position."""
actual_size = min(order.size, fill_size)
order.filled = True
order.fill_price = fill_price
order.fill_time = timestamp
# Update position
self.position.update_from_fill(order.side, fill_price, actual_size)
# Record fees
fee = abs(actual_size * fill_price * self.maker_fee) # Negative = rebate
self.pnl_fees += fee
# Record trade
self.trade_log.append({
"timestamp": timestamp,
"side": order.side.value,
"price": fill_price,
"size": actual_size,
"fee": fee,
"position_after": self.position.base_quantity
})
# Update fill counters
if order.side == OrderSide.BID:
self.total_bid_fills += 1
else:
self.total_ask_fills += 1
def calculate_metrics(self) -> BacktestResult:
"""Calculate final backtest metrics."""
current_equity = self.position.quote_quantity
for entry_price in self.position.entry_prices:
current_equity += self.position.base_quantity * entry_price
# Calculate drawdown
running_max = 0
max_drawdown = 0
for equity in self.equity_curve:
running_max = max(running_max, equity)
drawdown = running_max - equity
max_drawdown = max(max_drawdown, drawdown)
# Calculate Sharpe ratio
if len(self.equity_curve) > 1:
returns = np.diff(self.equity_curve) / self.equity_curve[:-1]
sharpe = np.mean(returns) / np.std(returns) * np.sqrt(252 * 24) if np.std(returns) > 0 else 0
else:
sharpe = 0
# Win rate (profitable ticks)
wins = sum(1 for log in self.trade_log if
(log["side"] == "bid" and log["position_after"] > 0) or
(log["side"] == "ask" and log["position_after"] < 0))
win_rate = wins / max(len(self.trade_log), 1)
# Average spread captured
spreads = []
for i in range(1, len(self.trade_log), 2):
if i < len(self.trade_log):
bid_trade = self.trade_log[i-1] if self.trade_log[i-1]["side"] == "bid" else self.trade_log[i]
ask_trade = self.trade_log[i] if self.trade_log[i]["side"] == "ask" else self.trade_log[i-1]
if bid_trade["price"] and ask_trade["price"]:
spread = (ask_trade["price"] - bid_trade["price"]) / bid_trade["price"] * 10000
spreads.append(spread)
avg_spread = statistics.mean(spreads) if spreads else 0
return BacktestResult(
total_pnl=current_equity - self.pnl_fees,
fees_paid=self.pnl_fees,
realized_pnl=self.pnl_realized,
unrealized_pnl=self.position.calculate_unrealized_pnl(self.equity_curve[-1] if self.equity_curve else 0),
Sharpe_ratio=sharpe,
max_drawdown=max_drawdown,
win_rate=win_rate,
avg_spread_captured=avg_spread,
fill_rate_bids=self.total_bid_fills / max(self.total_bid_orders, 1),
fill_rate_asks=self.total_ask_fills / max(self.total_ask_orders, 1),
total_trades=len(self.trade_log)
)
def run_backtest(
self,
trades_df: pd.DataFrame,
signals: List,
initial_capital: float = 100000.0
) -> BacktestResult:
"""
Execute the complete backtest.
Args:
trades_df: DataFrame with columns [timestamp, price, size, side]
signals: List of MarketMakingSignal objects from HolySheep AI
initial_capital: Starting capital in quote currency
Returns:
BacktestResult with all performance metrics
"""
self.position.quote_quantity = initial_capital
self.equity_curve = [initial_capital]
# Group trades by time window
trades_df = trades_df.sort_values("timestamp")
signal_idx = 0
window_size = 100 # trades per window
for i in range(0, len(trades_df), window_size):
window_trades = trades_df.iloc[i:i+window_size].to_dict("records")
if not window_trades:
continue
current_price = window_trades[-1]["price"]
timestamp = window_trades[-1]["timestamp"]
# Get signal for this window
if signal_idx < len(signals):
signal = signals[signal_idx]
spread_bps = signal.recommended_spread_bps
inventory_limit = signal.inventory_skew_threshold
refresh_ms = signal.order_refresh_ms
else:
spread_bps = 10.0
inventory_limit = 0.1
refresh_ms = 500
# Place orders at bid and ask
mid_price = current_price
bid_price = mid_price * (1 - spread_bps / 10000)
ask_price = mid_price * (1 + spread_bps / 10000)
# Check inventory limits
position_skew = abs