4. 市场微观结构:订单簿(Order Book)深度解析
做价差套利,尤其是高频那一套,你绕不开一个东西——订单簿。
说白了,订单簿就是交易所的「实时账本」。它记录了所有交易者挂出来的买单和卖单。我个人习惯把它看作市场的「心电图」,每一笔挂单、撤单、成交,都是市场情绪的跳动。
嗯,咱们今天就把这东西彻底拆开看看。
4.1 买卖盘口:Bid 和 Ask 的博弈
订单簿最核心的两个概念:Bid(买盘)和Ask(卖盘)。
- Bid:别人愿意出的最高买入价。说白了,就是「我最高出这个价买,谁卖给我?」
- Ask:别人愿意接受的最低卖出价。就是「我最低这个价卖,谁要?」
Bid 和 Ask 之间的差价,叫 Spread(价差)。这个数字越小,说明市场越活跃,流动性越好。
核心公式:
Spread = Ask_Price - Bid_Price
Mid_Price = (Ask_Price + Bid_Price) / 2
我在项目中遇到过一种情况:某个冷门币种,Spread 能大到几个百分点。你想想看,这种品种做套利,还没进场就被价差吃掉了利润。所以,Spread 是我们筛选品种的第一道门槛。
4.2 市场深度与流动性
光看 Spread 还不够。你得看 Depth(深度)。
市场深度,指的是在某个价格水平上,有多少挂单量。深度越厚,说明这个价位能承接的资金量越大。
举个例子:
| 价格 | 买盘量(Bid Volume) | 卖盘量(Ask Volume) |
|---|---|---|
| 100.00 | 500 | 200 |
| 99.99 | 800 | 150 |
| 99.98 | 1200 | 100 |
你看,在 100.00 这个价位,买盘有 500 手,卖盘只有 200 手。这说明什么?说明买方力量更强,价格有向上突破的倾向。
我自己的经验是:做套利,一定要避开深度薄的品种。曾经有一次,我在一个深度只有几十手的品种上做价差,结果一个大单砸下来,直接把我的挂单打穿,滑点损失惨重。嗯,从那以后,我给自己定了个规矩:买卖盘口前五档的总深度,至少是单笔交易量的 10 倍以上,才考虑进场。
避坑指南:
我曾经以为只看「盘口总量」就够了,后来发现不对。你得看「盘口分布」。如果所有深度都集中在第一档,后面几档稀稀拉拉,那这个流动性是「虚胖」的。真正健康的深度,应该是从第一档到第五档,逐层递减,但每一层都有足够的量。
4.3 Tick 数据与快照数据
做高频,数据是命根子。订单簿数据分两种:Tick 数据和快照数据。
4.3.1 Tick 数据
Tick 数据,也叫逐笔数据。它记录的是每一笔成交的详细信息:
- 成交价格
- 成交量
- 成交时间(精确到微秒甚至纳秒)
- 买卖方向(买方主动还是卖方主动)
说白了,Tick 数据就是市场的「原始录像带」。你回放它,就能看到每一笔交易是怎么发生的。
我个人习惯用 Tick 数据来做订单流分析。比如,连续出现大额主动买单,说明有大资金在吃货,价格大概率要涨。
4.3.2 快照数据
快照数据,是某一时刻订单簿的完整状态。它记录了所有档位的买卖挂单情况。
交易所通常每隔几毫秒或几十毫秒推送一次快照。快照数据的好处是:你能看到市场的全貌。但坏处是:它不连续。两次快照之间发生了什么,你不知道。
我的建议:
做价差套利,快照数据是基础,Tick 数据是进阶。
如果你刚开始做,先用快照数据搭建策略。等跑通了,再引入 Tick 数据做精细化优化。
4.4 知识体系结构图
下面这张图,是我自己梳理的订单簿知识体系。你一看就明白了:
4.5 实战:如何用 Python 解析订单簿快照
光说不练假把式。我给你看一段我实际用过的代码片段。这是从交易所 WebSocket 推送中解析订单簿快照的逻辑:
import json
from collections import defaultdict
class OrderBook:
def __init__(self):
self.bids = defaultdict(float) # 价格 -> 数量
self.asks = defaultdict(float)
def update_snapshot(self, data):
"""
更新订单簿快照
data 格式:{'bids': [[price, qty], ...], 'asks': [[price, qty], ...]}
"""
# 清空旧数据
self.bids.clear()
self.asks.clear()
# 更新买单
for price, qty in data['bids']:
if float(qty) > 0:
self.bids[float(price)] = float(qty)
# 更新卖单
for price, qty in data['asks']:
if float(qty) > 0:
self.asks[float(price)] = float(qty)
def get_top_n(self, n=5):
"""获取前 n 档买卖盘口"""
sorted_bids = sorted(self.bids.items(), reverse=True)[:n]
sorted_asks = sorted(self.asks.items())[:n]
return sorted_bids, sorted_asks
def get_spread(self):
"""计算当前价差"""
if not self.bids or not self.asks:
return None
best_bid = max(self.bids.keys())
best_ask = min(self.asks.keys())
return best_ask - best_bid
# 使用示例
ob = OrderBook()
sample_data = {
'bids': [['100.00', '500'], ['99.99', '800']],
'asks': [['100.01', '200'], ['100.02', '150']]
}
ob.update_snapshot(sample_data)
print(f"当前价差: {ob.get_spread():.4f}")
注意:
这段代码是简化版。实际生产中,你还要处理:
- 增量更新(只推送变化的部分,而不是全量快照)
- 数据校验(防止脏数据污染订单簿)
- 时间戳对齐(不同交易所的时钟可能不同步)
我曾经因为没做数据校验,导致订单簿里出现了一个「幽灵挂单」,策略跟着它跑了半天才发现问题。嗯,血的教训。
4.6 小结
订单簿这东西,看着简单,但里面的门道很深。你只要把买卖盘口、市场深度、Tick 和快照这几个概念吃透了,做价差套利就有了坚实的基础。
记住一句话:订单簿是市场的「底牌」。虽然你只能看到一部分,但足够你做出很多判断了。