第二章:订单簿数据噪声来源

做量化交易这些年,我踩过最大的坑,就是以为订单簿数据是「干净」的。

刚入行那会儿,我拿着原始数据直接跑策略,回测曲线漂亮得不行。结果一上实盘,直接被打脸。后来我才明白——订单簿数据里藏着各种各样的噪声,不处理干净,策略就是空中楼阁。

这一章,咱们就来聊聊噪声到底从哪来。

核心观点:订单簿噪声不是单一来源,而是多种因素叠加的结果。理解噪声来源,是降噪的第一步。

订单簿噪声来源 微观结构噪声 跳跃与异常值 买卖盘不平衡 采样频率影响 买卖价差 · 报价跳动 大单冲击 · 数据错误 挂单失衡 · 订单流 高频 vs 低频 噪声叠加 → 信号失真 → 策略失效

2.1 微观结构噪声

微观结构噪声,说白了就是交易机制本身带来的「毛刺」。

你想想看,订单簿不是连续流动的,它是离散的——每一笔报价、每一次成交,都是一个个独立的事件。这些事件之间,存在大量的随机波动。

我个人习惯把微观结构噪声分成三类:

  • 买卖价差噪声:买一和卖一之间的价差,本身就是一种噪声。价差越大,信号越模糊。
  • 报价跳动噪声:价格在买一和卖一之间来回跳,尤其是流动性差的品种,跳得你眼花。
  • 订单簿深度波动:挂单量忽大忽小,有时候几秒钟内变化几十倍。

我的经验:我在做股指期货订单簿分析时,发现微观结构噪声在开盘和收盘前特别严重。那段时间的订单簿数据,我建议直接打折扣使用。

举个例子,你看下面这段代码,能直观感受到微观结构噪声的「威力」:

# 模拟微观结构噪声
import numpy as np

# 真实价格信号
true_price = 100.0 + np.cumsum(np.random.randn(1000) * 0.01)

# 微观结构噪声:买卖价差导致的随机跳动
noise = np.random.choice([-0.02, 0, 0.02], size=1000, p=[0.3, 0.4, 0.3])

# 观测到的价格 = 真实价格 + 噪声
observed_price = true_price + noise

# 你看,观测价格比真实价格多了多少毛刺
print(f"真实价格波动: {np.std(np.diff(true_price)):.4f}")
print(f"观测价格波动: {np.std(np.diff(observed_price)):.4f}")

2.2 跳跃与异常值

跳跃噪声,是订单簿数据里最「要命」的一种。

为什么?因为它不是随机波动,而是突然的、大幅的跳变。这种跳变往往来自:

  1. 大单冲击:突然有人砸了一笔大单,价格瞬间跳了好几个档位。
  2. 数据错误:交易所数据推送偶尔会出问题,比如价格少了一个小数点。
  3. 流动性枯竭:某个价位上没人挂单,价格直接跳到下一个有挂单的位置。

避坑指南:我曾经因为没处理好异常值,导致策略在回测中「捡漏」了很多不存在的套利机会。实盘时这些机会根本不存在,白白亏了手续费。记住:异常值必须识别并处理,不能直接扔进模型。

识别跳跃噪声,我常用一个简单的方法——看价格变化是否超过N个标准差:

def detect_jumps(price_series, threshold=3):
    """
    检测价格序列中的跳跃噪声
    threshold: 标准差倍数阈值
    """
    returns = np.diff(price_series) / price_series[:-1]
    mean = np.mean(returns)
    std = np.std(returns)
    
    jump_indices = np.where(np.abs(returns - mean) > threshold * std)[0]
    return jump_indices

# 使用示例
jumps = detect_jumps(observed_price, threshold=4)
print(f"检测到 {len(jumps)} 个跳跃点")

2.3 买卖盘不平衡

买卖盘不平衡,这个我得多说两句。

很多人以为订单簿上的买量和卖量是「真实」的供需信号。其实不然——挂单可以随时撤掉,你看到的买一可能下一秒就没了。

买卖盘不平衡带来的噪声,主要体现在:

  • 虚假挂单:有人挂大单但不成交,只是为了制造假象。
  • 订单流不对称:买单和卖单的到达速度不一样,导致价格偏移。
  • 深度分布不均:某个价位上挂单特别多,但其他价位很稀疏。

我习惯用一个指标来衡量买卖盘不平衡——订单簿斜率。它比单纯的买卖量比更稳定:

def order_book_slope(bids, asks, levels=5):
    """
    计算订单簿斜率
    bids: 买单价格和数量 [(price, volume), ...]
    asks: 卖单价格和数量 [(price, volume), ...]
    """
    bid_prices = np.array([b[0] for b in bids[:levels]])
    bid_volumes = np.array([b[1] for b in bids[:levels]])
    ask_prices = np.array([a[0] for a in asks[:levels]])
    ask_volumes = np.array([a[1] for a in asks[:levels]])
    
    # 买单斜率:价格越低,量越大
    bid_slope = np.polyfit(bid_prices, bid_volumes, 1)[0]
    # 卖单斜率:价格越高,量越大
    ask_slope = np.polyfit(ask_prices, ask_volumes, 1)[0]
    
    return bid_slope, ask_slope

关键点:买卖盘不平衡噪声,本质上是「信息不对称」的体现。你看到的订单簿,是别人想让你看到的。

2.4 数据采样频率的影响

采样频率这个问题,我纠结了很久。

你想想看,采样频率越高,数据越「真实」吗?不一定。高频采样会引入更多的微观结构噪声。采样频率太低,又会丢失重要的市场信息。

我做过一个对比实验,结果很有意思:

采样频率 噪声水平 信号保留度 适用场景
Tick级(每笔) 极高 100% 高频做市
1秒 ~95% 高频趋势
10秒 中等 ~80% 日内交易
1分钟 ~60% 中低频策略

你看这个表,采样频率和噪声水平是正相关的。但信号保留度是负相关的。怎么选?

我个人习惯是:先高频采样,再降噪,最后重采样到目标频率。这样既保留了高频信息,又控制了噪声。

一个小技巧:如果你做的是中低频策略(比如持仓几小时),直接用1分钟数据就够了。别去碰tick级数据,那是给自己找麻烦。

最后总结一下这章的核心:

订单簿噪声不是「有」或「没有」的问题,而是「多少」和「怎么处理」的问题。微观结构噪声、跳跃异常值、买卖盘不平衡、采样频率——这四个来源,你每处理一个,信号质量就提升一截。

下一章,我会讲具体的降噪方法。但在这之前,我建议你先把自己的数据拿出来,看看这四种噪声分别占多大比例。知己知彼,才能对症下药。

本章要点:

  • 微观结构噪声来自交易机制的离散性
  • 跳跃异常值必须识别并处理,不能直接使用
  • 买卖盘不平衡反映的是信息不对称
  • 采样频率的选择是噪声与信号的权衡
蓝海数据掘金营,专注资料整理