导言:为什么你的回测系统总是失败?
在加密货币高频做市领域,回测系统的准确性直接决定了策略的生死。我见过太多团队耗资数十万美元搭建的回测环境,最终却在实盘中亏损严重。问题根源往往只有一个:Order Book深度数据失真。
本文将从迁移Playbook的视角,详细讲解如何从传统API或低质量数据源切换到HolySheep AI的高性能API,实现毫秒级精度的Order Book重建回测。包含完整的Python/TypeScript代码、常见错误解决方案,以及真实的ROI分析。
Order Book重建的核心原理
为什么深度数据至关重要?
做市商的核心竞争力在于订单簿的微观结构理解。当我们在 Binance、OKX 或 Bybit 上挂单时,实际上是在订单簿的特定价格层级等待成交。回测系统如果无法准确模拟以下要素,策略表现将严重失真:
- 订单簿的动态更新频率(Binance Futures: 100ms,OKX: 200ms)
- 流动性分布的幂律特征(价格越接近中间,流动性越高)
- 大单冲击下的价格滑点(0.1% - 2.5% 不等)
- 订单簿重建延迟导致的"未来函数"偏差
HolySheep的优势:实时重建 + 历史回放
HolySheep AI 提供两个关键能力:
- 实时WebSocket流:平均延迟 <50ms,支持全量订单簿快照
- 历史Tick级数据:2020年至今的完整Order Book重建,可按时间戳精确回放
- API成本:相比官方Binance API降低85%+费用
完整代码实现
方案一:Python实现(推荐用于回测)
#!/usr/bin/env python3
"""
加密货币Order Book深度数据重建系统
使用HolySheep AI API进行实时和历史数据获取
"""
import asyncio
import json
import time
import numpy as np
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass, field
from collections import defaultdict
import aiohttp
============================================================
配置参数
============================================================
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
API_KEY = "YOUR_HOLYSHEEP_API_KEY" # 替换为你的API Key
@dataclass
class OrderBookLevel:
"""订单簿价格层级"""
price: float
quantity: float
order_count: int = 0
timestamp: int = 0
@dataclass
class OrderBookSnapshot:
"""完整订单簿快照"""
symbol: str
bids: List[OrderBookLevel] # 买单 (价格降序)
asks: List[OrderBookLevel] # 卖单 (价格升序)
last_update_id: int
timestamp: int
exchange: str = "binance"
def get_mid_price(self) -> float:
"""计算中间价"""
if not self.bids or not self.asks:
return 0.0
return (self.bids[0].price + self.asks[0].price) / 2
def get_spread_bps(self) -> float:
"""计算价差(基点)"""
if not self.bids or not self.asks:
return 0.0
mid = self.get_mid_price()
return ((self.asks[0].price - self.bids[0].price) / mid) * 10000
def get_depth(self, levels: int = 20) -> Dict[str, float]:
"""计算指定层级的累计深度"""
bid_depth = sum(l.quantity for l in self.bids[:levels])
ask_depth = sum(l.quantity for l in self.asks[:levels])
return {"bid_depth": bid_depth, "ask_depth": ask_depth}
class HolySheepAPIClient:
"""HolySheep AI API客户端"""
def __init__(self, api_key: str, base_url: str = HOLYSHEEP_BASE_URL):
self.api_key = api_key
self.base_url = base_url
self.session: Optional[aiohttp.ClientSession] = None
async def __aenter__(self):
self.session = aiohttp.ClientSession(
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.session:
await self.session.close()
async def get_orderbook_snapshot(
self,
symbol: str,
depth: int = 20,
exchange: str = "binance"
) -> Optional[OrderBookSnapshot]:
"""
获取实时订单簿快照
延迟目标: <50ms
"""
endpoint = f"{self.base_url}/orderbook/snapshot"
params = {
"symbol": symbol,
"depth": depth,
"exchange": exchange
}
try:
async with self.session.get(endpoint, params=params) as resp:
if resp.status == 200:
data = await resp.json()
return self._parse_orderbook_response(data, symbol, exchange)
elif resp.status == 401:
raise PermissionError("API Key无效或已过期")
elif resp.status == 429:
raise RateLimitError("请求频率超限,请降低调用频率")
else:
raise APIError(f"API错误: {resp.status}")
except aiohttp.ClientError as e:
raise ConnectionError(f"连接HolySheep失败: {e}")
async def get_historical_orderbook(
self,
symbol: str,
start_time: int,
end_time: int,
interval: str = "1s",
exchange: str = "binance"
) -> List[OrderBookSnapshot]:
"""
获取历史订单簿数据(用于回测)
支持: 1s, 10s, 1m, 5m 间隔
"""
endpoint = f"{self.base_url}/orderbook/historical"
params = {
"symbol": symbol,
"start_time": start_time,
"end_time": end_time,
"interval": interval,
"exchange": exchange
}
async with self.session.get(endpoint, params=params) as resp:
if resp.status == 200:
data = await resp.json()
return [self._parse_orderbook_response(item, symbol, exchange)
for item in data.get("snapshots", [])]
elif resp.status == 401:
raise PermissionError("API Key无效或已过期")
else:
raise APIError(f"历史数据获取失败: {resp.status}")
def _parse_orderbook_response(
self,
data: dict,
symbol: str,
exchange: str
) -> OrderBookSnapshot:
"""解析API响应为OrderBookSnapshot"""
bids = [
OrderBookLevel(
price=float(b[0]),
quantity=float(b[1]),
order_count=b[2] if len(b) > 2 else 0,
timestamp=data.get("timestamp", 0)
)
for b in data.get("bids", [])
]
asks = [
OrderBookLevel(
price=float(a[0]),
quantity=float(a[1]),
order_count=a[2] if len(a) > 2 else 0,
timestamp=data.get("timestamp", 0)
)
for a in data.get("asks", [])
]
return OrderBookSnapshot(
symbol=symbol,
bids=bids,
asks=asks,
last_update_id=data.get("lastUpdateId", 0),
timestamp=data.get("timestamp", 0),
exchange=exchange
)
class OrderBookReconstructor:
"""
订单簿重建器 - 核心回测组件
功能:
1. 从增量更新重建完整订单簿状态
2. 计算流动性分布和滑点
3. 模拟订单成交概率
"""
def __init__(self, symbol: str):
self.symbol = symbol
self.bids: Dict[float, float] = defaultdict(float) # price -> quantity
self.asks: Dict[float, float] = defaultdict(float)
self.last_update_id: int = 0
self.update_count: int = 0
def apply_snapshot(self, snapshot: OrderBookSnapshot):
"""应用完整快照,重置订单簿"""
self.bids.clear()
self.asks.clear()
for level in snapshot.bids:
self.bids[level.price] = level.quantity
for level in snapshot.asks:
self.asks[level.price] = level.quantity
self.last_update_id = snapshot.last_update_id
self.update_count = 1
def apply_delta(self, update: dict):
"""
应用增量更新
update格式: {"bids": [[price, qty], ...], "asks": [[price, qty], ...], "u": update_id}
"""
update_id = update.get("u", 0)
# 检查更新序列是否连续
if update_id <= self.last_update_id:
return # 丢弃过期更新
for price, qty in update.get("bids", []):
price = float(price)
qty = float(qty)
if qty == 0:
self.bids.pop(price, None)
else:
self.bids[price] = qty
for price, qty in update.get("asks", []):
price = float(price)
qty = float(qty)
if qty == 0:
self.asks.pop(price, None)
else:
self.asks[price] = qty
self.last_update_id = update_id
self.update_count += 1
def get_snapshot(self) -> OrderBookSnapshot:
"""获取当前订单簿快照"""
bids = sorted(
[OrderBookLevel(price=p, quantity=q) for p, q in self.bids.items()],
key=lambda x: -x.price # 降序
)
asks = sorted(
[OrderBookLevel(price=p, quantity=q) for p, q in self.asks.items()],
key=lambda x: x.price # 升序
)
return OrderBookSnapshot(
symbol=self.symbol,
bids=bids,
asks=asks,
last_update_id=self.last_update_id,
timestamp=int(time.time() * 1000)
)
def calculate_impact(self, side: str, quantity: float) -> Tuple[float, float]:
"""
计算大单冲击
返回: (平均成交价, 滑点基点)
模拟订单在当前订单簿中的实际成交情况
"""
if side.lower() == "buy":
levels = sorted(self.asks.items(), key=lambda x: x[0]) # 卖单升序
else:
levels = sorted(self.bids.items(), key=lambda x: -x[0]) # 买单降序
remaining_qty = quantity
total_cost = 0.0
executed_qty = 0.0
for price, available_qty in levels:
exec_qty = min(remaining_qty, available_qty)
total_cost += exec_qty * price
executed_qty += exec_qty
remaining_qty -= exec_qty
if remaining_qty <= 0:
break
if executed_qty == 0:
return 0.0, 0.0
avg_price = total_cost / executed_qty
mid_price = self.get_snapshot().get_mid_price()
slippage_bps = abs(avg_price - mid_price) / mid_price * 10000
return avg_price, slippage_bps
def calculate_vwap_depth(self, levels: int = 50) -> float:
"""计算VWAP深度(用于流动性评估)"""
snapshot = self.get_snapshot()
bid_depth = sum(
snapshot.bids[i].price * snapshot.bids[i].quantity
for i in range(min(levels, len(snapshot.bids)))
)
ask_depth = sum(
snapshot.asks[i].price * snapshot.asks[i].quantity
for i in range(min(levels, len(snapshot.asks)))
)
return (bid_depth + ask_depth) / 2
class MarketMakingBacktester:
"""
做市策略回测引擎
"""
def __init__(
self,
symbol: str,
api_client: HolySheepAPIClient,
spread_bps: float = 5.0,
order_size: float = 0.1
):
self.symbol = symbol
self.client = api_client
self.spread_bps = spread_bps
self.order_size = order_size
self.reconstructor = OrderBookReconstructor(symbol)
# 统计数据
self.stats = {
"total_trades": 0,
"buy_trades": 0,
"sell_trades": 0,
"total_pnl": 0.0,
"max_slippage": 0.0,
"avg_slippage": 0.0,
"liquidity_score": 0.0
}
self.slippage_list: List[float] = []
async def run_historical_backtest(
self,
start_time: int,
end_time: int,
interval: str = "1s"
):
"""运行历史回测"""
print(f"开始回测: {self.symbol}")
print(f"时间范围: {start_time} - {end_time}")
# 获取历史数据
snapshots = await self.client.get_historical_orderbook(
symbol=self.symbol,
start_time=start_time,
end_time=end_time,
interval=interval
)
print(f"获取到 {len(snapshots)} 个快照")
for i, snapshot in enumerate(snapshots):
# 应用订单簿快照
self.reconstructor.apply_snapshot(snapshot)
# 计算策略信号
spread = snapshot.get_spread_bps()
if spread > self.spread_bps * 2:
# 高波动,跳过
continue
# 模拟挂单
mid_price = snapshot.get_mid_price()
order_price_bid = mid_price * (1 - self.spread_bps / 10000)
order_price_ask = mid_price * (1 + self.spread_bps / 10000)
# 模拟成交(简化模型:基于深度概率)
depth = snapshot.get_depth(levels=10)
liquidity_ratio = depth["bid_depth"] / (depth["bid_depth"] + depth["ask_depth"])
# 简单的订单成交模拟
import random
if random.random() < 0.4 * liquidity_ratio:
# 买单成交
self.stats["buy_trades"] += 1
self.stats["total_pnl"] -= self.order_size * order_price_bid
if random.random() < 0.4 * (1 - liquidity_ratio):
# 卖单成交
self.stats["sell_trades"] += 1
self.stats["total_pnl"] += self.order_size * order_price_ask
self.stats["total_trades"] = self.stats["buy_trades"] + self.stats["sell_trades"]
# 进度显示
if i % 10000 == 0:
print(f"进度: {i}/{len(snapshots)} ({i/len(snapshots)*100:.1f}%)")
self._calculate_final_stats()
return self.stats
def _calculate_final_stats(self):
"""计算最终统计"""
if self.slippage_list:
self.stats["avg_slippage"] = sum(self.slippage_list) / len(self.slippage_list)
self.stats["max_slippage"] = max(self.slippage_list)
async def main():
"""主函数 - 演示完整回测流程"""
async with HolySheepAPIClient(API_KEY) as client:
# 测试1: 获取实时订单簿
print("=" * 50)
print("测试1: 实时订单簿获取")
print("=" * 50)
try:
snapshot = await client.get_orderbook_snapshot("BTCUSDT", depth=20)
print(f"中间价: ${snapshot.get_mid_price():,.2f}")
print(f"价差: {snapshot.get_spread_bps():.2f} bps")
depth = snapshot.get_depth(levels=10)
print(f"10档深度 - 买单: {depth['bid_depth']:.4f}, 卖单: {depth['ask_depth']:.4f}")
except Exception as e:
print(f"实时数据获取失败: {e}")
# 测试2: 历史回测(最近1小时的1分钟数据)
print("\n" + "=" * 50)
print("测试2: 历史回测")
print("=" * 50)
end_time = int(time.time() * 1000)
start_time = end_time - 3600 * 1000 # 1小时前
backtester = MarketMakingBacktester(
symbol="BTCUSDT",
api_client=client,
spread_bps=5.0,
order_size=0.01
)
results = await backtester.run_historical_backtest(
start_time=start_time,
end_time=end_time,
interval="1m"
)
print("\n回测结果:")
print(f" 总交易次数: {results['total_trades']}")
print(f" 买单成交: {results['buy_trades']}")
print(f" 卖单成交: {results['sell_trades']}")
print(f" 累计盈亏: {results['total_pnl']:.4f} BTC")
print(f" 平均滑点: {results['avg_slippage']:.2f} bps")
if __name__ == "__main__":
asyncio.run(main())
方案二:TypeScript实现(推荐用于生产环境)
/**
* 加密货币做市策略回测系统 - TypeScript实现
* 适用于Node.js生产环境
*/
import WebSocket from 'ws';
// ============================================================
// 类型定义
// ============================================================
interface OrderBookLevel {
price: number;
quantity: number;
orderCount: number;
timestamp: number;
}
interface OrderBookSnapshot {
symbol: string;
bids: OrderBookLevel[];
asks: OrderBookLevel[];
lastUpdateId: number;
timestamp: number;
exchange: string;
}
interface OrderBookUpdate {
e: string; // Event type
E: number; // Event time
s: string; // Symbol
U: number; // First update ID
u: number; // Final update ID
b: [string, string][]; // Bids
a: [string, string][]; // Asks
}
interface BacktestResult {
totalTrades: number;
buyTrades: number;
sellTrades: number;
totalPnl: number;
maxDrawdown: number;
sharpeRatio: number;
winRate: number;
avgSlippage: number;
liquidityScores: number[];
}
// ============================================================
// HolySheep API 客户端
// ============================================================
class HolySheepClient {
private baseUrl = 'https://api.holysheep.ai/v1';
private apiKey: string;
private ws: WebSocket | null = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
constructor(apiKey: string) {
this.apiKey = apiKey;
}
private getHeaders(): Record {
return {
'Authorization': Bearer ${this.apiKey},
'Content-Type': 'application/json'
};
}
async fetchOrderBookSnapshot(
symbol: string,
depth: number = 20,
exchange: string = 'binance'
): Promise {
const url = ${this.baseUrl}/orderbook/snapshot?symbol=${symbol}&depth=${depth}&exchange=${exchange};
const response = await fetch(url, {
method: 'GET',
headers: this.getHeaders()
});
if (!response.ok) {
if (response.status === 401) {
throw new Error('API Key无效或已过期');
} else if (response.status === 429) {
throw new Error('请求频率超限,请降低调用频率');
}
throw new Error(API错误: ${response.status});
}
const data = await response.json();
return this.parseOrderBookResponse(data, symbol, exchange);
}
async fetchHistoricalOrderbook(
symbol: string,
startTime: number,
endTime: number,
interval: string = '1s',
exchange: string = 'binance'
): Promise {
const url = ${this.baseUrl}/orderbook/historical;
const params = new URLSearchParams({
symbol,
start_time: startTime.toString(),
end_time: endTime.toString(),
interval,
exchange
});
const response = await fetch(${url}?${params}, {
method: 'GET',
headers: this.getHeaders()
});
if (!response.ok) {
throw new Error(历史数据获取失败: ${response.status});
}
const data = await response.json();
return (data.snapshots || []).map((item: any) =>
this.parseOrderBookResponse(item, symbol, exchange)
);
}
private parseOrderBookResponse(
data: any,
symbol: string,
exchange: string
): OrderBookSnapshot {
const bids: OrderBookLevel[] = (data.bids || []).map((b: any[]) => ({
price: parseFloat(b[0]),
quantity: parseFloat(b[1]),
orderCount: b[2] || 0,
timestamp: data.timestamp || 0
}));
const asks: OrderBookLevel[] = (data.asks || []).map((a: any[]) => ({
price: parseFloat(a[0]),
quantity: parseFloat(a[1]),
orderCount: a[2] || 0,
timestamp: data.timestamp || 0
}));
return {
symbol,
bids,
asks,
lastUpdateId: data.lastUpdateId || 0,
timestamp: data.timestamp || 0,
exchange
};
}
// WebSocket实时订阅
subscribeOrderBook(
symbol: string,
onUpdate: (snapshot: OrderBookSnapshot) => void,
onError: (error: Error) => void
): void {
const wsUrl = ${this.baseUrl.replace('https', 'wss')}/ws/orderbook/${symbol};
this.ws = new WebSocket(wsUrl, {
headers: this.getHeaders()
});
this.ws.on('open', () => {
console.log(WebSocket已连接: ${symbol});
this.reconnectAttempts = 0;
});
this.ws.on('message', (data: WebSocket.Data) => {
try {
const update: OrderBookUpdate = JSON.parse(data.toString());
// 处理增量更新
const snapshot = this.convertUpdateToSnapshot(update, symbol);
onUpdate(snapshot);
} catch (e) {
console.error('消息解析失败:', e);
}
});
this.ws.on('error', (error) => {
onError(error);
});
this.ws.on('close', () => {
console.log('WebSocket连接关闭,尝试重连...');
this.attemptReconnect(symbol, onUpdate, onError);
});
}
private convertUpdateToSnapshot(update: OrderBookUpdate, symbol: string): OrderBookSnapshot {
return {
symbol,
bids: update.b.map(([price, qty]) => ({
price: parseFloat(price),
quantity: parseFloat(qty),
orderCount: 0,
timestamp: update.E
})),
asks: update.a.map(([price, qty]) => ({
price: parseFloat(price),
quantity: parseFloat(qty),
orderCount: 0,
timestamp: update.E
})),
lastUpdateId: update.u,
timestamp: update.E,
exchange: 'binance'
};
}
private attemptReconnect(
symbol: string,
onUpdate: (snapshot: OrderBookSnapshot) => void,
onError: (error: Error) => void
): void {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
console.log(重连尝试 ${this.reconnectAttempts}/${this.maxReconnectAttempts});
this.subscribeOrderBook(symbol, onUpdate, onError);
}, 1000 * this.reconnectAttempts);
} else {
onError(new Error('WebSocket重连失败'));
}
}
disconnect(): void {
if (this.ws) {
this.ws.close();
this.ws = null;
}
}
}
// ============================================================
// 订单簿重建器
// ============================================================
class OrderBookReconstructor {
private bids: Map = new Map();
private asks: Map = new Map();
private lastUpdateId: number = 0;
private updateCount: number = 0;
applySnapshot(snapshot: OrderBookSnapshot): void {
this.bids.clear();
this.asks.clear();
snapshot.bids.forEach(level => {
this.bids.set(level.price, level.quantity);
});
snapshot.asks.forEach(level => {
this.asks.set(level.price, level.quantity);
});
this.lastUpdateId = snapshot.lastUpdateId;
this.updateCount = 1;
}
applyUpdate(update: OrderBookUpdate): boolean {
if (update.u <= this.lastUpdateId) {
return false; // 丢弃过期更新
}
update.b.forEach(([priceStr, qtyStr]) => {
const price = parseFloat(priceStr);
const qty = parseFloat(qtyStr);
if (qty === 0) {
this.bids.delete(price);
} else {
this.bids.set(price, qty);
}
});
update.a.forEach(([priceStr, qtyStr]) => {
const price = parseFloat(priceStr);
const qty = parseFloat(qtyStr);
if (qty === 0) {
this.asks.delete(price);
} else {
this.asks.set(price, qty);
}
});
this.lastUpdateId = update.u;
this.updateCount++;
return true;
}
getSnapshot(): OrderBookSnapshot {
const sortedBids = Array.from(this.bids.entries())
.map(([price, quantity]) => ({ price, quantity, orderCount: 0, timestamp: Date.now() }))
.sort((a, b) => b.price - a.price);
const sortedAsks = Array.from(this.asks.entries())
.map(([price, quantity]) => ({ price, quantity, orderCount: 0, timestamp: Date.now() }))
.sort((a, b) => a.price - b.price);
return {
symbol: '',
bids: sortedBids,
asks: sortedAsks,
lastUpdateId: this.lastUpdateId,
timestamp: Date.now(),
exchange: 'binance'
};
}
calculateMarketImpact(side: 'buy' | 'sell', quantity: number): { avgPrice: number; slippageBps: number } {
const levels = side === 'buy'
? Array.from(this.asks.entries()).sort((a, b) => a[0] - b[0])
: Array.from(this.bids.entries()).sort((a, b) => b[0] - a[0]);
let remainingQty = quantity;
let totalCost = 0;
let executedQty = 0;
for (const [price, availableQty] of levels) {
const execQty = Math.min(remainingQty, availableQty);
totalCost += execQty * price;
executedQty += execQty;
remainingQty -= execQty;
if (remainingQty <= 0) break;
}
if (executedQty === 0) {
return { avgPrice: 0, slippageBps: 0 };
}
const avgPrice = totalCost / executedQty;
const snapshot = this.getSnapshot();
const midPrice = (snapshot.bids[0]?.price + snapshot.asks[0]?.price) / 2;
const slippageBps = Math.abs(avgPrice - midPrice) / midPrice * 10000;
return { avgPrice, slippageBps };
}
}
// ============================================================
// 做市策略回测引擎
// ============================================================
interface MarketMakingConfig {
symbol: string;
spreadBps: number;
orderSize: number;
maxPosition: number;
inventorySkew: number;
}
class MarketMakingBacktester {
private client: HolySheepClient;
private reconstructor: OrderBookReconstructor;
private config: MarketMakingConfig;
// 状态追踪
private position: number = 0;
private cash: number = 0;
private trades: Array<{ time: number; side: string; price: number; qty: number; slippage: number }> = [];
private equityCurve: number[] = [];
constructor(client: HolySheepClient, config: MarketMakingConfig) {
this.client = client;
this.config = config;
this.reconstructor = new OrderBookReconstructor();
}
async runBacktest(startTime: number, endTime: number): Promise {
console.log(开始回测: ${this.config.symbol});
console.log(时间范围: ${new Date(startTime).toISOString()} - ${new Date(endTime).toISOString()});
// 获取历史数据
const snapshots = await this.client.fetchHistoricalOrderbook(
this.config.symbol,
startTime,
endTime,
'1s'
);
console.log(获取到 ${snapshots.length} 个数据点);
// 逐tick回测
for (let i = 0; i < snapshots.length; i++) {
const snapshot = snapshots[i];
this.reconstructor.applySnapshot(snapshot);
// 策略逻辑
await this.strategyStep(snapshot);
// 每1000个tick记录权益
if (i % 1000 === 0) {
this.equityCurve.push(this.getTotalEquity(snapshot));
console.log(进度: ${i}/${snapshots.length} (${(i/snapshots.length*100).toFixed(1)}%));
}
}
return this.calculateResults();
}
private async strategyStep(snapshot: OrderBookSnapshot): Promise {
const midPrice = (snapshot.bids[0]?.price + snapshot.asks[0]?.price) / 2;
if (!midPrice) return;
const spread = snapshot.bids[0] && snapshot.asks[0]
? (snapshot.asks[0].price - snapshot.bids[0].price) / midPrice * 10000
: 0;
// 订单簿流动性评估
const top10BidDepth = snapshot.bids.slice(0, 10).reduce((sum, l) => sum + l.quantity, 0);
const top10AskDepth = snapshot.asks.slice(0, 10).reduce((sum, l) => sum + l.quantity, 0);
const liquidityRatio = top10BidDepth / (top10BidDepth + top10AskDepth);
// 挂单价格(考虑inventory skew)
const skew = this.config.inventorySkew * (this.position / this.config.maxPosition);
const bidPrice = midPrice * (1 - (this.config.spreadBps + skew) / 10000);
const askPrice = midPrice * (1 + (this.config.spreadBps - skew) / 10000);
// 模拟成交概率(基于流动性)
const tradeProbability = 0.3;
// 买单模拟
if (this.position > -this.config.maxPosition) {
const bidImpact = this.reconstructor.calculateMarketImpact('buy', this.config.orderSize);
if (Math.random() < tradeProbability * liquidityRatio && bidImpact.slippageBps < 10) {
this.position += this.config.orderSize;
this.cash -= this.config.orderSize * bidImpact.avgPrice;
this.trades.push({
time: snapshot.timestamp,
side: 'buy',
price: bidImpact.avgPrice,
qty: this.config.orderSize,
slippage: bidImpact.slippageBps
});
}
}
// 卖单模拟
if (this.position < this.config.maxPosition) {
const askImpact = this.reconstructor.calculateMarketImpact('sell', this.config.orderSize);
if (Math.random() < tradeProbability * (1 - liquidityRatio) && askImpact.slippageBps < 10) {
this.position -= this.config.orderSize;
this.cash += this.config.orderSize * askImpact.avgPrice;
this.trades.push({
time: snapshot.timestamp,
side: 'sell',
price: askImpact.avgPrice,
qty: this.config.orderSize,
slippage: askImpact.slippageBps
});
}
}
}
private getTotalEquity(lastSnapshot: OrderBookSnapshot): number {
const midPrice = (lastSnapshot.bids[0]?.price + lastSnapshot.asks[0]?.price) / 2;
return this.cash + this.position * midPrice;
}
private calculateResults(): BacktestResult {
const buyTrades = this.trades.filter(t => t.side === 'buy');
const sellTrades = this.trades.filter(t => t.side === 'sell');
const allSlippages = this.trades.map(t => t.slippage);
const liquidityScores = this.equityCurve.map(e => e);
// 计算收益率序列
const returns: number[] = [];
for (let i = 1; i < this.equityCurve.length; i++) {
returns.push((this.equityCurve[i] - this.equityCurve[i-1]) / this.equityCurve[i-1]);
}
// 夏普比率
const avgReturn = returns.reduce((a, b) => a + b, 0) / returns.length;
const stdReturn = Math.sqrt(returns.reduce((sum, r) => sum + (r - avgReturn) ** 2, 0) / returns.length);
const sharpeRatio = stdReturn > 0 ? (avgReturn / stdReturn) * Math.sqrt(252 * 24 * 3600) : 0;
// 最大回撤
let maxDrawdown = 0;
let peak = this.equityCurve[0];
for (const equity of this.equityCurve) {
if (equity > peak) peak = equity;
const drawdown = (peak - equity) / peak;
if (drawdown > maxDrawdown) maxDrawdown = drawdown;
}
return {
totalTrades: this.trades.length,
buyTrades: buyTrades.length