Стратегия mean reversion: возврат к среднему — как работает и где торгуется
Mean reversion — это идея, что цена колеблется вокруг своего среднего, и большие отклонения временные. Стратегии возврата к среднему — основа парного трейдинга, статистического арбитража, торговли по Боллинджеру и Z-score. Разбираем, почему это работает, на чём проверить (тест Дики-Фуллера на стационарность), как реализовать на Python и где главная ловушка — момент, когда среднее само начинает двигаться.
Что такое mean reversion
Mean reversion (возврат к среднему) — концепция, что временно́й ряд отклоняется от своего среднего, но эти отклонения не накапливаются: рано или поздно ряд возвращается к норме. Если это так — можно зарабатывать, продавая инструмент при отклонении вверх и покупая при отклонении вниз, ожидая возврата.
Противоположная идея — momentum / trend-following: цена движется в одну сторону, тренд продолжается. Эти два подхода почти зеркальны и работают на разных типах временны́х рядов. Главная задача алготрейдера — определить, к какому типу относится конкретный инструмент в конкретный период.
В чистом виде mean reversion работает на разностях, а не на ценах: пары активов, спреды между фьючерсом и спотом, кросс-биржевые корзины. На обычной цене акции долгосрочный mean reversion не работает — акция может расти годами и никогда не вернуться к средней. На разности же двух связанных акций (Сбер vs ВТБ) такая возвращаемость есть.
Почему работает: статистический фундамент
Mean reversion опирается на свойство стационарности временного ряда. Стационарный ряд — это ряд, у которого среднее, дисперсия и автокорреляция не меняются во времени. На таком ряду имеет смысл говорить о «среднем», к которому идёт возврат.
Формальная проверка — тест Дики-Фуллера (Augmented Dickey-Fuller, ADF). Гипотеза:
- H₀: ряд не стационарный (есть единичный корень).
- H₁: ряд стационарный.
Если p-value < 0.05 — отвергаем H₀, считаем ряд стационарным и применимым для mean reversion. Если p-value > 0.05 — стратегия применима с осторожностью или не применима вовсе.
from statsmodels.tsa.stattools import adfuller
def is_stationary(series, alpha=0.05):
result = adfuller(series.dropna())
return result[1] < alpha, result[1]
# Пример: проверка спреда двух акций
spread = sber_close - 5 * vtb_close
ok, pvalue = is_stationary(spread)
print(f"стационарность: {ok}, p-value={pvalue:.4f}")
Если спред стационарен — пара подходит для парного трейдинга (статистического арбитража). Если нет — придётся либо переподобрать коэффициент 5, либо искать другую пару.
Базовые реализации
Bollinger Bands reversion
Самая простая mean reversion-стратегия. Bollinger Bands — это скользящее среднее ± k стандартных отклонений (k = 2 по умолчанию).
Правила:
- Цена закрылась ниже нижней полосы → покупка.
- Цена закрылась выше верхней полосы → продажа (для лонг-онли — закрытие позиции).
- Возврат к скользящей средней → выход.
import pandas as pd
def bollinger_signals(df, n=20, k=2.0):
df = df.copy()
df["mean"] = df["close"].rolling(n).mean()
df["std"] = df["close"].rolling(n).std()
df["upper"] = df["mean"] + k * df["std"]
df["lower"] = df["mean"] - k * df["std"]
df["signal"] = 0
df.loc[df["close"] < df["lower"], "signal"] = 1 # buy
df.loc[df["close"] > df["upper"], "signal"] = -1 # sell
return df
Подходит для флэтового рынка. На трендах даёт серию убытков, потому что цена будет «ехать» по верхней полосе вверх (или нижней вниз) — бот будет шортить рост и покупать падение, теряя на каждом сигнале.
Z-score reversion
Z-score — нормализация отклонения от среднего:
z = (x − rolling_mean) / rolling_std
Z = 2 означает «два стандартных отклонения вверх» — статистически редкое событие. Сигналы:
- z > 2 → шорт (или закрытие лонга).
- z < −2 → лонг.
- |z| < 0.5 → выход (близко к среднему).
def zscore_signals(series, n=30, entry=2.0, exit=0.5):
mean = series.rolling(n).mean()
std = series.rolling(n).std()
z = (series - mean) / std
signal = pd.Series(0, index=series.index)
signal[z > entry] = -1
signal[z < -entry] = 1
signal[(z > -exit) & (z < exit)] = 0 # close
return signal, z
Преимущество перед Bollinger Bands — гибкие пороги: разные entry и exit для входа и выхода.
RSI reversion
RSI(2) на дневках — классическая mean-reversion стратегия Ларри Коннорса:
- RSI(2) < 10 → покупка.
- RSI(2) > 90 → продажа.
Короткий период RSI делает индикатор чувствительным к экстремальным движениям. Стратегия работает на индексных ETF (SPY, QQQ) и крупных blue-chip акциях; на сырьё и крипту — нет, там часто настоящие тренды.
Парный трейдинг (статистический арбитраж)
Самая надёжная разновидность mean reversion. Берём две сильно коррелирующих бумаги (Сбер и ВТБ, Лукойл и Роснефть, Северсталь и НЛМК), считаем линейную регрессию одной на другую:
ВТБ_t = α + β × Сбер_t + ε_t
Спред ε_t = ВТБ_t − α − β × Сбер_t должен быть стационарным (ADF p-value < 0.05). Если да — торгуем по Z-score этого спреда:
- z(спред) > 2 → шорт ВТБ + лонг (β × Сбер) (спред слишком большой, ждём сужения).
- z(спред) < −2 → лонг ВТБ + шорт (β × Сбер) (спред слишком маленький, ждём расширения).
from sklearn.linear_model import LinearRegression
import numpy as np
# Подбор коэффициента
X = sber_prices.values.reshape(-1, 1)
y = vtb_prices.values
reg = LinearRegression().fit(X, y)
alpha, beta = reg.intercept_, reg.coef_[0]
spread = vtb_prices - alpha - beta * sber_prices
ok, p = is_stationary(spread)
if ok:
z = (spread - spread.rolling(60).mean()) / spread.rolling(60).std()
# торгуем z по уже знакомым правилам
Главное преимущество — рыночно-нейтральная позиция: лонг + шорт практически равных размеров, движение всего рынка не влияет на P&L. Стратегия зарабатывает только на отклонениях пары.
Размер позиции и риск
В mean reversion стратегиях характер риска отличается от trend-following:
- Серии маленьких прибылей + редкие большие убытки, когда «средне-возвращаемость» внезапно ломается. Это противоположная trend-following кривая P&L.
- Просадки короткие, но глубокие в момент структурного сдвига (изменение фундаментального соотношения активов).
Размер позиции:
risk_money = depo × 1% # умеренный риск из-за частоты сделок
size = risk_money / |entry − stop|
Стоп-лосс ставится на сильное отклонение от точки входа:
- Для Bollinger — пробой 3-σ полосы.
- Для Z-score — выход за |z| > 4.
- Для парного трейдинга — рост спреда на 1.5-кратное стандартное отклонение от точки входа.
Тейк — возврат к среднему: |z| < 0.5 или пересечение скользящей средней.
Бэктест: как тестировать без overfit
Главная ошибка в mean reversion-бэктесте — подгонка параметров на исторических данных. С 5 параметрами (период, k, entry-z, exit-z, stop-z) и 1000 свечей вы найдёте настройку, которая показывает 200% годовых на бумаге. На реальных данных в следующем месяце она сольёт.
Защитные практики:
- Walk-forward анализ: окно обучения 1 год → окно теста 3 месяца → сдвиг → повтор. На каждом шаге переоптимизируем параметры на обучающем окне и проверяем результат на тестовом.
- Out-of-sample датасет: 70% истории — для подбора параметров, 30% — для финальной оценки. Параметры из первой части не трогаем при просмотре результатов на второй.
- Ограниченный набор параметров: чем меньше параметров, тем меньше overfit. Для Z-score достаточно
(period, entry, exit)= 3 параметра. Не вводите 5-й, если 3 уже дают стабильный результат. - Множественная проверка: тестируем не на одной бумаге, а на 10–20 однотипных. Если стратегия работает на 15 из 20 — это паттерн. Если на 4 из 20 — overfit на лучших.
# Walk-forward скелет
def walk_forward(prices, train_size=252, test_size=63):
results = []
for start in range(0, len(prices) - train_size - test_size, test_size):
train = prices.iloc[start:start + train_size]
test = prices.iloc[start + train_size:start + train_size + test_size]
params = optimize(train)
pnl = backtest(test, params)
results.append({"start": test.index[0], "pnl": pnl, "params": params})
return pd.DataFrame(results)
Где работает mean reversion на MOEX
- Парный трейдинг внутри сектора: Сбер—ВТБ, Лукойл—Роснефть, Северсталь—НЛМК—ММК, Магнит—X5. Высокая корреляция фундаменталий, временные расхождения цен.
- Спред между акцией и фьючерсом (например, SBER vs
SiMдля USD/RUB не подходит, ноSBRFфьючерс на Сбер и обыкновенные акции — подходит). - Облигации одного эмитента с разной дюрацией — рынок неэффективен на длинных сериях, спред между 3-летней и 5-летней ОФЗ часто mean-revertable.
- Внутридневные пары на ликвидных голубых фишках: экстремальные движения в первые 30 минут сессии часто откатываются к середине дня.
Где не работает:
- Узколиквидные второэшелонные акции — спреды большие, проскальзывание съедает edge.
- Криптовалюты в тренде — на бычьем/медвежьем 6-месячном движении mean reversion на цене теряет смысл.
- Бумаги с регуляторными рисками (санкции, расследования) — структурные сдвиги ломают стационарность.
Подводные камни
- «Не средне-возвращаемая» серия. Цена ушла в новую парадигму (новые фундаменталии) и не возвращается. Bot открывает лонг на падении, цена падает дальше, бот добавляет позицию, цена ещё ниже. Без жёсткого стопа — обнуление депозита.
- Сдвиг среднего. Среднее само по себе движется (по Brownian motion), и mean reversion работает только относительно текущего среднего. Это правильно учитывается через скользящее среднее, не статическое.
- Корреляция в стрессе. В кризисах все корреляции «диверсификации» обнуляются — пары перестают быть парами, спред расходится надолго. Парный трейдинг в марте 2020 показал крупнейшие просадки за десятилетие.
- Транзакционные издержки. Mean reversion = много мелких сделок. Если комиссия и spread больше edge — стратегия отрицательная даже при формально верных сигналах. Считайте edge за вычетом комиссий.
- Длительные просадки. Mean reversion может 6–12 месяцев не давать прибыли в трендовых режимах (2020 март–декабрь, 2022 на крипте). Психологически тяжело держать стратегию в это время — большинство трейдеров выключает бот именно перед возвратом edge.
Когда mean reversion не работает
Признаки трендового режима, в котором стратегия даёт убытки:
- Высокий ADX (> 25) — индикатор силы тренда.
- Цена держится далеко от 50/200-дневных средних на одном направлении.
- Z-score не возвращается к нулю в течение долгого времени, а накапливает значения в одну сторону.
В таких режимах разумно отключать mean reversion-боты или переключаться на trend-following. Гибридные системы (regime detection + переключение стратегий) — уровень развитого алготрейдинга, требующий 6+ месяцев исследований.
Что запомнить
- Mean reversion = торговля на возврате цены к среднему. Работает на стационарных рядах (тест ADF, p < 0.05).
- На обычной цене акции долгосрочный mean reversion не работает — нужен спред, разность или нормализация.
- Базовые реализации: Bollinger Bands, Z-score, RSI(2), парный трейдинг (статистический арбитраж).
- Парный трейдинг — самый надёжный mean-reversion подход благодаря рыночной нейтральности.
- Защита от overfit: walk-forward, out-of-sample, минимальный набор параметров, проверка на 15+ инструментах.
- Не работает на узколиквидных бумагах и в трендовых режимах — нужен фильтр режима (ADX, наклон скользящих).
- Серии маленьких прибылей + редкие большие убытки — характерная P&L кривая. Жёсткий стоп обязателен.