← К списку статей
August 2, 2026
5 мин. чтения

Двойное машинное обучение: оценка причинного параметра вместо прогнозирования доходности

Двойное машинное обучение: оценка причинного параметра вместо прогнозирования доходности
#causal-inference
#double-ML
#treatment-effect
#econometrics
#quant

Все статьи о моделировании в этом блоге до сих пор ставили одну и ту же форму вопроса: даны признаки, спрогнозируй число, затем проверь, что прогноз выживает вне выборки. Модели спреда прогнозируют спред. Модели заполнения прогнозируют вероятность заполнения. Весь аппарат валидации — очищенный walk-forward, сдутый Шарп, таксономия look-ahead — существует для проверки того, является ли прогноз настоящим.

Эта статья ставит другую форму вопроса, и это единственный инструмент, которого никогда не было в блоге: оценить один скалярный параметр, который имеет причинное значение, и прикрепить к нему стандартную ошибку, которая переживает факт того, что гибкая ML модель была использована для достижения этого.

Это не риторическое различие. "Позиция в очереди предсказывает вероятность заполнения" тривиально верна и операционно бесполезна — конечно, она это делает, оба определяются глубиной и волатильностью. "Сдвинуться на одну позицию вперед в очереди вызывает изменение на θ\theta в вероятности заполнения, при фиксированном состоянии рынка" — это число, которое можно вставить в политику размещения ордеров. Первое — это подгонка регрессии. Второе требует оценщика, которого не существует в стандартном ML инструментарии, потому что регуляризация и overfitting в гибкой первой стадии смещают именно тот коэффициент, который вам важен.

Двойное машинное обучение (Chernozhukov, Chetverikov, Demirer, Duflo, Hansen, Newey & Robins, 2018) — это оценщик, который это исправляет. Каждый количественный трейдер слышал это: корреляция не есть причинность. DML — это та часть, которая идет после этой фразы.

Почему наивная регрессия не работает

Скрытый конфаундер состояния рынка управляет и размещением в очереди, и исходами заполнения, создавая обманчивую наивную ассоциацию

Поставим вопрос на собственной территории блога. Мы хотим причинный эффект позиции в очереди на вероятность заполнения:

  • YiY_i: заполнился ли лимитный ордер в покое на наблюдении ii в пределах горизонта.
  • DiD_i: позиция в очереди при размещении (нормализованная размером уровня).
  • XiX_i: конфаундеры — реализованная волатильность, котируемый спред, дисбаланс глубины, размер уровня, время дня, метка режима. Это переменные состояния рынка, которые блог уже вычисляет в моделировании спреда с машинным обучением.

Причинный параметр — это θ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: Игнорировать конфаундеры. Регрессировать YY на DD только. Оценка поглощает эффект каждого конфаундера, коррелированного с обоими. Классическое смещение пропущенной переменной: тонкие уровни дают и хорошую позицию в очереди, и высокую частоту заполнения, поэтому вы преувеличиваете ценность самой позиции.

Подход 2: Линейная регрессия с контролем. Регрессировать YY на DD и XX. Это работает только если 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) — это nuisance-функция — часть результата, объясненная состоянием рынка.
  • m0(X)m_0(X) — другая nuisance-функция — условное ожидание лечения при данном состоянии рынка ("propensity" в контексте непрерывного лечения).
  • UU и VV — структурные остатки.

Ключевое понимание: θ0\theta_0 низкоразмерный, но g0g_0 и m0m_0 могут быть произвольно сложными. Мы хотим, чтобы ML обрабатывал nuisance-функции, при этом все еще доставляя валидный вывод о скаляре.

Почему "двойной"?

Две 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)

и оценить θ0\theta_0, регрессируя Y~\tilde{Y} на D~\tilde{D}:

θ^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 вместо линейной проекции, затем прочитать эффект лечения из остаточной вариации.

Ортогональность Неймана: почему это работает

Наивный подход частичного удаления (оценить g0g_0, вычесть, регрессировать) не работает, потому что ошибки ML оценки в g^0\hat{g}_0 напрямую передаются в θ^0\hat\theta_0. Оценка DML сконструирована так, чтобы быть Нейман ортогональной — нечувствительной к малым возмущениям в nuisance-функциях.

Ортогональная оценка для 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

