第二章:价差与深度——买卖价差、市场深度与订单簿不平衡指标的计算

各位同学,今天我们来聊聊订单簿里最核心的几个指标。说白了,就是看市场的「厚度」和「宽度」。我刚开始做高频的时候,觉得这些指标太基础了,后来才发现——嗯,越是基础的东西,越容易踩坑。

2.1 买卖价差:市场流动性的第一道门槛

买卖价差(Bid-Ask Spread),就是最优买价和最优卖价之间的差值。你想想看,如果你现在想立刻成交,就得付出这个成本。价差越小,说明市场越「滑溜」。

核心公式:
绝对价差 = 最优卖价(Ask) - 最优买价(Bid)
相对价差 = (Ask - Bid) / 中间价 × 100%

我个人习惯用相对价差,因为不同品种价格差异太大。比如螺纹钢和黄金,绝对价差完全没法比,但相对价差就能直接对比流动性。

我在项目中遇到过一件事:有一次做股指期货的套利策略,发现价差突然从0.2个点跳到0.8个点。我以为是数据出错了,后来一查——原来是交易所的撮合引擎在切换。所以啊,价差异常不一定是市场行为,也可能是系统行为。

实战小技巧:
计算价差时,记得用「中间价」作为基准。中间价 = (Bid + Ask) / 2。这样算出来的相对价差更稳定。

2.2 市场深度:不只是看第一档

很多新手只看最优买卖价,觉得价差小就是流动性好。其实不然。你想想看,如果最优价只有1手挂单,后面第二档直接跳开10个tick,那这个市场其实很「虚」。

市场深度,通常用「订单簿各档位的累计挂单量」来衡量。我一般会看三个维度:

  • 深度1(Depth1):最优买卖价上的挂单量
  • 深度5(Depth5):前5档的累计挂单量
  • 深度10(Depth10):前10档的累计挂单量

为什么要看多档?因为大资金进场时,不会只吃第一档。我曾经做过一个统计:在螺纹钢主力合约上,超过70%的大单成交会穿透前3档。所以只看第一档深度,你根本不知道真正的「承接力」在哪。

避坑指南:
我曾经犯过一个错误——直接用交易所的「深度快照」数据来计算深度。后来发现,快照数据有延迟,而且某些交易所的深度数据是「聚合」过的,不是真实的逐笔挂单。所以,如果你做高频,最好用逐笔委托数据自己重建订单簿。

2.3 订单簿不平衡指标:谁在主导市场?

这个指标很有意思。它衡量的是买单和卖单的力量对比。公式很简单:

订单簿不平衡(OBI):
OBI = (买单总深度 - 卖单总深度) / (买单总深度 + 卖单总深度)
取值范围:-1 到 1
正值表示买方力量强,负值表示卖方力量强

但这里有个坑——你用什么深度范围来计算?用第一档?还是前五档?还是整个订单簿?

我个人习惯用「加权深度」,给不同档位赋予不同权重。比如:

  • 第一档权重:0.4
  • 第二档权重:0.3
  • 第三档权重:0.2
  • 第四、五档权重:0.1

为什么这么设?因为离最优价越近的挂单,对价格的影响越大。你想想看,如果有人在第一档挂了1000手,和有人在第十档挂了1000手,意义完全不一样。

2.4 代码实现:从原始数据到指标计算

好了,理论说完了,我们来看看代码。下面这个函数,是我在实际项目中用的版本:

def calculate_spread_and_depth(order_book):
    """
    计算买卖价差和市场深度
    
    参数:
        order_book: dict, 包含 bids 和 asks 两个列表
                    每个元素是 [price, volume]
    
    返回:
        dict: 包含价差、深度、不平衡指标
    """
    # 提取最优买卖价
    best_bid = order_book['bids'][0][0]
    best_ask = order_book['asks'][0][0]
    best_bid_vol = order_book['bids'][0][1]
    best_ask_vol = order_book['asks'][0][1]
    
    # 绝对价差和相对价差
    spread_abs = best_ask - best_bid
    mid_price = (best_bid + best_ask) / 2
    spread_rel = spread_abs / mid_price * 100
    
    # 前5档深度
    bid_depth_5 = sum([vol for price, vol in order_book['bids'][:5]])
    ask_depth_5 = sum([vol for price, vol in order_book['asks'][:5]])
    
    # 加权不平衡指标(前5档)
    weights = [0.4, 0.3, 0.2, 0.1, 0.0]
    weighted_bid = sum([vol * weights[i] 
                       for i, (price, vol) in enumerate(order_book['bids'][:5])])
    weighted_ask = sum([vol * weights[i] 
                       for i, (price, vol) in enumerate(order_book['asks'][:5])])
    
    total = weighted_bid + weighted_ask
    obi = (weighted_bid - weighted_ask) / total if total > 0 else 0
    
    return {
        'spread_abs': spread_abs,
        'spread_rel': spread_rel,
        'mid_price': mid_price,
        'bid_depth_5': bid_depth_5,
        'ask_depth_5': ask_depth_5,
        'obi': obi
    }
注意:
这个函数假设订单簿已经按价格排序好了。实际生产中,你需要自己维护一个有序的订单簿结构。我一般用 sortedcontainers 库,或者自己写一个跳表。

2.5 知识体系总览

为了让你更直观地理解这些指标之间的关系,我画了一张图:

价差与深度指标体系 买卖价差 绝对价差 相对价差 中间价计算 市场深度 深度1/5/10 累计挂单量 多档穿透分析 订单簿不平衡 OBI计算公式 加权深度 多档权重分配 实际应用场景 流动性评估 → 交易成本估算 → 订单执行策略 市场微观结构建模 → 高频信号生成

这张图把三个核心指标串起来了。从买卖价差到市场深度,再到订单簿不平衡,层层递进。实际做策略时,这三个指标要结合起来看。比如:价差很小但深度很浅,说明市场「虚胖」;价差大但深度厚,说明市场「稳重」但成本高。

2.6 实战中的注意事项

最后,分享几个我在实战中总结的经验:

  1. 数据频率很重要:如果你用1秒的快照数据,算出来的价差和深度可能已经「过时」了。高频交易中,我一般用毫秒级甚至微秒级的数据。
  2. 注意交易所规则:不同交易所的订单簿结构不一样。比如上期所是「价格优先、时间优先」,但有些交易所是「价格优先、数量优先」。这会影响深度的计算方式。
  3. 异常值处理:我遇到过订单簿里出现「0价格」或者「负数量」的情况。虽然很少见,但一定要做数据清洗。
  4. 回测时别用未来数据:这个看起来是常识,但我见过有人用当天的全部数据算平均深度,然后去预测当天的价格——这明显是未来函数。
重要提醒:
这些指标单独看都有局限性。比如OBI指标,在盘口薄的时候很容易被大单「操纵」。所以,我一般会结合成交量、波动率等指标一起用。单一指标做决策,风险很大。

好了,这一章的内容就到这里。价差和深度是微观结构分析的基础,但也是最容易被忽视的。你想想看,如果连市场的「厚度」和「宽度」都搞不清楚,后面的策略设计就是空中楼阁。

专注资料整理