我在 2024 年第一次跑跨所套利机器人时,亲眼看着 6 万 USDT 的资金因为一个 800ms 的延迟被吃掉。当时我用的是各家交易所裸连,加上本地拼凑的 CSV Tick 数据做回测,结果实盘和回测完全对不上——这是典型的"垃圾进,垃圾出"。后来我把整条数据链路切到了 Tardis.dev 的逐笔成交 + Order Book 全字段回放,再配合一套异步 WebSocket 实时管道,套利毛差从年化 18% 提到了 41%。这篇文章我会把整条链路的架构、并发控制、回测陷阱、以及如何用 LLM 做信号增强一次性讲透,最后给出我自己在用的 HolySheep Tardis 中转 + 大模型 API 实测成本表。

一、为什么套利必须用 Tardis 历史 Tick?

做过统计套利的工程师都知道,普通 K 线(1m/5m)数据会抹平 90% 以上的真实波动结构。Binance/Bybit/OKX 的 BTCUSDT 永续合约每秒大约产生 4–12 笔成交流,价差窗口往往只存在于 80–300ms 的窗口里。用 1 分钟 K 线做回测,你看到的"套利机会"实际上根本来不及下单。

Tardis.dev 提供的是逐笔成交(trades)、Order Book L2/L20 增量、Funding Rate、强平订单的完整历史。我本地实测:从 Binance 的 data.binance.vision 裸下载 2024 年 BTCUSDT 永续全量 trades(压缩后约 47GB)耗时 3 小时 12 分钟,而通过中转节点拉到本地耗时下降非常明显。下面会给出代码。

二、整体架构(生产级)

我把整条链路拆成四层:


pipeline/core.py —— 四层架构主线 (Python 3.11 + asyncio + aiohttp)

import asyncio, json, time from dataclasses import dataclass, field from collections import deque from typing import Deque, Dict, List EXCHANGES = ["binance", "bybit", "okx"] # 支持的合约所 SYMBOL = "btcusdt-perp" @dataclass class Tick: exch: str ts_ms: int # 交易所本地时间 recv_ms: int # 本地接收时间 bid: float ask: float last: float class SpreadEngine: def __init__(self, window_ms: int = 500): self.books: Dict[str, Deque[Tick]] = {e: deque(maxlen=4096) for e in EXCHANGES} self.window_ms = window_ms self.signals: List[dict] = [] def on_tick(self, t: Tick): self.books[t.exch].append(t) # 对每对交易所计算 micro-spread keys = list(self.books.keys()) for i in range(len(keys)): for j in range(i+1, len(keys)): a, b = self.books[keys[i]][-1], self.books[keys[j]][-1] if abs(a.recv_ms - b.recv_ms) > self.window_ms: continue spread_bps = (a.bid - b.ask) / b.ask * 1e4 if abs(spread_bps) > 12: # 仅