Дисклеймер: Информация в этой статье предоставлена исключительно в образовательных и ознакомительных целях и не является финансовым, инвестиционным или торговым советом. Торговля криптовалютами сопряжена с высоким риском убытков.
Подпишитесь на нашу рассылку, чтобы получать эксклюзивную аналитику по AI-трейдингу и обновления платформы.
Вы прогнали стратегию через бэктест. Получили PnL +42%, Sharpe 1.8, MaxDD -12%. Результаты выглядят отлично. Вы запускаете бота в продакшен, и через месяц обнаруживаете, что просадка уже -28%, а PnL стремится к нулю.
Что пошло не так? Дело не в баге и не в «изменившемся рынке». Дело в том, что вы приняли решение на основе одного числа — single-point estimate. Вы узнали, что стратегия показала +42%, но не узнали, насколько вы можете доверять этому числу.
Проблема single-point estimate
Бэктест на исторических данных — это один прогон по одной конкретной последовательности рыночных событий. Результат зависит от порядка сделок: та же стратегия с теми же сделками, но в другом порядке, может показать совершенно другую максимальную просадку.
Представьте 491 сделку. Каждая сделка — это случайное событие с определённым распределением доходности. Исторический бэктест показывает лишь одну реализацию этого процесса. Это как бросить кубик один раз и заключить, что кубик всегда выпадает четвёркой.
Что нам действительно нужно:
Не точечную оценку, а интервал: «с вероятностью 95% итоговый PnL будет от X до Y»
Не одну максимальную просадку, а распределение: «в 5% худших сценариев просадка превышает Z%»
Не среднее, а хвосты: что произойдёт, если удача не на вашей стороне?
Именно для этого существует Monte Carlo bootstrap.
Что такое Monte Carlo bootstrap
Bootstrap — это метод ресемплинга, предложенный Брэдли Эфроном в 1979 году. Идея элегантна: если у нас есть выборка данных, мы можем сгенерировать тысячи «новых» выборок, случайным образом выбирая элементы из исходной с возвращением (with replacement).
В контексте бэктеста это работает так:
У вас есть массив доходностей по каждой сделке — например, 491 значение
Вы случайным образом выбираете из этого массива 491 значение с возвращением — некоторые сделки попадут дважды, некоторые не попадут вовсе
Строите equity curve из этой новой выборки
Повторяете 10 000 раз
Получаете распределение итоговых метрик, а не одно число
Каждая итерация — это один «альтернативный сценарий»: что могло бы случиться, если бы порядок и набор сделок были немного другими.
Бэктест показал PnL +42% — но в 5% худших сценариев PnL всего +18.3%
Бэктест показал MaxDD -12% — но в 5% худших сценариев просадка -23.4%
Sharpe 1.8 — но нижняя граница 1.12
5th percentile — это ваш «реалистичный худший случай». Если стратегия перестаёт быть прибыльной уже на 5-м перцентиле, запускать её в продакшен рискованно.
Визуализация: fan chart
Monte Carlo bootstrap естественно визуализируется как fan chart — веер эквити-кривых:
Fan chart даёт интуитивное понимание разброса возможных исходов. Узкий веер — стратегия стабильна. Широкий — результат сильно зависит от «везения» с последовательностью сделок.
Fan chart (слева) показывает веер возможных траекторий эквити, а гистограмма (справа) — плотность распределения итоговой доходности с выделенными доверительными интервалами (5%, 50%, 95%).
Продвинутый анализ: probability of ruin
Bootstrap позволяет ответить на критический вопрос: какова вероятность, что стратегия потеряет X% капитала?
Эти метрики невозможно получить из одного прогона бэктеста. А они критичны для принятия решения о запуске стратегии.
Подробнее о том, почему глубокие просадки математически опасны и как работает асимметрия доходностей, читайте в нашей статье Асимметрия убытков и прибылей.
Когда классический bootstrap не работает
У метода есть ограничения, о которых важно знать.
Автокорреляция доходностей
Классический bootstrap предполагает, что сделки независимы. В реальности это часто не так — стратегия может иметь серии выигрышных и проигрышных сделок (streaks). Если автокорреляция значима, используйте block bootstrap:
defblock_bootstrap(returns, block_size=10, n_simulations=10000):
"""Bootstrap с сохранением локальной структуры зависимостей."""
n = len(returns)
results = []
for _ inrange(n_simulations):
starts = np.random.randint(0, n - block_size + 1, size=n // block_size + 1)
sampled = np.concatenate([returns[s:s+block_size] for s in starts])[:n]
equity = np.cumprod(1 + sampled)
results.append({
"final_pnl": equity[-1] - 1,
"max_dd": max_drawdown(equity),
})
return pd.DataFrame(results)
Block bootstrap сохраняет локальные зависимости между последовательными сделками, давая более реалистичные confidence intervals для MaxDD.
Нестационарность рынка
Bootstrap работает с исходным распределением сделок. Если рынок структурно изменился (например, упала волатильность или изменилась ликвидность), исторические сделки могут быть нерепрезентативны. Для учёта этого:
Используйте скользящее окно: bootstrap только по последним N сделкам
Разбивайте данные по рыночным режимам и делайте bootstrap отдельно
Малое количество сделок
Bootstrap надёжен при n > 30 сделок. Если у вас 10 сделок — никакой ресемплинг не спасёт. 491 сделка — отличная выборка, результатам можно доверять.
Сравнение подходов к оценке робастности бэктеста
Метод
Что даёт
Сложность
Время
Когда использовать
Single backtest
Одну точечную оценку
Минимальная
Секунды
Никогда как финальный результат
Walk-forward
Out-of-sample метрики
Средняя
Минуты
Для проверки переобучения
Monte Carlo bootstrap
Confidence intervals
Минимальная
~2 сек
Всегда перед продакшеном
Monte Carlo path
Новые ценовые пути
Высокая
Минуты–часы
Для стресс-тестирования
Cross-validation
Средние метрики по фолдам
Средняя
Минуты
Для подбора параметров
Monte Carlo bootstrap — единственный метод, который за минимальное время даёт полную картину рисков.
Чек-лист: интерпретация результатов
Вот как мы рекомендуем интерпретировать результаты Monte Carlo bootstrap:
✅ Запускаем в продакшен, если:
PnL на 5-м перцентиле положительный
MaxDD на 5-м перцентиле приемлемый для вашего риск-аппетита
Probability of ruin < 1%
Sharpe на 5-м перцентиле > 0.5
⚠️ Дорабатываем, если:
PnL на 5-м перцентиле около нуля
MaxDD на 5-м перцентиле заметно больше, чем на 50-м
Широкий разброс fan chart — стратегия нестабильна
🛑 Не запускаем, если:
PnL на 5-м перцентиле отрицательный
Probability of ruin > 5%
Confidence interval для Sharpe включает 0
Наш опыт в marketmaker.cc
В marketmaker.cc мы разрабатываем собственный backtest-движок, и Monte Carlo bootstrap — неотъемлемая часть нашего пайплайна. Каждая стратегия проходит через bootstrap автоматически перед допуском к live-торговле.
Мы интегрировали bootstrap прямо в движок бэктеста: после прогона вы получаете не просто итоговый PnL, а полный отчёт с confidence intervals, fan chart, probability of ruin и сравнением block vs. standard bootstrap. Это занимает дополнительные 2-3 секунды — ничтожная цена за понимание реальных рисков.
Из нашего опыта: примерно 30% стратегий, которые выглядят привлекательно по single-point estimate, отсеиваются после Monte Carlo bootstrap. Их 5-й перцентиль PnL уходит в отрицательную зону или MaxDD оказывается неприемлемым. Без bootstrap эти стратегии попали бы в продакшен и с высокой вероятностью принесли бы убытки.
Заключение
Monte Carlo bootstrap — это ~10 строк кода и ~2 секунды вычислений. Он превращает одно число из бэктеста в полноценное распределение с confidence intervals. Это, пожалуй, самый высокий ROI из всех инструментов квантового анализа:
Если вы ещё не используете bootstrap — добавьте его в свой пайплайн сегодня. Это единственный способ узнать, насколько вы можете доверять результатам бэктеста.
@software{soloviov2026montecarlobootstrap,
author = {Soloviov, Eugen},
title = {Monte Carlo Bootstrap: как получить confidence intervals для бэктеста за 10 строк кода},
year = {2026},
url = {https://marketmaker.cc/ru/blog/post/monte-carlo-bootstrap-backtest},
version = {0.1.0},
description = {Почему single-point estimate из бэктеста — опасная иллюзия. Как Monte Carlo bootstrap за 2 секунды вычислений даёт 95% confidence interval для PnL и MaxDD, и почему это обязательный шаг перед запуском стратегии в продакшен.}
}