Huang-Stoll 价差成分分解:用两步回归量化价差的三大来源
报价价差 0.10 元里,多少是逆向选择的保险费、多少是做市商的库存补偿、多少纯粹是订单处理费?Huang-Stoll (1997) 用一个两步回归框架把这三块拆干净:信息成分造成永久价格冲击、库存成分制造暂时且均值回复的报价偏移、订单处理成分只产生 bid-ask bounce。本文从 Roll 的一阶负串行协方差直觉出发,讲清三种成分各自的「指纹」,用 10 万笔模拟数据跑通两步回归还原价差结构(隐含半价差 0.0501 对上真值 0.0500),并验证核心恒等式:有效价差 = 价格冲击 + 已实现价差(0.104 ≈ 0.042 + 0.063)。给出三个落地读法:横截面毒性排序、TCA 归因、做市回测的收益虚增陷阱。诚实边界:Lee-Ready 方向标错、成分时变、τ 是自由参数(中阶)。
一个 0.10 元的价差,到底装了什么#
你在盘口看到买一 9.95、卖一 10.05,报价价差 0.10 元。做市商真能把这 0.10 元揣进兜里吗?
答案是:大概率不能。这 0.10 元是三块完全不同的东西粘在一起,只有一块是做市商能稳稳赚到的利润,另外两块要么是他被知情交易者薅走的、要么是他为承担持仓风险索要的补偿。
Huang 和 Stoll (1997) 的贡献,是给出了一个能把这三块拆开定量的框架。这不是学术玩具——你做 TCA(交易成本分析)、给做市策略回测、给不同标的排毒性,用的都是同一套分解逻辑。
三种成分,三种指纹#
Huang-Stoll 把价差 分成三份:
关键是这三块在价格数据里留下的痕迹完全不同:
订单处理成分(order processing) 是最”干净”的一块——撮合、清算、场地这些固定成本。它的指纹是纯粹的 bid-ask bounce:成交价在买价和卖价之间来回弹,但中间价(efficient price)纹丝不动。买单成交在卖价、卖单成交在买价,价格变化呈现一阶负串行相关,但这个跳动不含任何信息,下一笔反向成交就弹回去了。
库存成分(inventory) 来自做市商的持仓风险。连续被塞了一堆买单,他手里空头堆积,于是主动把整套报价往上抬,引导后续订单流帮他平仓。指纹是:报价先偏移、再随着库存回归而均值回复。它是暂时的,但比订单处理成分多了一层”方向记忆”。
逆向选择成分(adverse selection / 信息) 最贵。你的对手手里有你不知道的信息,他一买,真实价值其实真的更高了。做市商成交后必须永久性地调整中间价——因为这次成交泄露了信息,价格回不来了。指纹是永久价格冲击:成交后中间价上一个台阶,不反弹。

上图是我们在模拟里预设的分解:0.10 元价差中,逆向选择占 30%(0.030 元)、库存占 15%(0.015 元)、订单处理占 55%(0.055 元)。真实市场里这个比例因标的而异——大盘蓝筹订单处理占比高,小盘或有事件催化的票逆向选择占比高。
从 Roll 到 Huang-Stoll:串行协方差是钥匙#
拆分的数学起点是 Roll (1984) 的一个漂亮观察:如果价格变化只有 bid-ask bounce,那么相邻两笔价格变化必然负相关。买单接卖单,价格 +half 再 −half,。Roll 由此反推价差:

左图是模拟数据里 对 的散点,负相关一目了然;一阶自协方差为负、更高阶迅速衰减到零——这正是 bid-ask bounce 的教科书指纹。用 Roll 公式反推得到价差 0.041 元,明显低估了真实 0.10 元的报价价差。
为什么低估?因为 Roll 把所有价格变化都当成 bounce,但实际上信息成分让价格同向移动(知情买单推高中间价、下一笔知情买单继续推高),这部分正相关抵消了 bounce 的负相关,压低了协方差绝对值。Roll 估计的其实是”扣除信息后的暂时价差”。Huang-Stoll 的两步回归,正是要把这个信息成分单独抠出来。
两步回归:把 α 估回来#
Huang-Stoll 的核心回归把价格变化写成成交方向的函数。核心方程可以简化为:把 对当期方向 和滞后方向 回归:
- 的系数 度量即时价差半幅(成交打在报价上的那一下);
- 的系数 混合了 bounce 的反向回弹(−half)和信息+库存的留存部分。两者之差就能解出逆向选择占比。
import numpy as np
rng = np.random.default_rng(7)
N = 100_000
s = 0.10; half = s / 2
alpha_true, beta_true = 0.30, 0.15 # 预设的信息、库存占比
# 成交方向:带序列相关(订单拆分导致方向延续)
pi = 0.35 # 方向反转概率
Q = np.empty(N); Q[0] = 1
for i in range(1, N):
Q[i] = -Q[i-1] if rng.random() < pi else Q[i-1]
# 中间价:信息成分让它随订单流永久移动 + 公共噪声
m = np.zeros(N)
for i in range(1, N):
m[i] = m[i-1] + alpha_true * half * Q[i-1] + rng.normal(0, half*0.6)
# 库存成分:暂时性报价偏移;成交价 = 中间价 + half*Q + 库存项
inv = np.concatenate([[0], beta_true * half * Q[:-1]])
p = m + half * Q + inv
# 两步回归:Δp 对 Q_t、Q_{t-1}
dp = np.diff(p)
A = np.column_stack([Q[1:], Q[:-1], np.ones(N-1)])
b0, b1, _ = np.linalg.lstsq(A, dp, rcond=None)[0]
print(f"隐含半价差 b0 = {b0:.4f} (真值 {half:.4f})")
print(f"滞后方向系数 b1 = {b1:.4f}")
# (b1 + b0)/b0 ≈ 暂时+永久留存占比
print(f"留存占比 (α+β) ≈ {(b1 + b0)/b0:.2f} (真值 {alpha_true+beta_true:.2f})")python跑出来隐含半价差 ,几乎精确命中真值 0.0500;留存占比也回到预设的 0.45 附近。

