halo 的技术博客

返回

Corwin-Schultz 高低价价差估计:用两日高低价把价差与波动拆开#

先说结论:Corwin-Schultz (2012) 只用日频的最高价和最低价就能估出买卖价差,核心杠杆是一个不对称:波动的贡献随时间长度线性增长,价差的贡献不随时间变。用单日与两日两个观测窗口联立方程,两个未知数(价差 s、波动 σ)恰好可解。模拟实测:真实价差 2% 时估计 2.05% 几乎无偏;但在低价差、高波动的区间它系统性高估,而隔夜跳空让它系统性低估——用之前必须知道这两个偏差往哪边拉。

上一篇《Roll 价差估计量》里我们用收盘价的自协方差反推价差,结局有点尴尬:越流动的股票越容易估出虚数。今天这个估计量用的是另一对被忽视的低频数据——每天的最高价和最低价——而且它对 Roll 失效的场景有明显改善。

一、两个观察,一个联立方程#

观察一:高低价比里藏着价差#

日最高价是全天成交的最高一笔——几乎必然是主动买单打在 ask 上;日最低价几乎必然是主动卖单打在 bid 上。所以观测到的高低价比:

HobsLobs=Htrue(1+s/2)Ltrue(1s/2)\frac{H^{obs}}{L^{obs}} = \frac{H^{true} \cdot (1+s/2)}{L^{true} \cdot (1-s/2)}

观测高低比 = 真实高低比 × 价差因子。高低价区间被价差撑大了,撑大的幅度就是 s。问题是真实高低比我们不知道——它由日内波动决定,一个方程两个未知数,解不出。

观察二:波动随时间涨,价差不随时间涨#

Corwin-Schultz 的破局点:把观测窗口从一天拉长到两天。

  • 波动的贡献:两日的真实高低区间来自两天的随机游走,期望上是单日的 2 倍(方差意义);
  • 价差的贡献:两日区间的顶仍然只被一个 ask 撑高、底只被一个 bid 压低——价差的贡献和单日完全一样

于是定义两个统计量:

β=j=01[lnHt+jLt+j]2,γ=[lnHt,t+1Lt,t+1]2\beta = \sum_{j=0}^{1}\left[\ln\frac{H_{t+j}}{L_{t+j}}\right]^2, \qquad \gamma = \left[\ln\frac{H_{t,t+1}}{L_{t,t+1}}\right]^2

β 是相邻两个单日高低比平方之和(价差被计入两次),γ 是两日联合区间的高低比平方(价差只计入一次)。两个方程、两个未知数,解出:

α=2ββ322γ322,s=2(eα1)1+eα\alpha = \frac{\sqrt{2\beta}-\sqrt{\beta}}{3-2\sqrt{2}} - \sqrt{\frac{\gamma}{3-2\sqrt{2}}}, \qquad s = \frac{2(e^\alpha - 1)}{1 + e^\alpha}

公式长得吓人,但骨架就一句话:β 里价差占两份、γ 里占一份,做差把波动消掉,剩下的就是价差

import numpy as np
K = 3 - 2 * np.sqrt(2)

def corwin_schultz(H, L):          # H, L 为对数最高/最低价序列
    hl2 = (H - L) ** 2
    beta = hl2[:-1] + hl2[1:]                      # 相邻两日各自区间平方和
    H2 = np.maximum(H[:-1], H[1:])                 # 两日联合最高
    L2 = np.minimum(L[:-1], L[1:])                 # 两日联合最低
    gamma = (H2 - L2) ** 2
    alpha = (np.sqrt(2*beta) - np.sqrt(beta)) / K - np.sqrt(gamma / K)
    s = 2 * (np.exp(alpha) - 1) / (1 + np.exp(alpha))
    return np.maximum(s, 0)                        # 负值截零(原文建议)
python

二、实验一:回收精度与负值陷阱#

