halo 的技术博客

返回

你训练出一个因子,样本内 IC 0.06,回测年化 18%,但你真知道它「为什么」赚钱吗?绝大多数时候你只知道它和历史收益相关——而相关,不等于因果。

经典反例:某地冰淇淋销量和溺水死亡人数高度正相关。难道少吃冰淇淋能救命?当然不是——两者都被「夏天高温」这个共同因素驱动。在量化里,这种伪相关无处不在:两只股票同涨同跌,可能只是都被同一只 ETF 抛售拖累;一个「低波因子」看起来有效,可能只是恰好样本期里价值风格在涨。

如果你要向上游(风控、合规、投委会)解释一个策略「因为什么而赚钱」,或者要评估「某次交易规则改动 / 某条监管政策」到底带来了多少真实的收益增量,你需要的是因果推断,而不仅是相关。本文聚焦其中最实用、落地成本最低的一种方法:双重差分(Difference-in-Differences, DID)

个股相关性网络(节点大小=系统性重要度,红=最大传染 hub)

一、潜在结果框架:什么是「因果效应」#

因果推断的基石是 Rubin 的潜在结果模型(Potential Outcomes)。对第 i 个个体(在量化里可以是一只股票、一个交易日、一个账户),它有两种潜在结果:

  • Yi(1)Y_i(1):如果「接受处理」(如被纳入指数、触发某政策、启用新规则)时的结果;
  • Yi(0)Y_i(0):如果「不接受处理」时的结果。

我们关心的个体因果效应τi=Yi(1)Yi(0)\tau_i = Y_i(1) - Y_i(0)。但残酷的事实是:你永远只能观测到其中一种,另一种被观测到的那个,叫做反事实(counterfactual)。这就是因果推断的根本困难——缺失数据。

所以实际能估计的,是平均处理效应(ATE)处理组平均处理效应(ATT)

τATT=E[Y(1)Y(0)D=1]\tau_{ATT} = \mathbb{E}[Y(1)-Y(0)\mid D=1]

其中 D=1D=1 表示接受处理。

二、双重差分:用「两组两期」挤出因果#

单纯比较「处理组处理前 vs 处理后」会混入时间趋势(比如整个市场在涨);单纯比较「处理组 vs 控制组」会混入组间固有差异(比如处理组本来就更活跃)。DID 同时做两个差分,把这两类混杂都减掉。

设定:有处理组(treatment)和控制组(control),各有处理前(pre)和处理后(post)两期。记四格均值为:

处理前处理后差分
处理组Yˉ11\bar Y_{11}Yˉ12\bar Y_{12}ΔT=Yˉ12Yˉ11\Delta_T=\bar Y_{12}-\bar Y_{11}
控制组Yˉ01\bar Y_{01}Yˉ02\bar Y_{02}ΔC=Yˉ02Yˉ01\Delta_C=\bar Y_{02}-\bar Y_{01}

DID 估计量为:

τ^DID=ΔTΔC=(Yˉ12Yˉ11)(Yˉ02Yˉ01)\hat\tau_{DID} = \Delta_T - \Delta_C = (\bar Y_{12}-\bar Y_{11}) - (\bar Y_{02}-\bar Y_{01})

关键识别假设叫「平行趋势(parallel trends)」:如果没有发生处理,处理组和控制组的结果会沿着相同的轨迹演变。换句话说,处理前的差异是可解释的固有差异,但处理前后的变化量对两组应该一致。DID 用控制组的变化量,去「扣除」处理组本就会发生的自然变化,剩下的就是处理的净效应。

三、可复现模拟:一次政策冲击的因果效应#

下面用一个完全可复现的面板数据演示。设定:90 只股票、120 个交易日,第 60 天发生一次事件冲击;30 只为处理组(受事件影响),60 只控制组(不受影响)。每只股票的日异常收益由「个体固定效应 + 共同时间趋势 + 处理效应 + 噪声」构成,处理组仅在事件后获得每天 +0.40% 的异常收益(真实因果效应 τ=0.40%\tau=0.40\%)。我们假装不知道 τ\tau,用 DID 把它估回来。

跑出来:

DID 估计 = 0.415%  (真实 τ = 0.40%)
plaintext

DID 几乎完美还原了真实处理效应(0.415% vs 0.40%)。四格均值分别是:处理·前 0.040%、处理·后 0.570%、控制·前 0.029%、控制·后 0.144%。注意处理组在事件后跳升了约 0.53 个百分点,但控制组同期也上升了约 0.11 个百分点——那 0.11 是共同市场趋势,必须扣掉,否则你会把市场 beta 当成策略 alpha。

四格均值与 DID 估计量 + 2000 次集群 bootstrap 分布

四、置信区间:以股票为聚类单位的 bootstrap#

DID 估计量本身是一个数,但你要知道它稳不稳。面板数据里同一只股票的日度收益高度自相关,不能逐日重采样(会把相关性打散)。正确做法是以个体(股票)为聚类单位的 bootstrap:每次整只股票(它所有 120 天)一起抽,重复 2000 次,看 τ^\hat\tau 的分布。

