Why Diversify
Correlation Is the Key
Two strategies with identical Sharpe ratios but correlation of 0 combined into a portfolio produce a portfolio Sharpe of √2 × individual Sharpe. Diversification is free return improvement — the only "free lunch" in trading.
PYTHONcorrelation_check.py
import pandas as pd # Returns for three strategies over same period returns = pd.DataFrame({ "EMA_Cross" : ema_result.returns, "Supertrend" : st_result.returns, "VWAP_Reversal": vwap_result.returns, }) corr = returns.corr() print(corr) # ── What to look for ───────────────────────────────────── # correlation > 0.7 → strategies behave similarly, limited benefit # correlation 0.3–0.7 → moderate diversification # correlation < 0.3 → excellent diversification value # correlation < 0 → negative correlation = hedging effect
Allocation
Portfolio Construction Methods
| Method | Formula | Best for |
|---|---|---|
| Equal Weight | 1/N per strategy | Similar volatility strategies |
| Volatility Parity | 1/σ per strategy, normalised | Strategies with different risk levels |
| Sharpe Weight | Sharpe_i / Σ Sharpe | When strategies have different edge strength |
| Min Correlation | Maximise portfolio Sharpe via covariance | Full portfolio optimisation |
PYTHONportfolio.py
import pandas as pd import numpy as np class StrategyPortfolio: def __init__(self, strategy_returns: dict[str, pd.Series]): """strategy_returns: {name: pd.Series of per-candle returns}""" self.returns = pd.DataFrame(strategy_returns).dropna() def correlation_matrix(self) -> pd.DataFrame: return self.returns.corr().round(2) def equal_weight(self) -> pd.Series: w = 1 / len(self.returns.columns) return (self.returns * w).sum(axis=1) def volatility_parity(self) -> pd.Series: vols = self.returns.std() w = (1 / vols) / (1 / vols).sum() return (self.returns * w).sum(axis=1) def sharpe_weight(self, risk_free: float = 0.065 / 94500) -> pd.Series: excess = self.returns - risk_free sharpes = excess.mean() / excess.std() * np.sqrt(94500) sharpes = sharpes.clip(lower=0) # ignore negative-Sharpe strategies if sharpes.sum() == 0: return self.equal_weight() w = sharpes / sharpes.sum() return (self.returns * w).sum(axis=1) def equity_curve(self, method: str = "vol_parity", capital: float = 100_000) -> pd.Series: portfolio_rets = getattr(self, { "equal" : "equal_weight", "vol_parity": "volatility_parity", "sharpe" : "sharpe_weight", }[method])() return capital * (1 + portfolio_rets).cumprod() def compare_methods(self, capital: float = 100_000) -> pd.DataFrame: methods = { "Equal Weight" : self.equal_weight(), "Vol Parity" : self.volatility_parity(), "Sharpe Weight" : self.sharpe_weight(), } from performance import PerformanceReport rows = [] for name, rets in methods.items(): eq = capital * (1 + rets).cumprod() rpt = PerformanceReport(eq, pd.DataFrame()) s = rpt.summary() rows.append({"method": name, **s}) return pd.DataFrame(rows).set_index("method")
Usage
Portfolio in Practice
PYTHONrun_portfolio.py
# Run three strategies on same data engine = BacktestEngine() r_ema = engine.run(df, ema_cross_signal) r_st = engine.run(df, supertrend_signal) r_vwap = engine.run(df, vwap_reversal_signal) # Build portfolio port = StrategyPortfolio({ "EMA" : r_ema.returns, "ST" : r_st.returns, "VWAP" : r_vwap.returns, }) print(port.correlation_matrix()) print(port.compare_methods())
Aim for different strategy types
The best portfolio mixes trend-following (EMA cross, Supertrend), mean-reversion (VWAP, Bollinger) and momentum (RSI breakout) strategies. These tend to have naturally low or negative correlation because they profit in different market conditions.
