订单簿深度解析:限价订单簿(LOB)结构、买卖盘口与价差、订单簿动态变化、订单簿数据获取
做量化交易这些年,我越来越觉得——订单簿就是市场的X光片。你想想看,K线图告诉你价格去了哪,但订单簿告诉你价格为什么去了那。今天咱们就把订单簿这层窗户纸捅破。
一、限价订单簿(LOB)的核心结构
限价订单簿,说白了就是一个价格-数量的二维排队系统。买方挂单在左边排队,卖方挂单在右边排队,中间就是成交价。
我个人习惯把LOB想象成一个多层停车场:
- 买盘(Bid):从高到低排列,最高价在最上面
- 卖盘(Ask):从低到高排列,最低价在最上面
- 价差(Spread):最高买价和最低卖价之间的空隙
来看一个典型的BTC/USDT订单簿快照:
卖盘(Ask):
价格 数量 累计
10005.0 0.5 BTC 2.3 BTC
10004.5 1.0 BTC 1.8 BTC
10004.0 0.8 BTC 0.8 BTC
-------------------------------
价差 = 10004.0 - 10003.5 = 0.5 USDT
-------------------------------
买盘(Bid):
10003.5 1.2 BTC 1.2 BTC
10003.0 0.6 BTC 1.8 BTC
10002.5 0.4 BTC 2.2 BTC
这里有个细节——价格档位。交易所通常按最小价格变动单位(tick size)来划分档位。比如BTC的tick size是0.5 USDT,那价格就是10000.0、10000.5、10001.0这样跳。
关键点:订单簿的深度不是看单笔挂单,而是看累计深度。比如你想买2个BTC,看卖一档只有0.8个,那你就得吃掉卖一到卖三档,成交均价会更高。
二、买卖盘口与价差——市场的呼吸
盘口数据里,我最关注三个指标:
- 买卖盘口(Bid-Ask):最高买价和最低卖价,这是市场的"门面"
- 价差(Spread):买卖价差,流动性越好的品种价差越小
- 盘口深度(Depth):各档位的挂单量,决定了你能吃多少货
价差这东西,我踩过坑。有一次做ETH的套利,看到价差只有0.01 USDT,觉得流动性很好。结果一进去发现——价差是假的。为什么?因为卖一档只有0.1个ETH,买一档也只有0.1个,真正的流动性在第三档以后。所以我现在看价差,一定会同时看盘口前五档的累计深度。
实战技巧:真正的流动性指标是加权价差。比如你想交易1个BTC,计算吃掉1个BTC所需的平均价格与当前买一价的差值,这才是你实际要付出的成本。
三、订单簿的动态变化——市场在说话
订单簿不是静止的,它每毫秒都在变。我总结了几种常见的变化模式:
| 变化类型 | 表现 | 含义 |
|---|---|---|
| 价差收窄 | 买一上移,卖一下移 | 多空博弈激烈,即将变盘 |
| 深度堆积 | 某一价格档位挂单突然增多 | 可能是大资金在"筑墙" |
| 冰山订单 | 小单吃掉后,同价位又出现新单 | 大资金在隐藏真实意图 |
| 撤单潮 | 大量挂单同时消失 | 市场情绪突变,小心闪崩 |
我曾经遇到过一个经典场景:某山寨币的买一档突然出现一个超级大单,价格比当前买一高了2%。很多人以为是利好,跟着追涨。结果呢?那是庄家在挂单诱多,等散户把价格推上去,他瞬间撤单反手做空。嗯,这就是为什么我总说——别只看挂单,要看挂单的持续性。
避坑指南:我曾经因为没注意冰山订单,在ETH上吃了大亏。当时看到卖一档只有0.5个ETH,觉得可以轻松吃掉。结果连续吃了5次,每次吃完又冒出0.5个,最后发现是同一个地址在反复挂单。从那以后,我写代码时一定会检测同价位重复挂单的模式。
四、订单簿数据获取——从理论到实战
数据获取这块,我建议分两步走:
- WebSocket实时流:用于做高频策略
- REST API快照:用于做历史回测
以币安为例,获取订单簿数据的代码长这样:
import requests
import json
# 获取订单簿快照
def get_orderbook(symbol='BTCUSDT', limit=100):
url = f'https://api.binance.com/api/v3/depth'
params = {
'symbol': symbol,
'limit': limit # 可选5/10/20/50/100/500/1000
}
resp = requests.get(url, params=params)
data = resp.json()
# 解析数据
bids = data['bids'] # 买盘 [价格, 数量]
asks = data['asks'] # 卖盘 [价格, 数量]
# 计算价差
best_bid = float(bids[0][0])
best_ask = float(asks[0][0])
spread = best_ask - best_bid
return {
'bids': bids[:5], # 前五档
'asks': asks[:5],
'spread': spread,
'mid_price': (best_bid + best_ask) / 2
}
# 使用示例
orderbook = get_orderbook('BTCUSDT', 10)
print(f"买一: {orderbook['bids'][0]}")
print(f"卖一: {orderbook['asks'][0]}")
print(f"价差: {orderbook['spread']}")
这里有个坑——数据延迟。REST API的订单簿快照通常有100-500ms的延迟,做高频策略根本不够用。我个人的做法是:
- 用WebSocket维持一个本地订单簿
- 每收到一个增量更新(增删改),就更新本地数据
- 每隔30秒用REST API做一次全量校验
为什么这么做?因为WebSocket偶尔会丢包,导致本地订单簿和交易所不一致。全量校验就是用来纠偏的。
核心要点:订单簿数据是微结构套利的原材料。数据质量直接决定策略成败。我建议你至少缓存前20档的订单簿数据,因为很多套利机会藏在深度里,而不是表面。
五、订单簿知识体系总览
下面这张图是我自己梳理的订单簿知识框架,你可以把它当作学习路线图:
这张图把订单簿的知识分成了四个层次:结构 → 指标 → 变化 → 数据。我个人建议你按照这个顺序来学习,每一步都动手写代码验证一下。
学习建议:别只看理论。打开交易所的WebSocket,订阅一个交易对的订单簿数据,盯着屏幕看10分钟。你会发现——市场比你想象的更有规律。那些大单的进出、价差的收窄、深度的变化,都在告诉你下一步该怎么走。
好了,订单簿这块就聊到这。记住一句话:订单簿是市场的底层语言。读懂了它,你就能听懂市场在说什么。
公众号:蓝海数据掘金营,微信deep3321