B = 2000
boot = np.empty(B)
idx = np.arange(N)
for b in range(B):
    samp = rng.choice(idx, size=N, replace=True)
    boot[b] = did_estimate(Y[samp], treated[samp], post_bool)
ci_lo, ci_hi = np.percentile(boot, [2.5, 97.5])
print(f"95% CI = [{ci_lo*100:.3f}%, {ci_hi*100:.3f}%]")
python

得到 95% CI = [0.328%, 0.498%],稳稳覆盖了真实值 0.40%。这说明我们的因果结论在统计上是可信的,而不只是「看起来相关」。

五、事件研究:处理组 − 控制组的累计异常收益#

DID 给的是单一标量效应,但策略上你往往还想知道效应怎么随时间展开。事件研究做法很简单:每天取「处理组均值 − 控制组均值」,这一步直接扣掉了共同时间趋势(gamma),得到纯净的「处理带来的日度异常差」。对其累计,就是 CAR(累计异常收益)。

diff = Y[treated].mean(axis=0) - Y[~treated].mean(axis=0)
car = np.cumsum(diff)
win = 20
car_win = car[(t0 - win):(t0 + win + 1)]     # 事件前 20 天到后 20 天
python

事件前 CAR 在 0 附近平稳游走(说明两组本来同步),事件后稳步抬升,窗口末尾累计约 9.78%(理论值 τ×208%\tau\times 20 \approx 8\%,加上噪声略高)。这正是 DID 识别假设「平行趋势」的可视化证据:如果事件前 CAR 已经开始分叉,说明处理组和控制在事件前轨迹就不平行,DID 的前提就破了。

事件研究:事件前 CAR 平稳,事件后稳步抬升

六、DID 在量化策略里的真实用法#

DID 不是学术玩具,它在以下场景能直接落地:

  1. 政策 / 监管事件评估:某条交易规则、某次印花税调整、某只股票被纳入 / 剔除指数,用处理组(受影响标的)vs 控制组(未受影响同类标的)做 DID,干净地估出事件带来的收益增量或成本。
  2. 策略 / 规则上线的 A/B 验证:把账户或标的随机(或按可观测特征)分成两组,一组启用新信号 / 新执行算法,一组维持旧方案,用 DID 估「新方案相对旧方案的净提升」——这比单纯看新方案收益更可信。
  3. 因子改动的归因:改了因子定义后,用 DID 区分「市场本身在涨」和「改动真正带来的改善」。

七、诚实的陷阱:DID 不是万能钥匙#

  1. 平行趋势不可直接检验。你只能看到处理前的走势是否平行(本文图 3 事件前确实平稳),但「事件后若没发生处理会怎样」是反事实,永远观测不到。一旦你怀疑处理前就不平行(比如处理组恰好是更敏感的成长股),DID 就偏了。缓解:加更多前置期做趋势检验、用协变量调整、或换合成控制。
  2. 预期效应(anticipation)。如果市场在事件「正式宣布」前就提前反应(利好泄露),那么处理效应会前移,DID 会把一部分效应算到事件前,低估真实效应。事件研究的 CAR 曲线能帮你看到这种前移。
  3. 只能识别「对处理组的平均效应」。DID 告诉你处理组平均变了 0.4%/天,但不保证每只股票都变了这么多,也不告诉你「哪只股票反应最强」。要细粒度,得上异质性处理效应(Causal Forest 等)。
  4. 时变混杂。如果有第三个因素在事件后同时改变了处理组和控制组(比如事件只影响了处理组,但宏观冲击同时打了所有人),且这个因素与处理相关,DID 会把它混进去。缓解:加入时间×组固定效应、控制可观测时变协变量。
  5. 控制组必须「可比」。最危险的隐性错误是控制组选得和处理组根本不是一类资产(例如用债券当股票的控制组)。可比性是 DID 成立的前提,比数学更重要。

八、总结#

相关性告诉你「什么和收益一起动」,因果推断告诉你「动哪个能改变收益」。DID 用「两组两期、两次差分」把共同时间趋势和组间固有差异都减掉,在「有清晰处理 / 控制边界的事件」场景下,是性价比最高的因果工具:一个 did_estimate 函数、一次聚类 bootstrap、一张事件研究 CAR 图,就能把「这个政策 / 这次规则改动到底有没有用」说清楚。

但它的诚实边界也很硬:平行趋势不可证伪、预期效应会前移、只能给平均效应。当你需要更强的反事实构造时,下一步是自然走向合成控制法(Synthetic Control)因果森林(Causal Forest)——那是把 DID 的「找一组可比控制」从人工升级为数据驱动的文章了。

因果推断与双重差分(DID):从相关性到策略可解释性
https://blog.halo26812.eu.org/blog/causal-inference-quant
Author halo
Published at 2026年7月11日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