Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Portfolio Optimization Across Risk Profiles

This project compares three portfolio-allocation strategies across three equity universes designed to represent different risk profiles:

  • Equal Weight
  • Minimum Variance
  • Maximum Sharpe Ratio

The central question is not simply which optimizer produces the highest return. The project tests how stock-universe selection and allocation method jointly affect return, volatility, risk-adjusted performance, and drawdown.

Project Structure

portfolio-optimization/
├── README.md
├── portfolio_analysis.ipynb
├── requirements.txt
├── .gitignore
└── src/
    ├── __init__.py
    ├── optimization.py
    ├── backtest.py
    └── metrics.py

The notebook is the analysis and presentation layer. Reusable portfolio logic lives in the src/ modules.

Equity Universes

High Risk

ZS, AMD, NVDA, MU, TSLA, NET, PLTR, SNOW, CRWD

Moderate

AMD, NVDA, MU, CRWD, PLTR, WMT, COST, TGT, TJX, HD, ULTA, PG, JNJ, JPM, DUK

Low Risk

WMT, COST, TGT, TJX, HD, PG, JNJ, JPM, DUK

These groups are manually constructed risk profiles rather than statistically learned classifications. A data-driven classification is a natural extension of the project.

Methodology

Daily adjusted closing prices are downloaded with yfinance. The cleaned notebook uses a fixed research window from January 2022 through August 14, 2026 so that the published results are reproducible.

Each strategy is evaluated with a walk-forward backtest:

  • 252-trading-day estimation window
  • monthly rebalancing
  • long-only portfolios
  • 20% maximum weight per asset
  • 0.1% transaction cost applied to turnover
  • portfolio weights drift between rebalances
  • only information available before a rebalance date is used to choose weights

Equal Weight

Each asset receives the same target weight:

[ w_i = \frac{1}{N} ]

This is the benchmark because it requires no estimates of future returns or covariance.

Minimum Variance

The optimizer solves:

[ \min_w w^\top \Sigma w ]

subject to the portfolio constraints. The covariance matrix is estimated with Ledoit-Wolf shrinkage.

Maximum Sharpe

The optimizer solves:

[ \max_w \frac{w^\top \mu-r_f} {\sqrt{w^\top\Sigma w}} ]

Historical mean daily returns are annualized to estimate (\mu). A constant annualized risk-free rate of 3.81% is used.

To preserve the original notebook's methodology and results, the maximum-Sharpe implementation currently uses the sample covariance matrix, while minimum variance uses Ledoit-Wolf shrinkage. Standardizing the covariance estimator across both optimizers is listed below as a robustness extension.

Backtest Results

Results from the original experiment:

Universe Strategy Total Return Annualized Return Annualized Volatility Sharpe Max Drawdown
High Risk Equal Weight 872.98% 89.09% 37.35% 1.793 -33.11%
High Risk Minimum Variance 562.08% 69.77% 36.38% 1.534 -39.13%
High Risk Maximum Sharpe 958.11% 93.58% 41.03% 1.725 -36.04%
Moderate Equal Weight 304.19% 47.86% 17.30% 2.132 -20.49%
Moderate Minimum Variance 76.79% 17.30% 10.75% 1.190 -10.66%
Moderate Maximum Sharpe 239.58% 40.82% 16.66% 1.915 -16.22%
Low Risk Equal Weight 73.81% 16.74% 12.53% 1.000 -13.75%
Low Risk Minimum Variance 69.14% 15.85% 11.37% 1.022 -10.29%
Low Risk Maximum Sharpe 112.62% 23.52% 12.61% 1.443 -12.54%

Main Findings

  • High Risk + Maximum Sharpe produced the highest raw and annualized return.
  • Moderate + Equal Weight produced the highest realized Sharpe ratio.
  • Moderate + Minimum Variance produced the lowest annualized volatility, even lower than the manually defined low-risk universe.
  • Low Risk + Minimum Variance produced the smallest maximum drawdown.
  • No allocation strategy dominated every universe and every metric.
  • Equal weighting remained a strong benchmark despite requiring no return or covariance forecasts.

These results are historical backtest outcomes, not forecasts of future performance.

Limitations

  1. Expected-return estimation is noisy. Maximum-Sharpe optimization uses trailing historical mean returns as an estimate of future expected return.
  2. The risk-free rate is constant. The backtest uses a 3.81% annualized rate throughout the sample rather than a historical Treasury series.
  3. Risk universes are manually defined. The high/moderate/low groupings are not generated from volatility, beta, or clustering.
  4. Covariance estimators differ by optimizer. Minimum variance uses Ledoit-Wolf shrinkage, while maximum Sharpe uses sample covariance to preserve the original experiment.
  5. Results depend on the sample period and hyperparameters. A 252-day lookback and 20% weight cap are only one specification.
  6. The portfolios contain individual equities only. The low-risk universe is lower risk relative to the other equity universes, not a truly conservative multi-asset portfolio.

Potential Extensions

  • Compare 126-, 252-, and 504-day lookback windows
  • Test 10%, 20%, and 30% maximum-weight constraints
  • Use a historical Treasury-bill series for the risk-free rate
  • Use the same covariance estimator for both optimizers
  • Compare sample covariance, Ledoit-Wolf, and other shrinkage estimators
  • Build risk universes from measured volatility, beta, or clustering
  • Add Sortino ratio, turnover, and realized-vs-predicted volatility
  • Compare against broad-market ETF benchmarks

Installation

pip install -r requirements.txt

Launch Jupyter from the repository root:

jupyter lab

Then open portfolio_analysis.ipynb.

Technologies

Python, pandas, NumPy, SciPy, scikit-learn, matplotlib, yfinance, Jupyter.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages