Correlation coefficient (-1 to +1):
- +1: Perfect positive correlation (move together)
- 0: No correlation (independent movement)
- -1: Perfect negative correlation (move opposite)
Most cryptocurrencies are highly correlated:
- BTC/ETH: ~0.85
- BTC/major alts: 0.7-0.9
- Alt/Alt within sectors: 0.8-0.95
During market stress, correlations spike toward 1. Everything sells together.
- Diversification illusion: High correlation means your "diversified" portfolio isn't
- Relative value: Divergences from normal correlation = opportunities
- Risk management: Correlated positions compound risk
import pandas as pd
def calculate_correlation(prices_a, prices_b, window=30):
"""Calculate rolling correlation between two assets"""
returns_a = prices_a.pct_change()
returns_b = prices_b.pct_change()
correlation = returns_a.rolling(window).corr(returns_b)
return correlation
def correlation_matrix(price_df):
"""Calculate correlation matrix for multiple assets"""
returns = price_df.pct_change()
return returns.corr()
| Correlation |
Interpretation |
| 0.9+ |
Extremely high, nearly interchangeable |
| 0.7-0.9 |
High, strong relationship |
| 0.4-0.7 |
Moderate, some relationship |
| 0.2-0.4 |
Weak, limited relationship |
| <0.2 |
Very weak or no relationship |
Find two correlated assets. When they diverge from their normal relationship, bet on convergence:
- Long the underperformer
- Short the outperformer
- Profit when they converge
- High historical correlation (>
0.7)
- Fundamental relationship (same sector, use case)
- Sufficient liquidity for both assets
- Similar market cap tier (avoid pairing BTC with micro caps)
| Pair |
Relationship |
Typical Correlation |
| BTC/ETH |
Top 2 |
0.85 |
| ETH/SOL |
Smart contract platforms |
0.75 |
| LINK/BAND |
Oracle protocols |
0.70 |
| UNI/SUSHI |
DEX tokens |
0.80 |
| AAVE/COMP |
Lending protocols |
0.75 |
def calculate_spread(prices_a, prices_b, method='ratio'):
"""
Calculate spread between two assets
"""
if method == 'ratio':
spread = prices_a / prices_b
elif method == 'difference':
# Normalize first
norm_a = prices_a / prices_a.iloc[0]
norm_b = prices_b / prices_b.iloc[0]
spread = norm_a - norm_b
return spread
def zscore_spread(spread, window=20):
"""Calculate z-score of spread"""
mean = spread.rolling(window).mean()
std = spread.rolling(window).std()
zscore = (spread - mean) / std
return zscore
Entry: - Z-score > 2: Short spread (short A, long B)
- Z-score < -2: Long spread (long A, short B)
Exit: - Z-score returns to 0: Close position
- Stop loss: Z-score > 3 (or <-3)
ETH/SOL spread
Normal ratio: 0.05 (20 SOL per ETH)
Current ratio: 0.045 (22 SOL per ETH)
Z-score: -2.3 (ETH underperforming)
Position: - Long 1 ETH
When correlations break, something fundamental has changed—or there's a temporary dislocation.
def detect_correlation_breakdown(correlation, threshold=0.5, lookback=60):
"""
Detect when correlation drops significantly
"""
avg_correlation = correlation.rolling(lookback).mean()
breakdown = correlation < (avg_correlation - threshold)
return breakdown
Scenario 1: Temporary dislocation
- One asset hit by news, other unaffected
- Correlation will likely restore
- Trade: Mean reversion (pair trade)
Scenario 2: Fundamental change
- Real difference in fundamentals emerged
- Correlation may stay low
- Trade: Follow the divergence
Ask:
- Is there news explaining the divergence?
- Has something fundamental changed?
- How long has the breakdown persisted?
- Are other related pairs showing similar patterns?
Group assets by sector and track correlation changes:
sectors = {
'L1': ['ETH', 'SOL', 'AVAX', 'ADA'],
'DeFi': ['UNI', 'AAVE', 'CRV', 'MKR'],
'Gaming': ['AXS', 'SAND', 'MANA', 'GALA'],
'Infrastructure': ['LINK', 'GRT', 'FIL', 'AR']
}
def sector_correlation(price_df, sectors):
"""Calculate intra-sector correlations"""
results = {}
for sector, tokens in sectors.items():
sector_prices = price_df[tokens]
sector_corr = sector_prices.pct_change().corr()
results[sector] = sector_corr.mean().mean()
return results
- Rising sector correlation: Sector moving as unit (macro driven)
- Falling sector correlation: Dispersion (alpha opportunity within sector)
When intra-sector correlation drops, individual token selection matters more.
def pair_position_size(account, risk_per_trade, spread_volatility):
"""
Size pair trade positions
"""
# Risk amount
risk_amount = account * risk_per_trade
# Spread standard deviation (recent)
spread_std = spread_volatility
# Position size (each leg)
position_size = risk_amount / (2 * spread_std)
return position_size
Fixed spread stop: - Enter at z-score 2
- Stop at z-score 3
- Risk = 1 spread standard deviation
Time stop: - Maximum holding period: 10-20 days
Correlations aren't stable. A 0.85 correlation can drop to 0.5 during stress:
- Monitor correlation changes during trade
- Exit if correlation breaks significantly
- Don't assume historical correlation holds
- TradingView: Built-in correlation indicator
- CoinMetrics: Professional correlation data
- Messari: Sector analysis
- Custom: Build with Python + price data
import seaborn as sns
import matplotlib.pyplot as plt
def plot_correlation_heatmap(correlation_matrix):
"""Plot correlation matrix as heatmap"""
plt.figure(figsize=(12, 10))
sns.heatmap(correlation_matrix,
annot=True,
cmap='RdYlGn',
center=0,
vmin=-1,
vmax=1)
plt.title('Crypto Correlation Matrix')
plt.show()
Set alerts for:
- Z-score threshold breaches
- Correlation breakdown events
- Spread hitting extremes
Correlation measures linear relationship.
Cointegration measures long-term equilibrium.
Two assets can be cointegrated but have low short-term correlation. Cointegration is more robust for pair trading.
from statsmodels.tsa.stattools import coint
def test_cointegration(prices_a, prices_b):
"""Test if two price series are cointegrated"""
score, pvalue, _ = coint(prices_a, prices_b)
return {
'cointegrated': pvalue < 0.05,
'pvalue': pvalue,
'score': score
}
Trade baskets instead of pairs:
- Long: 50% ETH + 50% SOL
- Short: 100% BTC
- Bet on alt outperformance vs BTC
Adjust hedge ratio based on changing correlation:
def optimal_hedge_ratio(returns_a, returns_b, window=30):
"""Calculate rolling optimal hedge ratio"""
covariance = returns_a.rolling(window).cov(returns_b)
variance_b = returns_b.rolling(window).var()
hedge_ratio = covariance / variance_b
return hedge_ratio
Correlations change, especially during stress. Don't treat historical correlation as guaranteed.
Pair trades require multiple executions. Fees compound on both legs.
Pair trades seem safe but can diverge further than expected. Size conservatively.
Don't pair trade against strong trends. Underperformance can persist longer than you expect.
Focus on 2-3 well-understood pairs rather than trading every divergence.
What's the minimum capital for pair trading?
$5,000+ recommended to make fee impact manageable. $10,000+ for multiple pairs.
How long do pair trades typically last?
Days to weeks. Quick convergence (hours) is rare. Extended divergence (months) suggests fundamental change.
Should I trade highly correlated or moderately correlated pairs?
High correlation (0.8+) for mean reversion. Moderate (0.5-0.7) for breakdown/regime change trades.
Can I pair trade with leverage?
Yes, but reduce leverage since you have exposure on both legs. 2-3x maximum for pair trades.