第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 重建流程

  1. 获取快照(Snapshot):拿到当前时刻的完整订单簿
  2. 订阅增量流(Update Stream):接收后续的限价单、市价单、撤单事件
  3. 逐事件更新:每个事件来了,修改订单簿的对应位置
  4. 定期校验:每隔一段时间重新拉取快照,防止数据漂移

我的经验:建议每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 小结

订单簿动态分析,说白了就是三件事:看懂事件、重建状态、提取信号。

我个人觉得,这是量化交易里最「接地气」的技术。它不像机器学习那么玄乎,也不像高频交易那么烧钱。只要你愿意花时间,就能从订单簿里读出市场的真实意图。

记住一句话:订单簿不会骗人,但解读订单簿的人可能会。

我的建议:刚开始别急着写策略。先花一周时间,每天盯着订单簿看。看它怎么变、为什么变。等你能「感觉」到市场的呼吸了,再动手写代码。

交易系统化学习资料 微信Strategy888888