第1章:订单簿动态分析——从事件到重建的实战之路
做量化交易这些年,我越来越觉得订单簿就是市场的「心跳」。
你想想看,每一笔限价单、市价单、撤单,都是市场参与者在用真金白银投票。读懂这些信号,你就能提前感知价格走向。
这一章,咱们就聊聊订单簿的动态分析。说白了,就是搞清楚订单簿怎么变、为什么变、变了之后我们能做什么。
1.1 订单簿的三大核心事件
订单簿的变化,归根结底就三种事件。我在项目中遇到过不少新手,一上来就盯着K线看,却忽略了这些最原始的数据。
1.1.1 限价单(Limit Order)
限价单是「挂单」。你告诉交易所:我愿意在某个价格买/卖,但绝不接受更差的价格。
举个例子:
// 限价买单:在100.50买入100股
{
"type": "limit",
"side": "buy",
"price": 100.50,
"quantity": 100,
"timestamp": 1699000000000
}
限价单进入订单簿后,会按价格优先、时间优先的原则排队。价格越优(买价越高、卖价越低),排得越靠前。
关键点:限价单提供流动性。做市商就是靠挂限价单赚取买卖价差的。
1.1.2 市价单(Market Order)
市价单是「吃单」。你不管价格,只求立即成交。
我习惯把市价单比作「饿狼」——它一来,就会吃掉订单簿上最优的对手单。
// 市价买单:立即买入200股,按最优卖价成交
{
"type": "market",
"side": "buy",
"quantity": 200,
"timestamp": 1699000001000
}
市价单会消耗流动性。如果订单簿深度不够,市价单可能导致严重的滑点。
避坑指南:我曾经在流动性差的品种上吃过亏。一个市价单下去,成交价直接跳了3个tick。后来我学乖了,市价单前一定先看订单簿深度。
1.1.3 撤单(Cancel Order)
撤单就是取消之前挂的限价单。原因很多:价格不合适、策略调整、或者单纯手滑了。
// 撤单:取消ID为abc123的订单
{
"type": "cancel",
"order_id": "abc123",
"timestamp": 1699000002000
}
撤单在订单簿里很常见。高频交易中,撤单率可能高达90%以上。你想想看,很多订单其实只是「试探」一下市场反应。
1.2 订单簿重建——从零开始拼图
交易所通常不会把完整的订单簿推给你,而是发增量数据(snapshot + update)。
重建订单簿,说白了就是:先拿一张快照,然后不断用增量事件更新它。
1.2.1 重建流程
- 获取快照(Snapshot):拿到当前时刻的完整订单簿
- 订阅增量流(Update Stream):接收后续的限价单、市价单、撤单事件
- 逐事件更新:每个事件来了,修改订单簿的对应位置
- 定期校验:每隔一段时间重新拉取快照,防止数据漂移
我的经验:建议每5分钟重新拉一次快照。因为增量数据在传输中可能丢包,时间长了订单簿会「漂移」——价格对不上,那就麻烦了。
1.2.2 代码实现
下面是一个简化版的订单簿重建逻辑:
class OrderBook:
def __init__(self):
self.bids = {} # 买单:价格 -> 数量
self.asks = {} # 卖单:价格 -> 数量
def apply_snapshot(self, snapshot):
"""应用快照"""
self.bids = {item['price']: item['qty'] for item in snapshot['bids']}
self.asks = {item['price']: item['qty'] for item in snapshot['asks']}
def apply_update(self, event):
"""应用增量事件"""
price = event['price']
qty = event['quantity']
side = self.bids if event['side'] == 'buy' else self.asks
if qty == 0:
# 数量为0表示撤单
side.pop(price, None)
else:
# 新增或更新限价单
side[price] = qty
def get_top(self, level=1):
"""获取最优买卖价"""
best_bid = max(self.bids.keys()) if self.bids else None
best_ask = min(self.asks.keys()) if self.asks else None
return best_bid, best_ask
1.3 逐笔数据解析——最细粒度的市场信号
逐笔数据(Tick-by-Tick Data)记录了每一笔成交的细节。它比K线更原始,信息量更大。
1.3.1 逐笔数据的结构
| 字段 | 说明 | 示例 |
|---|---|---|
| timestamp | 成交时间(纳秒级) | 1699000000123456 |
| price | 成交价格 | 100.50 |
| quantity | 成交数量 | 200 |
| side | 主动方方向(买/卖) | buy |
| aggressor | 主动方标识 | market_maker_01 |
1.3.2 从逐笔数据中提取信号
我个人习惯从逐笔数据中看三个东西:
- 买卖压力:主动买单 vs 主动卖单的数量比。比值大于1.5,说明买方强势。
- 大单识别:单笔成交量超过平均量3倍以上的,可能是机构在动手。
- 成交速度:单位时间内的成交笔数。突然加速,往往意味着有大行情要来了。
实战技巧:我曾经用逐笔数据抓过「老鼠仓」。某只股票在重大利好公布前,连续出现小额买单,但每笔都刚好吃掉卖一。这种模式,逐笔数据一看就露馅了。
1.4 订单簿动态分析的核心逻辑
下面这张图,是我自己总结的订单簿分析框架:
1.5 实战中的几个坑
嗯,这里我要多说几句。订单簿分析看着简单,实际坑不少。
- 数据延迟:网络延迟会导致你看到的订单簿和真实市场有偏差。我建议用本地时钟校准,或者直接用交易所的WebSocket时间戳。
- 订单簿深度不足:有些品种只有几层深度,市价单很容易打穿。这时候看价差和深度比,比看价格本身更有意义。
- 撤单率过高:高频交易中,很多订单是「假单」。我曾经见过一个策略,挂单后0.1秒就撤,纯粹是为了制造流动性假象。
重要提醒:千万别把订单簿数据直接用于回测。因为订单簿是「快照」,而回测需要的是「状态」。你想想看,回测时你看到的订单簿,可能已经被后续事件改变了。
1.6 小结
订单簿动态分析,说白了就是三件事:看懂事件、重建状态、提取信号。
我个人觉得,这是量化交易里最「接地气」的技术。它不像机器学习那么玄乎,也不像高频交易那么烧钱。只要你愿意花时间,就能从订单簿里读出市场的真实意图。
记住一句话:订单簿不会骗人,但解读订单簿的人可能会。
我的建议:刚开始别急着写策略。先花一周时间,每天盯着订单簿看。看它怎么变、为什么变。等你能「感觉」到市场的呼吸了,再动手写代码。