← 返回文章列表
August 2, 2026
5 分钟阅读

Double Machine Learning:估计因果参数而非预测收益

Double Machine Learning:估计因果参数而非预测收益
#causal-inference
#double-ML
#treatment-effect
#econometrics
#quant

本博客至今所有建模文章都提出同样形态的问题:给定特征,预测一个数字,然后验证该预测在样本外成立。价差模型预测价差。成交模型预测成交概率。整个验证机制——清理的 walk-forward、缩水的 Sharpe、前瞻分类——的存在是为了检查预测是否真实。

本文提出不同形态的问题,这是博客从未拥有的唯一机械部分:估计具有因果解释的单一纯量参数,并为其附上一个标准误,该误差在到达该点时使用了灵活 ML 模型的情况下仍然有效。

这不仅仅是修辞上的区别。「队列位置预测成交概率」显然正确但操作上无用——当然如此,两者都由深度和波动性驱动。「在队列中前进一个位置导致成交概率变化 θ\theta,保持市场状态不变」这是一个可以放入订单放置政策的数字。前者是回归拟合。后者需要一个不存在于标准 ML 工具箱中的估计器,因为灵活第一阶段的正则化和过拟合会使您关心的系数产生偏误。

Double Machine Learning (Chernozhukov, Chetverikov, Demirer, Duflo, Hansen, Newey & Robins, 2018) 是修正此问题的估计器。每个量化交易员都听说过:相关性不意味因果性。DML 是这句话之后的部分。

为何朴素回归失败

隐藏的市场状态干扰因素同时驱动队列放置和成交结果,产生欺骗性的朴素关联

在博客自己的领域中建立问题。我们想要队列位置对成交概率的因果效应:

  • YiY_i:观测 ii 处的休市限价单是否在时间范围内成交。
  • DiD_i:放置时的队列位置(由层级大小标准化)。
  • XiX_i:干扰因素——实现波动性、报价价差、深度不平衡、层级大小、一天中的时间、制度标签。这些是博客已经在 spread modeling with machine learning 中计算的市场状态变量。

因果参数是 θ0\theta_0,在

Yi=Diθ0+g0(Xi)+ϵiY_i = D_i \theta_0 + g_0(X_i) + \epsilon_i

其中 g0(Xi)g_0(X_i) 捕捉市场状态与成交结果之间的(可能复杂、非线性)关系。

处理并非随机分配。您位于队列前列是因为层级薄,或因为您在平静时期发布,或因为订单簿对您有利。相同条件独立驱动您是否成交。这就是干扰。

方法 1:忽略干扰因素。YYDD 单独回归。估计吸收与两者都相关的每个干扰因素的效应。教科书式的遗漏变量偏误:薄层级既给您好的队列位置又给您高成交率,因此您夸大了位置本身的价值。

方法 2:带控制的线性回归。YYDDXX 回归。这仅在 g0(X)g_0(X) 真正线性时才有效。订单簿动态并非如此——成交/波动性关系有阈值,深度不平衡效应按制度翻转符号。错误指定 g0g_0 会重新引入偏误。

方法 3:ML 预测。(D,X)(D, X) 上拟合梯度提升模型。您获得良好的样本外判别,但完全没有因果解释。模型捕捉每个预测模式,无论因果与否;正则化以偏误 θ\theta 的方式缩小处理的贡献;且没有可信赖的标准误。

这是核心张力。ML 擅长预测,但对因果参数的朴素应用会产生偏误、非正态、不可靠的估计。

部分线性模型

两个同步残差化通道在干净的残差流于正交因果估计相遇之前移除烦人的市场状态

DML 在结构框架内运作。主力是部分线性回归 (PLR)

Y=Dθ0+g0(X)+U,E[UX,D]=0Y = D\theta_0 + g_0(X) + U, \quad \mathbb{E}[U \mid X, D] = 0

D=m0(X)+V,E[VX]=0D = m_0(X) + V, \quad \mathbb{E}[V \mid X] = 0

  • θ0\theta_0 是感兴趣的因果参数。
  • g0(X)g_0(X) 是烦函数——结果中由市场状态解释的部分。
  • m0(X)m_0(X) 是另一个烦函数——给定市场状态的处理条件期望(连续处理设置中的「propensity」)。
  • UUVV 是结构残差。

