
У любой системы, обученной на исторических данных, есть один общий страх, и он серьёзнее переобучения, утечек и сидового шума вместе взятых. Звучит просто: рынок, на котором вы будете торговать, не обязан быть похож на рынок, на котором вы учились.
Переобучение ловится честной валидацией. Утечки — дисциплиной признаков. Сидовый шум — ансамблями. А смена режима не ловится ничем заранее: вы узнаёте о ней тогда, когда система уже теряет деньги в реальном времени.
Эта статья — про то, как я пытался измерить этот риск заранее, что из этого вышло (спойлер: включая одну ошибку, которая на время убедила меня, что всё гораздо хуже, чем на самом деле), и какие страховки по результатам измерений работают, а какие — красивые идеи с отрицательной ценностью.
Это продолжение серии (сидовый шум и протокол проверки, пять провалов новостных признаков). Вопросы из комментариев ко второй статье — про еженедельное переобучение, инерцию режима и стоп-кран — разобраны здесь по пунктам.
Стенд, кратко
Для тех, кто присоединился: часовые свечи 20 ликвидных акций Мосбиржи, 2021–2026; рекуррентная сеть, бинарная метка «вырастет ли цена больше порога за несколько часов»; walk-forward с зазором, ансамбль 5 сидов, все сравнения — парные, в единой методике издержек. Признаки: цена, положение бумаги относительно корзины, микроструктура часа, новостные счётчики.
Как вообще измерить «чужой режим»
Идея эксперимента лобовая. Моя система обучена и отвалидирована на 2021–2026. Берём 2015–2020 — шесть лет, которых она не видела ни в обучении, ни в подборе порогов, ни в одном решении по архитектуре. Другая эпоха по всему: другая ставка, другая волатильность, другой состав лидеров, бычий рынок против нашего бокового с обвалами.
Дальше два режима прогона:
Замороженная модель: боевые веса как есть, без перенастройки. Отвечает на вопрос «что будет, если режим сменится, а я не замечу».
Walk-forward на той эпохе: модель еженедельно переобучается, как в бою, но только на данных 2015–2020. Отвечает на вопрос «спасает ли регулярное переобучение».
Важная деталь дизайна: историческая эпоха живёт в отдельной базе и никогда не попадает в прод-пайплайн — это одноразовый полигон, а не второй обучающий период.
Первый результат: катастрофа (опубликованная)
Замороженная модель на 2015–2020: −28%, Sharpe −0.65, просадка −34%. Худший год — 2020-й: −20%.
Walk-forward на той же эпохе: +17%, Sharpe 0.51.
Вывод, который я из этого сделал и опубликовал, звучал хорошо: модель выучила режим 2021–2026 и в чужом режиме уверенно торгует прошлым; еженедельное переобучение — не гигиена, а несущая конструкция, оно превращает катастрофу в слабый плюс; главный риск — скорость адаптации на переходе между эпохами.
Хорошо, правда? Одна беда: половина этого вывода оказалась артефактом.
Детектив: кто нарисовал −28%

Через месяц, в плановом аудите кода перед стартом реальных денег, нашлась дыра — не в модели, в тесте.
Часть входных признаков системы — микроструктурные и новостные ряды — физически существует только с 2020–2021 годов. Раньше этих данных просто нет: не «не скачал», а не существовало источников. На стресс-периоде загрузчик признаков не падал и не предупреждал — он молча подставлял нули. Виновник анекдотично классический: except Exception: pass, который «удобно» глотал отсутствующую таблицу.
То есть тест измерял не «модель в чужой эпохе». Он измерял модель, у которой отключили две трети входов, в чужой эпохе. Представьте, что вы проверяете пилота в незнакомой местности, предварительно закрасив ему половину приборной панели — и по результатам делаете вывод о навигационных способностях.
Честный перемер: я обучил версию модели только на признаках, существующих в обеих эпохах (цена + положение относительно корзины). На своём периоде она полноценна. На чужой эпохе:
слепая боевая модель: −25%, Sharpe −0.64;
зрячая модель: +22%, Sharpe 0.53, все шесть лет в диапазоне от −1% до +8.5%.
Катастрофы не было. Минус рисовали нули в данных, а не смена режима. Числа этого сравнения — в методике исходного теста, чтобы быть сопоставимыми с опубликованной цифрой; сравнение парное, обе стороны считались одинаково. Уточнение с полными издержками — чуть ниже, и оно важное.
А теперь самое интересное — каскадное следствие. Помните «переобучение — несущая конструкция», потому что walk-forward давал +17% против −28%? Сравните честно: walk-forward с обучением на той эпохе — +17%, Sharpe 0.51. Зрячая замороженная модель — +22%, Sharpe 0.53. Переобучение ничего не спасало. Спасать было нечего.
Два вывода из детектива, оба переносятся на любой проект:
Кромка переносится между эпохами даже заморозкой — но она тонкая. ~3–4% годовых до проскальзывания при просадке −22% против Sharpe ~1.8 на родном периоде. А теперь обещанное уточнение: если применить к чужой эпохе полную модель издержек (комиссия + проскальзывание на обе ноги), эти 3–4% годовых превращаются в −7% за шесть лет. Кромка в чужом режиме тоньше издержек. Чужой режим — это не место, где система умирает драматично; это место, где она тихо работает на брокера.
Стресс-тест обязан видеть те же данные, что бой. Молчаливый ноль вместо отсутствующего признака — не «консервативная оценка», а другой эксперимент с неизвестным смыслом.
Что реально работает: скучный эталон и грубый выключатель

