我在 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 分钟,而通过中转节点拉到本地耗时下降非常明显。下面会给出代码。
二、整体架构(生产级)
我把整条链路拆成四层:
- L1 数据回放层:Tardis 历史 Tick(trades.book 30 分钟回放窗口)
- L2 实时管道层:每交易所一个 WebSocket 协程,Faster-Parser 解析 + 时间戳对齐
- L3 信号计算层:异步价差矩阵 + 协整检验 + LLM 二次过滤(神经过滤无效噪声)
- L4 下单执行层:双账户并发 HTTP,速率限制 5 req/s,超时熔断
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: # 仅