3、订单簿特征构建:订单簿不平衡、订单簿斜率、加权买卖价差、订单簿压力指标

好,咱们进入正题。订单簿特征,说白了就是把交易所那一堆挂单数据,变成能用的量化信号。我刚开始做高频策略的时候,也犯过傻——直接把十档行情扔进模型,结果过拟合得一塌糊涂。后来才明白,关键不在于数据多,而在于特征精。

这一节,我带你手撸四个核心特征:订单簿不平衡、订单簿斜率、加权买卖价差、订单簿压力指标。每个我都会讲清楚数学逻辑,再给Python实现。嗯,代码都是我在实盘环境里打磨过的,你直接拿去用问题不大。

3.1 订单簿不平衡(Order Book Imbalance)

这个特征最简单,也最实用。它衡量的是买方和卖方的力量对比。你想想看,如果买一档挂了1000手,卖一档只有100手,那价格往上走的概率是不是更大?

公式其实很直观:

 imbalance = (bid_volume - ask_volume) / (bid_volume + ask_volume)

取值范围在-1到1之间。正数表示买方占优,负数表示卖方占优。绝对值越大,单边力量越强。

我在项目中遇到过一个问题:只用第一档算出来的imbalance太毛躁了。后来我改成用前五档加权,效果好了不少。权重怎么给?我个人习惯用指数衰减,离当前价格越近的档位权重越大。

核心要点: 订单簿不平衡是微观结构里最基础的特征,但别小看它。很多高频做市策略的核心信号就是它。

代码实现如下:

def compute_imbalance(bids, asks, levels=5, decay=0.8):
    """
    bids: [(price, volume), ...] 买盘列表,已按价格降序排列
    asks: [(price, volume), ...] 卖盘列表,已按价格升序排列
    """
    bid_vol = 0
    ask_vol = 0
    weight = 1.0
    
    for i in range(min(levels, len(bids))):
        bid_vol += bids[i][1] * weight
        weight *= decay
    
    weight = 1.0
    for i in range(min(levels, len(asks))):
        ask_vol += asks[i][1] * weight
        weight *= decay
    
    if bid_vol + ask_vol == 0:
        return 0.0
    
    return (bid_vol - ask_vol) / (bid_vol + ask_vol)
小技巧: decay参数我一般设0.7到0.9之间。回测时可以调参,你会发现它对策略收益影响挺大的。

3.2 订单簿斜率(Order Book Slope)

斜率这个概念,其实是从经济学里的需求曲线借过来的。它描述的是:价格每变动一个单位,挂单量会变化多少。说白了,就是市场的「弹性」。

计算方式也不复杂。我们把买盘和卖盘分别拟合一条直线,斜率就是这条线的系数。具体来说:

  1. 对买盘:价格从低到高,累计挂单量从大到小。拟合价格与累计量的关系。
  2. 对卖盘:价格从低到高,累计挂单量从小到大。同样拟合。
  3. 斜率绝对值越大,说明这个价位附近的流动性越集中。

我曾经犯过一个错:直接用原始挂单量去拟合,结果噪声特别大。后来改成用累计挂单量,曲线平滑多了。你想想看,原始挂单量可能某一档突然冒出一个大单,但累计量就不会被这种单点异常带偏。

import numpy as np

def compute_slope(bids, asks, levels=10):
    # 买盘:价格升序,累计量递减
    bid_prices = np.array([b[0] for b in bids[:levels]])
    bid_cumvol = np.cumsum([b[1] for b in bids[:levels]][::-1])[::-1]
    
    # 卖盘:价格升序,累计量递增
    ask_prices = np.array([a[0] for a in asks[:levels]])
    ask_cumvol = np.cumsum([a[1] for a in asks[:levels]])
    
    # 线性拟合
    bid_slope = np.polyfit(bid_prices, bid_cumvol, 1)[0]
    ask_slope = np.polyfit(ask_prices, ask_cumvol, 1)[0]
    
    return bid_slope, ask_slope
注意: 当某一侧挂单档位不足时,拟合结果会不稳定。我建议至少要有5档数据才做斜率计算,否则直接返回NaN。

3.3 加权买卖价差(Weighted Spread)

传统的买卖价差就是卖一价减买一价。但这里有个问题:如果买一只有1手,卖一有1000手,这个价差能真实反映交易成本吗?显然不能。

