Главная
#Математика и ML #Data Science #Time Series

Что такое Backtesting в Data Science и как проверять стратегию на прошлых периодах в 2026 году?

Backtesting — это способ проверить стратегию на прошлых периодах так, как будто мы жили в той точке времени и ещё не знали будущего. Для студента Data Science это одна из самых полезных тем, потому что она учит отличать красивый график от честной проверки. Пока модель существует в ноутбуке, очень легко получить впечатляющий результат просто потому, что в код случайно попало знание о будущем. Backtesting нужен именно для того, чтобы этот самообман пресечь.

Содержание
  1. Почему обычной валидации здесь недостаточно
  2. Главная мысль: backtesting — это симуляция deployment на истории
  3. Как выглядит минимальная структура backtesting
  4. Формула доходности шага стратегии
  5. Где чаще всего ломается backtesting
  6. Геометрический смысл backtesting
  7. Python: минимальный walk-forward backtesting
  8. Что важно вынести из темы
  9. Kaggle notebook по теме:

Интуиция здесь простая. Если стратегия говорит: “в такие моменты нужно покупать, в такие — продавать, а в такие — ничего не делать”, мы хотим понять, как она вела бы себя на реальной временной траектории. Не на случайно перемешанной таблице, не на данных после ручной подгонки, а в живом порядке событий: день за днём, неделя за неделей, месяц за месяцем.

Такие задачи лучше изучать как единый продуктовый контекст. Временная логика модели, способ валидации и прикладной сценарий должны собираться в одну систему, иначе стратегия выглядит убедительно только на графике, но не в реальной эксплуатации. Именно поэтому backtesting полезно разбирать не как частную технику из трейдинга, а как общий способ честно проверять решения, которые живут во времени.

В 2026 году backtesting важен не только в алготрейдинге. Он нужен в прогнозировании спроса, управлении запасами, рекламе, подписках, антифроде, кредитном скоринге, системах удержания, динамическом ценообразовании и вообще в любой задаче, где решение принимается последовательно во времени. Как только модель влияет на следующее действие системы, вопрос “как это работало бы на прошлом без утечки” становится центральным.

Хабровская схема временной проверки стратегии и ограничений обычной валидации

Почему обычной валидации здесь недостаточно

Если данные живут во времени, случайный train/test split часто даёт слишком оптимистичную картину. В train может попасть статистика из будущего, а test окажется подозрительно похожим на train по распределению. В результате модель проверяется не в условиях реального принятия решения, а в лаборатории, где будущее уже незаметно подсказывает ответ.

Backtesting идёт дальше обычной validation. Он не просто делит выборку, а имитирует реальную эксплуатацию стратегии: в каждый момент у нас есть только прошлые данные, на их основе принимается решение, после этого раскрывается новый факт реальности, и только затем стратегия движется дальше. Эта последовательность и делает проверку честной. Если хочется отдельно увидеть, как именно строится такая дисциплина на временных данных, полезно рядом посмотреть time-based validation без утечек.

Главная мысль: backtesting — это симуляция deployment на истории

Хороший backtest не отвечает на вопрос “можно ли красиво объяснить прошлое?”. Он отвечает на более жёсткий вопрос: “если бы стратегия реально жила в этих датах и видела только доступную на тот момент информацию, какой результат она бы показала?” Это и есть мост между математикой, Data Science и инженерной практикой.

По сути, backtesting — это историческая симуляция decision pipeline. Модель делает прогноз, стратегия переводит его в действие, после этого наступает наблюдаемый исход, затем мы считаем метрику качества и двигаемся к следующему шагу. Если хотя бы одно звено нарушает причинный порядок, backtest перестаёт быть backtest и превращается в ретроспективную иллюзию.

Хабровская техническая схема структуры backtesting и последовательных шагов оценки

Как выглядит минимальная структура backtesting

Пусть в момент времени (t) у нас есть признаки (x_t). Модель строит прогноз, а стратегия переводит его в действие: например, войти в позицию, удерживать ресурс, запустить кампанию удержания или изменить уровень запаса. Дальше приходит фактический результат периода (r_{t+1}), и мы можем оценить полезность решения.

Это похоже на очень дисциплинированную игру. На каждом шаге мы обязаны забыть всё, что произойдёт позже текущего момента. Именно из этой дисциплины рождается доверие к результату backtest.

Формула доходности шага стратегии

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

(\mathcal{D}_{\mathrm{train}}^{(k)}=\{(x_t, y_t): t \le \tau_k\}, \quad \mathcal{D}_{\mathrm{test}}^{(k)}=\{(x_t, y_t): \tau_k < t \le \tau_k + h\})

Раздел математики: временные ряды и статистическое моделирование.

Что означает каждый символ:

(\mathcal{D}_{\mathrm{train}}^{(k)}) — обучающее окно на шаге (k).

(\mathcal{D}_{\mathrm{test}}^{(k)}) — тестовое окно сразу после train.

(\tau_k) — правая граница обучающего окна.

(h) — длина горизонта тестирования.

Формула описывает принцип walk-forward backtesting: на каждом шаге train оканчивается в момент (\tau_k), а test начинается строго после него. Это защищает нас от подглядывания в будущее.

Численный пример: если train покрывает январь–июнь, а тест — июль, то на следующем шаге train может покрывать январь–июль, а тест — август. Так стратегия проходит по истории последовательно, без перемешивания времён.