Интуитивно, оценка использует только вариацию в DD и YY, которая независима от XX, и ошибки в одной nuisance-функции компенсируются другой. Если m^\hat{m} немного переоценивает лечение, D~\tilde{D} немного слишком маленькая, но соответствующая ошибка в Y~\tilde{Y} от неправильной оценки \ell толкает в компенсирующем направлении. Смещение становится второго порядка — произведение двух ошибок первой стадии — а не первого порядка.

Формально, если оба nuisance-оценщика сходятся со скоростью 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 сходится с параметрической скоростью и асимптотически нормален.

Cross-fitting: почему здесь обязательно

Одной ортогональности недостаточно. Если nuisance-моды подогнаны на тех же строках, которые используются для оценки θ0\theta_0, overfitting первой стадии загрязняет вторую стадию — и специфический вред стоит того, чтобы быть точно сформулированным, потому что это не тот вред, к которому вы привыкли. В другом месте overfitting появляется как раздутая оценка валидации: вы замечаете, делаете скидку, продолжаете. Здесь он появляется как смещенная точечная оценка для θ0\theta_0, с доверительным интервалом, который все еще узок и все еще центрирован на неправильном числе. Нет оценки, которой можно подозревать. Оценщик просто тихо лжет.

Cross-fitting разрывает зависимость: nuisance-прогнозы каждого наблюдения приходят из модели, обученной без него, и θ0\theta_0 оценивается из pooled held-out остатков. Механика — это обычная K-fold машинерия, описанная в моделировании спреда с машинным обучением; что важно ниже — это какие folds вы ему даете.

Алгоритм DML шаг за шагом

Ввод: данные {(Yi,Di,Xi)}i=1N\{(Y_i, D_i, X_i)\}_{i=1}^N, ML методы M\mathcal{M}_\ell и Mm\mathcal{M}_m, folds KK.

Шаг 1 — Разбиение: разделить {1,,N}\{1, \ldots, N\} на KK непересекающихся folds.

Шаг 2 — Cross-fit nuisance-модели: для k=1,,Kk = 1, \ldots, K, обучить ^(k)\hat{\ell}^{(-k)} и m^(k)\hat{m}^{(-k)} на дополнении fold kk, затем вычислить 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) для iIki \in I_k.

Шаг 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 результат что-то, и где большинство приложений метода тихо разваливаются.

Вышеописанная асимптотическая нормальность — это утверждение об одном заранее специфицированном лечении, одном заранее специфицированном наборе конфаундеров, одной заранее специфицированной оценке. Зафиксируйте это заранее, выполните оценщик один раз, и интервал означает то, что говорит. Попробуйте три кандидат-лечения, или четыре набора конфаундеров, или меняйте learners пока p-значение не будет выглядеть лучше, и вы больше не делаете вывод — вы выполняете поиск, и сообщенное p-значение — это p-значение максимума, а не выборки.

Блог уже измерял, что это делает. В исследовании сдутого Sharpe ratio поиски по чистому шуму с нулевым реальным преимуществом производят наивную частоту ложных открытий 1.000 — неадJUSTированный тест срабатывает каждый раз — в то время как медианное наивное p-значение победителя сидит около 0.0007. Ничто в ортогональности Неймана не защищает вас от этого. Ортогональность исправляет смещение от nuisance-оценки; она ничего не говорит о смещении от поиска спецификации. p-значение DML 1e-05, полученное после попытки шести спецификаций, заслуживает точно такое же отношение 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-значения, и проблема множественного тестирования, которую итоговая таблица не упоминает. Если вы читаете все три, корректируйте все три. Если только одна была предварительно зарегистрированным вопросом, скажите это, и обращайтесь с другими двумя явно как с исследовательскими.

Cross-fitting на временных рядах: purge, embargo, кастомные folds

Вращающиеся folds временных рядов используют разрывы purge и зоны embargo перед тем, как out-of-fold остатки собираются в одну причинную оценку

Стандартный DML предполагает i.i.d. наблюдения. Данные книги не таковы, и режим сбоя тот, который блог уже подробно документировал: соседние строки делят перекрывающиеся forward окна, поэтому простой TimeSeriesSplit все еще протекает ответ через границу fold. Смотрите моделирование спреда с машинным обучением для реализации очищенного и embargoed walk-forward и причины, по которой требуется разрыв минимум horizon строк, и таксономию look-ahead bias для полного каталога утечек и их измеренных величин.

Действительно специфичная для DML часть — это как вы передаете очищенные folds оценщику, потому что set_sample_splitting имеет контракт, на котором спотыкаются люди:

