Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Ваш бэктест нейронки врёт на ±10 процентных пунктов

Дата публикации: 12-08-2026 09:13:53

Недавно я решил проверить на себе классический сюжет: может ли нейронка зарабатывать на бирже. Не «взял стратегию с ютуба», а как положено инженеру: собрал данные Мосбиржи, построил честный walk‑forward пайплайн, зашил комиссии в каждую цифру и стал измерять.Эта статья — про самый неочевидный урок, который я получил. Он не про рынок и не про архитектуры. Он про то, что результат бэктеста нейронной сети — случайная величина с разбросом в десять процентных пунктов годовой доходности, и если вы это не учитываете, все ваши выводы — шум.Подозреваю, что этим страдает заметная часть статей вида «я обучил LSTM на котировках и получил X% годовых». Проверить легко: спросите автора, что будет, если поменять random seed. Читать далее

Основное содержимое страницы с новостью.

45e834f19db76215bc39f031639c52a3.png

Недавно я решил проверить на себе классический сюжет: может ли нейронка зарабатывать на бирже. Не «взял стратегию с ютуба», а как положено инженеру: собрал данные Мосбиржи, построил честный walk‑forward пайплайн, зашил комиссии в каждую цифру и стал измерять.

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

Подозреваю, что этим страдает заметная часть статей вида «я обучил LSTM на котировках и получил X% годовых». Проверить легко: спросите автора, что будет, если поменять random seed.

Стенд

Чтобы дальнейшие цифры имели смысл, фиксирую условия эксперимента:

  • Данные: часовые свечи 20 самых ликвидных акций Мосбиржи (TQBR), 2021–2026, ~370 тысяч баров. Источник — бесплатный ISS API биржи, складываю в SQLite.

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

  • Метка: вырастет ли цена больше чем на пороговую величину за фиксированный горизонт в несколько часов. Бинарная классификация, на выходе — вероятность.

  • Портфель: капитал поровну на 20 бумаг, вход при вероятности выше порога, выход — по гистерезису (порог выхода ниже порога входа, чтобы не дёргаться на границе). Комиссия 0.05% за сторону — тариф реального брокера.

  • Железо: одна потребительская GPU; один прогон walk‑forward — около трёх минут. Это важно: всё, что я опишу, доступно любому энтузиасту.

5bd0d8b345c12a7ae434c958b821d05a.pngЧто такое честный walk‑forward (и где все ошибаются)

Схема, без которой остальное бессмысленно. Модель НИКОГДА не видит будущего:

# упрощённо: суть схемы разрезов
HORIZON = ...          # горизонт метки в барах
STEP = ...             # переобучаемся каждые STEP баров
window = []            # скользящее окно train (последние N баров)

for cut in range(MIN_TRAIN, len(timeline), STEP):
    # ЗАЗОР: метка бара t созревает только через HORIZON баров.
    # Обучаться можно лишь на барах, чьи метки уже известны
    # к моменту cut — иначе утечка будущего в train.
    safe_train_end = timeline[cut - 1 - HORIZON]
    train = bars[(bars.ts >= train_lo) & (bars.ts <= safe_train_end)]
    predict = bars[(bars.ts > timeline[cut-1]) & (bars.ts <= timeline[cut+STEP-1])]

    model.fit(train)                      # тёплый старт от прошлого фолда
    oos_probs[predict.index] = model.predict(predict)

Три места, где чаще всего врут бэктесты из статей:

  1. Нет зазора между train и test. Метка «вырастет за N часов» у последних N часов обучающей выборки подглядывает в тестовый период. Утечка крошечная на вид — и достаточная, чтобы нарисовать несуществующую кромку на сигнале с AUC 0.55.

  2. Исполнение по цене сигнала. Сигнал посчитан по цене закрытия бара — исполнять его можно только по цене СЛЕДУЮЩЕГО бара. Одна строчка shift(1), разница в итоге — проценты годовых.

  3. Комиссии «потом добавим». На часовом горизонте средняя сделка приносит десятки базисных пунктов, круг комиссий стоит десять. «Потом» превращает плюс в минус.

Я закрыл все три с самого начала. И именно поэтому следующий эффект оказался таким болезненным.

Эксперимент, который всё сломал

Первый прогон: +15% за тестовый период. Неплохо!

Меняю в конфиге одно число — random seed. Данные те же, признаки те же, код тот же.

+6%.