模拟真实的日内路径:78 个 5 分钟中间价随机游走,观测高价 = 路径最高点 + s/2,低价 = 路径最低点 − s/2。日波动 2%,T=252,真实价差从 0.2% 扫到 5%:

真实价差CS 截零均值CS 原始均值逐对负值占比
0.2%0.83%0.14%40.7%
0.5%1.02%0.44%36.0%
1.0%1.31%0.86%29.8%
2.0%2.05%1.84%17.5%
3.0%2.86%2.77%9.3%
5.0%4.69%4.68%1.4%

回收精度与负值比例

三个层次的读法:

  1. 中高价差区几乎无偏:s=2% 估出 2.05%,s=3% 估出 2.86%。信噪比(s/σ日)过 1 之后 CS 是个合格的点估计。
  2. 低价差区被截零规则拉高:真值 0.2% 估出 0.83%,偏差 4 倍。机制和 Roll 的虚数价差同源——逐对估计噪声大,40% 的样本落到负区;截零只砍左尾不砍右尾,均值必然上移。注意原始均值(不截零)反而低估(0.14%),说明”截零 vs 不截零”是在两个方向的偏差之间选边。
  3. 负值占比本身就是信息:它单调反映信噪比,类似 Roll 失效率——负值占比超过 30% 时,这只股票的 CS 点估计基本不可信,只能做截面排名。

三、实验二:与 Roll 同台#

同一批模拟数据(既有收盘价也有高低价),CS 对 Roll:

真实价差CSRoll(失效剔除)Roll 失效率
0.2%0.83%0.74%45%
0.5%1.00%0.91%37%
1.0%1.31%1.03%20%
2.0%2.07%1.91%0%
5.0%4.73%4.92%0%

CS vs Roll

高信噪比区两者打平。差异全在低信噪比区:Roll 直接弃赛(45% 失效率,且失效非随机——上一篇讲过这是选择偏差),CS 至少给出一个有偏但存在的数字。原因在信息含量:收盘价只是日内路径的一个采样点,高低价是整条路径的两个极值统计量——同样一天数据,高低价对波动和价差的信息压缩效率高得多。这也是为什么 CS 用 T=252 就能工作,而 Roll 在同样窗口下大面积失效。

实务选择:两者都要低频数据,CS 的信息效率更高、无失效样本问题,默认优先 CS;Roll 的价值在于只需要收盘价(部分历史久远或另类资产只有收盘价)。

四、实验三:隔夜跳空——最大的污染源#

CS 的推导假设价格过程连续。但真实市场每天闭市 17.5 小时,隔夜信息在开盘瞬间跳进价格。跳空对两个统计量的污染不对称:单日区间 β 不含跳空(高低价都在盘中),两日联合区间 γ 却把跳空整个吞进去——γ 被撑大,而公式里 γ 前面是负号,结果是低估

隔夜波动不修正平移修正后真值
0%1.33%1.33%1%
1%1.14%1.18%1%
2%0.89%1.00%1%
3%0.69%0.83%1%

隔夜跳空的污染

隔夜波动 3%(大约是 A 股受隔夜美股影响的量级)时,估计被压掉三成。Corwin-Schultz 原文的修正:如果今天的整个高低区间在昨收之上(跳空高开),就把今天的高低价整体平移下来贴住昨收,反之亦然。模拟里这个修正在隔夜波动 2% 时把 0.89% 救回 1.00%,完全命中真值;3% 时救回一半。

推论:没有隔夜的市场是 CS 的最优场景——加密货币 7×24、外汇周内连续,都不需要修正。反过来,对跳空频繁的品种(财报季个股、A 股受外盘驱动的时期),不做修正的 CS 会系统性低估价差,恰好在流动性最差的时候最失真。

五、实验四:滚动 CS 做危机温度计#

与上一篇 Roll 的同款实验:250 天平静(真实价差 0.6%)→ 120 天危机(3%,波动同步放大 3 倍)→ 250 天恢复(0.8%),60 日滚动均值:

