У любой системы, обученной на исторических данных, есть один общий страх, и он серьёзнее переобучения, утечек и сидового шума вместе взятых. Звучит просто: рынок, на котором вы будете торговать, не обязан быть похож на рынок, на котором вы учились.

Переобучение ловится честной валидацией. Утечки — дисциплиной признаков. Сидовый шум — ансамблями. А смена режима не ловится ничем заранее: вы узнаёте о ней тогда, когда система уже теряет деньги в реальном времени.

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

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

Стенд, кратко

Для тех, кто присоединился: часовые свечи 20 ликвидных акций Мосбиржи, 2021–2026; рекуррентная сеть, бинарная метка «вырастет ли цена больше порога за несколько часов»; walk-forward с зазором, ансамбль 5 сидов, все сравнения — парные, в единой методике издержек. Признаки: цена, положение бумаги относительно корзины, микроструктура часа, новостные счётчики.

Как вообще измерить «чужой режим»

Идея эксперимента лобовая. Моя система обучена и отвалидирована на 2021–2026. Берём 2015–2020 — шесть лет, которых она не видела ни в обучении, ни в подборе порогов, ни в одном решении по архитектуре. Другая эпоха по всему: другая ставка, другая волатильность, другой состав лидеров, бычий рынок против нашего бокового с обвалами.

Дальше два режима прогона:

  1. Замороженная модель: боевые веса как есть, без перенастройки. Отвечает на вопрос «что будет, если режим сменится, а я не замечу».

  2. 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. Переобучение ничего не спасало. Спасать было нечего.

Два вывода из детектива, оба переносятся на любой проект:

  1. Кромка переносится между эпохами даже заморозкой — но она тонкая. ~3–4% годовых до проскальзывания при просадке −22% против Sharpe ~1.8 на родном периоде. А теперь обещанное уточнение: если применить к чужой эпохе полную модель издержек (комиссия + проскальзывание на обе ноги), эти 3–4% годовых превращаются в −7% за шесть лет. Кромка в чужом режиме тоньше издержек. Чужой режим — это не место, где система умирает драматично; это место, где она тихо работает на брокера.

  2. Стресс-тест обязан видеть те же данные, что бой. Молчаливый ноль вместо отсутствующего признака — не «консервативная оценка», а другой эксперимент с неизвестным смыслом.

Что реально работает: скучный эталон и грубый выключатель

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

Выводы

  1. Смена режима — реальный риск, но мой самый страшный опубликованный результат о ней был наполовину артефактом теста. Проверяйте тест раньше модели.

  2. Кромка ML-модели привязана к эпохе. В чужом режиме зрячая модель не умирает — она становится посредственной. Это меняет задачу с «пережить катастрофу» на «заметить посредственность и переключиться».

  3. Работает грубое: трейлинг-качество собственных кривых, гистерезис, кэш как третье плечо. Не работает умное: внешние индикаторы режима, обучаемые привратники, шорт-хеджи.

  4. Еженедельное переобучение — гигиена, а не страховка от смены эпохи. Оно держит модель свежей внутри режима; на границе эпох его вклад — в пределах шума.

  5. Страховка обязана иметь положительный или нулевой керри. Страховка с отрицательным керри проигрывает кэшу по худшему из режимов — по крайней мере там, где ключевая ставка двузначная.

Система тем временем прошла аудит, торгует виртуально и готовится к реальным деньгам с публичным трек-рекордом — о первых неделях реального исполнения против бэктеста, видимо, и будет следующая статья. Если есть вопросы к постановкам из этой — комментарии открыты, стенд под рукой; предыдущие два расчёта по мотивам комментариев уже случились.


Не является индивидуальной инвестиционной рекомендацией.