↓ 跳过正文
  1. Posts/

从 OBI、OFI 到 Microprice:高频 Alpha 信号的直觉与验证

·5530 字·12 分钟

前两篇写 Bid-Ask Spread 和 Adverse Selection 以及执行感知回测的时候,讨论的核心问题是"预测对了方向,为什么交易还可能亏钱"。结论是执行成本、成交概率和 Adverse Selection 都会侵蚀预测收益。

但我一直没有回到一个更前面的问题:预测本身从哪来?

具体一点:在价格还没变的时候,订单簿的当前状态和最近的变化,能不能给出关于下一次价格变动方向的概率信息?

这篇文章是我学习三个相关概念的笔记——Order Book Imbalance(OBI)、Order Flow Imbalance(OFI)和 Microprice。我想搞清楚它们各自在测量什么,以及如何把它们变成一个可以验证的 Alpha 假设。

盘口不平衡与价格方向
#

先从一个 BTC 永续合约的 L1 盘口开始:

1Best Bid = 99,999 USDT    Volume = 100 BTC
2Best Ask = 100,000 USDT   Volume = 20 BTC

Mid-price 是 99,999.5 USDT。

买卖挂单量明显不平衡的 L1 盘口示意

这个盘口有一个很直观的特征:卖一只有 20 BTC,买一有 100 BTC。如果接下来有一笔主动买入吃掉卖一的全部 20 BTC,Ask 就得移到下一个更高的价位,Mid-price 上升;反过来,要吃掉买一的 100 BTC 才能让 Bid 下移,需要 5 倍的卖出量。

在其他条件相近时(主动买卖到达速率差不多),卖一被吃掉的概率大于买一被吃掉的概率。也就是说,下一次 Mid-price 变化更可能是向上的。

两个需要立刻框定的边界。第一,这是概率信息,不是确定性判断——买一虽然厚,但如果一笔大额市价卖单砸进来,100 BTC 瞬间就没了。第二,这里讨论的是被动挂单(resting orders)的分布,不是主动买卖压力——挂在盘口的 100 BTC 买单提供的是流动性,它本身不会推动价格移动;推动价格移动的是主动到达的 taker 订单。关于 maker/taker 的角色区分,在订单簿那篇里已经详细写过。

OBI:一个盘口快照的不平衡度
#

把上面的直觉量化。定义 \(V^b_t\) 和 \(V^a_t\) 分别是 \(t\) 时刻 Best Bid 和 Best Ask 上的挂单量,Order Book Imbalance:

$$ OBI_t = \frac{V^b_t - V^a_t}{V^b_t + V^a_t} $$

取值范围是 \([-1, 1]\)。买一挂单远多于卖一时 OBI 接近 +1,反过来接近 -1。两侧相等时 OBI = 0。

用前面的盘口算一下:

$$ OBI = \frac{100 - 20}{100 + 20} = \frac{80}{120} \approx 0.667 $$

OBI 为正,买一比卖一厚,对应前面说的"下一次 Mid-price 变化更可能向上"的直觉。Gould 和 Bonart 在 2016 年的论文(Queue Imbalance as a One-Tick-Ahead Price Predictor in a Limit Order Book)中,对多只股票做了实证研究,发现 OBI 对下一次 tick 级别的价格变化方向确实具有统计上的预测能力。

再看另一组盘口:

1Best Bid = 99,999 USDT    Volume = 50 BTC
2Best Ask = 100,000 USDT   Volume = 10 BTC
$$ OBI = \frac{50 - 10}{50 + 10} = \frac{40}{60} \approx 0.667 $$

OBI 和之前一模一样。但两个盘口的绝对深度完全不同——第一个总共有 120 BTC 挂在最优档位,第二个只有 60 BTC。一笔 15 BTC 的市价卖单在第一个盘口里连买一的 15% 都吃不掉,在第二个盘口里已经消耗了买一的 30%。

相同的 OBI 可以对应截然不同的流动性环境。 OBI 只描述两侧挂单量的比例关系,不反映绝对深度。用 OBI 做研究时,可能需要把绝对深度或 Spread 作为独立的控制变量。

