halo 的技术博客

返回

问题:你付的价差,到底进了谁的口袋?#

你用市价单买了 1000 股,成交价比当时的中间价高了 4.2 分。你会本能地说:这 4.2 分是我付给做市商的过路费。

错了一半。

这 4.2 分里,一部分确实是做市商提供即时性收到的报酬——他愿意在你想买的时候立刻卖给你,这个服务值钱。但另一部分不是。如果你之所以买,是因为你(或者和你同方向的一群人)知道了某个还没反映到价格里的利好,那么做市商卖给你这一手其实是亏的:他刚卖出,价格就往上走,他被你逆向选择了。这部分不是他的利润,是他的损失,最终会转嫁成更宽的报价。

所以同一个价差数字,同时装着两样性质完全相反的东西:

  • 做市商的补偿性利润:临时的,成交后价格会回复,这部分他真的赚到了。
  • 信息损失(逆向选择成本):永久的,成交后价格朝你的方向走掉了,这部分他没赚到,是被信息更灵的你拿走的。

有效价差(effective spread)是你实付的总账单。但要判断一个市场的做市商是不是真的在「印钞」、要判断你的执行是不是在持续泄露信息,你必须把这两块拆开。拆它的工具,就是已实现价差(realized spread)

有效价差的两分解:做市商真实利润 vs 信息损失

三个价差,一条恒等式#

先把定义钉死。设某笔成交价为 p,成交时刻的中间价为 m_0,成交后 τ 个 bar 的中间价为 m_τ,成交方向 d(买 +1,卖 −1)。所有量用相对中间价的基点表示。

有效半价差(effective half-spread) —— 你实付的即时成本:

ES = d × (p − m_0)
plaintext

买单时 p > m_0d=+1,ES 为正;卖单时 p < m_0d=−1,ES 还是正。它衡量”成交价偏离成交时刻中间价多远”。

已实现半价差(realized half-spread) —— 做市商到手的部分:

RS = d × (p − m_τ)
plaintext

关键差别就一个字:锚点从成交时的中间价 m_0 换成了成交后 τ 期的中间价 m_τ。它衡量做市商如果在成交后 τ 期用当时的中间价平掉这一手,还能剩多少。

价格冲击(price impact) —— 信息损失的部分:

PI = d × (m_τ − m_0)
plaintext

它衡量成交本身把中间价推动了多远——这就是市场从你的成交里”学到”的信息。

三者构成一条恒等式,这是整套方法的骨架:

ES = RS + PI
有效价差 = 已实现价差 + 价格冲击
你实付的  = 做市商赚的 + 信息拿走的
plaintext

这条恒等式的美在于:它把一个你能直接观测的量(ES),拆成了一个有经济含义、但需要”等一会儿看结果”才能算的量(RS)加上另一个(PI)。ES 是当下就知道的账单,RS 和 PI 要等成交后 τ 期的中间价出来才能回填。

机制:为什么成交后的中间价能拆开它#

想象两种极端的成交。

场景 A:纯流动性需求。 你买 1000 股只是因为要调仓,没有任何信息。你的市价单吃掉了卖一档,把中间价瞬间顶高了一点——但这只是暂时的簿本失衡。几分钟后,新的限价单补进来,中间价回落到成交前的水平。这种情况下 m_τ ≈ m_0,价格冲击 PI ≈ 0,你付的价差几乎全额进了做市商口袋,RS ≈ ES。做市商这一单赚得干干净净。

场景 B:知情交易。 你买,是因为你知道半小时后要出利好。你的成交给市场发了信号,中间价被推高后不再回落,因为这是”真实”的价值重估。这种情况下 m_τ > m_0 显著为正,PI 很大,而 RS = ES − PI 被压得很小甚至为负。做市商卖给你这一手,转头发现价格涨上去了,他其实亏了。

真实世界是这两种场景的混合。成交后中间价的回复行为,就是区分它们的自然实验:回得来的是临时冲击(做市商利润),回不来的是永久冲击(信息损失)。下面这张图是 2000 笔买方发起成交对齐平均后的中间价路径——临时的鼓包指数衰减,永久的台阶留在那里。