关键见解:θ0\theta_0 是低维的,但 g0g_0m0m_0 可以任意复杂。我们希望 ML 处理烦函数,同时在标量上提供有效推断。

为何「双重」?

两个 ML 模型,不是一个:

  1. 结果模型^(X)E[YX]\hat{\ell}(X) \approx \mathbb{E}[Y \mid X] —— 仅从市场状态预测结果。
  2. 处理模型m^(X)E[DX]\hat{m}(X) \approx \mathbb{E}[D \mid X] —— 仅从市场状态预测处理。

形成残差

Y~i=Yi^(Xi),D~i=Dim^(Xi)\tilde{Y}_i = Y_i - \hat{\ell}(X_i), \quad \tilde{D}_i = D_i - \hat{m}(X_i)

并通过将 Y~\tilde{Y}D~\tilde{D} 回归来估计 θ0\theta_0

θ^0=iD~iY~iiD~i2\hat{\theta}_0 = \frac{\sum_i \tilde{D}_i \tilde{Y}_i}{\sum_i \tilde{D}_i^2}

这是 Frisch-Waugh-Lovell 的加强版:用 ML 而非线性投影将干扰因素部分化,然后从残差变异中读取处理效应。

Neyman 正交性:为何有效

朴素部分外方法(估计 g0g_0,减去,回归)失败,因为 g^0\hat{g}_0 中的 ML 估计误差直接传播到 θ^0\hat\theta_0。DML 分数构建为 Neyman 正交——对烦函数中的小干扰不敏感。

PLR 的正交分数:

ψ(W;θ,η)=[Y(X)θ(Dm(X))][Dm(X)]\psi(W; \theta, \eta) = \big[Y - \ell(X) - \theta(D - m(X))\big] \cdot \big[D - m(X)\big]

其中 η=(,m)\eta = (\ell, m)。正交条件是

ηE[ψ(W;θ0,η)]η=η0=0\left. \frac{\partial}{\partial \eta} \mathbb{E}\big[\psi(W; \theta_0, \eta)\big] \right|_{\eta = \eta_0} = 0

直观上,分数仅使用 DDYY 中独立于 XX 的变异,且一个烦函数中的误差被另一个补偿。如果 m^\hat{m} 稍微过高预测处理,D~\tilde{D} 稍微太小,但来自错误估计 \ellY~\tilde{Y} 中对应误差会向补偿方向推动。偏误变成二阶——两个第一阶误差的乘积——而非一阶。

正式地,如果两个烦扰估计器以 n1/4n^{-1/4} 速度收敛(温和;大多数合理的 ML 方法通过),则

N(θ^0θ0)dN(0,σ2)\sqrt{N}(\hat{\theta}_0 - \theta_0) \xrightarrow{d} \mathcal{N}(0, \sigma^2)

因此 θ^0\hat\theta_0 以参数速度收敛且渐近正态。

交叉拟合:为何在此必须

仅正交性不够。如果烦扰模型在用于估计 θ0\theta_0 的相同行上拟合,第一阶过拟合会污染第二阶段——具体伤害值得精确说明,因为它不是您习惯的伤害。在其他地方,过拟合作为膨胀的验证分数出现:您注意到它,您打折它,您继续。在这里它作为 θ0\theta_0偏移点估计出现,伴随仍然狭窄且仍然以错误数字为中心的置信区间。没有可疑的分数。估计器只是安静地撒谎。

交叉拟合打破依赖:每个观察的烦扰预测来自未包含它的模型,且 θ0\theta_0 从池化的 held-out 残差估计。机制是普通的 K-fold 机械,在 spread modeling with machine learning 中涵盖;下面重要的是您给它哪些折叠。

DML 算法逐步

输入:数据 {(Yi,Di,Xi)}i=1N\{(Y_i, D_i, X_i)\}_{i=1}^N,ML 方法 M\mathcal{M}_\ellMm\mathcal{M}_m,折叠 KK

步骤 1 —— 分割:将 {1,,N}\{1, \ldots, N\} 分割为 KK 个不相交折叠。