还有一点:上面用的都是 L1(仅最优档位)数据。实际上可以把 OBI 扩展到多档——比如前 5 档挂单量的加权和,或者按价格距离衰减加权。多档 OBI 信息更丰富,但计算复杂度和参数选择也增加了。这篇文章只讨论 L1 OBI,后续的推导和案例都基于此。

OFI:从快照到变化
#

OBI 描述的是某个时刻盘口的静态状态。但一个自然的问题是:如果两个时刻的 OBI 完全一样,是不是说明这段时间内盘口没有发生任何有意义的变化?

答案是不一定。

回到前面的两组数字。假设它们不是两个独立的盘口,而是同一个合约在 \(t_0\) 和 \(t_1\) 两个时刻的状态:

\(t_0\):Bid Volume = 100,Ask Volume = 20,OBI = 0.667

\(t_1\):Bid Volume = 50,Ask Volume = 10,OBI = 0.667

OBI 没有变。但如果看挂单量的变化:

  • 买一:从 100 减少到 50,\(\Delta V_{bid} = 50 - 100 = -50\)
  • 卖一:从 20 减少到 10,\(\Delta V_{ask} = 10 - 20 = -10\)

两侧都在流失挂单。买一的流失速度远大于卖一。

Order Flow Imbalance(OFI)就是用来捕捉这种变化量的不对称性的。在 Best Bid 和 Best Ask 价格都没有变化的简化情况下:

$$ OFI = \Delta V_{bid} - \Delta V_{ask} $$

代入数字:

$$ OFI = (-50) - (-10) = -40 $$

OFI 为负,意味着买方挂单的流失净多于卖方。虽然两个时刻的 OBI 一样,OFI 告诉我们这段时间内买方的相对支撑实际上在减弱。

同一合约两个时刻盘口:OBI 相同,OFI 不同

这里我需要停下来澄清几件事。

第一,OFI 不是 OBI 的一阶导数,也不是相邻两次 OBI 之差。OBI 是一个比值,OFI 是两侧绝对变化量的差。两个 OBI 完全相同的时刻之间,OFI 可以是任意值;两个 OBI 不同的时刻之间,OFI 也可以为零(两侧等量变化但方向相反)。

第二,\(\Delta V\) 里包含了多种事件的净效果。在买一价格不变的前提下,买一挂单量减少 50 BTC,可能是撤单导致的,也可能是被主动卖单消耗了,或者两者的组合。OFI 不区分这些来源,它度量的是净结果。

具体来说,各类订单簿事件对 OFI 的贡献方向:

事件对 OFI 的贡献
买一新增限价单正(\(\Delta V_{bid}\) 增加)
买一撤单负(\(\Delta V_{bid}\) 减少)
主动卖单消耗买一负(\(\Delta V_{bid}\) 减少)
卖一新增限价单负(\(\Delta V_{ask}\) 增加)
卖一撤单正(\(\Delta V_{ask}\) 减少)
主动买单消耗卖一正(\(\Delta V_{ask}\) 减少)

这里第三个关键点:上面的公式 \(OFI = \Delta V_{bid} - \Delta V_{ask}\) 只在 Best Bid/Ask 价格不变时成立。 如果在 \(t_0\) 到 \(t_1\) 之间 Best Bid 或 Best Ask 的价格发生了变化——比如买一被完全吃掉,新的 Best Bid 跌到了下一档——直接拿两个时刻的挂单量做差就不对了,因为你在比较不同价位上的量。

Cont、Kukanov 和 Stoikov 在 2014 年的论文(The Price Impact of Order Book Events)中给出了 OFI 的标准定义,用分段函数处理了价格档位变化的情况。大意是:如果 Best Bid 价格上升,说明原来的买一被更高的新买单替代了,这相当于买方力量增强,OFI 获得正贡献;如果 Best Bid 价格下降,说明原来的买一被完全消耗或撤走了,OFI 获得负贡献。Ask 侧的逻辑对称。

为了保证文章可读性,这里不展开完整的分段公式。但做实证研究时必须用标准定义,否则在价格频繁跳动的高频数据上,简化公式会引入系统性的测量误差。

最后一个容易混淆的地方:OFI 不是 Trade Imbalance(成交不平衡)。 Trade Imbalance 通常定义为一段时间内主动买入成交量减去主动卖出成交量,只涉及实际发生的成交。OFI 还包含了限价单的新增和撤销——没有任何成交发生,仅仅是有人在卖一挂了一大笔新单,也会导致 OFI 变负。两个指标度量的东西不同,在特征工程中不应该互相替代。

