4、市场深度分析:Level 2 订单簿构建、深度图绘制、加权深度计算

各位同学,今天我们来聊聊市场深度分析。说实话,这个主题在量化交易里属于「硬核」部分。很多人看行情只看价格和成交量,但真正的流动性细节,藏在 Level 2 数据里。

我个人习惯把市场深度比作「水池的深浅」。价格只是水面,而订单簿告诉你水下到底有多少鱼。嗯,咱们一步步来拆解。

4.1 订单簿的构建:从原始数据到实时快照

Level 2 数据,说白了就是交易所推送的逐笔委托。每一笔都包含价格、数量、方向(买/卖)。我们要做的,就是把这些数据拼成一张「实时挂单表」。

我在项目中遇到过一个问题:交易所的数据是增量推送的,不是全量。比如第一笔告诉你「有人在 10.00 元挂了 100 手买单」,第二笔告诉你「10.00 元的买单被撤了 50 手」。如果你不维护一个状态机,数据就会乱套。

正确的做法是维护两个字典:bids(买单)和 asks(卖单)。每次收到增量,就更新对应价格档位的数量。数量归零时,删除该档位。

核心逻辑:订单簿 = 价格 → 数量的映射,按价格排序。

# 伪代码:增量更新订单簿
bids = {}  # 买单字典 {价格: 数量}
asks = {}  # 卖单字典 {价格: 数量}

def update_orderbook(side, price, size):
    if side == 'buy':
        if size == 0:
            bids.pop(price, None)  # 撤单
        else:
            bids[price] = size     # 新单或修改
    elif side == 'sell':
        if size == 0:
            asks.pop(price, None)
        else:
            asks[price] = size

# 排序后得到深度快照
sorted_bids = sorted(bids.items(), reverse=True)  # 从高到低
sorted_asks = sorted(asks.items())                # 从低到高

你想想看,如果不用增量更新,每次全量拉取,网络开销和延迟都受不了。交易所也不会给你全量推送,太费带宽了。

我的经验:订单簿的更新频率极高,尤其在快市时。建议用 dictOrderedDict,别用列表遍历,否则性能会崩。我曾经用列表写过一版,回测时跑得飞快,一上实盘就卡死……嗯,血的教训。

4.2 深度图绘制:把数字变成视觉

有了订单簿,下一步就是画深度图。深度图本质上是一个累计曲线:从最优价格开始,往两边累加挂单量。

举个例子:买一价 10.00 元挂了 100 手,买二价 9.99 元挂了 200 手。那么累计到 9.99 元时,总买单量是 300 手。卖单同理。

画图时,X 轴是价格,Y 轴是累计量。买单曲线从左到右递增,卖单曲线从右到左递增。两条曲线中间的空隙,就是「价差区间」。

import matplotlib.pyplot as plt

def plot_depth(bids, asks):
    # bids: [(price, size), ...] 已排序
    # asks: [(price, size), ...] 已排序
    
    bid_prices = [p for p, s in bids]
    bid_cum = []
    total = 0
    for p, s in bids:
        total += s
        bid_cum.append(total)
    
    ask_prices = [p for p, s in asks]
    ask_cum = []
    total = 0
    for p, s in asks:
        total += s
        ask_cum.append(total)
    
    plt.step(bid_prices, bid_cum, where='post', label='Bids')
    plt.step(ask_prices, ask_cum, where='post', label='Asks')
    plt.xlabel('Price')
    plt.ylabel('Cumulative Volume')
    plt.legend()
    plt.show()

这里要注意:where='post' 表示阶梯线在价格点之后跳变,更符合订单簿的「挂单」逻辑。我之前用默认的 where='pre',画出来的图总是差一个档位,找了半天 bug……

避坑指南:深度图的 Y 轴刻度可能非常大(比如比特币深度动辄几千个 BTC)。建议使用对数刻度或归一化处理,否则图形会「压扁」在底部。我曾经画过一个深度图,买单曲线几乎成了一条水平线,因为卖单量是买单的 100 倍……

4.3 加权深度计算:量化流动性

深度图是直观的,但我们需要一个数值来量化「深度」。加权深度就是干这个的。

最简单的加权深度公式:

加权深度 = Σ (价格 × 数量) / Σ 数量

但实际中,我们更关心「在某个价格范围内,吃掉多少量才能把价格推到目标位置」。比如,我想把价格从 10.00 推到 10.05,需要吃掉多少卖单?

这个计算其实就是对卖单做累计,直到累计量对应的价格达到目标价。

def weighted_depth(asks, target_price):
    # asks: [(price, size), ...] 已排序
    total_volume = 0
    total_value = 0
    for price, size in asks:
        if price > target_price:
            break
        total_volume += size
        total_value += price * size
    return total_value / total_volume if total_volume > 0 else 0

这个值有什么用?我举个例子:假设加权深度是 10.02 元,而当前卖一价是 10.00 元。说明平均下来,你要花 10.02 元才能买到足够的量。这个「溢价」就是市场深度的直接体现。

关键指标:加权深度与当前价的差值,可以衡量「滑点成本」。差值越大,流动性越差。

我个人习惯同时计算「买侧加权深度」和「卖侧加权深度」,然后取两者的平均值作为整体深度指标。如果两侧差距很大,说明市场方向性偏斜——比如卖侧深度远大于买侧,可能有大户在压盘。

4.4 知识体系总览

下面这张图,是我自己总结的 Level 2 深度分析流程。你可以把它当作一个检查清单:

Level 2 市场深度分析流程 原始增量数据 订单簿构建(增量更新) 深度图绘制(累计曲线) 加权深度计算(滑点成本) 流动性评估 & 交易决策

这张图的核心逻辑是:原始数据 → 订单簿 → 两个分支(深度图 + 加权深度)→ 最终决策。你写代码的时候,也可以按这个顺序来组织模块。

4.5 实战中的几个坑

最后,分享几个我在实盘里踩过的坑:

  • 数据延迟:Level 2 数据量很大,如果处理速度跟不上,订单簿会「滞后」。我建议用异步 I/O 或多线程,别在主线程里做计算。
  • 价格精度:有些交易所的价格是浮点数,但累计计算时浮点误差会放大。我习惯把所有价格乘以 10000 转成整数再算。
  • 快市时的数据风暴:行情剧烈波动时,每秒可能有上千笔增量。如果逐笔更新,CPU 会爆。可以改成「定时快照」+「增量合并」的方式。

一个小技巧:加权深度不要只看当前时刻,可以计算过去 N 秒的平均值。比如 10 秒平均加权深度,能过滤掉瞬间的大单干扰。我自己的策略里,这个参数设为 5 秒效果最好。

好了,关于市场深度分析的核心内容就这些。记住:订单簿是基础,深度图是视觉,加权深度是量化。三者结合,你才能看清市场的真实流动性。


专注资料整理