Разработка алгоритма статистического арбитража
Статистический арбитраж (stat arb) — это торговля на временных отклонениях от исторически устойчивых статистических соотношений между активами. В отличие от чистого арбитража (безрисковой прибыли), stat arb несёт в себе риск — соотношение может временно расшириться перед тем как вернётся. Именно эта рискованность и создаёт возможность для прибыли.
Фундамент: коинтеграция и mean-reversion
Коинтеграция (cointegration) — статистическая связь двух временных рядов. В отличие от корреляции (связь изменений), коинтеграция означает, что linейная комбинация двух рядов стационарна. Проще говоря: активы могут расходиться, но в долгосрочной перспективе возвращаются друг к другу.
Тест Engle-Granger для проверки коинтеграции:
from statsmodels.tsa.stattools import coint
def find_cointegrated_pairs(prices_dict, p_threshold=0.05):
symbols = list(prices_dict.keys())
pairs = []
for i, sym1 in enumerate(symbols):
for sym2 in symbols[i+1:]:
score, p_value, _ = coint(
prices_dict[sym1],
prices_dict[sym2]
)
if p_value < p_threshold:
pairs.append((sym1, sym2, p_value))
return sorted(pairs, key=lambda x: x[2])
Хорошие кандидаты в крипте: BTC/ETH, BTC-SPOT/BTC-PERP, аналогичные Layer-1 токены, ETH/LDO (staking derivative).
Модель: Spread и Z-score
Для коинтегрированной пары (X, Y) находим hedge ratio β через OLS:
from sklearn.linear_model import LinearRegression
def calculate_hedge_ratio(price_x, price_y, window=60):
# Rolling OLS для динамического hedge ratio
hedge_ratios = []
for i in range(window, len(price_x)):
x = price_x[i-window:i].values.reshape(-1, 1)
y = price_y[i-window:i].values
model = LinearRegression().fit(x, y)
hedge_ratios.append(model.coef_[0])
return hedge_ratios
Spread = Y - β × X
Z-score нормализует спред:
Z-score = (Spread - mean(Spread)) / std(Spread)
Торговые сигналы:
- Z-score > +2: спред аномально широкий → продаём Y, покупаем X (лонг спред)
- Z-score < -2: спред аномально узкий → покупаем Y, продаём X (шорт спред)
- |Z-score| < 0.5: закрываем позицию (возврат к среднему)
Управление рисками спреда
Stop-loss по Z-score: если Z-score расширился до 3+ вместо сужения — это может означать структурный сдвиг. Выходим из позиции.
Half-life of mean reversion: оцениваем через модель AR(1):
from statsmodels.regression.linear_model import OLS
def calculate_half_life(spread):
spread_lag = spread.shift(1).dropna()
spread_diff = spread.diff().dropna()
result = OLS(spread_diff, spread_lag).fit()
half_life = -np.log(2) / result.params[0]
return half_life
Half-life < 5 дней — быстрый mean reversion, подходит для краткосрочной торговли. > 30 дней — медленный, требует более длинных позиций.
Lookback window: период для расчёта mean и std спреда. Слишком короткий — много ложных сигналов. Слишком длинный — медленная реакция на изменения. Оптимизируется через walk-forward.
Kalman Filter для динамического hedge ratio
Статическое β устаревает. Kalman Filter адаптирует hedge ratio в реальном времени:
from pykalman import KalmanFilter
kf = KalmanFilter(
transition_matrices=[1],
observation_matrices=price_x.values.reshape(-1, 1, 1),
initial_state_mean=0,
initial_state_covariance=1,
observation_covariance=1,
transition_covariance=0.05
)
state_means, state_covs = kf.filter(price_y.values)
hedge_ratio_dynamic = state_means.flatten()
Kalman Filter дает более стабильные сигналы и меньше ложных breakout'ов.
Мультипарная stat arb
Вместо торговли парами — портфельный подход с несколькими коинтегрированными парами:
- Диверсификация снижает risk конкретной пары
- Корреляция между парами должна быть минимальной
- PCA (Principal Component Analysis) для поиска общих факторов и построения стационарных портфелей
Eigenvector portfolio: из матрицы ковариаций N активов через PCA извлекаем стационарные собственные векторы. Торгуем отклонение от стационарного состояния.
Execution и transaction costs
Stat arb прибылен только если доходность превышает транзакционные издержки:
- Биржевые fees (taker: 0.04–0.07%, maker: 0–0.02%)
- Funding rate для perpetual позиций
- Slippage при исполнении
- Borrowing cost для short позиций
Минимальный Z-score для входа подбирается с учётом costs: если entry при Z=1.5 не покрывает costs с учётом вероятности возврата — используем Z=2.0.
Backtesting
Walk-forward validation: обучение на 6-12 месяцах, тестирование на следующих 1-2 месяцах, повтор со сдвигом.
Ключевые метрики: Sharpe Ratio > 1.5, max drawdown < 15%, средняя продолжительность позиции (half-life совпадает с реальным?), количество прибыльных vs убыточных сделок.
Overfitting check: параметры, оптимизированные на одном периоде, должны работать на другом. Если параметры сильно меняются между периодами — модель переобучена.
Технический стек
Python (pandas, numpy, statsmodels, sklearn), PostgreSQL для хранения позиций и P&L, CCXT для подключения к биржевым API, Celery для scheduled заданий (ежеминутный расчёт спреда и Z-score), Grafana для мониторинга. Разворачивается на AWS/GCP с co-location близко к бирже.