回归系数与模型真值几乎重合——这验证了两步回归确实能从只有成交价和方向的数据里,把藏在价差里的信息成分反解出来。注意:这里我们用了真实方向 ;实盘中方向要靠 Lee-Ready 之类的算法猜(见下一篇),错标会系统性地偏误 α。
核心恒等式:有效价差 = 价格冲击 + 已实现价差#
分解最实用的产出是三个可直接测量的价差,它们满足一个恒等式:
- 有效价差(effective spread):,投资者实际付出的成本;
- 价格冲击(price impact):成交后中间价的永久移动,,即被信息成分吃走的部分;
- 已实现价差(realized spread):,做市商在持有一段时间后真正落袋的部分。
三者关系:

模拟里:有效价差 0.104 = 价格冲击 0.042 + 已实现价差 0.063。也就是说,投资者付出的 0.104 元里,0.042 元被知情交易者以信息优势拿走(做市商成交后价格朝对他不利的方向永久移动),做市商实际只赚到 0.063 元。这就是为什么”报价价差”从来不等于”做市利润”。
三个落地读法#
一、横截面毒性排序,决定挂单该躲多远。 同样是 0.10 元价差,A 股票信息成分占 40%、B 股票占 10%,交易 A 的隐性成本远高于 B。做市或被动挂单前,先按 α 给标的排个毒性榜:信息占比高的票,限价单要挂得更远、更新更勤,否则你就是那个被信息碾压的对手方。
二、TCA 要盯着”已实现价差”,而不是报价价差。 你作为买方评估自己的执行质量时,如果发现你的成交对应的做市商已实现价差持续为负——意味着做市商在跟你成交后系统性亏钱——恭喜,你的订单流毒性很高(大概率你带着 alpha),这本身是个好信号;反过来,如果对手方已实现价差很肥,说明你在为流动性支付溢价,该考虑更耐心的执行。
三、做市回测别用报价价差算收益,会虚增 66%。 这是最容易踩的坑。用报价价差(0.104 有效)当每笔利润回测做市策略,会得到一条漂亮的净值曲线;但真实落袋只有已实现价差(0.063)。0.104 / 0.063 ≈ 1.66——收益直接虚增六成。任何忽略价格冲击的做市回测都是幻觉,上线必然被逆向选择打回原形。
诚实的边界#
- 方向要靠猜,α 会被污染。 两步回归假设你知道每笔是买还是卖,但真实 tick 数据里方向要用 Lee-Ready 等算法推断,错标率 10-15% 会系统性压低估出的信息成分——分解精度的上限,就是方向分类的精度。
- τ 是自由参数。 价格冲击和已实现价差都依赖”多久之后算永久中间价”这个 τ。τ 太短,暂时成分没回弹完,冲击被高估;τ 太长,混进无关的后续信息。5 分钟、30 笔、1 天——不同 τ 给出不同分解,没有唯一正确答案。
- 成分不是常数。 α、β 在日内非平稳:开盘信息不对称最严重(逆向选择占比最高),午盘趋于平稳。用全天数据估一个 α,抹掉了这个结构,做日内执行决策时会误导。
- 库存与信息难完全正交。 现实中做市商调整报价,既因为库存也因为他也在学信息,两步回归里 β 和 α 的分离依赖模型设定,实证中经常纠缠。
Huang-Stoll 的价值不在于给出精确到小数点后三位的分解,而在于它把”价差”这个模糊的数字,翻译成三个有明确经济含义、可分别测量、可分别优化的量。你每次评估执行成本、每次给做市策略估收益,本质上都在做这道拆分题。
参考文献#
- Huang, R. D., & Stoll, H. R. (1997). The Components of the Bid-Ask Spread: A General Approach. Review of Financial Studies, 10(4), 995-1034.
- Roll, R. (1984). A Simple Implicit Measure of the Effective Bid-Ask Spread in an Efficient Market. Journal of Finance, 39(4), 1127-1139.
- Glosten, L. R., & Harris, L. E. (1988). Estimating the Components of the Bid/Ask Spread. Journal of Financial Economics, 21(1), 123-142.
- Stoll, H. R. (1989). Inferring the Components of the Bid-Ask Spread: Theory and Empirical Tests. Journal of Finance, 44(1), 115-134.