加权买卖价差就是来解决这个问题的。它把每一档的挂单量作为权重,算出一个「等效」的价差。公式如下:

 weighted_spread = (sum(ask_price_i * ask_vol_i) / sum(ask_vol_i)) 
                  - (sum(bid_price_i * bid_vol_i) / sum(bid_vol_i))

说白了,就是买卖双方的加权平均价格之差。这个指标比简单价差更稳健。我记得有一次做回测,简单价差策略亏得一塌糊涂,换成加权价差后立马扭亏为盈。原因就是加权价差过滤掉了那些「虚挂」的单子。

def weighted_spread(bids, asks, levels=5):
    bid_wavg_price = sum(b[0] * b[1] for b in bids[:levels]) / sum(b[1] for b in bids[:levels])
    ask_wavg_price = sum(a[0] * a[1] for a in asks[:levels]) / sum(a[1] for a in asks[:levels])
    return ask_wavg_price - bid_wavg_price
实战经验: 加权价差和简单价差的差值,本身也是一个有用的特征。差值越大,说明订单簿的「形状」越不对称,往往预示着价格即将变动。

3.4 订单簿压力指标(Order Book Pressure)

这个指标是我个人比较偏爱的。它综合了价格和量两个维度,算出一个「压力值」。具体做法是:

  1. 把买盘和卖盘分别按价格分成几个区间(比如每0.1%一个区间)。
  2. 每个区间内,计算挂单总量。
  3. 用距离当前价格的远近做权重,越近权重越大。
  4. 最终压力 = 买方加权总量 - 卖方加权总量。

为什么要这么做?因为单纯看imbalance,它只考虑了总量,没考虑分布。而压力指标能告诉你:买方力量是集中在眼前,还是分散在远处。这两者的市场含义完全不同。

举个例子:如果买方压力集中在买一买二,说明多头很急,想立刻成交。如果买方压力分散在买五买六,说明多头只是「挂个单试试」,并不着急。这两种情况,对价格的预测意义截然不同。

def order_book_pressure(bids, asks, price_bins=10, bin_size=0.001):
    """
    price_bins: 分多少个区间
    bin_size: 每个区间的价格宽度(比例)
    """
    mid_price = (bids[0][0] + asks[0][0]) / 2
    
    bid_pressure = 0
    for price, vol in bids:
        dist = (mid_price - price) / mid_price
        bin_idx = int(dist / bin_size)
        if bin_idx < price_bins:
            weight = 1.0 / (bin_idx + 1)  # 距离越近权重越大
            bid_pressure += vol * weight
    
    ask_pressure = 0
    for price, vol in asks:
        dist = (price - mid_price) / mid_price
        bin_idx = int(dist / bin_size)
        if bin_idx < price_bins:
            weight = 1.0 / (bin_idx + 1)
            ask_pressure += vol * weight
    
    return bid_pressure - ask_pressure
避坑指南: 我曾经把bin_size设得太小,结果大部分挂单都落在第一个区间里,压力指标几乎等于imbalance。后来改成动态bin_size——根据最近N笔交易的波动率来调整,效果好了很多。

3.5 四个特征的综合运用

单独用任何一个特征,效果都有限。真正的价值在于组合。我一般会把这四个特征加上它们的滚动均值、滚动标准差,一起喂给模型。比如:

特征名称 原始值 滚动均值(20笔) 滚动标准差(20笔)
订单簿不平衡 0.32 0.15 0.28
订单簿斜率(买) -1.2e4 -1.1e4 3.2e3
加权买卖价差 0.021 0.019 0.005
订单簿压力 450 320 210

你看,光看原始值可能看不出什么,但结合滚动统计量,就能发现一些规律。比如不平衡的滚动标准差突然变大,说明市场分歧在加剧——这时候往往是大行情的前兆。

好了,这四个特征就讲完了。代码你直接拿去用,但记得根据你的交易品种和频率调整参数。嗯,实盘之前一定要回测,别偷懒。

订单簿特征构建知识体系 订单簿原始数据 订单簿不平衡 订单簿斜率 加权买卖价差 订单簿压力 买卖力量对比 [-1, 1]区间 正数买方占优 流动性集中度 线性拟合系数 绝对值越大越集中 真实交易成本 加权平均价差 过滤虚挂单 多空压力分布 距离加权 区分急迫程度 组合使用 + 滚动统计量 → 预测信号

公众号:蓝海资料掘金营,微信deep3321