Ещё раз. +14%. Ещё. Другая инициализация весов — другая судьба портфеля.

1f3f7f28a4cd8e35ea9897c21edce0ff.pngПрямое измерение шума

Тогда я сделал то, что стоило сделать с самого начала: зафиксировал всё, кроме сида, и прогнал пять одинаковых обучений. В том первом замере одиночные сиды легли в диапазон от −0.12 до +0.85 по Sharpe — от «стратегия убыточна» до «вполне прилично» на одинаковых данных, признаках и коде.

Эффект никуда не делся и на зрелой версии системы. Вот пять сидов текущей боевой конфигурации (замер на этой неделе, полный OOS‑период, одинаковый порог):

seed

Sharpe (OOS)

42

0.91

43

0.92

44

0.39

45

1.28

46

0.91

d4737a232f7cc9cfbe0391ae73a55eba.png

Признаки стали сильнее, минусовых сидов больше нет — но между худшим и лучшим по‑прежнему более трёх раз. Разница между «выбросить проект» и «показывать друзьям» — один seed.

Почему у нейросетей на финансовых данных так плохо с воспроизводимостью? Потому что сигнал исчезающе мал. Хорошая модель на часовом горизонте угадывает направление с AUC 0.54–0.55 — едва лучше монетки. Лосс‑ландшафт почти плоский, и куда именно скатится Adam из случайной инициализации — лотерея. В компьютерном зрении сигнал силён, все сиды сходятся к похожим решениям; в финансах каждая инициализация находит собственный способ переобучиться.

Отдельно замечу: torch.manual_seed фиксирует лишь часть недетерминизма — есть ещё порядок батчей в DataLoader, недетерминированные CUDA‑ядра, инициализация воркеров. Для воспроизводимости фиксировать нужно всё; для оценки шума — наоборот, честно варьировать.

Что с этим делать: протокол из трёх правил

Правило 1. Одиночный прогон не значит ничего. Торгует только ансамбль.

Усреднение вероятностей пяти моделей с разными сидами:

# каждый сид -> свой полный walk-forward -> свои OOS-вероятности
ensemble_prob = (
    pd.concat([run_walkforward(seed=s) for s in range(42, 47)])
      .groupby(["ticker", "ts"])["prob"].mean()
)

Результат на тех же пяти сидах из таблицы выше: Sharpe 1.88 — заметно лучше даже самого удачливого одиночного (1.28). В первом историческом замере эффект был ещё драматичнее: ансамбль 1.39 против лучшего сида 0.85 и худшего −0.12. Это не «среднее по больнице»: ошибки переобучения у разных сидов декоррелированы (я мерил попарные корреляции OOS‑вероятностей — они далеки от единицы), и усреднение гасит именно шумовую компоненту, оставляя общий сигнал. Классика ансамблей — но в финансах она не «немного улучшает», а отделяет случайность от результата.

Правило 2. Любая новая идея сравнивается ансамбль против ансамбля.

Хотите проверить новый признак? Пять моделей с ним против пяти без него. Да, впятеро дороже по GPU. Альтернатива — сравнивать одну случайную величину с другой и называть разницу «улучшением». У меня прижилось и второе усиление: если средний AUC пяти сидов не сдвинулся — портфельные метрики можно даже не считать, дальше эта идея не идёт.

Правило 3. Атрибуция: разбирайте, откуда именно пришла прибавка.

Самое коварное — когда правила 1–2 соблюдены, а бэктест всё равно обманывает. Инструмент — примитивный:

per_ticker = trades.groupby("ticker")["pnl"].sum()
# Прибавка пришла из бумаг, у которых нового признака НЕТ? Это шум.

Три кейса из моей практики:

Кейс 1. Позиции физлиц по фьючерсам. Признак существует только у 3 бумаг из 20 (у остальных в этой колонке нули). Доходность ансамбля выросла с +14% до +21.5%. Победа? Атрибуция: весь прирост — на 17 бумагах без признака, а три бумаги с признаком стали хуже. Признак не работал — он изменил размерность входа, перетасовал внутренний шум обучения, и новая случайность оказалась удачливее старой. На следующем переобучении «прибавка» испарилась бы.

Кейс 2. Дивидендный календарь. Близость отсечки, дивидендная доходность. Скрининг: Sharpe 2.16 → 2.49. Атрибуция: у дивидендных плательщиков суммарный минус, весь плюс — на одной бумаге, которая в тот период дивиденды не платила. Та же перетасовка, вид сбоку.

