第二十三讲:订单流与套利——跨交易所订单流差异与盘口价差套利
套利,说白了就是赚「信息差」的钱。在量化交易里,这个信息差往往藏在订单流里。
我个人习惯把套利分成两类:一类是统计套利,靠数学模型找相关性;另一类是盘口套利,靠订单流的速度差和价差。今天咱们聊的是后者——跨交易所的订单流差异,以及怎么用Python抓这种机会。
核心逻辑:同一资产在不同交易所的订单流不可能完全同步。谁先捕捉到这种「不同步」,谁就能吃到价差。
23.1 跨交易所订单流差异的本质
为什么会有差异?原因其实很简单:
- 物理延迟:交易所服务器分布在不同的数据中心,网络传输有快有慢
- 撮合机制不同:有的用连续竞价,有的用集合竞价,订单簿更新频率不一样
- 流动性分布:大单砸在一个交易所,另一个交易所可能还没反应过来
我在项目中遇到过最典型的情况:比特币在币安突然出现一笔500BTC的卖单,价格瞬间砸了0.3%。但OKX那边订单簿还没更新,价差瞬间拉大到0.5%以上。嗯,这就是套利窗口。
避坑指南:我曾经以为这种窗口能持续好几秒,结果回测发现大部分窗口不到200毫秒就消失了。所以,别想着手动操作,必须用程序。
23.2 盘口价差套利的数学模型
套利信号其实就是一个条件判断:
# 伪代码逻辑
if (交易所A的卖一价 - 交易所B的买一价) > 手续费 + 滑点成本:
触发套利信号
但实际没那么简单。你想想看,价差大不一定能成交,还要考虑:
- 深度够不够:卖一挂了10个币,你想吃100个,那成交价就不是卖一了
- 对手盘速度:你下单的同时,别人也在抢
- 结算风险:跨所转账需要时间,万一价差反转了呢?
所以我一般用这个公式来算「有效套利空间」:
有效价差 = (卖一价 - 买一价) - 手续费率 * 2 - 滑点预估 - 风险缓冲
滑点预估怎么算?我习惯用订单簿前5档的加权平均价差。说白了,就是模拟一下你吃单进去,实际成交价会偏离多少。
23.3 Python实现套利信号
下面是我常用的一个套利信号检测模块。代码不长,但核心逻辑都在里面了。
import asyncio
import aiohttp
import json
from decimal import Decimal, ROUND_DOWN
class ArbitrageDetector:
"""
跨交易所套利信号检测器
支持多交易所订单流实时对比
"""
def __init__(self, fee_rate=0.001, slippage=0.0005, buffer=0.0002):
self.fee_rate = fee_rate # 手续费率
self.slippage = slippage # 滑点预估
self.buffer = buffer # 风险缓冲
self.orderbooks = {} # 存储各交易所订单簿
async def fetch_orderbook(self, exchange, symbol):
"""异步获取订单簿数据"""
# 这里以币安和OKX为例
urls = {
'binance': f'https://api.binance.com/api/v3/depth?symbol={symbol}&limit=10',
'okx': f'https://www.okx.com/api/v5/market/books?instId={symbol}&sz=10'
}
async with aiohttp.ClientSession() as session:
async with session.get(urls[exchange]) as resp:
data = await resp.json()
self.orderbooks[exchange] = self._parse_orderbook(data, exchange)
def _parse_orderbook(self, data, exchange):
"""解析不同交易所的订单簿格式"""
if exchange == 'binance':
return {
'bids': [[Decimal(p), Decimal(q)] for p, q in data['bids']],
'asks': [[Decimal(p), Decimal(q)] for p, q in data['asks']]
}
elif exchange == 'okx':
return {
'bids': [[Decimal(item[0]), Decimal(item[1])] for item in data['data'][0]['bids']],
'asks': [[Decimal(item[0]), Decimal(item[1])] for item in data['data'][0]['asks']]
}
def calculate_effective_spread(self, ex_a, ex_b):
"""计算有效套利价差"""
if ex_a not in self.orderbooks or ex_b not in self.orderbooks:
return None
# 交易所A的卖一价 vs 交易所B的买一价
ask_a = self.orderbooks[ex_a]['asks'][0][0]
bid_b = self.orderbooks[ex_b]['bids'][0][0]
# 原始价差
raw_spread = ask_a - bid_b
# 扣除成本
cost = (ask_a + bid_b) * (self.fee_rate * 2 + self.slippage + self.buffer)
effective_spread = raw_spread - cost
return float(effective_spread)
def detect_signal(self, ex_a='binance', ex_b='okx', threshold=0.0):
"""检测套利信号"""
spread = self.calculate_effective_spread(ex_a, ex_b)
if spread is None:
return None
if spread > threshold:
return {
'signal': 'BUY_EX_B_SELL_EX_A',
'spread': spread,
'ex_a_ask': self.orderbooks[ex_a]['asks'][0][0],
'ex_b_bid': self.orderbooks[ex_b]['bids'][0][0],
'timestamp': asyncio.get_event_loop().time()
}
elif spread < -threshold:
return {
'signal': 'BUY_EX_A_SELL_EX_B',
'spread': abs(spread),
'ex_a_bid': self.orderbooks[ex_a]['bids'][0][0],
'ex_b_ask': self.orderbooks[ex_b]['asks'][0][0],
'timestamp': asyncio.get_event_loop().time()
}
return None
# 使用示例
async def main():
detector = ArbitrageDetector(fee_rate=0.001, slippage=0.0003, buffer=0.0002)
while True:
# 同时获取两个交易所的订单簿
await asyncio.gather(
detector.fetch_orderbook('binance', 'BTCUSDT'),
detector.fetch_orderbook('okx', 'BTC-USDT')
)
signal = detector.detect_signal(threshold=0.0)
if signal:
print(f"套利信号: {signal['signal']}, 价差: {signal['spread']:.4f}")
await asyncio.sleep(0.1) # 100ms轮询一次
if __name__ == '__main__':
asyncio.run(main())
注意:这个代码只是信号检测,没有包含下单和风控逻辑。实际生产中,你还需要考虑:
- 订单簿快照与增量更新的同步问题
- WebSocket连接断线重连
- 交易所API限频控制
- 资金划转的延迟预估
23.4 订单流差异的可视化分析
光看数字不够直观。我习惯把两个交易所的订单簿画在一起,一眼就能看出哪里有机会。
下面这张图展示了跨交易所订单流的典型差异:
从图上能清楚看到:交易所A的卖盘深度明显大于买盘,价格被压得较低;而交易所B正好相反,买盘厚实,价格偏高。这种结构性的订单流差异,就是套利机会的来源。
23.5 实战中的几个坑
讲几个我踩过的坑,希望能帮你省点学费:
- 别只看第一档:卖一和买一的价差再大,如果深度不够,你一进去就把价格打穿了。我建议至少看前5档的加权平均。
- 手续费是隐形杀手:有些交易所的maker费率很低,但taker费率很高。你以为是套利,结果手续费一扣,反而亏了。
- 网络延迟比你想象的严重:我曾经在AWS新加坡节点跑策略,结果发现信号出来的时候,交易所的撮合引擎已经先我一步了。后来我把服务器搬到了交易所的同一机房。
- 回测数据要小心:历史数据里的价差看起来很美,但那是「事后诸葛亮」。实际交易中,你的订单可能根本成交不了。
我的建议:刚开始做跨所套利,先选流动性好的品种(比如BTC、ETH),用最小仓位跑一段时间。别一上来就上杠杆,那是在给交易所送手续费。
23.6 总结
跨交易所订单流套利,说白了就是比谁快、谁准。快,靠的是低延迟的网络和高效的代码;准,靠的是对订单簿结构的深刻理解。
我个人觉得,这个策略最迷人的地方在于:它不预测价格涨跌,只赚「不同步」的钱。只要两个交易所还在物理上分离,这种机会就永远存在。
嗯,今天就聊到这儿。代码已经给了,剩下的就是你自己去跑一跑、调一调。记住,纸上得来终觉浅,绝知此事要躬行。