Парный трейдинг: стратегия, поиск пар, индикаторы и реализация на Python
Парный трейдинг — рыночно-нейтральная стратегия: одновременно лонг одной бумаги и шорт другой, коррелирующей. Заработок идёт не на движении рынка, а на временном расхождении пары. Разбираем, как искать пары на MOEX, чем коинтеграция отличается от корреляции, как считать хедж-коэффициент и Z-score спреда, какие индикаторы использовать, как реализовать стратегию на Python и где её главные ловушки — разрыв коинтеграции и корпоративные события эмитента.
Что такое парный трейдинг
Парный трейдинг (pairs trading) — стратегия одновременной покупки одного инструмента и продажи (шорта) другого, связанного с ним. Идея в том, что пара активов с общими фундаменталиями (один сектор, общая выручка, схожая бизнес-модель) исторически движется вместе. Когда они расходятся — рынок временно неэффективен, и расхождение должно сократиться.
Прибыль возникает не из движения рынка, а из сужения спреда между ценами пары. Если общий рынок вырастет на 5% — обе бумаги вырастут, ваш лонг приносит +5%, шорт даёт −5%, P&L по рынку нулевой. Если внутри пары спред сужается — заработок есть. Это и есть «рыночная нейтральность».
Стратегия родилась в институциональной среде (Morgan Stanley, конец 1980-х, отдел Нунцио Тартальи и Дэвида Шоу). С тех пор парный трейдинг — один из главных кирпичиков статистического арбитража и стратегий хедж-фондов.
Зачем рыночная нейтральность
Главное преимущество парного трейдинга по сравнению с направленной торговлей:
- Не нужно угадывать направление рынка. Только относительное движение пары.
- Меньшие просадки в кризисах. Когда рынок падает на 30%, портфель направленных лонгов теряет 30%; нейтральная парная книга — близка к нулю по дельте.
- Большее количество сделок. Можно одновременно держать 5–20 пар, диверсифицируя риск каждой.
- Стабильнее P&L. При правильно подобранных парах — серия мелких прибылей с предсказуемой волатильностью.
Главный недостаток — меньшая ожидаемая доходность по сравнению с трендовой торговлей. Парный трейдинг даёт типично 8–15% годовых при скрытом риске 5–10% maximum drawdown. Это не «обыграть рынок в три раза» — это «зарабатывать стабильно при низкой волатильности».
Корреляция vs коинтеграция
Это ключевая концепция. Большинство новичков ищут пары по корреляции — и ошибаются.
Корреляция — статистика мгновенных движений. Если две бумаги обычно растут или падают вместе — у них высокая корреляция. Это необходимое, но недостаточное условие парного трейдинга. Корреляция говорит «они движутся вместе», но не говорит про возврат к среднему. Две бумаги могут идеально коррелировать, но их спред — расходиться навсегда.
Коинтеграция — наличие линейной комбинации двух нестационарных рядов, которая стационарна. То есть: цены SBER и VTB — нестационарные ряды (они движутся как случайное блуждание). Но VTB − β × SBER — стационарный ряд, у которого есть среднее и есть возврат к нему. Это и есть условие применимости парного трейдинга.
Проверка коинтеграции — тест Энгла-Грейнджера:
from statsmodels.tsa.stattools import coint
# pair_a, pair_b — серии цен (pd.Series)
score, pvalue, _ = coint(pair_a, pair_b)
print(f"p-value коинтеграции: {pvalue:.4f}")
# pvalue < 0.05 → пара коинтегрирована
Альтернатива — тест Йохансена (для трёх и более бумаг одновременно), но для частной торговли пары достаточно Энгла-Грейнджера.
Хедж-коэффициент: какие позиции открывать
Когда коинтеграция есть, нужно понять в каком соотношении открывать лонг и шорт. Это и есть хедж-коэффициент β.
Считается через линейную регрессию одной бумаги на другую:
VTB_t = α + β × SBER_t + ε_t
import numpy as np
from sklearn.linear_model import LinearRegression
X = sber_prices.values.reshape(-1, 1)
y = vtb_prices.values
reg = LinearRegression().fit(X, y)
alpha, beta = reg.intercept_, reg.coef_[0]
print(f"VTB = {alpha:.4f} + {beta:.6f} × SBER")
Если β = 0.0003 — на каждый 1 ₽ движения SBER, VTB движется на 0.3 коп. Значит для рыночно-нейтральной позиции:
1 акция SBER (≈ 280 ₽) шорт = 1 акция × 280 = 280 ₽ экспозиции
N акций VTB лонг = 280 / β / VTB_цена
При VTB ≈ 0.012 ₽:
N = 280 / 0.0003 / 0.012 ≈ 77 700 акций ≈ 7770 лотов VTB (лот = 10000)
В России для большинства blue-chip бумаг проще брать денежно-нейтральную пару: одинаковая сумма в рублях лонг и шорт, без расчёта β. Это менее точно, но проще и не требует регулярного пересчёта β.
Z-score сигнала
После расчёта спреда ε_t = VTB_t − α − β × SBER_t — нормализуем его:
z_t = (ε_t − rolling_mean_60(ε)) / rolling_std_60(ε)
Сигналы:
z > 2→ продать VTB, купить SBER (спред «слишком вверх», ждём сужения).z < −2→ купить VTB, продать SBER (спред «слишком вниз», ждём расширения).|z| < 0.5→ закрыть позицию (вернулись к среднему).|z| > 4→ стоп-лосс, фундаменталии пары сломались.
Чем шире пороги entry/exit — тем меньше сделок, но больше ожидание на каждой. Стандартное соотношение: entry = 2, exit = 0.5, stop = 4. Подгонка этих параметров — главный риск overfitting на бэктесте.
Реализация на Python: полный пример
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import coint
from sklearn.linear_model import LinearRegression
def find_pairs(prices: pd.DataFrame, alpha=0.05) -> list[tuple]:
"""Возвращает список коинтегрированных пар."""
pairs = []
tickers = prices.columns.tolist()
for i in range(len(tickers)):
for j in range(i + 1, len(tickers)):
t1, t2 = tickers[i], tickers[j]
score, pvalue, _ = coint(prices[t1], prices[t2])
if pvalue < alpha:
pairs.append((t1, t2, pvalue))
return sorted(pairs, key=lambda x: x[2])
def compute_spread(p1: pd.Series, p2: pd.Series, window=60):
"""Скользящая регрессия и Z-score спреда."""
spread = []
zscore = []
for i in range(window, len(p1)):
x = p1.iloc[i-window:i].values.reshape(-1, 1)
y = p2.iloc[i-window:i].values
reg = LinearRegression().fit(x, y)
beta = reg.coef_[0]
alpha = reg.intercept_
eps = p2.iloc[i] - alpha - beta * p1.iloc[i]
spread.append(eps)
# Z-score по самому спреду в окне
if len(spread) >= window:
z = (eps - np.mean(spread[-window:])) / np.std(spread[-window:])
else:
z = 0
zscore.append(z)
return pd.Series(spread, index=p1.index[window:]), \
pd.Series(zscore, index=p1.index[window:])
def backtest_pair(p1: pd.Series, p2: pd.Series,
entry=2.0, exit=0.5, stop=4.0):
spread, z = compute_spread(p1, p2)
position = 0 # +1 = long p2/short p1; -1 = short p2/long p1
pnl = []
entry_spread = 0
for i in range(1, len(z)):
if position == 0:
if z.iloc[i] > entry:
position = -1
entry_spread = spread.iloc[i]
elif z.iloc[i] < -entry:
position = 1
entry_spread = spread.iloc[i]
else:
if abs(z.iloc[i]) > stop:
pnl.append(position * (entry_spread - spread.iloc[i]))
position = 0
elif abs(z.iloc[i]) < exit:
pnl.append(position * (spread.iloc[i] - entry_spread))
position = 0
return pd.Series(pnl)
Полный pipeline:
# 1. Загружаем цены банковского сектора с MOEX
prices = pd.DataFrame({
"SBER": load_close("SBER"),
"VTBR": load_close("VTBR"),
"TCSG": load_close("TCSG"),
"MOEX": load_close("MOEX"),
"BSPB": load_close("BSPB"),
})
# 2. Ищем коинтегрированные пары
pairs = find_pairs(prices)
for t1, t2, p in pairs[:5]:
print(f"{t1} ↔ {t2}: p-value={p:.4f}")
# 3. Бэктестим лучшую пару
best = pairs[0]
result = backtest_pair(prices[best[0]], prices[best[1]])
print(f"средняя сделка: {result.mean():.2f}")
print(f"sharpe: {result.mean() / result.std() * np.sqrt(252):.2f}")
Это упрощённая модель — продакшен-версия должна учитывать комиссии (≥0.05% × 4 на каждую сделку: вход и выход × 2 ноги пары), проскальзывание, размер тика и доступность шорта.
Где работают пары на MOEX
Лучшие кандидаты — внутри одного сектора, где общие макро-факторы синхронизируют цены:
Банковский сектор
- Сбер (SBER) ↔ ВТБ (VTBR) — классическая пара, исторически очень коинтегрированы.
- Сбер ↔ Сбер-преф (SBERP) — внутрибумажная пара, спред определяется дивидендной премией.
- Сбер ↔ ТКС (TCSG) — менее устойчиво, ТКС больше зависит от ритейла.
Нефтегаз
- Лукойл (LKOH) ↔ Роснефть (ROSN) — обе на нефти Brent, очень похожая фундаменталия.
- Лукойл ↔ Татнефть-преф (TATNP) — преф со специфической дивполитикой.
- Газпром (GAZP) ↔ Новатэк (NVTK) — раньше работала, после санкций 2022 коинтеграция нестабильная.
Металлургия
- Северсталь (CHMF) ↔ ММК (MAGN) — крупнейшие сталелитейные.
- Северсталь ↔ НЛМК (NLMK) — пара тех же активов.
- ГМК Норникель (GMKN) ↔ Полюс (PLZL) — драгметаллическая, но часто разваливается на корпоративных событиях.
Ритейл
- Магнит (MGNT) ↔ X5 Retail (FIVE) — двое крупнейших, коинтегрированы.
Преф vs обыкновенные одной компании
- Сургутнефтегаз обыкн (SNGS) ↔ Сургутнефтегаз-преф (SNGSP) — спред определяется специфической дивполитикой Сургута.
- Татнефть (TATN) ↔ Татнефть-преф (TATNP) — относительно стабильная пара.
- Роллеру (ROLO) ↔ Роллеру-преф (ROLOP) — для эмитентов с двумя классами акций.
Проверять коинтеграцию надо на свежих данных (последние 6–12 месяцев). Историческая коинтеграция не гарантирует текущую.
Размер позиции и риск
Несколько способов «равновесить» пару:
- Равные деньги (cash-neutral). Проще всего: одинаковая сумма в рублях лонг и шорт. Подходит для пар, где β близка к 1. Подходит большинству ритейл-сетапов.
- Равная дельта (β-neutral). Через расчёт β из регрессии. Точнее, но требует регулярного пересчёта.
- Равная волатильность (vol-neutral). Веса обратно пропорциональны исторической волатильности каждой ноги. Важно для пар с разной величиной свинга.
Размер общей позиции — фиксированный риск ≤1% депозита на пару. Для 10 одновременных пар совокупный максимальный убыток в один день не должен превышать 5% депозита.
Стоп — пробой |z| > 4 или ухудшение коинтеграции (если в скользящем окне p-value > 0.1). Тейк — возврат к нулевому Z или ослабление спреда до пол-стандартного отклонения.
Главные риски
- Разрыв коинтеграции. Самая болезненная ловушка. Корпоративное событие (доп. эмиссия, делистинг, M&A, санкции на конкретного эмитента) ломает связь между бумагами навсегда. Спред идёт в одну сторону и не возвращается. Если удерживать позицию «по теории», убыток нарастает.
- Корпоративные события одной ноги. Дивиденды одной бумаги пары без аналога у другой ломают спред на размер дивиденда. Перед отсечкой стоит закрывать позицию вручную или хеджировать ногу опционами.
- Изменение фундаменталий. Сбер vs ВТБ были коинтегрированы до санкций 2022; после санкций ВТБ стал отдельной историей. Каждые 6 месяцев надо проверять, что пара осталась коинтегрированной.
- Падение ликвидности. В стрессе ликвидность второй ноги пары может пропасть быстрее первой; вы окажетесь с непрогнозируемым «голым» риском.
- Шорт-комиссии. В России шорт акций облагается комиссией за заём бумаг (0.5–4% годовых от объёма). На годовой горизонт это съедает значимую часть прибыли.
Парный трейдинг на форексе
На валютном рынке прямые пары менее очевидны (там все «пары» — это уже валютные пары как один инструмент). Но коинтеграционные сетапы существуют:
- EUR/USD ↔ GBP/USD — обе движутся против доллара, часто коинтегрированы.
- USD/CAD ↔ нефть Brent (фьючерс) — канадский доллар сильно завязан на нефть.
- AUD/USD ↔ медь (фьючерс) — австралийский доллар на сырьё.
- USD/JPY ↔ доходность 10-летних USTreasury — чистая carry-связь.
Для российского физлица в 2026 году доступ к Forex и иностранным фьючерсам ограничен; через MOEX и FORTS можно частично закрыть эти стратегии (например, Si vs Brent-фьючерс на ICE — но Brent на MOEX недоступен).
Индикаторы парного трейдинга
Часто ищут «индикаторы для парного трейдинга» в QUIK или TradingView. Прямых готовых нет — обычно используются производные индикаторы на спред:
- Bollinger Bands на спред — классическая визуализация Z-score.
- RSI на спред — экстремальные значения индикатора.
- MACD на спред — для подтверждения смены направления.
В QUIK Lua можно посчитать спред парой строк кода:
local price1 = getParamEx("TQBR", "SBER", "LAST").param_value
local price2 = getParamEx("TQBR", "VTBR", "LAST").param_value
local beta = 0.0003 -- из бэктеста
local spread = price2 - beta * price1
-- далее любые индикаторы на серии spread
В TradingView можно построить кастомный символ через функцию (VTBR/SBER)*1000, что даёт нормированный спред — не идеально, но визуально работает.
Что запомнить
- Парный трейдинг = одновременно лонг одной бумаги и шорт другой коинтегрированной. Заработок на сужении спреда.
- Коинтеграция (стационарный спред) важнее корреляции (мгновенное соответствие движений).
- Пара тестируется через тест Энгла-Грейнджера, p-value < 0.05.
- Хедж-коэффициент β из линейной регрессии; для простоты допустимо cash-neutral балансирование.
- Сигналы по Z-score спреда: вход при |z| > 2, выход при |z| < 0.5, стоп при |z| > 4.
- Лучшие пары на MOEX — внутри секторов: банки (Сбер ↔ ВТБ), нефтегаз, металлургия, преф vs обыкновенные.
- Главный риск — разрыв коинтеграции на корпоративных событиях; перепроверка пары каждые 6 месяцев обязательна.
- Шорт-комиссии (0.5–4% годовых) и тонкая ликвидность второй ноги — практические факторы, ломающие математически верный сетап.