Дисклеймер: Информация в этой статье предоставлена исключительно в образовательных и ознакомительных целях и не является финансовым, инвестиционным или торговым советом. Торговля криптовалютами сопряжена с высоким риском убытков.
Подпишитесь на нашу рассылку, чтобы получать эксклюзивную аналитику по AI-трейдингу и обновления платформы.
Вы оптимизировали стратегию. 12 параметров разделения, 9 мета-параметров — итого 21. Бэктест на 25 месяцах одной пары показывает PnL +3342% при MaxLev. Equity curve растёт почти без просадок. Sharpe выше 3. Всё выглядит идеально.
Вы запускаете бота. Через две недели стратегия сливает 18% капитала. Через месяц — 34%. Параметры, которые «работали» на исторических данных, оказались подогнаны под конкретную последовательность рыночных событий. Вы не нашли закономерность — вы запомнили шум.
Это классический overfitting. И единственный системный способ его обнаружить до запуска в продакшен — Walk-Forward Optimization (WFO).
Ловушка single train/test split
Стандартный подход: разбиваем данные на 70% train и 30% test. Оптимизируем на train, проверяем на test. Если результат положительный — запускаем.
Проблема: это один тест на одном разбиении. Результат зависит от того, где проведена граница. Сдвиньте границу на месяц — и out-of-sample PnL может измениться с +40% до -15%.
Три разных разбиения — три разных вывода. Какому верить? Ни одному. Один train/test split — это тот же single-point estimate, о проблемах которого мы писали в Monte Carlo Bootstrap. Вам нужна не одна проверка, а систематическая серия проверок на последовательных участках данных.
Именно для этого существует Walk-Forward Optimization.
Что такое Walk-Forward Optimization
WFO — это процедура последовательной оптимизации и проверки стратегии на скользящих (или расширяющихся) окнах данных. Идея: имитировать реальный процесс торговли, при котором вы периодически переоптимизируете параметры на доступных данных и затем торгуете до следующей переоптимизации.
Каждое «окно» состоит из двух частей:
In-Sample (IS) — период, на котором оптимизируются параметры
Out-of-Sample (OOS) — период, на котором проверяются найденные параметры без подгонки
Ключевое свойство: OOS-периоды не пересекаются и в совокупности покрывают значительную часть данных. Итоговая equity curve строится только из OOS-участков — это и есть честная оценка стратегии.
Anchored WFO (расширяющееся окно)
В anchored WFO начало train-периода фиксировано, а его конец расширяется с каждым окном:
Window 1: Train [2024-01] → Test [2024-04]
Window 2: Train [2024-01..04] → Test [2024-07] (growing train)
Window 3: Train [2024-01..07] → Test [2024-10]
Window 4: Train [2024-01..10] → Test [2025-01]
Window 5: Train [2024-01..2025-01] → Test [2025-04]
Преимущества:
Каждый последующий train-период содержит больше данных — оптимизация стабильнее
Ранние паттерны не теряются — они всегда в обучающей выборке
Проще реализовать
Недостатки:
Старые данные могут «разбавлять» актуальные паттерны
Если рынок структурно изменился — старые данные вредят
Train-период растёт неограниченно, увеличивая время оптимизации
Rolling WFO (скользящее окно)
В rolling WFO train-период фиксированной длины «скользит» по данным:
Window 1: Train [2024-01..06] → Test [2024-07..09]
Window 2: Train [2024-04..09] → Test [2024-10..12]
Window 3: Train [2024-07..12] → Test [2025-01..03]
Window 4: Train [2024-10..2025-03] → Test [2025-04..06]
Window 5: Train [2025-01..06] → Test [2025-07..09]
Преимущества:
Адаптация к текущему рыночному режиму
Постоянное время оптимизации
Старые, нерелевантные данные не влияют
Недостатки:
Меньше данных для обучения — выше дисперсия оптимальных параметров
Чувствителен к выбору длины окна
Может «забывать» редкие, но важные события (flash crashes)
Combinatorial Purged Cross-Validation (CPCV)
Продвинутый метод, предложенный Marcos Lopez de Prado. Данные разбиваются на N групп, из которых выбираются k для test. Ключевое отличие от обычной cross-validation — purging (удаление данных на границе train/test) и embargo (дополнительный зазор для предотвращения data leakage):
Числокомбинаций=(kN)
При N=10,k=2: 45 комбинаций train/test. Каждая комбинация даёт OOS-результат, и финальная оценка — среднее по всем комбинациям.
from itertools import combinations
import numpy as np
defcpcv_splits(n_groups: int, k_test: int, purge_pct: float = 0.01):
"""
Генерация CPCV splits с purging.
Args:
n_groups: число групп
k_test: число test-групп в каждом split
purge_pct: доля данных для purging (на границе train/test)
"""
groups = list(range(n_groups))
splits = []
for test_groups in combinations(groups, k_test):
train_groups = [g for g in groups if g notin test_groups]
splits.append({
"train": train_groups,
"test": list(test_groups),
"purge_groups": _get_purge_groups(train_groups, test_groups),
})
return splits
def_get_purge_groups(train, test):
"""Группы на границе train/test для purging."""
purge = set()
for t in test:
if t - 1in train:
purge.add(t - 1)
if t + 1in train:
purge.add(t + 1)
returnlist(purge)
CPCV лучше rolling WFO при малом количестве данных, но вычислительно дороже. Для стратегии с 21 параметром и 25 месяцами данных рекомендуем начать с rolling WFO, а CPCV использовать как дополнительную проверку.
Ключевые параметры WFO
Длина train-периода
Слишком короткий train — недостаточно данных для надёжной оптимизации. Слишком длинный — старые данные размывают текущие паттерны.
Эмпирическое правило: train должен содержать минимум 200-300 сделок. Если стратегия делает 2 сделки в день:
Tmin=2сделки/день300сделок=150дней≈5месяцев
Для крипты с её режимными переключениями рекомендуем не более 6-12 месяцев rolling window.
Длина test-периода
Test-период должен быть достаточным для статистически значимой оценки, но не слишком длинным — иначе параметры успевают деградировать.
Правило: test = 20-33% от train. Если train = 6 месяцев, test = 1.5-2 месяца.
Overlap (перекрытие)
В rolling WFO окна могут перекрываться. Overlap увеличивает число OOS-точек, но вносит корреляцию между оценками:
Без overlap:
Train [01..06] → Test [07..09]
Train [07..12] → Test [01..03]
С overlap 50%:
Train [01..06] → Test [07..09]
Train [04..09] → Test [10..12]
Train [07..12] → Test [01..03]
Рекомендация: overlap 50% по train-периоду — хороший баланс между количеством окон и независимостью оценок.
Частота реоптимизации
Определяет, как часто вы пересчитываете параметры. На крипторынке оптимальная частота — каждые 1-3 месяца. Более частая реоптимизация увеличивает риск overfitting на шум; более редкая — риск устаревания параметров.
Walk-Forward Efficiency Ratio и Degradation Rate
Walk-Forward Efficiency Ratio (WFER)
Ключевая метрика WFO — отношение OOS-доходности к IS-доходности:
WFER=PnLISPnLOOS
Интерпретация:
WFER
Интерпретация
> 0.8
Отличная робастность. Параметры переносятся на новые данные.
0.5 — 0.8
Приемлемая робастность. Стратегия работает, но с деградацией.
Высокий IS PnL, низкий/отрицательный OOS PnL — классический overfitting
Параметры сильно различаются между окнами — нет стабильного оптимума
WFER < 0.3 в большинстве окон — параметры не переносятся
Degradation rate сильно отрицательный — быстрая деградация
Подробнее о анализе устойчивости параметров — в статье Plateau analysis. Если оптимум «острый» (резко падает при малом изменении параметров) — это дополнительный сигнал overfitting.
Особенности WFO для криптовалют
Криптовалюты создают уникальные проблемы для WFO, которых нет на традиционных рынках.
Режимные переключения
Крипторынок переключается между радикально разными режимами: бычий тренд, медвежий тренд, боковик с высокой/низкой волатильностью. Параметры, оптимальные в одном режиме, могут быть убыточными в другом.
Решение: используйте rolling WFO (не anchored) с окном 4-6 месяцев. Это позволяет «забывать» старые режимы. Дополнительно — кластеризуйте данные по волатильности и проводите WFO отдельно по каждому кластеру.
Короткая история
Большинство альткоинов имеют менее 3 лет торговой истории. При train = 6 месяцев и test = 2 месяца вы получите всего 4-5 окон — статистически слабая оценка.
Решение: используйте CPCV вместо или в дополнение к rolling WFO. CPCV генерирует больше комбинаций из тех же данных. Для 10 групп и k=2: 45 комбинаций вместо 4-5 окон.
Структурные изменения ликвидности
Ликвидность криптопар нестационарна: пара может быть ликвидной 6 месяцев, затем объёмы падают в 10 раз. Параметры, оптимизированные на ликвидном рынке, не работают на неликвидном.
Решение: добавьте фильтр ликвидности в WFO pipeline. Исключайте окна, где средний дневной объём ниже порога. Проверяйте, что ликвидность в test-периоде сопоставима с train-периодом.
Влияние funding rates
Для фьючерсных стратегий с плечом funding rates могут кардинально изменить OOS-результат. Стратегия показывает +5% OOS за 2 месяца, но при 10x плече funding съедает 3.6%.
Подробный анализ влияния funding — в нашей статье Funding rates убивают ваш leverage. Обязательно учитывайте funding costs при оценке OOS PnL в WFO.
Многопараметрические стратегии: почему WFO критичен при 12+ параметрах
Стратегия с 21 параметром (12 separation + 9 meta) на 25 месяцах одной пары — это модель с колоссальным пространством поиска.
Проклятие размерности
Число комбинаций параметров растёт экспоненциально с числом параметров:
Combinations=∏i=1n∣Pi∣
Если каждый из 21 параметра принимает хотя бы 10 значений:
1021=10секстиллионовкомбинаций
Даже при Bayesian-оптимизации (подробнее в Координатный спуск vs Bayesian) вы исследуете ничтожную долю пространства. Вероятность того, что найденный оптимум — артефакт шума, а не реальная закономерность, растёт с числом параметров.
Формула Бонферрони для множественных сравнений
Если вы проверяете M комбинаций параметров, вероятность ложного «открытия» (найти хороший результат случайно):
P(false discovery)=1−(1−α)M≈1−e−αM
При α=0.05 и M=10000 испробованных комбинаций:
P≈1−e−500≈1.0
Гарантированно найдёте «работающие» параметры — которые на самом деле подогнаны под шум. Без WFO у вас нет способа отличить реальный edge от статистического артефакта.
Правило: число OOS-точек vs число параметров
Эмпирическое правило для доверия к WFO-результатам:
ParametersOOS trades>10
Для 21 параметра нужно минимум 210 OOS-сделок. Если ваш WFO генерирует меньше — результату нельзя доверять.
Стратегия с +3342% PnL@ML: 21 параметр, 25 месяцев данных. Допустим, 5 OOS-окон по 60 дней, 2 сделки/день — итого 5×60×2=600 OOS-сделок. Соотношение 600/21=28.6 — приемлемо, но только если WFER > 0.5.
Интеграция WFO с Optuna
В каждом окне WFO нужно оптимизировать параметры. Для 21 параметра grid search невозможен, координатный спуск неэффективен. Оптимальный выбор — Bayesian optimization через Optuna.
Важно: внутри WFO оптимизируйте Sharpe, а не PnL. Оптимизация по PnL находит параметры, которые максимизируют profit на конкретной последовательности сделок. Оптимизация по Sharpe находит параметры с лучшим соотношением доходности к риску — они робастнее OOS.
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
defplot_wfo_results(result: WFOResult, data: np.ndarray):
"""Визуализация результатов Walk-Forward Optimization."""
fig, axes = plt.subplots(3, 1, figsize=(16, 14))
ax = axes[0]
ax.plot(result.oos_equity, color='#4FC3F7', linewidth=1.5)
ax.axhline(1.0, color='#FF5252', linestyle='--', alpha=0.5, label='Break-even')
ax.set_title(f'OOS Equity Curve (WFER={result.wfer:.2f}, Sharpe={result.oos_sharpe:.2f})')
ax.set_ylabel('Equity')
ax.legend()
ax.grid(True, alpha=0.3)
ax = axes[1]
wfers = [w.wfer for w in result.windows]
colors = ['#69F0AE'if w >= 0.5else'#FFAB40'if w >= 0.3else'#FF5252'for w in wfers]
ax.bar(range(len(wfers)), wfers, color=colors, edgecolor='#1A237E', alpha=0.8)
ax.axhline(0.5, color='#E040FB', linestyle='--', label='Threshold (0.5)')
ax.axhline(0, color='gray', linestyle='-', alpha=0.3)
ax.set_title('Walk-Forward Efficiency Ratio по окнам')
ax.set_xlabel('Window')
ax.set_ylabel('WFER')
ax.legend()
ax = axes[2]
x = np.arange(len(result.windows))
width = 0.35
ax.bar(x - width/2, [w.is_pnl for w in result.windows],
width, label='IS PnL', color='#7C4DFF', alpha=0.7)
ax.bar(x + width/2, [w.oos_pnl for w in result.windows],
width, label='OOS PnL', color='#4FC3F7', alpha=0.7)
ax.set_title('In-Sample vs Out-of-Sample PnL')
ax.set_xlabel('Window')
ax.set_ylabel('PnL')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('wfo_results.png', dpi=150)
plt.show()
Практические рекомендации
Чек-лист перед запуском стратегии в продакшен
1. Запустите WFO (rolling + anchored)
Сравните результаты обоих режимов. Если rolling WFO проваливается, а anchored проходит — скорее всего, стратегия работает только на ранних данных.
2. Проверьте WFER по каждому окну
Не только aggregate WFER, но и каждое окно отдельно. Если 2 из 6 окон имеют WFER < 0 — это проблема, даже если aggregate > 0.5.
3. Сравните параметры между окнами
Если оптимальные параметры «прыгают» от окна к окну — стабильного edge нет. Используйте Plateau analysis для проверки стабильности оптимума.
4. Проверьте degradation rate
Сильно отрицательный degradation rate = параметры быстро теряют эффективность. Нужна более частая реоптимизация или пересмотр стратегии.
5. Примените Monte Carlo bootstrap к OOS-результатам
Aggregate OOS PnL — тоже single-point estimate. Примените Monte Carlo bootstrap к массиву OOS-доходностей для получения confidence intervals.
6. Учтите costs
OOS PnL должен учитывать комиссии, slippage и funding rates. Красивый OOS PnL без costs — иллюзия. Подробнее — Funding rates убивают ваш leverage.
Минимальные требования по данным
Число параметров
Min OOS trades
Min окон WFO
Min данных (2 сделки/день)
2-5
50
3
~6 месяцев
6-10
100
4
~12 месяцев
11-15
150
5
~18 месяцев
16-21
210
6
~24 месяца
22+
300+
8+
~36+ месяцев
Стратегия с 21 параметром и 25 месяцами данных
Вернёмся к вопросу из начала статьи: 21 параметр, оптимизированных на 25 месяцах данных одной пары. PnL@ML = +3342%. Как валидировать?
Шаг 1. Rolling WFO: train = 8 месяцев, test = 2 месяца, step = 2 месяца. Получим ~8 окон.
Шаг 2. Anchored WFO: первый train = 8 месяцев, test = 2 месяца. Получим ~8 окон.
Шаг 3. CPCV: 10 групп по ~2.5 месяца, k = 2. Получим 45 комбинаций.
Шаг 4. Для каждого метода проверить:
WFER >= 0.5?
Параметры стабильны между окнами?
Degradation rate приемлемый?
OOS trades / Parameters >= 10?
Шаг 5. Monte Carlo bootstrap по aggregate OOS returns. 5th percentile PnL > 0?
Если хотя бы один из этих тестов проваливается — стратегия с +3342% скорее всего переобучена. 21 параметр на 25 месяцах одной пары — это экстремально высокое соотношение параметров к данным. Без прохождения WFO доверия быть не может.
Дополнительно рекомендуем проверить эффективность стратегии с учётом PnL по активному времени — это даст понимание, какая часть +3342% обусловлена временем в позиции, а какая — реальным edge.
Заключение
Walk-Forward Optimization — не опция, а необходимость. Это единственный метод, который системно проверяет переносимость параметров на новые данные. Single train/test split — это лотерея. Полный бэктест на всех данных — это self-deception.
Ключевые выводы:
WFER < 0.5 = overfitting. Если out-of-sample PnL меньше половины in-sample — параметры подогнаны.
Стабильность параметров важнее максимума. Параметры, которые дают +15% в каждом окне, лучше параметров, которые дают +40% в одном и -10% в другом.
Rolling WFO для крипты. Режимные переключения делают anchored WFO менее надёжным. Rolling window 4-6 месяцев — оптимальный баланс.
Чем больше параметров — тем строже требования. 21 параметр требует минимум 210 OOS-сделок и 6+ окон WFO. Без этого результат не верифицируем.
WFO + Monte Carlo bootstrap + Plateau analysis — три слоя защиты от overfitting. Каждый слой ловит то, что пропускают остальные.
Стратегия, прошедшая WFO с WFER > 0.5 по всем окнам, стабильными параметрами и положительным 5th-percentile bootstrap — это стратегия, которой можно доверять реальные деньги. Всё остальное — curve fitting с красивым equity curve.
@article{soloviov2026walkforwardoptimization,
author = {Soloviov, Eugen},
title = {Walk-Forward Optimization: единственный честный тест стратегии},
year = {2026},
url = {https://marketmaker.cc/ru/blog/post/walk-forward-optimization},
version = {0.1.0},
description = {Почему single train/test split не защищает от overfitting, как walk-forward optimization системно проверяет робастность параметров, и почему стратегия с +3342\% PnL@ML на 21 параметре — бомба замедленного действия без WFO.}
}