Statistical Arbitrage Algorithm Development for Cryptocurrencies
You've found two coins that seem to move together — BTC and ETH. But when one surges and the other lags, you want to profit from that. Statistical arbitrage (stat arb) is exactly that: temporary deviations from historically stable relationships. Unlike pure arbitrage (risk-free profit), stat arb carries risk — the spread may widen further before reverting. This very risk creates the opportunity for profit. Our 5+ years of experience and dozens of implemented projects allow us to turn this idea into a working algorithm. Development cost typically ranges from $5,000 to $15,000, and our clients often recoup this investment within a few months.
How to Build a Statistical Arbitrage Algorithm for Cryptocurrencies
Step 1: Identify Cointegrated Pairs
Correlation shows how prices move together but does not guarantee mean reversion. Cointegration is a statistical relationship where a linear combination of two series is stationary. Simply put: assets may diverge but in the long run revert to each other. This property is essential for stat arb.
The Engle-Granger test for cointegration:
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]) Good candidates in crypto: BTC/ETH, BTC-SPOT/BTC-PERP, similar Layer-1 tokens, ETH/LDO (staking derivative).
Step 2: Model the Spread and Z-score
For a cointegrated pair (X, Y), we find the hedge ratio β via OLS:
from sklearn.linear_model import LinearRegression def calculate_hedge_ratio(price_x, price_y, window=60): # Rolling OLS for dynamic 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 normalizes the spread:
Z-score = (Spread - mean(Spread)) / std(Spread) Trading signals:
- Z-score > +2: spread abnormally wide → sell Y, buy X (long spread)
- Z-score < -2: spread abnormally narrow → buy Y, sell X (short spread)
- |Z-score| < 0.5: close position (return to mean)
Step 3: Choose Hedge Ratio Method
Static β via OLS is simple but becomes outdated. Kalman Filter adapts the hedge ratio in real time, producing 2-3 times fewer false signals compared to static OLS. This improvement can boost Sharpe ratio by 0.5 or more. Comparison of methods:
| Parameter | Rolling OLS | Kalman Filter |
|---|---|---|
| Adaptability | low | high |
| Sensitivity to outliers | high | low |
| Number of signals | medium | high |
| False signal rate | 3x higher | baseline |
Example implementation of Kalman Filter:
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() More about the Kalman Filter
The Kalman Filter is a recursive algorithm that estimates the state of a system from noisy measurements. In our case, it updates the hedge ratio on each new tick, weighting the previous estimate and the new observation. This is especially useful for pairs whose relationship changes over time (e.g., due to hard forks or liquidity changes).Step 4: Manage Risk with Stop-Loss and Position Sizing
Stop-loss by Z-score: if Z-score expands to 3+ instead of narrowing, it may signal a structural shift. Exit the position.
Half-life of mean reversion: estimate via AR(1) model:
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 days — fast mean reversion, suitable for short-term trading. > 30 days — slow, requires longer positions.
Lookback window: period for calculating spread mean and std. Optimized via walk-forward.
Step 5: Diversify with Multi-Pair Portfolios
Instead of pair trading, a portfolio approach with multiple cointegrated pairs:
- Diversification reduces pair-specific risk
- Correlation between pairs should be minimal
- PCA to find common factors
Eigenvector portfolio: from the covariance matrix of N assets, extract stationary eigenvectors via PCA. Trade the deviation from the stationary state.
Step 6: Account for Transaction Costs
Stat arb is profitable only if returns exceed transaction costs. Savings on slippage can reach 30% with optimized execution.
| Cost Item | Typical Range |
|---|---|
| Exchange fees (taker) | 0.04–0.07% |
| Maker fees | 0–0.02% |
| Funding rate (perpetual) | market dependent |
| Slippage | 0.01–0.1% depending on liquidity |
| Borrowing cost (short) | 0.01–0.03% per day |
Minimum Z-score for entry is selected considering costs: if entry at Z=1.5 does not cover costs with the probability of reversion, use Z=2.0.
Step 7: Backtest with Walk-Forward Validation
Walk-forward validation: train on 6-12 months, test on the next 1-2 months, repeat with a shift. Key metrics: Sharpe Ratio > 1.5, max drawdown < 15%, average position duration (does half-life match reality?).
Overfitting check: parameters optimized on one period should work on another.
What's Included in the Work
- Research and selection of cointegrated pairs
- Algorithm development with Kalman filter or OLS hedge ratio
- Backtesting on historical data with walk-forward
- Parameter and signal optimization
- Integration with exchange via CCXT
- Monitoring and support after launch
We guarantee code quality and transparent results. We use Python (pandas, numpy, statsmodels, sklearn, pykalman), PostgreSQL for data storage, Celery for real-time calculations, Grafana for visualization. We deploy on AWS/GCP with low latency.
Development cost is calculated individually but typically ranges from $5,000 to $15,000 and recoups within a few months. Contact us to discuss your project and get a consultation.
Source: Engle & Granger (1987) — Co-Integration and Error Correction: Representation, Estimation, and Testing.