步骤 2 —— 交叉拟合烦扰模型:对于 k=1,,Kk = 1, \ldots, K,在折叠 kk 的补集上训练 ^(k)\hat{\ell}^{(-k)}m^(k)\hat{m}^{(-k)},然后对于 iIki \in I_k 计算 Y~i=Yi^(k)(Xi)\tilde{Y}_i = Y_i - \hat{\ell}^{(-k)}(X_i)D~i=Dim^(k)(Xi)\tilde{D}_i = D_i - \hat{m}^{(-k)}(X_i)

步骤 3 —— 估计

θ^0=(i=1ND~i2)1i=1ND~iY~i\hat{\theta}_0 = \left(\sum_{i=1}^N \tilde{D}_i^2\right)^{-1} \sum_{i=1}^N \tilde{D}_i \tilde{Y}_i

步骤 4 —— 推断

σ^2=1Ni=1N(Y~iθ^0D~i)2D~i2/(1Ni=1ND~i2)2\hat{\sigma}^2 = \frac{1}{N} \sum_{i=1}^N \big(\tilde{Y}_i - \hat{\theta}_0 \tilde{D}_i\big)^2 \tilde{D}_i^2 \bigg/ \left(\frac{1}{N} \sum_{i=1}^N \tilde{D}_i^2\right)^2

带有区间 θ^0±zα/2σ^/N\hat{\theta}_0 \pm z_{\alpha/2} \cdot \hat{\sigma} / \sqrt{N}

置信区间仅对一个问题有效

这是决定 DML 结果是否有价值的警告,也是大多数该方法应用默默崩溃的地方。

上述渐近正态性是关于一个预先指定的处理、一个预先指定的干扰因素集、一个预先指定的分数的陈述。预先固定这些,运行估计器一次,区间意味它所说的。尝试三个候选处理,或四个干扰因素集,或交换学习器直到 p 值看起来更好,您就不再在做推断——您在运行搜索,报告的 p 值是最大值的 p 值,不是单次抽样的。

博客已经测量了这样做的效果。在 the deflated Sharpe ratio study 中,在零真实边缘的纯噪声上的搜索产生 1.000 的朴素虚假发现率——未调整测试每次都触发——而赢家的中位数朴素 p 值接近 0.0007。关于 Neyman 正交性的没有任何保护您免受此害。正交性修正烦扰估计的偏误;它没说规格搜索的偏误。在尝试六个规格后获得的 1e-05 DML p 值,完全应该获得与任何其他从网格中提取的赢家相同的 Bonferroni/Holm/BHY 处理,其中 MM 设置为您实际运行的规格数量。

这对工具的便利特性有直接的实际后果。DoubleMLData 接受 d_cols 中的列表,并会在一个摘要表中愉快地返回三个处理效应:

dml_data_multi = dml.DoubleMLData(
    df, y_col='filled', d_cols=['queue_pos', 'toxicity', 'spread_at_post'],
    x_cols=confounder_cols,
)

三行、三个 p 值,以及摘要表未提及的多重测试问题。如果您读取全部三个,请调整全部三个。如果只有一个是预先注册的问题,请这样说,并明确将其他两个视为探索性。

时间序列上的交叉拟合:清理、禁运、自定义折叠

旋转时间序列折叠使用清理间隙和禁运区域,然后样本外残差组合成单一因果估计

标准 DML 假设 i.i.d. 观察。订单簿数据不是,失败模式是博客已经详细记录的模式:相邻行共享重叠的前瞻窗口,因此简单的 TimeSeriesSplit 仍然会跨越折叠边界泄漏答案。参见 spread modeling with machine learning 以了解清理的禁运 walk-forward 实现以及为何需要至少 horizon 行间隙的原因,以及 the look-ahead bias taxonomy 以了解完整的泄漏目录及其测量幅度。

真正的 DML 特定部分是如何将清理的折叠交给估计器,因为 set_sample_splitting 有一个让人绊倒的契约:

import numpy as np
import doubleml as dml

def purged_folds(n: int, n_splits: int, horizon: int):
    """带有 `horizon` 行清理/禁运间隙的扩展窗口折叠。

    与 spread-modeling 文章中清理的 walk-forward CV 相同构造:
    间隙移除训练行前瞻窗口与验证行之间的重叠。
    """
    fold_size = n // (n_splits + 1)
    for k in range(1, n_splits + 1):
        train_end = fold_size * k
        val_start = train_end + horizon
        val_end = val_start + fold_size
        if val_end > n:
            break
        yield np.arange(0, train_end - horizon), np.arange(val_start, val_end)

