4、OBI指标计算:实时计算买卖压力差、标准化处理、阈值设定

好,咱们进入正题。

前面聊了订单簿的基础结构,也讲了不平衡的基本逻辑。现在该动手了——怎么把「买卖压力差」这个抽象概念,变成一行行能跑的代码。

我个人习惯把OBI指标的计算拆成三步:原始压力差 → 标准化 → 阈值判定。每一步都有坑,我一个个说。

4.1 原始买卖压力差:从盘口数据到单边信号

先看最朴素的定义。

订单簿不平衡,说白了就是买盘和卖盘谁更「重」。我见过很多新手直接拿「买一量 - 卖一量」来算,结果被庄家挂单耍得团团转。你想想看,如果有人在买一挂了1000手,但卖一到卖五全是空单,这能说明买盘强吗?

所以我的做法是:取多档深度加权求和

公式长这样:

OBI_raw = (∑(bid_volume_i * weight_i) - ∑(ask_volume_i * weight_i)) / (∑(bid_volume_i * weight_i) + ∑(ask_volume_i * weight_i))

其中权重 weight_i 我习惯用指数衰减:weight_i = e^(-λ * i)。λ取0.5左右,近档权重大,远档权重小。这样既考虑了深度,又不会被远端虚挂单干扰。

核心要点:OBI_raw 的取值范围是 [-1, 1]。正值表示买盘压力大,负值表示卖盘压力大。0表示均衡。

我在项目中遇到过一个问题:不同币种、不同交易所的盘口档位数量不一样。有的只有5档,有的有50档。如果统一取前5档,信息损失太大;如果全取,又没法横向对比。

我的解决方案是:统一取前10档,不足10档的用0补齐。这样既保留了大部分流动性信息,又保证了计算的一致性。

4.2 标准化处理:让不同品种站在同一起跑线

好,现在你算出了OBI_raw。但问题来了——

比特币的OBI_raw是0.3,以太坊的是0.5。能说以太坊的买盘压力更大吗?

不一定。因为不同品种的波动特性、流动性深度都不一样。0.3在比特币可能已经是极端值,在以太坊可能只是正常波动。

所以需要标准化

我常用的方法有两种:

方法 公式 适用场景
Z-score标准化 OBI_z = (OBI_raw - μ) / σ 历史数据充足,波动稳定的品种
Min-Max标准化 OBI_mm = (OBI_raw - min) / (max - min) 新上市品种,或需要固定阈值区间

我个人更偏爱Z-score。为什么?因为它能告诉你当前的不平衡程度「偏离均值多少个标准差」。说白了,就是看这个信号有多「异常」。

我的经验:计算μ和σ时,建议用滚动窗口,比如过去24小时的5分钟切片。窗口太短容易过拟合,太长又反应迟钝。我一般取1440个数据点(24小时×60分钟/5分钟),效果比较稳。

嗯,这里要注意:标准化参数需要定期更新。我曾经吃过亏——用了一年前的μ和σ,结果市场结构变了,标准化后的信号全是噪音。现在我的做法是每天凌晨重新计算一次参数。

4.3 阈值设定:从连续信号到离散交易信号

标准化之后,你得到的是一个连续值。但交易决策需要的是「开仓」或「不开仓」的二元信号。

所以需要设定阈值。

阈值怎么定?我总结了三类方法:

  1. 固定阈值法:比如OBI_z > 2.0 开多,OBI_z < -2.0 开空。简单粗暴,但需要根据品种手动调参。
  2. 分位数法:取历史数据的90%分位数作为阈值。这样能保证信号出现的频率稳定,比如每天出现5-10次。
  3. 动态阈值法:根据当前波动率调整阈值。波动大时放宽,波动小时收紧。我常用ATR(平均真实波幅)来动态调整。

避坑指南:我曾经把阈值设得太低(比如1.5个标准差),结果一天触发上百次信号,手续费都亏麻了。后来改成2.5个标准差,信号质量明显提升。记住:阈值不是越高越好,也不是越低越好,而是要和你的交易频率、手续费成本匹配

下面是我实际项目中用的代码片段:

def calculate_obi(bids, asks, lambda_decay=0.5, z_threshold=2.0):
    # 计算加权买卖量
    bid_weighted = sum([vol * np.exp(-lambda_decay * i) for i, vol in enumerate(bids[:10])])
    ask_weighted = sum([vol * np.exp(-lambda_decay * i) for i, vol in enumerate(asks[:10])])
    
    # 原始OBI
    obi_raw = (bid_weighted - ask_weighted) / (bid_weighted + ask_weighted + 1e-8)
    
    # Z-score标准化(假设已有滚动均值和标准差)
    obi_z = (obi_raw - rolling_mean) / rolling_std
    
    # 阈值判定
    if obi_z > z_threshold:
        signal = 'BUY'
    elif obi_z < -z_threshold:
        signal = 'SELL'
    else:
        signal = 'NEUTRAL'
    
    return obi_raw, obi_z, signal

这段代码我用了好几年,核心逻辑没变过。唯一调整的就是 lambda_decayz_threshold 这两个参数——不同市场、不同周期,最优值差别很大。

4.4 本章知识体系:一张图说清楚

下面这张SVG图,把OBI指标计算的完整流程画出来了。从原始盘口数据,到最终的交易信号,每一步的输入输出都很清楚。

OBI指标计算流程 步骤1:原始盘口数据 买1~买10量价 / 卖1~卖10量价 步骤2:加权买卖压力差 指数衰减权重 λ=0.5 步骤3:标准化 Z-score / Min-Max 步骤4:阈值判定 固定/分位数/动态 输出:交易信号 BUY / SELL / NEUTRAL 关键参数调优建议 • λ(衰减系数):0.3~0.7,波动大取小值,波动小取大值 • 标准化窗口:建议24小时滚动,1440个5分钟切片 • 阈值:先试2.0,根据信号频率和盈亏比调整

这张图我每次培训都会用。你看,从左到右是数据流,从上到下是决策流。每一步都清晰可追溯——这也是量化交易的核心:可解释、可复现、可优化

一个小技巧:在实际部署时,我会把OBI_raw、OBI_z、signal三个值都记录下来。这样回测时能分析「到底是哪一步出了问题」。比如发现OBI_z很高但没赚钱,那可能是阈值设错了;如果OBI_raw本身就没区分度,那就要回头改权重参数。

好了,OBI指标的计算就这些。说白了就是三步:算压力差、标准化、设阈值。每一步都有细节,但核心逻辑不复杂。你动手写一遍代码,跑几个品种的数据,很快就能找到感觉。


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