Microprice:为什么要修正 Mid-price
#

到这里我有了 OBI 和 OFI 两个盘口指标。但在想怎么用它们之前,先要处理一个参考价格的问题。

Mid-price 的定义很简单:

$$ Mid = \frac{P_{ask} + P_{bid}}{2} $$

它只用到了买一和卖一的价格,完全没有利用挂单量的信息。前面讨论 OBI 时已经看到,当两侧挂单量严重不对称时,Mid-price 作为"公允价值"的估计可能存在偏差。

一个自然的想法:能不能构造一个利用挂单量的加权价格?

写成公式:

$$ P_w = \frac{P_{ask} \cdot V_{bid} + P_{bid} \cdot V_{ask}}{V_{bid} + V_{ask}} $$

这里的加权方式是交叉的——Ask Price 乘以 Bid Volume,Bid Price 乘以 Ask Volume。初看有点反直觉,但想一下就通了:如果买一的挂单量远大于卖一,说明卖一更容易被消耗,下一个成交价更可能靠近 Ask,所以加权价格应该偏向 Ask。在 \(P_w\) 的公式里,\(V_{bid}\) 越大,\(P_{ask}\) 的权重越高,结果越靠近 Ask。逻辑是自洽的。

我想把这个公式和前面的 OBI 联系起来。把 \(P_w\) 展开试试:

$$ P_w = \frac{P_{ask} \cdot V_{bid} + P_{bid} \cdot V_{ask}}{V_{bid} + V_{ask}} $$

分子加减 \(P_{bid} \cdot V_{bid}\):

$$ = \frac{P_{bid}(V_{bid} + V_{ask}) + (P_{ask} - P_{bid}) \cdot V_{bid}}{V_{bid} + V_{ask}} $$$$ = P_{bid} + Spread \cdot \frac{V_{bid}}{V_{bid} + V_{ask}} $$

其中 \(Spread = P_{ask} - P_{bid}\)。同样的手法也可以从 \(P_{ask}\) 方向推。换一种形式,利用 \(Mid = (P_{ask} + P_{bid})/2\):

$$ P_w = Mid + \frac{Spread}{2} \cdot \frac{V_{bid} - V_{ask}}{V_{bid} + V_{ask}} = Mid + \frac{Spread}{2} \cdot OBI $$

最终结果非常简洁:

$$ \boxed{P_w = Mid + \frac{Spread}{2} \cdot OBI} $$

用数字验证一下。取 Bid = 99,999,Ask = 100,000,Bid Volume = 900,Ask Volume = 100:

  • Mid = 99,999.5
  • Spread = 1
  • OBI = (900 - 100) / (900 + 100) = 0.8
  • \(P_w = 99{,}999.5 + 0.5 \times 0.8 = 99{,}999.9\)

因为买一远厚于卖一,加权价格从 Mid 向 Ask 方向偏移了 0.4 USDT,非常接近卖一。

Bid 与 Ask 之间:Mid-price 与 Weighted Mid-price 在不同 OBI 下的位置

推到这一步,有两个 insight 我觉得值得记下来。

第一个:简单加权价格不是一个独立于 OBI 的新信息源。 \(P_w = Mid + \frac{Spread}{2} \cdot OBI\) 意味着,一旦你知道了 Mid、Spread 和 OBI,加权价格就完全确定了。它不会给你带来 OBI 和 Spread 之外的额外预测信息。在特征工程中,如果已经用了 OBI 和 Spread 作为特征,再加上 \(P_w\) 或者 \(P_w - Mid\) 作为特征,本质上是放了一个冗余变量。

第二个:这个简单的加权价格,和 Stoikov 在 2018 年论文(The Micro-Price: A High Frequency Estimator of Future Prices)里定义的 Micro-price 不完全是一回事。 Stoikov 的 Micro-price 是从一个盘口状态的马尔可夫模型出发,用未来 Mid-price 变化的条件期望来定义的——它需要估计不同盘口状态之间的转移概率和每个状态下的价格变化分布。最终结果在形式上和 Weighted Mid-price 相似(都是 Mid 加上一个依赖盘口状态的修正项),但构造逻辑不同:一个是对当前信息的确定性加权,一个是对未来动态的统计估计。在文献和实务中,“Microprice"这个词有时指前者,有时指后者,需要看上下文。这篇文章后面提到 Weighted Mid-price 时,指的都是上面这个基于 L1 Volume 的简单公式。

