20-дневный Donchian в спецификации выглядит безобидно. Агент пишет 20-барный скользящий максимум, бэктест стартует с первой строки parquet-файла, и первые 2 недели «20-дневный максимум» — это просто нарастающий максимум по тем барам, которые уже есть. На 4-й день это 4-дневный максимум. Стратегия, которую вы думали тестируете, — не та, что породила первый десяток исполнений.
У меня есть ноутбук по дневным барам ETHUSDT, где Sharpe на полной выборке равен 1.4. Отрежьте первые 20 сессий — и он падает до 0.6. Оставьте эти 20 и выбросьте остаток первого года — и он печатает 3.1. В завтра никто не утекал. Стратегия использовала прошлое, просто недостаточно прошлого, а отчёт принял эти недостроенные сделки за доказательство.
Пусть окно торгует, пока ещё наполняется
Обычная катастрофа — min_periods=1. Pandas посчитает скользящее среднее по одному бару, если вы ему позволите. Агенты делают это постоянно: NaN вверху фрейма ощущается как баг, а заполнить его — и unit-тест зеленеет.
20-дневный таргет realized-vol с 2 наблюдениями доходности — это взрыв размера. Дисперсия 2 чисел — это орёл или решка: вы её инвертируете, и первая позиция оказывается в несколько раз больше задуманного риска. Z-score относительно почти нулевого стандартного отклонения проделывает тот же трюк со знаком: кажется, у стратегии бритвенно точный вход, который больше никогда не появляется, как только окно становится честным.
Пробои хуже, потому что выглядят как настоящее правило. 20-барный канал на баре 6 — это 6-барный канал. Вы получаете кластер сделок сразу на старте, часто на спокойном участке, который не сработал бы у достроенного индикатора. Спокойные рынки чуть дрейфуют. Узкий канал стреляет. Кривая капитала получает фору, которую ни один более поздний год не повторяет.
Заполнение нулём и forward-fill выдумывают другую ложь. RSI, равный 0, читается как перепроданность. Отсутствующий ATR, равный 0, взрывает размер юнита, и бэктест послушно покупает склад. Я лучше позволю NaN убить заявку, чем дам 0 её выдумать.
Я до сих пор ловлю себя на том, что рисую график со строки 0, потому что пустой месяц слева на чарте выглядит незавершённым. Пустой месяц — это как раз честная часть.
Обрезать кривую капитала, сохранить позицию
Эта проходит код-ревью. Вы корректно считаете 20-дневные признаки, выбрасываете первые 20 строк из нарисованной кривой и публикуете с 21 января. Позиция 21 января была открыта 14 января. High-water трейлинг-стопа, счётчик пирамиды, кэш после этих сделок прогрева: всё это вошло в «чистую» выборку.
Катастрофа — бумажная стратегия, которую нельзя запустить с даты из статьи. Перезапустите с нулевой позиции на заявленной дате старта — и заявки первой недели не совпадут. Иногда бэктест уже в сделке, в которую бумажной системе нечем входить. Иногда бумажная система входит, потому что её lookback холодный, а бэктест сидит в стороне, потому что скрытый месяц уже взял пробой. Две стартовые книги, одна опубликованная дата старта.
Фолды walk-forward делают это тихо. У каждого тестового окна есть хвост lookback, который вы не рисуете. Не симулировать этот хвост — и первые тестовые сделки снова сделки незавершённого окна. Симулировать его и отчитываться только по тестовым дням — и позиция тестового дня обусловлена исполнениями, которые вы не показываете. Второй вариант может быть законным исследовательским выбором. Называть первое утро фолда холодным стартом, пока трейлинг-стоп уже жив с хвоста, — вот как два исследователя получают два Sharpe из одной конфигурации.
Прогревать в календарных днях, когда признак — не день
«Дайте ему 30 дней» оставляет признак недоопределённым. 30 календарных дней на 5-минутном Donchian — это тысячи баров сверх 20-периодного lookback. 30 дней на дневном признаке, которому нужно 20 сессий, — либо жирно, либо тонко, в зависимости от выходных и дыр.
Z-score ставки фондирования на одной из наших perp-площадок получил 30-дневный прогрев, потому что в спецификации стояло 30 периодов, а кто-то перевёл периоды в календарь. Фондирование там публикуется каждые 8 часов. 30 дней — это около 90 расчётов. 30 периодов — это 10 дней. Лишние 2 месяца прогрева случайно захватили неделю депега. Выкинув её, живое окно стало выглядеть спокойнее, чем реальная история стратегии. Люди винили чувствительность к дате старта. Прогрев съел режим.
А крипто против акций заставляет то же число менять смысл без правки параметра. 20 «дней» ETH включают субботы. 20 сессий акций — это почти календарный месяц. Скопируйте 20-дневный lookback из ноутбука по акциям на часовые свечи перпетуалов — и получите 20-часовой канал с 20-дневным именем. Первый валидный бар тогда либо через 20 часов, либо через 20 дней, если какая-то более поздняя функция ресемплирует, и я видел оба варианта в одном и том же diff агента.
Зафиксируйте первую торгуемую метку времени
Прогрев — это требование к данным, а не бонусная выборка. Каждый признак называет число завершённых наблюдений, которые ему нужны, в тех единицах, в которых он считается. Первая торгуемая метка времени — это первый бар, на котором всё это истинно и счёту с нулевой позицией было бы разрешено отправить заявку.
| Признак | Что обычно пишет спецификация | Что на самом деле должно существовать | Тихая подмена |
|---|---|---|---|
| 20-дневный Donchian | 20 дневных максимумов | 20 завершённых сессий | Скользящий максимум с min_periods=1 |
| 30-дневная realized vol | 30-дневное окно | 30 доходностей close-to-close | 2-дневная дисперсия, инвертированная в размер |
| z-score фондирования на 20 принтах | 20 дней | 20 расчётов фондирования | 20 календарных дней (~90 принтов) или 20 баров неверного таймфрейма |
| 14-периодный RSI на 5m | 14 периодов | 14 закрытых 5-минутных баров | 14 дней или 14 баров с дырами, заполненными forward-fill |
P&L до этой метки времени остаётся вне трек-рекорда. Состояние до этой метки времени всё равно может понадобиться симулировать, чтобы трейлинг-стоп или кулдаун были настоящими, когда часы доходят до первого торгуемого бара, но эти исполнения не идут в Sharpe, в число сделок и в расчёт ёмкости. Затем прогоните неприятную проверку: перезапуск с нулевой позиции на этой метке времени, lookback доступен только как данные. Если заявки меняются, вы выкатывали скрытые сделки.
Бумажная торговля стартует по тому же правилу. И каждое тестовое окно walk-forward тоже. Если график выглядит так, будто не хватает месяца, оставьте его недостающим.
← Все статьи


