100 млн бэктестов BTC: как отличить навык от удачи
Суть идеи
Авторы из Rulyfi поставили масштабный эксперимент: сгенерировали и прогнали ~99.9 млн вариаций стратегий на BTC/USDT, чтобы эмпирически продемонстрировать проблему множественного тестирования. Результат предсказуем, но цифры отрезвляют: 336 818 стратегий прошли стандартный тест на значимость (PSR > 0.95), то есть их Sharpe статистически отличался от нуля. Но когда применили Deflated Sharpe Ratio (DSR) — осталось лишь 3 130, меньше 0.003%.
DSR — это поправка Байли–Лопес де Прадо. Идея простая: если ты тестируешь N стратегий, лучший Sharpe из них будет выше нуля уже чисто по законам статистики. DSR поднимает планку сравнения не с нулём, а с тем Sharpe, который ты ожидал бы получить от случайного победителя при N попытках. Важно: эта планка растёт лишь логарифмически с N — то есть даже 100 млн тестов не делают задачу принципиально неразрешимой, просто требуют честного учёта.
Формально DSR также учитывает негауссовость доходностей (скос и эксцесс) и длину серии — оба фактора критичны для крипты, где хвосты жирнее, чем в акциях, а история коротче.
Применимость к крипте
Прямее некуда: данные — BTC/USDT, не какие-то синтетические акции. Всё, что описано, ложится на наш стек без адаптации. Для опционных стратегий (VRP, skew-carry, gamma-flip фильтры) проблема множественного тестирования стоит ещё острее: параметров больше, независимых событий меньше, а история IV на крипте — от силы 5-6 лет. Каждый раз, когда мы перебираем пороги по GEX, delta-страйки или окна rolling-z, мы неявно прогоняем десятки вариантов. DSR — это именно тот инструмент, который должен стоять на выходе такого перебора.
Для on-chain сигналов (STH-MVRV, SOPR, Puell) ситуация аналогична: циклов мало, параметров много, и PSR там будет систематически завышен.
Тестируемо ли
Высокая реализуемость. В нашем Python bt_engine DSR можно добавить как финальный фильтр поверх walk-forward: считаем число протестированных конфигураций N, вычисляем ожидаемый максимальный Sharpe при N попытках, и сравниваем с ним OOS-Sharpe лучшей стратегии. Формула открытая (Bailey et al., 2014), реализация — несколько строк. Единственное, что нужно честно считать — это реальное N, включая все «ручные» итерации, которые исследователь делал до финального прогона. Именно здесь большинство команд жульничают (осознанно или нет).
Сомнения
Статья методологически честная, но у неё есть слепое пятно: 100 млн стратегий — это, судя по всему, параметрические вариации ограниченного числа семейств (скользящие средние, пробои и т.п.). Если все они коррелированы между собой, эффективное N значительно меньше номинального, и DSR-порог окажется завышен. Авторы, судя по аннотации, не раскрывают структуру пространства стратегий — а это ключевой вопрос. Кроме того, DSR предполагает, что все N стратегий тестировались на одних и тех же данных; если часть прогонов была на разных периодах, поправка некорректна.
Второй момент: 3 130 «выживших» стратегий — это ещё не доказательство навыка, это лишь нижняя граница отсева. OOS-тест на свежих данных (2024–2025) для этих 3 130 — вот что было бы по-настоящему интересно.
Следующий шаг
Взять наш текущий grid-search по параметрам rolling-z для VRP-стратегии (окно, порог входа, горизонт удержания), зафиксировать N протестированных конфигураций, добавить DSR-фильтр на выходе walk-forward и сравнить: сколько конфигураций проходят PSR > 0.95 vs DSR > 0.95. Если разрыв большой — у нас проблема с data-snooping прямо сейчас.
Результаты тестов
Дописано 2026-08-07 — прогнали «Следующий шаг» буквально, на BTC и ETH. Разрыв оказался максимальным из возможных: PSR пропускает до половины меню, DSR не пропускает никого. Но главное не это. Мы измерили то, что статья назвала своим открытым вопросом — эффективное число испытаний — и ответ противоположен ожидаемому: наши 504 конфигурации требуют такой же планки, как их 100 миллионов.
Спека выполнена как написана. VRP = DVOL (30-дневная подразумеваемая вола Deribit) минус RV30 (реализованная из дневного спота). Сигнал — rolling-z этого VRP, три оси ровно из спеки плюс ось «вкуса» z: окно L {30,45,60,90,120,180,250} × порог θ {0, 0.25, 0.5, 0.75, 1, 1.5} × горизонт удержания H {1,2,3,5,10,20} × z {классический / «scaled» без вычитания среднего} = 504 конфигурации, зафиксированы до прогона. Окно 2021-03-24 (старт DVOL) … 2026-07-31, 1956 дней. Walk-forward катящийся: 365 дней IS → 90 дней OOS, 15 фолдов, OOS-отрезок с 2022-12-29 (1311 дней). Вход на следующий день после сигнала.
Прогнали два рукава — с раздельными меню, потому что DSR требует N того поиска, который породил победителя:
- A · long спот при богатом VRP — наш текущий живой рычаг, издержки 6 б.п. с оборота;
- B · шорт варианса — собственно сбор премии волы, дневной mark-to-market (IV²−RV²)/2IV, издержки 0.5 вол-пункта с оборота.
Разрыв: 245 против нуля
| Рукав (OOS, 1311 дней) | лучший SR | прошли PSR > 0.95 | прошли DSR > 0.95 |
|---|---|---|---|
| BTC · long спот | +1.39 | 212 / 504 (42%) | 0 |
| BTC · шорт варианса | +2.35 | 245 / 504 (49%) | 0 |
| ETH · long спот | +1.26 | 77 / 504 (15%) | 0 |
| ETH · шорт варианса | +0.55 | 0 / 504 | 0 |
У авторов из 336 818 прошедших PSR выжило 3 130 — 0.93%. У нас выживает ровно ноль во всех четырёх рукавах. Ответ на вопрос «если разрыв большой — у нас проблема с data-snooping прямо сейчас»: разрыв максимальный, проблема есть.
Оба обязательных контроля отработали, и они же показывают цену фильтра:
| Мир (20 / 5 независимых прогонов) | PSR > 0.95 | DSR > 0.95 |
|---|---|---|
| ПУСТЫШКА BTC — чистый шум, эджа нет | 25 в среднем, до 235 | 0.0 (макс 0) |
| ПУСТЫШКА ETH — то же | 29 в среднем, до 270 | 0.0 (макс 0) |
| Внедрён ИСТИННЫЙ эдж ann-SR 1.5, BTC | 254 | 8.8 (макс 43) |
| Внедрён ИСТИННЫЙ эдж ann-SR 1.5, ETH | 239 | 5.0 (макс 25) |
Верхние две строки — приговор стандартному тесту: на данных, где эджа нет вообще, PSR в отдельных мирах пропускает до 270 конфигураций из 504. DSR не пропускает ни одной ни разу. Но нижние две строки — честная цена: когда истинный эдж ann-SR 1.5 есть, DSR находит его лишь в 5–9 конфигурациях из 504, а на выходе WF-отбора даёт 0.44–0.48, то есть отвергает настоящую стратегию. DSR — не бесплатный фильтр, а очень тупой нож: на нашей длине истории он режет ложное вместе с настоящим.
Слепое пятно статьи: эффективное N больше номинального, а не меньше
Главное возражение в «Сомнениях» — если конфигурации коррелированы, эффективное N много меньше номинального, и DSR-порог завышен. Это проверяемо: ротационный нуль (общий сдвиг драйвера при сохранённых позициях) даёт E[max SR] прямо по реальному меню, сохраняя корреляцию испытаний, — и не требует догадок о числе независимых попыток.
Получилось наоборот. Эмпирический максимум под нулём выше аналитического порога во всех четырёх рукавах:
| Рукав | E[max SR] нуля | порог DSR при N=504 | промах | = разброс | × форма |
|---|---|---|---|---|---|
| BTC · long спот | +1.45 | 0.76 | ×1.91 | ×1.34 | ×1.42 |
| BTC · шорт варианса | +2.66 | 1.66 | ×1.60 | ×0.91 | ×1.77 |
| ETH · long спот | +1.10 | 0.82 | ×1.34 | ×1.10 | ×1.22 |
| ETH · шорт варианса | +1.55 | 0.70 | ×2.20 | ×1.55 | ×1.42 |
Промах раскладывается на два канала: sr0 = sd_SR · m(N). Канал разброса — насколько sd_SR, замеренный по реальному меню, отличается от разброса под нулём — гуляет в обе стороны (×0.91…×1.55) и общей причиной быть не может. А канал формы — насколько гауссова порядковая статистика m(N) ≈ √(2·lnN) недооценивает реальный максимум при том же разбросе — положителен всегда: ×1.22…×1.77. Формула написана для N независимых нормальных испытаний, а у нас H-дневные удержания и жирные хвосты драйвера; максимум такого меню систематически выше гауссова.
Дальше начинается арифметика логарифма. Множитель формулы при N=504 равен 3.05, эмпирический — 3.7–5.4. Разница выглядит скромно, но N сидит под логарифмом, и чтобы поднять множитель до эмпирического, объявленное число испытаний должно стать таким:
| Рукав | нужное N вместо 504 |
|---|---|
| ETH · long спот | 5 963 |
| BTC · long спот | 83 051 |
| ETH · шорт варианса | 75 909 |
| BTC · шорт варианса | 16 381 895 |
То есть наш поиск из 504 конфигураций требует ровно той планки, которую статья получает на 100 миллионах. Число бэктестов — не то, что задаёт порог. Это тот же вывод, к которому мы пришли на прошлой статье серии через другой рычаг (там ручкой оказался sd_SR), но здесь он жёстче: даже подставив в формулу правильный разброс, взятый прямо из нуля, промах никуда не девается — виновата сама гауссова посылка.
Практический вывод: не «считайте N честно», как советует статья, а не считайте N вообще. Ротационный нуль даёт порог напрямую по вашему меню, с его реальной корреляцией, разреженностью и хвостами, и не требует ни числа испытаний, ни предположения о нормальности.
И заодно: нашего VRP-эджа в тайминге нет
Раз уж меню построено, полезно спросить не «сколько конфигураций красивы», а «нашёл ли наш поиск навык». Правильная процедура: каждый фолд выбираем лучшую по IS-Sharpe из 504 и торгуем её OOS — это то, что реально было бы задеплоено. Сравниваем с нулём самой процедуры отбора (тот же отбор на сдвинутом драйвере):
| Рукав | SR отбора, OOS | нуль процедуры | p_emp | PSR | DSR |
|---|---|---|---|---|---|
| BTC · long спот | +0.57 | +0.22 | 0.29 | 0.864 | 0.358 |
| BTC · шорт варианса | +1.49 | +1.82 | 0.74 | 0.989 | 0.395 |
| ETH · long спот | +0.36 | +0.05 | 0.24 | 0.757 | 0.193 |
| ETH · шорт варианса | +0.28 | +0.85 | 0.80 | 0.687 | 0.232 |
Ни один рукав не значим. А в обоих вариансных рукавах нуль обгоняет реальность: сдвиг драйвера сохраняет положительный снос премии волы, но разрушает тайминг — и результат становится лучше. Значит наш rolling-z тайминг на VRP не просто бесполезен, он активно вредит против «постоянно быть в шорте волы». Отдельно отметим: BTC · шорт варианса даёт PSR 0.989 — по стандартному тесту это уверенно «работающая стратегия», которая на деле хуже собственного нуля.
Две оговорки. Первая: у конфигурации с фиксированными параметрами внутри неё нечего переподбирать, поэтому «выход walk-forward» в поконфигурационной таблице — это её ряд после первого IS-окна; настоящий walk-forward живёт в таблице отбора выше. Вторая: контроль корректности нуля (EW всего меню должен лечь на медиану) прошёл на вариансных рукавах (35-й и 11-й перцентиль), но на long-спотовых дал 80-й и 91-й — часть результата там держится на безусловном сносе BTC/ETH, который ротация воспроизводит не полностью. Для спотовых рукавов это делает оценку скорее консервативной, вывод не меняет.
Скрипт: research_dsr_vrp_gate.py (пре-регистрация осей — в шапке файла, до прогона).