Пока моя нейросеть в чужой эпохе в лучшем случае выходила в ноль, простой SMA-кросс — эталонная стратегия, которую система обгоняет на родном периоде в разы — показывал там Sharpe 1.24 при полных издержках, вдвое лучше собственного результата на 2021–2026. Тренд-фолловинг на бычьем рынке 2015–2020 был ровно в своей стихии.
Это переворачивает постановку задачи. Вопрос не «как заставить модель работать в любом режиме» (никак — кромка привязана к эпохе данных, на которых есть чему учиться). Вопрос — как автоматически замечать, что твоя лошадь захромала, и пересаживаться на другую.
Мой ответ — выключатель, максимально тупой из работающих:
на каждом баре считается трейлинг-качество (скользящий Sharpe) кривых капитала обеих стратегий — нейросети и эталона — на окне порядка тысячи часовых баров (~полгода);
капитал отдан той, что лучше, с гистерезисом: переключение только при уверенном превосходстве, чтобы не дёргаться на шуме;
третье плечо — кэш под ставку (денежный рынок): если обе торгуют хуже, чем просто лежать в деньгах под ключевую, — капитал уходит в кэш.
Числа — все три кривые в полной модели издержек, зрячая версия модели на чужой эпохе (пересчитал специально для этой статьи, чтобы не тащить сюда ни слепоту, ни забытое проскальзывание):
Sharpe |
Нейросеть |
Эталон |
Выключатель |
|---|---|---|---|
2021–2026 (родная эпоха) |
1.77 |
0.57 |
0.80 |
2015–2020 (чужая эпоха) |
−0.15 |
1.24 |
1.13 |
худшее из двух |
−0.15 |
0.57 |
0.80 |
Читается таблица так. На родной эпохе выключатель заметно уступает чистой нейросети — это цена страховки: он держит нейросеть ~67% времени, а остаток пережидает в эталоне и кэше, в том числе весь первый год, пока накапливает своё окно статистики. На чужой эпохе он быстро распознаёт, что нейросеть не тянет (в ней всего 23% времени), и живёт в эталоне. А решает нижняя строка: по худшему из режимов выключатель лучше любой из стратегий по отдельности. Это и есть определение страховки — она не выигрывает ни один отдельный мир, зато не проигрывает катастрофически ни в одном.
И про кэш-плечо: добавление третьего плеча снизило просадку выключателя в обеих эпохах ценой части доходности — на нашем рынке с двузначной ключевой ставкой «не торговать» — это не пассивность, а полноценная стратегия с положительной доходностью.
Что НЕ работает: кладбище красивых идей

