Diversification is a core pillar of risk management, complementing
micro-level controls like position sizing and stop-losses. Its
effectiveness lies in its ability to smooth out returns and reduce
drawdowns by combining uncorrelated sources of alpha. This article
provides a hands-on guide to implementing and analyzing diversification
in Python using a complete backtesting framework based on your provided
backtrader code.
We will explore two key forms of diversification—across multiple assets and across multiple strategies—and demonstrate how a well-structured backtest can quantitatively prove its benefits.
The provided code sets up a robust backtesting environment with several essential components:
The CustomSharpeRatio class is a powerful tool for
reliable performance analysis. Unlike default analyzers, it provides a
transparent calculation of the Sharpe Ratio based on a specified
risk-free rate, along with detailed debug information on returns and
volatility.
The framework includes three distinct trading strategies:
TrendFollowingStrategy: A simple
moving average crossover model.MeanReversionStrategy: An RSI-based
strategy that buys on oversold conditions and sells on overbought
conditions.MomentumStrategy: Buys when price
shows significant positive momentum over a period.Each strategy represents a different market hypothesis, which is the foundation for strategy diversification.
The code uses dedicated functions to manage the backtesting workflow:
download_data: Retrieves and processes market data,
using yfinance with auto_adjust=False and a
flattened column index as previously discussed.run_single_strategy_backtest: A utility to test a
single strategy on a single asset.run_diversified_backtest: Runs a strategy across
multiple assets, demonstrating asset diversification.calculate_correlation: A crucial function for analyzing
the relationship between assets.Asset diversification involves spreading capital across different instruments or asset classes that do not move in perfect lockstep. The provided code demonstrates this by running a simple trend-following strategy across four distinct ETFs:
SPY: The S&P 500 ETF (U.S.
stocks)GLD: The gold ETF (commodities)TLT: A long-term bond ETFVTI: A total U.S. stock market ETF
(similar to SPY, included to show high correlation)The core logic resides in the SimpleDiversifiedStrategy,
which dynamically applies the same trend-following rule to each data
feed.
The code calculates and prints a correlation matrix, which is a vital
step in diversification. A matrix with low off-diagonal values (e.g.,
SPY-TLT correlation of -0.177) indicates that these assets
are good candidates for diversification because they tend to move
independently or in opposite directions. In contrast, the high
correlation between SPY-VTI (0.994) shows that trading both
offers little diversification benefit.
The backtest results clearly show the impact of asset diversification. The combined, diversified portfolio typically exhibits a higher Sharpe Ratio and a lower Maximum Drawdown compared to the average of the individual, single-asset strategies. This is a quantitative proof of the benefit of diversification: you achieve better risk-adjusted returns and a smoother equity curve for the same level of capital.
Strategy diversification involves combining multiple trading approaches to create a more robust, low-correlation portfolio. By blending strategies that have different biases (e.g., a trend follower and a mean reversion model), you create a system that can perform well in a wider range of market conditions.
Here is a new utility function to demonstrate this concept by running multiple strategies on a single asset.
def run_multi_strategy_backtest(data_feed, strategies, strategy_name):
"""Run backtest showing diversification across multiple strategies on a single asset"""
cerebro = bt.Cerebro()
cerebro.adddata(data_feed)
for strategy_class in strategies:
cerebro.addstrategy(strategy_class)
cerebro.broker.setcash(10000)
cerebro.broker.setcommission(commission=0.001)
cerebro.addanalyzer(CustomSharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
print(f"\nRunning {strategy_name} (Strategy Diversification)...")
results = cerebro.run()
final_value = cerebro.broker.getvalue()
total_return = ((final_value - 10000) / 10000) * 100
strat = results[0]
sharpe = strat.analyzers.sharpe.get_analysis().get('sharperatio', 0)
max_dd = strat.analyzers.drawdown.get_analysis().get('max', {}).get('drawdown', 0)
print(f"Final Portfolio Value: ${final_value:.2f}")
print(f"Total Return: {total_return:.2f}%")
print(f"Sharpe Ratio: {sharpe:.3f}")
print(f"Max Drawdown: {max_dd:.2f}%")
return {
'final_value': final_value,
'total_return': total_return,
'sharpe_ratio': sharpe,
'max_drawdown': max_dd,
'cerebro': cerebro
}You would then integrate this into your main function to
run and compare the results:
# ... inside main() function ...
# Run diversified backtest on a single asset with multiple strategies
multi_strat_results = run_multi_strategy_backtest(
spy_data,
[TrendFollowingStrategy, MeanReversionStrategy, MomentumStrategy],
"Strategy Diversified Portfolio"
)
results['Strategy Diversified Portfolio'] = multi_strat_results
# ... then plot and print the summary, including the new result.By adding this backtest, you can directly compare the benefits of portfolio-level diversification with a single asset (e.g., SPY) against the individual performance of each of the three strategies.
Your code provides a strong foundation for further development:
self.trailing_stop(data=self.data, trailpercent=0.03) to
lock in profits and limit potential drawdowns.Your backtesting framework is a powerful and practical tool for understanding and implementing diversification. The code correctly demonstrates how to handle multi-asset data, analyze correlations, and compare the performance of different portfolios. By integrating both asset and strategy diversification, and by continuing to refine your risk management with tools like trailing stops and fixed fractional sizing, you can build a robust, scalable, and resilient quantitative trading system.