Кейс 3. Новости. Архив в 476 тысяч постов финансовых телеграм‑каналов, пять постановок: счётчики упоминаний, тональность (разметил rubert‑tiny2), типы событий (регэкспы по дивидендам/отчётности/санкциям), дневной календарь, реакция цены за 15 минут после поста. Две из пяти показали «прирост» на агрегатной цифре — обе рассыпались на атрибуции по тому же паттерну: прибавка живёт там, где признака нет. Самая злая деталь: у типов событий рухнуло всё сразу — AUC 0.519 против 0.544 у всех пяти сидов и Sharpe 0.38 против 2.16. Фоновые признаки вроде «на рынке санкционная неделя», константные внутри длинных окон, учат модель запоминать эпоху вместо сигнала. А две «удачные» постановки прошли скрининг ровно на перетасовке шума — и рассыпались на атрибуции.

64e56bbe30dba0ea7dec676d8bd30c64.png

Отсюда главное эвристическое правило: если улучшение пришло не оттуда, куда указывает механизм признака — это не улучшение, а шум. Агрегатная цифра бэктеста не является доказательством. Доказательство — сдвиг, который переживает ансамбль, атрибуцию и честный out‑of‑sample.

Бонусный уровень: эпохиd43cf4ab229a35d755450bdecff184ad.png

Даже идеальный по протоколу результат остаётся результатом своей эпохи. Я прогнал замороженную боевую систему (Sharpe 2.7 на 2021–2026) по данным 2015–2020 без перенастройки: минус 28%. Модель выучила режим рынка и в чужом режиме уверенно торгует прошлым.

Дальше интереснее: я повторил на 2015–2020 полный walk‑forward — то есть дал системе переобучаться, как она делает в бою еженедельно, но только на данных той эпохи. Результат: +17% вместо −28%. Вывод, который стоил мне пары бессонных ночей: главный риск — не «модель не работает в другую эпоху», а скорость адаптации на переходе между эпохами. Регулярное переобучение — не гигиена, а несущая конструкция. Плюс у системы есть автоматический «выключатель»: если трейлинг‑метрики нейронки уступают простой эталонной стратегии — капитал уходит в неё или в денежный рынок. Но это тема отдельной статьи.

Для читателя бэктестов вывод проще: спрашивайте не только «какой Sharpe», но и «на скольких эпохах».

Чек‑лист для чтения статей про ML‑трейдинг
  1. Что будет с результатом при смене random seed? (Автор не знает — дальше можно не читать.)

  2. По какой цене исполняются сигналы — не по той ли, по которой посчитаны?

  3. Есть ли зазор между train и test на длину горизонта метки?

  4. Какая комиссия зашита в цифры и есть ли она вообще?

  5. Проверялась ли система на другой рыночной эпохе?

  6. Если добавили признак и стало лучше — показана ли атрибуция: лучше стало ТАМ, где признак есть?

Шесть вопросов отсеивают ~95% красивых эквити.

Что дальше

Система сейчас торгует виртуально в реальном времени, осенью выпускаю её на реальные деньги — с публичным трек‑рекордом, чтобы самому не сжульничать. Все эксперименты (включая провальные — из ~35 гипотез выжило 6) документирую публично: каждый месяц буду показывать PnL как есть, включая минусы.

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


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

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Парадигма машинного обучения: специализированные нейросети ещё хуже решают математические проблемы ...05.4303-08-2026
2Как я первый в Казахстане измерил ИИ видимость банков (и что из этого вышло)08.0812-08-2026
3Нейросети: кому на самом деле принадлежат права на то, что вы там генерируете — разбираемся по-человечески-5609-07-2026
4Message to community07.6224-07-2026
5Нейросеть-автопилот вместо 400 Playwright-тестов0707-07-2026
6Как желание быстрее читать чужой код превратилось в войну с недетерминизмом LLM0528-06-2026
7Здравствуйте, Евгений Павлович. Я перехожу к самому важному и прагматичному ...07.3926-07-2026
8https://dzen.ru/a/anBP3WTg8Wn46JCl Народ часто спрашивает: «А на чём всё это крутить?». ...012.906-08-2026
9Линейная алгебра для нейросетей: векторы на практике08.5915-03-2026

Классификация: Мнения. Схожих патентов: 0. Схожих новостей: 9. Тональность: 0. Информативность: 8.99. Источник: habr.com.