滚动 CS 危机温度计

平静期均值 0.79%(真值 0.6%,正偏来自截零),危机峰值 3.58%(真值 3.0%,滚动窗口平滑加噪声)。形状完整还原:进入危机的爬坡、峰值平台、恢复期的衰减都清晰可辨。作为不需要任何盘口数据的流动性监控指标,这个还原度足够做风控输入——重要的是相对变化(价差翻了 5 倍)而非绝对水平。

六、诚实边界#

截零规则在截面上不是无害的。 低流动性股票负值少、高流动性股票负值多,截零对不同股票的拉高幅度不同——和 Roll 的选择偏差异曲同工,只是表现形式从”样本缺失”变成”系统性正偏”。做截面回归时,负值占比应该作为控制变量或过滤条件。

非连续交易导致低估。 推导假设盘中连续成交,真实高价是”最后一笔主买”而非”理论最高 ask”。成交稀疏的股票观测区间小于理论区间,价差被低估——注意这与截零偏差方向相反,两个偏差部分抵消但不精确抵消,净方向取决于股票的具体特征。

波动率假设。 β 到 γ 的换算用了扩散过程的 √t 缩放。日内波动有 U 型结构、危机期有跳跃,都偏离扩散假设。CS 原文的模拟显示这些偏离的影响温和,但在极端行情下(涨跌停、熔断)单日估计可以完全失真——涨停日高低区间被人为压缩,CS 会把它读成”流动性极好”,实际恰恰相反。A 股使用时涨跌停日必须剔除

它估的是有效价差的均值,不是挂单价差。 与 Roll 相同,CS 反映的是成交价相对中间价的偏离,报价价差、盘口深度它都看不到。

七、工程落地清单#

  1. 默认配置:对数高低价 → 逐对估计 → 隔夜平移修正 → 负值截零 → 21 日或 60 日滚动均值;
  2. A 股特化:剔除涨跌停日和停牌复牌首日;隔夜修正必做(外盘驱动的跳空频繁);
  3. 质量门槛:滚动窗口内负值占比 >30% 时降级为”仅截面排名可用”;
  4. 搭配使用:CS(价差)+ Zeros(交易活跃度)+ Amihud(价格冲击)三件套,全部只需日频数据,拼出流动性三维画像——分别对应”吃一口的成本、有没有人交易、连续吃的边际成本”;
  5. 用途分层:截面流动性因子(最稳)> 时序危机监控(滚动均值)> 绝对成本预算(偏差多、慎用)。

结语#

Roll (1984) 和 Corwin-Schultz (2012) 相隔 28 年,解决同一个问题:不用盘口数据估价差。Roll 用收盘价的时间序列性质(自协方差),CS 用高低价的截面性质(极值区间)——后者的信息效率高一个档次,代价是引入了对价格过程更强的假设(连续扩散、无跳空)。

方法论上的启示值得记住:高低价是被严重低估的数据。它们是日内整条路径的极值统计量,信息含量远超”又两个价格点”——从 Parkinson 波动率到 Garman-Klass,再到今天的 CS 价差,一条完整的研究线都建立在这个观察上。你的日频数据库里躺着的 High 和 Low 两列,比你以为的值钱得多。


参考文献:

  • Corwin, S. A., & Schultz, P. (2012). A Simple Way to Estimate Bid-Ask Spreads from Daily High and Low Prices. Journal of Finance, 67(2), 719-760.
  • Roll, R. (1984). A Simple Implicit Measure of the Effective Bid-Ask Spread in an Efficient Market. Journal of Finance, 39(4), 1127-1139.
  • Parkinson, M. (1980). The Extreme Value Method for Estimating the Variance of the Rate of Return. Journal of Business, 53(1), 61-65.
Corwin-Schultz 高低价价差估计:用两日高低价把价差与波动拆开
https://blog.halo26812.eu.org/blog/corwin-schultz-high-low
Author halo
Published at 2026年7月28日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