Розробка алгоритму статистичного арбітража
Статистичний арбітраж (stat arb) — це торговля на тимчасових відхиленнях від історично стійких статистичних співвідношень між активами. На відміну від чистого арбітража (безризикового прибутку), stat arb несе у собі ризик — співвідношення може тимчасово розширитися перед тим як повернутися. Саме ця рискованість створює можливість для прибутку.
Фундамент: коінтеграція та mean-reversion
Коінтеграція (cointegration) — статистичний зв'язок двох часових рядів. На відміну від кореляції (зв'язок змін), коінтеграція означає, що лінійна комбінація двох рядів є стаціонарною. Простіше кажучи: активи можуть розходитися, але в довгостроковій перспективі повертаються один до одного.
Тест 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 (long spread)
- Z-score < -2: спред аномально вузький → купуємо Y, продаємо X (short spread)
- |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: період для розрахунку середнього та 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
Замість торговлі парами — портфельний підхід з кількома коінтегрованими парами:
- Диверсифікація знижує ризик конкретної пари
- Кореляція між парами повинна бути мінімальною
- PCA (Principal Component Analysis) для пошуку спільних факторів та побудови стаціонарних портфелів
Eigenvector portfolio: з матриці коваріацій N активів через PCA витягуємо стаціонарні власні вектори. Торгуємо відхилення від стаціонарного стану.
Execution та transaction costs
Stat arb прибутковий тільки якщо дохідність перевищує транзакційні видатки:
- Біржові комісії (taker: 0.04–0.07%, maker: 0–0.02%)
- Funding rate для perpetual позицій
- Slippage при виконанні
- Borrowing cost для short позицій
Мінімальний Z-score для входу підбирається з урахуванням видатків: якщо entry при Z=1.5 не покриває видатки з урахуванням ймовірності повернення — використовуємо Z=2.0.
Backtesting
Walk-forward validation: навчання на 6-12 місяцях, тестування на наступних 1-2 місяцях, повтор зі зсувом.
Ключові метрики: Sharpe Ratio > 1.5, max drawdown < 15%, середня тривалість позиції (половина-життя збігається з реальними даними?), кількість прибуткових vs збиткових угод.
Overfitting check: параметри, оптимізовані на одному періоді, повинні працювати на іншому. Якщо параметри сильно змінюються між періодами — модель переобучена.
Технічний стек
Python (pandas, numpy, statsmodels, sklearn), PostgreSQL для зберігання позицій та P&L, CCXT для підключення до біржевого API, Celery для запланованих завдань (розрахунок спреду та Z-score кожну хвилину), Grafana для моніторингу. Розгорнуто на AWS/GCP з co-location близько до біржи.







