4. 特征工程(下):订单簿斜率、订单簿不平衡度、微观结构特征(VPIN、买卖压力)

好,咱们接着聊特征工程。上一节我们把基础特征撸了一遍,这一节要上点硬菜了——订单簿斜率、不平衡度,还有微观结构里的VPIN和买卖压力。这些特征,说白了就是帮你从订单簿的“表面波动”里,挖出更深层的市场博弈信息。

我个人习惯,做高频策略时,斜率和不平衡度是必选项。它们就像市场的“心电图”,能告诉你当前是买方在发力,还是卖方在砸盘。而VPIN这类微观结构特征,更像是一台“CT机”,能扫描出订单流里的毒瘤——比如老鼠仓或者信息泄露。

4.1 订单簿斜率:市场的“坡度”

订单簿斜率,衡量的是价格深度关系的陡峭程度。你可以想象成爬山——坡度越陡,说明往上走(买)或往下走(卖)的阻力越大。

怎么算? 我一般用线性回归拟合买卖两侧的价量关系。具体来说,取前N档的累计深度和对应价格,拟合一条直线,斜率就是我们要的值。

def order_book_slope(df, levels=5):
    """
    计算订单簿斜率
    df: 包含 bid_price, bid_size, ask_price, ask_size 的DataFrame
    """
    # 买方斜率
    bid_prices = [f'bid_price_{i}' for i in range(1, levels+1)]
    bid_sizes = [f'bid_size_{i}' for i in range(1, levels+1)]
    
    # 用累计深度作为x,价格作为y
    x_bid = df[bid_sizes].cumsum(axis=1).values
    y_bid = df[bid_prices].values
    
    # 简单线性回归,返回斜率
    slope_bid = np.polyfit(x_bid, y_bid, deg=1)[0]
    
    # 卖方同理
    ask_prices = [f'ask_price_{i}' for i in range(1, levels+1)]
    ask_sizes = [f'ask_size_{i}' for i in range(1, levels+1)]
    x_ask = df[ask_sizes].cumsum(axis=1).values
    y_ask = df[ask_prices].values
    slope_ask = np.polyfit(x_ask, y_ask, deg=1)[0]
    
    return slope_bid, slope_ask
我的经验: 斜率绝对值越大,说明市场越“薄”。比如买方斜率很大,意味着稍微买一点价格就跳得很高——这是流动性枯竭的信号。我在做股指期货时,经常用这个来预警闪崩。

4.2 订单簿不平衡度:谁在主导?

不平衡度,就是看买卖双方谁更“用力”。公式很简单:(买方总深度 - 卖方总深度) / (买方总深度 + 卖方总深度)。值域在[-1, 1],正数表示买方占优,负数表示卖方占优。

但这里有个坑——直接用原始深度算,容易被大单刷屏。你想想看,一个挂单1000手的幽灵单,能瞬间把不平衡度拉到0.8,但实际成交可能只有10手。

避坑指南: 我曾经在实盘里吃过这个亏。后来我改用“加权不平衡度”——用价格加权,或者用近几档的深度加权。比如给第1档权重0.5,第2档0.3,第3档0.2,这样更贴近真实博弈。
def weighted_imbalance(df, weights=[0.5, 0.3, 0.2]):
    """
    加权订单簿不平衡度
    """
    bid_depth = sum(df[f'bid_size_{i}'] * w for i, w in enumerate(weights, 1))
    ask_depth = sum(df[f'ask_size_{i}'] * w for i, w in enumerate(weights, 1))
    
    imbalance = (bid_depth - ask_depth) / (bid_depth + ask_depth + 1e-8)
    return imbalance

4.3 VPIN:订单流毒性的“CT机”

VPIN(Volume-synchronized Probability of Informed Trading),翻译过来就是“成交量同步知情交易概率”。名字很唬人,但核心思想很简单:把成交量分成若干桶(bucket),每个桶里统计买卖方向的不平衡度。如果某个桶里买卖方向极度失衡,说明可能有知情交易者在动手。

计算步骤:

  1. 把成交量分成等量的桶(比如每个桶1000手)
  2. 每个桶内,用Tick数据判断每笔交易是买方主动还是卖方主动
  3. 统计每个桶的买卖不平衡度
  4. 取最近N个桶的不平衡度标准差,就是VPIN