folds = list(purged_folds(len(df), n_splits=5, horizon=HORIZON))

dml_plr = dml.DoubleMLPLR(dml_data, ml_l=ml_l, ml_m=ml_m)
// 元组列表的列表。一次重复:
dml_plr.set_sample_splitting([folds])
dml_plr.fit()

需要注意两件事,没有一件从库文档中明显:

  1. 清理的 walk-forward 折叠不覆盖每一行。 清理间隙和初始训练块从不是任何人的测试折叠,因此 θ0\theta_0 从严格少于 NN 个残差估计。这是正确行为,不是错误,但这意味着方差公式中的有效 NN池化测试行的数量——检查它而非假设。

  2. n_rep 重复在此不 free。 对随机 K-fold,重复交叉拟合和平均是廉价的方差减少。对确定性时间顺序分割只有一个分割,因此 n_rep 不购买任何东西也不隐藏任何东西;稳定性必须来自在不同数据窗口上重新运行。

对于面板结构(同期多个符号),DoubleML 支持聚类稳健标准误——按符号聚类,而非按时间,参见 multi-symbol validation 以了解博客关于何时真正确立跨工具结果的立场。

测量案例:队列位置与成交概率

FIFO 队列中的订单位置汇聚成带有置信光晕的 DML 调整队列位置效应

这是文章中项目已经有数据的唯一因果问题,它应该被运行而非提出。队列位置分析 已经涵盖真实订单簿数据上的位置估计、FIFO 机制、排水率和成交时间;成交模拟 涵盖成交概率建模和对实际成交的校准循环。两者都产生成交的预测模型。DML 将相同输入转换为因果估计。

预先注册的规格(在查看估计之前):

  • 结果 YY:在 HORIZON 快照内成交(二元)。
  • 处理 DD:放置时的标准化队列位置。
  • 干扰因素 XX:1s 实现波动性、以 bps 为单位的报价价差、深度不平衡、发布时的层级大小、以 tick 为单位的距离中点距离、一天中的时间编码、制度标签。
from sklearn.ensemble import GradientBoostingRegressor
import doubleml as dml

confounder_cols = [
    'rv_1s', 'spread_bps', 'depth_imbalance', 'level_size',
    'dist_from_mid_ticks', 'tod_sin', 'tod_cos', 'regime',
]

dml_data = dml.DoubleMLData(
    df, y_col='filled', d_cols='queue_pos_norm', x_cols=confounder_cols,
)

ml_l = GradientBoostingRegressor(n_estimators=300, max_depth=5)
ml_m = GradientBoostingRegressor(n_estimators=300, max_depth=5)

dml_plr = dml.DoubleMLPLR(dml_data, ml_l=ml_l, ml_m=ml_m, score='partialling out')
dml_plr.set_sample_splitting([folds])
dml_plr.fit()

print(dml_plr.summary)
dml_plr.sensitivity_analysis()
print(dml_plr.sensitivity_summary)

要报告的结果是四列比较,不是单一系数:仅 DDYY朴素 OLS 估计、DDXXYY线性控制估计、带有其标准误的 DML 估计,以及灵敏度分析的稳健值——未观察干扰因素需要多强才能使效应无效。朴素和 DML 列之间的差距是真正感兴趣的数量:它是队列位置的表面价值中有多少是市场状态的伪装。

此处的零或负 θ\theta 是可发布的结果,且比干净的正结果更适合本博客。如果一旦波动性和层级大小被部分化,队列位置的因果效应就崩溃,那是关于订单放置政策的直接发现:位置不是赚取成交的东西,获得位置的条件才是。

因子因果分析

因子投资的标准方法是关联的:按特征排序、形成多空投资组合、观察报酬不同。DML 启用不同的测试——估计特征对报酬的直接因果效应,抵消其他特征的干扰效应。如果效应在 DML 下消失,因子不是独立因果;它是代理。