Здесь коротко о трёх страховках, которые выглядели умнее выключателя — и проиграли ему в измерениях. Это, возможно, самая полезная часть статьи.
1. «Умная» кондиция по волатильности. Идея: зачем ждать, пока кривая капитала просядет, — давайте переключаться заранее, по индексу волатильности (аналог VIX): вола выше порога → принудительно в эталон. Измерение: на основном периоде — вред. 184 переключения против 31 у чистого трейлинг-качества. Индикатор дёргается и выгоняет систему из прибыльных волатильных отрезков — а у моей модели волатильные периоды как раз кормовые. Трейлинг-качество собственной кривой уже несёт всю режимную информацию, внешний индикатор добавляет только шум.
2. Обучаемый привратник (meta-labeling). Идея красивая: обучить вторую модель предсказывать, когда первой можно доверять. Скрининг соблазнял как ни один эксперимент в проекте — а честная вторая половина развернула всё в минус. Причина поучительна: гейт выучил медленные рыночные признаки (наклон рынка за сутки) и стал запоминать эпохи — та же ловушка, что убила у меня шесть семейств признаков, только теперь в роли фильтра. При этом информация в задаче есть (meta-AUC 0.56), и лучший признак гейта — трейлинг-качество основной модели. То есть обучаемый привратник пытался выучить ровно то, что грубый выключатель делает арифметикой. Грубая форма оказалась робастной там, где обучаемая переобучилась.
3. Шорт-хедж. Ветка длинная (проверял и шорт-ногу от собственной модели, и независимый трендовый шорт-хедж), итог у обеих одинаковый: перманентный шорт — это страховка с отрицательным керри, вы платите премию все спокойные годы ради редких крахов. А кэш-плечо выключателя — страховка с положительным керри: в плохом режиме вы не платите премию, а получаете ставку. При двузначной ключевой второй вариант доминирует первый по худшему из режимов. Бонусный урок: антикорреляция дневных PnL двух ног — ещё не диверсификация; если обе ноги питаются кромкой одной модели, в чужом режиме они умирают вместе.
Про инерцию режима и сколько ждать статистики
Частый вопрос: окно в тысячу баров — не слишком ли медленно? Полгода часовиков, режим за это время может смениться дважды.
Ответ из измерений: быстрые окна проигрывают на обеих эпохах. Причина — асимметрия ошибок. Медленный выключатель опаздывает на переходах (недели посредственной торговли), но почти не делает ложных переключений внутри режима. Быстрый ловит переход раньше, но внутри режима постоянно дёргается — а каждое ложное переключение — это выход из стратегии ровно тогда, когда она восстанавливается. Переходов между эпохами — единицы за десятилетие; шумных просадок внутри эпохи — десятки в год. Оптимизировать надо частый случай.
Отсюда же — трезвый взгляд на live: выключатель в реальном времени станет по-настоящему осмысленным только после накопления собственного окна live-статистики. До того он работает на затравке из бэктест-кривых — честная цена холодного старта любой режимной автоматики.
Выводы
Смена режима — реальный риск, но мой самый страшный опубликованный результат о ней был наполовину артефактом теста. Проверяйте тест раньше модели.
Кромка ML-модели привязана к эпохе. В чужом режиме зрячая модель не умирает — она становится посредственной. Это меняет задачу с «пережить катастрофу» на «заметить посредственность и переключиться».
Работает грубое: трейлинг-качество собственных кривых, гистерезис, кэш как третье плечо. Не работает умное: внешние индикаторы режима, обучаемые привратники, шорт-хеджи.
Еженедельное переобучение — гигиена, а не страховка от смены эпохи. Оно держит модель свежей внутри режима; на границе эпох его вклад — в пределах шума.
Страховка обязана иметь положительный или нулевой керри. Страховка с отрицательным керри проигрывает кэшу по худшему из режимов — по крайней мере там, где ключевая ставка двузначная.
Система тем временем прошла аудит, торгует виртуально и готовится к реальным деньгам с публичным трек-рекордом — о первых неделях реального исполнения против бэктеста, видимо, и будет следующая статья. Если есть вопросы к постановкам из этой — комментарии открыты, стенд под рукой; предыдущие два расчёта по мотивам комментариев уже случились.
Не является индивидуальной инвестиционной рекомендацией.
jbenderov
То, что вы описали, в ML называется concept drift, а «выключатель по трейлинг-Sharpe» — это детектор дрейфа по выходу. У него встроенный лаг: скользящий Sharpe на окне в тысячу баров реагирует, когда просадка уже случилась, — вы сами закрываете это гистерезисом против дёрганья. Размен фундаментальный: короткое окно ловит шум, длинное реагирует поздно, середины нет.
Рядом стоит поставить детекторы дрейфа не по результату, а по входу. Page-Hinkley, DDM, ADWIN следят за статистикой потока — распределением признаков и ошибкой предсказания — и сигналят о смене распределения раньше, чем она доедет до кривой капитала. Логика простая: режим меняется во входных данных, а P&L — уже следствие, с задержкой.
Оговорка, без которой это в трейдинге не работает: feature drift и P&L drift связаны не жёстко. Рынок может сменить распределение, а стратегия останется прибыльной, и наоборот. Так что входной детектор — это ранний сигнал «пора присмотреться и, может, ужать риск», а не готовая команда переключиться. Ваш выходной выключатель как финальный арбитр всё равно нужен, но входной даёт фору по времени, которой у чисто выходного нет.
svtoroi Автор
терминологически все так, и оговорка про нежесткую связь feature drift и P&L - это вообще главный урок моих замеров)
входные детекторы я в двух вариантах уже пробовал (вола-кондиция и обучаемый гейт), оба сигналят раньше, но ложные срабатывания обошлись дороже опоздания
Page-Hinkley по ошибке предсказания не гонял, дойдут руки замерить - вернусь с цифрами