从盘口指标到 Alpha 验证
#

有了 OBI、OFI 和 Weighted Mid-price 三个候选特征之后,下一步是什么?

一个我很容易犯的错误是:算出某个指标的数值,发现它和价格变化"看起来相关”,就直接当成 alpha 信号用。但这里面至少有几个问题需要先理清楚。

要回答的核心问题是:

$$ E[r_{t, t+\Delta} \mid X_t] $$

给定 \(t\) 时刻的盘口特征 \(X_t\),未来 \(\Delta\) 时间内的收益 \(r\) 的条件期望是否非零?如果非零,它有多大,是否足够稳定?

以下用一个理想化的 BTCUSDT 永续合约研究实验来串联完整链路。

明确研究目标与构造特征
#

假设研究目标是预测未来 1 秒的 Mid-price Return。

特征向量可以是:

$$ X_t = [OBI_t,\; OFI_{[t-1s, t]},\; Spread_t] $$

也可以考虑加入当前的总深度(\(V^b_t + V^a_t\))、历史若干秒的 OBI 和 OFI、Weighted Mid-price 与 Mid-price 的偏移等。但要注意前面推导过的确定性关系——\(P_w - Mid\) 是 \(Spread\) 和 \(OBI\) 的函数,在线性模型中与 \(OBI\) 和 \(Spread\) 存在完全共线性。

构造 Label
#

$$ Y_t = 10^4 \cdot \frac{Mid_{t+1s} - Mid_t}{Mid_t} $$

单位是 bps。用 bps 而不是 USDT 是为了跨不同价格水平的可比性。

这里有一个不太明显但重要的区分:预测"未来 1 秒的 Mid-price Return"和预测"下一次 Mid-price 变化的方向"是不同的研究目标。 前者是固定时间窗口,在 1 秒内可能有零次、一次或多次 Mid-price 变化;后者是事件驱动的,关注的是下一个 tick 的方向,不确定什么时候发生。两种 label 的构造方式、统计性质和适用的模型都不一样。这篇文章后续按固定时间窗口来讨论。

时间对齐与信息泄漏
#

在高频研究中,一个极容易出错的地方是时间戳。

考虑这样一个事件序列:

100.000s  交易所撮合引擎处理了一笔订单簿更新(Exchange Event Time)
200.015s  本地收到这条行情推送(Local Receive Time)
300.018s  策略完成特征计算并生成预测(Local Decision Time)

特征 \(X_t\) 在本地真正可用的时间是 00.018s,不是交易所事件发生的 00.000s。如果在回测中用 Exchange Event Time 作为特征的时间戳,相当于假设策略在事件发生的同一瞬间就知道了盘口状态并做出了决策——这是不可能的。

如果行情数据只提供 Exchange Time 而没有 Local Receive Time,必须对这个延迟做出明确的假设。不管假设多少毫秒,关键是把假设写出来,而不是默认延迟为零。

滚动验证与标签重叠
#

Feature Window 与 Future Label Window 的时间关系

高频数据通常按时间顺序分成训练集和测试集。标准做法是 rolling(或 walk-forward)验证:用固定长度的历史窗口训练,在紧随其后的一段上测试,然后窗口向前滑动。

但高频标签有一个特殊问题:如果 label 的时间窗口是 1 秒,那么训练集最后一个样本的 label \(Y_T\) 使用了 \([T, T+1s]\) 的价格信息。如果测试集从 \(T+0.1s\) 开始,那前 0.9 秒的测试标签与训练标签在时间上是重叠的。

Rolling Window 训练、Purge Gap 与测试的时间划分

处理方式是在训练集和测试集之间留一个 Purge Gap,长度至少等于 label 的预测窗口。这在 Marcos López de Prado 的 Advances in Financial Machine Learning 中有详细讨论。

检验预测效果
#