这是因子怀疑论的第二种独立形式,值得明确说明它与博客已经发布的内容的关系。The deflated Sharpe ratio选择侧攻击因子动物园:有足够的试验,因子可能看起来显著只是因为您看了很多次。DML 从干扰侧攻击它:因子可能在单次诚实测试上显著,仍然是条件集中其他东西的代理。因子必须通过两者才有意思,且两种失败模式是独立的——通过一个并不告诉您关于另一个的任何事。

DML 不能做什么

  1. 它要求干扰因素被观察。 如果未观察变量同时驱动处理和结果,DML 有偏,且没有 ML 精致性修正识别问题。灵敏度分析有界风险;它不消除风险。

  2. 它估计平均效应。 如果队列位置的效应在制度间急剧变化,点估计是您样本制度混合的平均值。对于异质性使用 Interactive Regression Model (DoubleMLIRM) 或因果森林。

  3. 它假设结构模型。 部分线性规格要求处理以特定方式进入结果方程。如果真实过程根本不同,DML 自信地错误。

  4. 它不发现因果结构。 DML 估计预先指定的处理的效应。它不告诉您哪些变量是原因。

  5. 它不免除您多重测试。 重复上述点因为它是最常跳过的点:正交性去偏烦扰估计,而非规格搜索。

实用注记

样本大小。 DML 需要烦扰模型以 n1/4n^{-1/4} 收敛,实践中这意味着足够的行让 ML 模型以任何方式近似 g0g_0m0m_0。与其信任取整数的阈值,不如像 multi-symbol validation 那样实证建立充分性——检查估计是否在工具和子周期上成立,并将不稳定性视为其信号。

学习器选择。 DML 特定事实狭窄但有用:给定 n1/4n^{-1/4} 收敛,学习器影响 θ^0\hat\theta_0效率(区间宽度),而非其一致性。哪些学习器值得在表格市场数据上触及,以及为何梯度提升是默认值,已经在 spread modeling with machine learning 中涵盖。如果 θ^0\hat\theta_0 在学习器间实质移动,那不是选择菜单——这是烦函数估计不良的证据,根据上述部分,选择最友好的会将练习变成搜索。

结论

DML 给博客带来了它没有的东西:一种将市场微结构主张声明为带有可辩护标准误的因果参数,而非带有良好验证分数的预测的方法。

三个承重想法是:

  • 正交化分数,使第一阶误差补偿到二阶。
  • 交叉拟合,在时间序列数据上使用清理和禁运折叠,使第一阶过拟合无法移动 θ0\theta_0
  • 预先指定,使您报告的区间是您实际获得的区间。

估计器是容易的部分。困难部分不变:决定哪些干扰因素重要,论证识别假设成立,以及抵抗再次运行规格的冲动。


参考文献

DML 血统短且 worth 一行:它是 Robinson (1988) 的部分线性模型,ML 替换核估计器,达到半参数效率界限,具有追溯 Neyman C(α\alpha) 测试的正交条件,以及在定向学习 (TMLE) 文献中的近亲。Chernozhukov 等人的贡献是展示这可以用任意 ML 学习器运作,同时保持 N\sqrt{N} 一致、渐近正态推断。

  1. Chernozhukov, V., Chetverikov, D., Demirer, M., Duflo, E., Hansen, C., Newey, W., & Robins, J. (2018). Double/Debiased Machine Learning for Treatment and Structural Parameters. The Econometrics Journal, 21(1), C1-C68.
  2. Robinson, P. M. (1988). Root-N-Consistent Semiparametric Regression. Econometrica, 56(4), 931-954.
  3. Bach, P., Chernozhukov, V., Kurz, M. S., & Spindler, M. (2022). DoubleML — An Object-Oriented Implementation of Double Machine Learning in Python. Journal of Machine Learning Research, 23(53), 1-6.
  4. Facure, M. (2022). Causal Inference for the Brave and True. Chapter 22: Debiased/Orthogonal Machine Learning.
  5. Cahan, E., Bai, J., & Ng, S. (2024). Causal Factor Investing. Quantitative Finance.
免责声明:本文提供的信息仅用于教育和参考目的,不构成财务、投资或交易建议。加密货币交易涉及重大损失风险。

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

紧跟市场步伐

订阅我们的时事通讯,获取独家 AI 交易见解、市场分析和平台更新。

我们尊重您的隐私。您可以随时退订。