С точки зрения оптимизации это очень важно. Если гиперпараметры подбираются на нечестной схеме, поверхность качества становится ложной. Мы оптимизируем не устойчивость стратегии, а её способность выигрывать в присутствии утечки. Walk-forward backtesting делает оптимизацию гораздо более дорогой вычислительно, но и гораздо более содержательной.

Хабровская визуализация сравнения качества стратегии и риска ошибки при неправильной проверке на истории

Где чаще всего ломается backtesting

Самая распространённая ошибка — look-ahead bias. Это ситуация, когда стратегия использует информацию, которая на самом деле стала известна только позже. Например, признак посчитан на полном датасете, нормализация обучена на всей истории, rolling-statistics захватили будущие даты, а target leakage замаскирован под innocuous feature engineering.

Вторая ошибка — подгонка под историю. Человек перебирает так много правил и параметров, что в итоге находит красивую траекторию просто случайно. Формально это напоминает переобучение в ML: стратегия идеально “объясняет” конкретную историю, но плохо переносится на новые периоды.

Третья ошибка — игнорирование транзакционных издержек, задержек исполнения, ограничений объёма, запаздывания данных и любых friction effects. Без этих факторов backtest может выглядеть блестяще, а в реальной системе развалиться при первом контакте с исполнением. Очень похожая логика возникает и там, где модель вмешивается в продуктовые эксперименты, поэтому полезно отдельно посмотреть, как правильно соединять A/B тесты и ML в продукте.

Геометрический смысл backtesting

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

Такой взгляд полезен и в не финансовых задачах. Например, стратегия управления запасами тоже имеет траекторию: дефицит, излишек, стоимость хранения, потери продаж. В продуктовой аналитике похожую динамику часто видно не в одном числе, а в разрезе групп и времени, поэтому рядом полезно посмотреть и как читать cohort analysis по группам удержания. Backtesting в таких системах помогает увидеть решение как геометрию пути, а не как одну усреднённую метрику в конце периода.

Python: минимальный walk-forward backtesting

import pandas as pd  # Подключаем pandas для работы с таблицей.
import numpy as np  # Подключаем numpy для численных операций.
from sklearn.linear_model import LinearRegression  # Берем простую модель прогноза как baseline.

df = pd.read_csv("backtest_data.csv")  # Загружаем исторические данные.
df["date"] = pd.to_datetime(df["date"])  # Преобразуем столбец даты в datetime.
df = df.sort_values("date").reset_index(drop=True)  # Сортируем строки по времени и сбрасываем индекс.

features = ["feature_1", "feature_2", "feature_3"]  # Задаем список признаков.
target = "next_return"  # Определяем целевую переменную: результат следующего периода.

train_window = 120  # Фиксируем длину train-окна в наблюдениях.
test_window = 20  # Фиксируем длину test-окна в наблюдениях.

strategy_returns = []  # Здесь будем накапливать доходности стратегии.

for start in range(0, len(df) - train_window - test_window + 1, test_window):  # Идем по истории последовательными окнами.
    train = df.iloc[start:start + train_window]  # Берем train-часть только из прошлого.
    test = df.iloc[start + train_window:start + train_window + test_window]  # Берем следующее тестовое окно.

    X_train = train[features]  # Формируем train-признаки.
    y_train = train[target]  # Формируем train-target.
    X_test = test[features]  # Формируем test-признаки.
    y_test = test[target]  # Берем фактический результат тестового окна.

    model = LinearRegression()  # Создаем новую модель на текущем шаге backtest.
    model.fit(X_train, y_train)  # Обучаем модель только на train-окне.

    preds = model.predict(X_test)  # Получаем прогнозы на ближайшее будущее.
    positions = np.where(preds > 0, 1.0, 0.0)  # Превращаем прогноз в простое действие: держать позицию или нет.
    step_returns = positions * y_test.to_numpy()  # Считаем результат стратегии на фактических исходах.

    strategy_returns.extend(step_returns.tolist())  # Добавляем результаты текущего окна в общую траекторию.

equity_curve = (1 + pd.Series(strategy_returns)).cumprod()  # Строим накопленную кривую капитала.
total_return = equity_curve.iloc[-1] - 1  # Считаем итоговую доходность стратегии.
max_drawdown = ((equity_curve.cummax() - equity_curve) / equity_curve.cummax()).max()  # Считаем максимальную просадку.

print("Total return:", round(float(total_return), 4))  # Печатаем итоговый результат.
print("Max drawdown:", round(float(max_drawdown), 4))  # Печатаем максимальную просадку.

Этот пример специально сделан простым. Но в нём уже есть всё главное: сортировка по времени, обучение только на прошлом окне, прогноз на следующем интервале, преобразование прогноза в действие и расчёт траектории стратегии. Именно так математика, алгоритм и код соединяются в одну инженерную процедуру.

Что важно вынести из темы

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

Если сформулировать совсем коротко, хороший backtest должен уважать причинность, учитывать реальный режим исполнения и измерять не только прибыль, но и риск траектории. Именно поэтому в зрелом Data Science backtesting — это не опциональная красивая надстройка, а обязательная проверка того, что стратегия вообще имеет право называться рабочей.

Kaggle notebook по теме:

https://www.kaggle.com/code/ihtishammehmood/backtesting-trading-strategies

Что читать дальше

Связанные статьи по этой теме

Инструменты Python в 2026 году: современный стек для профессиональной разработки Что должен знать Junior Data Scientist в 2026 году? Как собрать GitHub-портфолио для Data Science
Вернуться в блог