import numpy as np
import doubleml as dml

def purged_folds(n: int, n_splits: int, horizon: int):
    """Folds с расширяющимся окном и разрывом purge/embargo `horizon` строк.

    Та же конструкция, что и очищенный walk-forward CV в статье о
    моделировании спреда: разрыв удаляет перекрытие между forward окном
    тренировочной строки и валидационной строки.
    """
    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 folds не покрывают каждую строку. Разрывы purge и начальный тренировочный блок никогда не являются test fold никого, поэтому θ0\theta_0 оценивается строго менее чем из NN остатков. Это правильное поведение, не баг, но это означает, что эффективное NN в формуле дисперсии — это количество pooled test строк — проверьте это вместо предположения.
  2. Повторения n_rep здесь не бесплатны. Со случайным K-fold повторение cross-fitting и усреднение — это дешевое снижение дисперсии. С детерминированным упорядоченным по времени разделением есть только одно разделение, поэтому n_rep ничего не покупает и не скрывает; стабильность должна приходить от повторного запуска на разных окнах данных.

Для панельных структур (много символов за тот же период) DoubleML поддерживает кластер-робастные стандартные ошибки — кластеризуйте по символу, а не по времени, и смотрите множественную валидацию на позицию блога о том, когда действительно устанавливается cross-instrument результат.

Измеренный кейс: позиция в очереди и вероятность заполнения

Позиции ордеров в FIFO очереди сходятся к эффекту позиции в очереди, скорректированному DML, с halo доверия

Это единственный причинный вопрос в статье, где проект уже имеет данные, и он должен быть выполнен, а не просто предложен. Анализ позиции в очереди уже покрывает оценку позиции, механику FIFO, скорости дренажа и время-до-заполнения на реальных данных книги; симуляция заполнения покрывает моделирование вероятности заполнения и цикл калибровки против live заполнений. Оба производят прогностическую модель заполнений. DML преобразует те же входы в причинную оценку.

Спецификация, предварительно зарегистрированная перед взглядом на оценку:

  • Результат $Y: заполнен в пределах HORIZON` снимков (бинарно).
  • Лечение DD: нормализованная позиция в очереди при размещении.
  • Конфаундеры $X`: реализованная 1s волатильность, котируемый спред в bps, дисбаланс глубины, размер уровня при посте, расстояние от mid в тиках, кодирование времени дня, метка режима.
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)

Результат для отчета — это сравнение четырех колонок, а не одного коэффициента: наивная OLS оценка YY на DD только, оценка линейного контроля YY на DD и XX, оценка DML с ее стандартной ошибкой, и значение робастности из анализа чувствительности — насколько сильным должен быть ненаблюдаемый конфаундер, чтобы аннулировать эффект. Разрыв между наивной и DML колонками — это действительно интересное количество: это сколько мнимой ценности позиции в очереди было состоянием рынка в маскараде.

Нулевой или отрицательный θ\theta здесь — это публикуемый результат и лучше подходит этому блогу, чем чистый положительный. Если причинный эффект позиции в очереди коллапсирует, как только волатильность и размер уровня частично удалены, это прямое открытие о политике размещения ордеров: не позиция зарабатывает заполнение, условия, при которых вы получили позицию, — да.

Причинный факторный анализ

Стандартный подход к факторному инвестированию — ассоциативный: сортировать по характеристике, формировать long-short портфели, наблюдать, что доходности различаются. DML позволяет другой тест — оценить прямой причинный эффект характеристики на доходности, очистив эффекты конфаундинга других характеристик. Если эффект исчезает под DML, фактор не является независимо причинным; это прокси.

Это вторая, независимая форма факторного скептицизма, и стоит быть явным о том, как она relates к той, которую блог уже публикует. Сдутый Sharpe ratio атакует факторный зоопарк со стороны селекции: с достаточно испытаний фактор может выглядеть значимым чисто потому что вы много раз смотрели. DML атакует со стороны конфаундинга: фактор может выглядеть значимым на одном честном тесте и все еще быть прокси чего-то другого в conditioning set. Фактор должен выжить в обоих, чтобы быть интересным, и два режима сбоя независимы — прохождение одного не говорит вам о другом.