def compute_vpin(trades, bucket_volume=1000, n_buckets=50):
    """
    计算VPIN
    trades: 包含 volume, side 的DataFrame(side: 1=买, -1=卖)
    """
    trades['cum_vol'] = trades['volume'].cumsum()
    trades['bucket'] = trades['cum_vol'] // bucket_volume
    
    # 每个桶内计算买卖不平衡
    bucket_stats = trades.groupby('bucket').agg(
        buy_vol=('volume', lambda x: x[trades.loc[x.index, 'side']==1].sum()),
        sell_vol=('volume', lambda x: x[trades.loc[x.index, 'side']==-1].sum())
    )
    bucket_stats['imbalance'] = abs(bucket_stats['buy_vol'] - bucket_stats['sell_vol']) / bucket_volume
    
    # VPIN = 最近N个桶的imbalance均值
    vpin = bucket_stats['imbalance'].rolling(n_buckets).mean().iloc[-1]
    return vpin
关键点: VPIN越高,说明订单流毒性越强。我记得在2010年美股闪崩时,VPIN指标提前几分钟就飙到了历史高位。如果你在做高频策略,VPIN可以作为风控开关——当VPIN超过阈值时,自动降低仓位或暂停交易。

4.4 买卖压力:微观结构的“压力测试”

买卖压力,我把它理解为“订单簿的压强”。公式是:买卖压力 = (买方主动成交量 - 卖方主动成交量) / 总成交量。但这里有个细节——要用Tick级别的逐笔数据,而不是快照数据。

为什么?因为快照数据只能看到挂单,看不到真实成交的“推力”。比如一个1000手的买单,如果它被拆成100笔10手的小单慢慢吃掉,那压力是逐渐释放的;但如果是一笔1000手的市价单直接吃掉,那就是瞬间的“高压”。

实战中我这样用:

  • 计算过去1分钟内的买卖压力
  • 如果压力持续为正且增大,说明买方在主动吃货,价格大概率要涨
  • 如果压力突然由正转负,可能是主力在出货,要警惕
def buy_sell_pressure(trades, window='1min'):
    """
    计算买卖压力
    """
    trades['datetime'] = pd.to_datetime(trades['timestamp'], unit='ms')
    trades.set_index('datetime', inplace=True)
    
    # 按时间窗口聚合
    resampled = trades.resample(window).agg(
        buy_vol=('volume', lambda x: x[trades.loc[x.index, 'side']==1].sum()),
        sell_vol=('volume', lambda x: x[trades.loc[x.index, 'side']==-1].sum())
    )
    resampled['pressure'] = (resampled['buy_vol'] - resampled['sell_vol']) / (resampled['buy_vol'] + resampled['sell_vol'] + 1e-8)
    return resampled['pressure']

4.5 特征组合:1+1 > 2

单独用某个特征,效果有限。我习惯把斜率、不平衡度、VPIN、买卖压力组合起来,构建一个“市场状态向量”。比如:

状态 斜率 不平衡度 VPIN 买卖压力 策略建议
强势上涨 买方陡峭 > 0.6 持续正 追多
虚假突破 买方陡峭 > 0.6 突然转负 观望或做空
流动性枯竭 双侧陡峭 接近0 极高 剧烈波动 暂停交易

你看,同样的斜率和不平衡度,加上VPIN和买卖压力后,就能区分出“真突破”还是“假突破”。这就是微观结构特征的威力。

我的小技巧: 这些特征不要直接喂进模型。我一般会先做标准化,然后做PCA降维,保留前3-5个主成分。这样既能保留信息,又能减少噪声。嗯,这里要注意——PCA之前一定要检查特征的相关性,如果两个特征高度相关(比如斜率和不平衡度),可以考虑只保留一个。

4.6 本章知识体系

下面这张图,是我自己梳理的本章知识结构。你可以把它当作一个“特征工程地图”,方便以后查阅。

订单簿预测特征工程 订单簿斜率 订单簿不平衡度 VPIN 买卖压力 线性回归拟合 流动性预警 加权深度计算 买卖方向判断 成交量分桶 知情交易概率 Tick级逐笔统计 特征组合 → 市场状态向量

好了,这一节的内容就到这。这些特征看起来多,但用起来其实就那几行代码。关键是理解每个特征背后的市场逻辑——为什么它有效?什么时候会失效?想通了这些,你就能在实战中灵活运用了。


公众号:蓝海数据掘金营,微信deep3321