从简单的非参数方法入手:

  1. 按 OBI(或 OFI)的值将样本分成若干分位数桶(比如 5 等分)。
  2. 计算每个桶内未来 1 秒 Mid-price Return 的平均值。
  3. 检查条件期望是否随分位数单调递增(或递减)。

如果 OBI 最高的那组平均 return 显著高于 OBI 最低的那组,说明 OBI 和未来短期收益之间存在条件关系。

更定量的指标是 Pearson IC(信息系数)或 Spearman Rank IC:

$$ IC = Corr(X_t, Y_t) $$

这里有一个关键区分。如果计算的是:

$$ Corr(OFI_{[0,1]}, Return_{[0,1]}) $$

这是同期相关——OFI 和 Return 在同一个时间窗口内的关联。这度量的是订单流和价格变化之间的 contemporaneous relationship,可以理解为 price impact 的度量(Cont 等人的论文主要讨论的就是这个)。

而真正对未来预测有意义的是:

$$ Corr(OFI_{[0,1]}, Return_{[1,2]}) $$

特征和标签的时间窗口不重叠。只有这种非同期的条件关系才有可能在实际交易中使用——因为你在 \(t=1\) 时刻才知道 \([0,1]\) 的 OFI 值,\([1,2]\) 的 Return 还没有发生。

以上分析如果做完了,得到了一个在样本外稳定的、特征与未来收益之间的条件关系——这还只是 Alpha Research 的第一步。从这里到一个可执行的策略,还需要考虑交易成本、成交概率和 Adverse Selection 的影响。这些问题在执行感知回测那篇里已经讨论过,这里不再展开。

写在最后
#

这篇文章串联了三个概念:OBI 度量盘口快照的静态不平衡,OFI 度量订单簿变化的动态不平衡,Weighted Mid-price 是 Mid-price 加上一个由 OBI 和 Spread 决定的修正项。它们各自捕捉的信息层次不同,但在数学上存在明确的联系——尤其是 \(P_w = Mid + \frac{Spread}{2} \cdot OBI\) 这个关系,意味着简单加权价格并不比 OBI 本身多出独立信息。

从指标到 Alpha 的距离比我最初想的要远。有一个看起来和价格相关的特征,和有一个能赚钱的策略,中间隔着 label 定义、时间对齐、训练测试分割、同期 vs 未来相关的区分,以及执行层面的全部成本。这条链路上任何一步出了问题,都可以让一个"看起来很好"的信号在实盘中毫无价值。

参考资料
#

相关文章

大量主动卖出,股价为什么不跌?——从 SOXL 成交统计理解流动性与价格冲击

10月16日,美东时间上午,我在看 SOXL 的成交统计时看到了一组让我反复盯着屏幕的数字。 SOXL 是 3 倍做多半导体 ETF,那天跌了超过 6%。我打开成交统计,本来预期应该看到一个主动卖出量远超买入量的页面——毕竟已经跌成这样了。结果是这样的:

从成交量到资金净流入:重新理解股票价格是如何形成的

之前写量化交易系统那篇时提到过 execution 层——每笔 order 多大、对手盘的流动性够不够、是挂盘口还是主动吃价差。当时一笔带过,没有认真想 order book 这一层到底怎么运转。 最近第一次认真看市场微观结构(market microstructure)的一些材料,想搞清楚几个一直模模糊糊的问题。结果发现,不是模糊,是错的。

预测正确,策略为何仍可能亏钱?——从 Markout 到执行感知回测

上一篇写到,预测对价格方向,不等于交易能赚钱。主动成交要付执行成本,挂单等成交又可能筛出对自己不利的行情。 顺着这个问题往下想,我还有一个疑问:如果把手续费算进去,回测里也只让一部分挂单成交,是不是就离真实收益很近了?

从价格发现到流动性螺旋:股票市场为什么需要“国家队”?

在很长一段时间里,我对股票市场的理解非常机械,甚至带着一种理所当然的工程偏见。 作为一个长期和分布式系统、性能优化与反馈控制打交道的工程师,我习惯把撮合引擎看作一个纯粹的分布式信息处理系统:全市场的参与者带着各自的信息、算力和风险偏好,用真金白银在订单簿上挂单出清。价格上涨,说明对未来的盈利预期转好;价格下跌,说明基本面恶化或风险溢价上升。