Что DML не может делать

  1. Требует, чтобы конфаундеры были наблюдены. Если ненаблюдаемая переменная управляет и лечением, и результатом, DML смещен, и никакая ML изощренность не исправляет проблему идентификации. Анализ чувствительности ограничивает риск; не удаляет его.

  2. Оценивает средний эффект. Если эффект позиции в очереди резко varies по режимам, точечная оценка — это среднее по смеси режимов вашей выборки. Для гетерогенности используйте Interactive Regression Model (DoubleMLIRM) или causal forest.

  3. Предполагает структурную модель. Частично линейная спецификация требует, чтобы лечение входило в уравнение результата определенным способом. Если истинный процесс фундаментально другой, DML уверенно ошибается.

  4. Не открывает причинную структуру. DML оценивает эффект заранее специфицированного лечения. Он не говорит вам, какие переменные — причины.

  5. Не освобождает от множественного тестирования. Повторяю вышеуказанную точку, потому что она чаще всего пропускается: ортогональность de-biasит nuisance-оценку, а не поиск спецификации.

Практические заметки

Размер выборки. DML требует, чтобы nuisance-модели сходились в n1/4n^{-1/4}, что на практике означает достаточно строк для ML моделей, чтобы вообще аппроксимировать g0g_0 и m0m_0. Вместо доверия круглым порогам установите адекватность эмпирически тем способом, как это делает множественная валидация — проверьте, держится ли оценка через инструменты и под-периоды, и обращайтесь с нестабильностью как с сигналом, который она есть.

Выбор learner. Специфический для DML факт узок но полезен: при сходимости n1/4n^{-1/4}, learner влияет на эффективность θ^0\hat\theta_0 (ширину интервала), а не на согласованность. Какие learners стоят того, чтобы достигать на табличных рыночных данных, и почему градиентный бустинг — дефолт, уже описано в моделировании спреда с машинным обучением. Если θ^0\hat\theta_0 существенно moves через learners, это не меню для выбора — это доказательство, что nuisance-функции плохо оценены, и согласно вышеуказанной секции, выбор самого дружелюбного превращает упражнение в поиск.

Заключение

DML дает блогу то, чего у него не было: способ сформулировать утверждение о микроструктуре рынка как причинный параметр с defendable стандартной ошибкой, а не как прогноз с хорошей оценкой валидации.

Три несущие идеи:

  • Ортогонализируйте оценку так, чтобы ошибки первой стадии компенсировались до второго порядка.
  • Cross-fit, с очищенными и embargoed folds на данных временных рядов, чтобы overfitting первой стадии не мог сместить θ0\theta_0.
  • Предварительно специфицируйте, чтобы интервал, который вы report, — это интервал, который вы действительно заслужили.

Оценщик — это легкая часть. Трудные части остаются неизменными: решать, какие конфаундеры важны, аргументировать, что идентификационные предположения выполняются, и сопротивляться импульсу выполнить спецификацию еще раз.


Ссылки

Линия DML коротка и стоит одной строки: это частично линейная модель Robinson (1988) с ML заменяющим kernel estimators, достигающая semiparametric efficiency bound, с условием ортогональности, tracing back к тесту C(α\alpha) Неймана и близкому кузену в литературе targeted learning (TMLE). Вклад Chernozhukov et al. был демонстрацией того, что это можно операционализировать с произвольными ML learners, сохраняя N\sqrt{N}-consistent, асимптотически нормальный вывод.

  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.
Дисклеймер: Информация в этой статье предоставлена исключительно в образовательных и ознакомительных целях и не является финансовым, инвестиционным или торговым советом. Торговля криптовалютами сопряжена с высоким риском убытков.

Авторы

Eugen Soloviov
Eugen Soloviov

Инженер торговых систем

Разработка торговых ботов с 2017 года: межбиржевой арбитраж (подключал до 30 бирж), парный арбитраж на коинтеграции между спотом и фьючерсами, скальпинг, фронтраннинг, торговля по новостям, сентиментный анализ, трендовые алгоритмы, а также алгоритмы управления и балансировки портфелей. Делает выставление ордеров до 1 мс, warehouse для big data, бэктестинг-движки, AI-агентов и интерфейсы для ботов (в т.ч. open-source profitmaker.cc). Стек: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, архитектура.

Newsletter

Будьте в курсе событий

Подпишитесь на нашу рассылку, чтобы получать эксклюзивную аналитику по AI-трейдингу и обновления платформы.

Мы уважаем вашу конфиденциальность. Отписаться можно в любой момент.