Quantitative Finance · /portfolio-optimizer
Stock Portfolio Optimizer
Quantitative Asset Allocation & Risk Analytics Platform using Modern Portfolio Theory
- Role:
- Developer
- Team:
- Solo Project
An interactive Streamlit app that computes maximum-Sharpe, long-only asset allocations with Modern Portfolio Theory and SciPy SLSQP.

Overview
A solo Streamlit application for mean-variance portfolio allocation using Modern Portfolio Theory (MPT). It downloads daily prices with yfinance, estimates expected returns and the covariance matrix from history, and finds the maximum-Sharpe allocation under long-only weight caps with SciPy's SLSQP optimizer. The result is plotted against 10,000 random-weight portfolios, summarised with parametric VaR / CVaR, and backtested against SPY and three historical crisis windows.
Problem
Equal-weight or intuition-based allocations ignore how assets move together and how large the downside can get. The goal was an interactive tool that turns mean-variance theory into charts a user can adjust, with clear risk metrics and historical stress tests.
Solution
Constrained Optimization: SciPy's Sequential Least Squares Programming (SLSQP) finds the maximum Sharpe ratio allocation with no short-selling and an adjustable per-asset weight cap. Random-Portfolio Frontier: 10,000 random-weight portfolios plotted as an interactive Plotly scatter of risk vs. expected return, with the optimal portfolio marked. Parametric Tail Risk: Value at Risk (VaR) and Conditional VaR (CVaR / Expected Shortfall) at a configurable confidence level. Backtesting & Stress Testing: cumulative growth of the optimized weights against an equal-weight portfolio and the SPY benchmark, plus maximum drawdown during three historical crises (2008 Financial Crisis, COVID-19 Crash, 2022 Tech Bear Market).
Architecture & Core Systems
Modular Pipeline: separate modules for data ingestion (yfinance + caching), metrics calculation, SciPy optimization, risk metrics, backtesting, and visualization. Streamlit UI: sidebar configuration controls, multi-tab result views, and themed Plotly charts. Two Entry Points: the Streamlit web app and a standalone CLI runner (main.py).
Technical Implementation
Caching: Streamlit @st.cache_data memoizes price downloads, avoiding repeated API calls and rate limits. Data Alignment: forward / backward fill handles non-trading days and gaps across tickers. Input Validation: the app rejects fewer than two tickers and weight caps that cannot reach a fully invested portfolio.
Quantitative Methodology
Optimization Model: Markowitz mean-variance framework. Expected returns are the mean of simple daily returns and risk comes from their sample covariance matrix, both annualised over 252 trading days. Numerical Optimization: SciPy SLSQP maximizes the Sharpe ratio (expected excess return / portfolio volatility, risk-free rate default 2%) subject to: (1) weights sum to 1, (2) 0 ≤ wᵢ ≤ cap (long-only, cap adjustable from 0.10 to 1.00). Random-Portfolio Frontier: 10,000 portfolios whose weights are uniform random draws normalised to sum to 1. Each portfolio's annualised return and volatility are computed from the same estimates and plotted interactively. Parametric Risk Metrics: VaR and CVaR are computed from the portfolio's annualised expected return and volatility, assuming normally distributed returns (normal quantile for VaR, closed-form normal expected shortfall for CVaR), at a configurable confidence level (default 95%). Backtest & Stress Testing: optimal weights applied to historical daily returns to simulate cumulative growth from a configurable starting capital. Maximum drawdown is measured in three windows: 2008 Financial Crisis (Oct 2007 – Mar 2009), COVID-19 Crash (Feb – Apr 2020), and the 2022 Tech Bear Market.
Challenges
Keeping the app responsive while re-optimizing whenever inputs change, solved by caching price downloads. Presenting tail risk honestly: parametric CVaR assumes normal returns, so it is shown alongside historical crisis drawdowns rather than on its own.
Results
Deployed on Streamlit Community Cloud and open-source on GitHub, with a modular package layout and a CLI runner.
Limitations & Assumptions
- Estimation error: Markowitz mean-variance optimization is highly sensitive to errors in the estimated expected returns; small changes in sample means can shift the optimal weights substantially.
- In-sample results: historical mean returns and sample covariance matrices are noisy and non-stationary, so the frontier, backtest, and stress tests are in-sample results, not out-of-sample guarantees.
- Data: daily adjusted close prices from Yahoo Finance via yfinance (close prices if adjusted prices are unavailable). The default lookback is the last 5 years up to today, adjustable in the sidebar back to 2000.
- Constraints enforced: long-only weights 0 ≤ wᵢ ≤ cap (default cap 1.00) and Σ wᵢ = 1.
- Normality: VaR and CVaR are parametric and assume normally distributed returns, which understates fat-tailed losses.
- Planned next steps: Ledoit-Wolf covariance shrinkage, transaction-cost penalties, and Black-Litterman priors are not implemented yet.
Lessons Learned
Turning financial theory into working software needs careful data cleaning and explicit handling of bad inputs. Expected Shortfall (CVaR) says more about downside risk than volatility alone, but only as much as its distributional assumptions allow.
Screenshots & Preview
Visual assets and captures from Stock Portfolio Optimizer. Click any image to open the full-resolution preview.