成交后中间价回复:临时鼓包衰减,永久台阶留下

单笔成交的成交后路径全是噪声,力量在对齐-平均:把成千上万笔同方向成交按成交时刻对齐,再逐期平均,噪声相互抵消,临时与永久两个成分就显形了。

Python 实现:从逐笔数据到分解#

我们用一个可复现的模拟,把机制显式写进数据生成过程,再用估计量把它还原——这是验证估计量无偏最干净的方式。

数据生成:把临时和永久显式植入#

注意一个刻意的设计:我们让**成交价直接以”相对成交时中间价 m_0 的偏移”**表示,也就是 m_0 被归零。这样 ES 就是 direction × trade_price,代码干净,且不损失一般性——真实数据里你只要先算出每笔的 p − m_0 即可。

估计三个价差#

运行结果(τ=10):

有效价差_ES     8.401
已实现价差_RS    3.108
价格冲击_PI     5.293
恒等式检查      0.000
plaintext

有效价差 8.4bp 被干净地拆成已实现价差 3.1bp + 价格冲击 5.3bp,恒等式严格闭合。信息损失占了 63%——这个市场里做市商真正到手的只有三分之一多一点,剩下的都被逆向选择吃掉了。

窗口 τ 是一个建模决策,不是常数#

这里有一个初学者最容易忽略的坑:RS 和 PI 的数值都依赖于你选的 τ。τ 太小,临时冲击还没衰减完,你会把一部分做市商利润误算进”未回复”;τ 太大,中间价的独立随机游走噪声积累,估计方差爆炸,甚至混入了与这笔成交无关的后续信息。

for tau in [1, 2, 3, 5, 10, 15, 30]:
    r = decompose_spread(df, tau=tau)
    print(f"τ={tau:2d}  已实现价差={r['已实现价差_RS']:5.2f}bp  "
          f"价格冲击={r['价格冲击_PI']:5.2f}bp")
python
τ= 1  已实现价差= 5.79bp  价格冲击= 2.61bp
τ= 2  已实现价差= 4.98bp  价格冲击= 3.42bp
τ= 3  已实现价差= 4.32bp  价格冲击= 4.08bp
τ= 5  已实现价差= 3.51bp  价格冲击= 4.89bp
τ=10  已实现价差= 3.11bp  价格冲击= 5.29bp
τ=30  已实现价差= 3.09bp  价格冲击= 5.31bp
plaintext

τ 越大,已实现价差越小、越贴近”永久冲击的补数”,因为临时鼓包被等到几乎完全衰减。到 τ=30 时已经收敛。

窗口选择是一个建模决策:τ 越大,已实现价差越接近永久冲击

没有”正确”的 τ,只有”和你研究问题匹配”的 τ。 研究做市商在极短窗口的利润,用 τ=1;研究信息的最终定价效果,用 τ 覆盖到衰减常数的 5-6 倍。经典文献常用 5 分钟,但那是对特定市场的经验选择,不是物理常数。报告 RS 时必须同时报告 τ,否则数字无法解读。

横截面:小盘股的价差里装了更多信息#

单只股票的分解只是开始。真正有价值的是横截面——不同流动性的股票,价差的构成天差地别。

大盘蓝筹: 已实现价差=  1.8bp 价格冲击=  1.1bp 信息占比=38%
中盘:     已实现价差=  3.1bp 价格冲击=  3.4bp 信息占比=52%
小盘:     已实现价差=  5.6bp 价格冲击=  7.8bp 信息占比=58%
微盘:     已实现价差=  9.2bp 价格冲击= 14.5bp 信息占比=61%
plaintext

横截面:小盘股的价差里,信息损失占比越来越大

规律很清晰:越往小盘走,价差里的信息成分占比越高。大盘蓝筹的报价宽度主要是做市商的补偿性利润(信息占比 38%),因为大盘股信息透明、知情交易者难有优势;而微盘股的价差 61% 是逆向选择成本——在这些票上做市,你面对的更多是”知道点什么”的对手方。

这个结论有直接的实战含义:

  • 对做市商/流动性提供者:小盘股名义价差虽宽,但已实现价差(真正利润)没那么宽,因为大半要吐给知情交易者。用有效价差评估做市盈利能力会系统性高估。
  • 对执行方:如果你的成交在某只票上持续产生高价格冲击(PI 大、RS 被压低甚至为负),说明你的订单流被市场判定为”知情”——要么你真有 alpha 在泄露,要么你的执行算法太可预测被人抢跑。TCA(交易成本分析)里,PI 持续偏高是信息泄露的红旗。

三段式总结#

A. 实现细节#

  • 信号字段:分解只用三样——成交价 p、成交方向 d、成交前后的中间价 m_0/m_τ。中间价用买一卖一的算术中点,成交方向在模拟里显式给定,真实数据里需先用 Lee-Ready 或 tick 规则打标。
  • 执行时点语义:这是事后诊断而非交易信号,不存在 signal-on-i/execute-on-i+1 的执行时滞问题;所有量都在成交发生后回填。
  • 锚点口径:ES 锚在成交时中间价 m_0,RS 锚在成交后 τ 期中间价 m_τ,PI 是两个中间价之差。三者严格满足 ES = RS + PI,代码里用 恒等式检查 ≈ 0 做断言。
  • 归一化:模拟中把 m_0 归零、成交价以相对偏移表示,不损失一般性;真实数据先对每笔算 p − m_0 再进流程。
  • 单位:全程基点(bp),方向乘子 d 保证买卖单符号一致可平均。

B. 已知偏差#

  • 方向标签依赖:整套方法建立在可靠的买卖方向标签上。真实逐笔数据里方向靠 Lee-Ready/tick 规则推断,10-15% 的错标会同时污染 RS 和 PI,稀释分解的清晰度。高频、报价频繁更新的市场里错标更严重。
  • 中间价构造敏感m_τ 用哪个时点、买一卖一还是加权中间价、跨越报价更新时如何取值——这些构造选择直接改动分账结果。报告分解必须同时说明中间价定义。
  • 日线粒度不可识别:临时冲击的衰减发生在分钟乃至秒级。日线数据下根本看不到成交后的中间价回复路径,τ 无法定义,本方法失效——这是它和 Roll、Amihud 这类”只要收盘价”的度量的根本区别,它必须要高频数据。
  • 模拟设定 vs 实证:本文的衰减常数 τ=4bar、永久/临时幅度都是生成过程的设定值,实证中这些参数需从数据估计且随品种、时段大幅漂移。

C. 结果解读#

  • 信息占比是核心产出:有效价差 8.4bp → 已实现价差 3.1bp + 价格冲击 5.3bp,信息损失占 63%。判断一个市场”做市商是不是在暴利”,要看 RS 而非 ES——名义价差宽不等于做市赚得多。
  • 横截面单调性:信息占比从大盘 38% 升到微盘 61%,说明小盘股的报价宽度主要是逆向选择保护,不是垄断利润。监管讨论”价差太宽”时,拆出信息成分才不会错杀。
  • 窗口即结论:RS 从 τ=1 的 5.8bp 衰减到 τ=30 的 3.1bp,选择 τ 本身是建模决策的一部分,必须显式披露,否则数字不可比。
  • TCA 应用:对执行方而言,PI 持续偏高 = 订单流被判定为知情 = 信息泄露或算法可预测。已实现价差为负是明确的危险信号——你付的价差里做市商反而亏钱,那些钱去了抢跑你的对手方。
  • 诚实的边界:本文用无偏模拟验证了估计量的正确性,但真实数据的信噪比远低于此,实证中 RS/PI 分解的置信区间会宽得多,需要大样本对齐平均才能稳定。
已实现价差分解:用成交后中间价拆临时成本
https://blog.halo26812.eu.org/blog/realized-spread-decomposition
Author halo
Published at 2026年7月27日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