Production-grade portfolio construction — from estimation to execution.
Quickstart • Installation • Methods • Results • References
QuantOpt is a Python library implementing production-grade portfolio construction workflows grounded in modern portfolio theory. It provides a unified pipeline spanning four stages:
| Stage | Methods |
|---|---|
| Return Estimation | Historical Mean, CAPM, Black-Litterman |
| Covariance Estimation | Sample, Exponentially Weighted, Ledoit-Wolf OAS, PCA Factor Model |
| Portfolio Optimization | Mean-Variance Efficient Frontier, Equal Risk Contribution, CVaR Minimization |
| Backtesting | Walk-Forward Engine with Transaction Costs & Mark-to-Market Drift |
Designed for quantitative practitioners who require mathematically rigorous, easily auditable implementations with analytical gradients and PSD-guaranteed covariance matrices. Distinguishes itself through tight integration of the estimation and optimization layers, explicit L2 weight regularization support, and a walk-forward backtesting engine with configurable transaction cost models.
- Black-Litterman integration — equilibrium-anchored return estimates that prevent extreme historical noise from dominating the optimizer
-
Ledoit-Wolf OAS shrinkage — analytically optimal covariance conditioning for tractable
$T/N$ regimes - Analytical gradients — all SLSQP objectives use closed-form derivatives; no finite-difference approximations
- Multi-restart optimization — Dirichlet-initialized restarts for non-convex objectives (Max Sharpe, Risk Parity)
- Fluent constraint builder — composable long-only, sector-neutral, turnover, and factor-exposure constraints
- Walk-forward backtester — monthly rebalancing with mark-to-market weight drift and proportional cost deduction
- Full risk decomposition — MRC, CRC, PRC, diversification ratio, HHI, VaR, CVaR, and factor attribution
| Package | Min Version | Purpose |
|---|---|---|
numpy |
1.26 | Linear algebra, RNG |
pandas |
2.1 | Time series, alignment |
scipy |
1.11 | SLSQP optimization |
scikit-learn |
1.3 | OAS estimator, PCA |
matplotlib |
3.8 | Figure rendering |
seaborn |
0.13 | Statistical graphics |
# Clone and install
git clone https://github.com/FelipeCardozo0/Systematic-Portfolio-Optimization-MPT.git
cd Systematic-Portfolio-Optimization-MPT
python -m venv .venv
source .venv/bin/activate # macOS / Linux
# .venv\Scripts\activate # Windows
pip install -e ".[dev]"
# Verify
python -c "import quantopt; print(quantopt.__version__)" # → 1.0.0
pytest tests/ -vimport numpy as np
import pandas as pd
from quantopt.returns.estimators import BlackLittermanReturn
from quantopt.risk.covariance import LedoitWolfCovariance
from quantopt.optimization.efficient_frontier import EfficientFrontier
# ── Synthetic price data (GBM) ───────────────────────────────────────
rng = np.random.default_rng(0)
T, N = 504, 8
tickers = [f"ASSET_{chr(65+i)}" for i in range(N)]
dates = pd.bdate_range("2022-01-03", periods=T)
log_ret = rng.normal(0.0004, 0.012, size=(T, N))
prices = pd.DataFrame(
100 * np.exp(np.cumsum(log_ret, axis=0)),
index=dates, columns=tickers,
)
returns = pd.DataFrame(log_ret, index=dates, columns=tickers)
# ── Covariance estimation ────────────────────────────────────────────
lw = LedoitWolfCovariance().fit(returns)
Sigma = lw.covariance()
print(f"LW shrinkage alpha: {lw.shrinkage_:.4f}")
# ── Black-Litterman returns ──────────────────────────────────────────
market_caps = pd.Series(np.ones(N) / N, index=tickers)
bl = BlackLittermanReturn(
market_caps=market_caps, risk_aversion=2.5, tau=0.05,
).fit(returns)
mu = bl.expected_returns()
# ── Mean-variance optimization ───────────────────────────────────────
ef = EfficientFrontier(mu=mu, Sigma=Sigma)
weights = ef.max_sharpe(risk_free_rate=0.02)
ret, vol, sr = ef.portfolio_performance(mu=mu, Sigma=Sigma, risk_free_rate=0.02)
print(f"Expected return : {ret:.2%}")
print(f"Volatility : {vol:.2%}")
print(f"Sharpe ratio : {sr:.3f}")
print("\nPortfolio weights:")
print(ef.clean_weights(threshold=0.005).round(4).to_string())quantopt/
├── quantopt/ # Main package (39 exported symbols)
│ ├── returns/
│ │ ├── preprocessing.py # Price ↔ returns, winsorization, demeaning
│ │ └── estimators.py # MeanHistorical, CAPM, BlackLitterman
│ ├── risk/
│ │ ├── covariance.py # Sample, EWM, LedoitWolf, FactorModel
│ │ └── metrics.py # MRC, CRC, PRC, DR, HHI, VaR, CVaR
│ ├── optimization/
│ │ ├── efficient_frontier.py # Max Sharpe, Min Vol, Efficient Return/Risk
│ │ ├── risk_parity.py # ERC / generalized risk budgeting
│ │ ├── cvar_optimizer.py # Rockafellar-Uryasev (2000)
│ │ ├── constraints.py # Fluent constraint builder
│ │ └── factory.py # Strategy dispatch
│ ├── backtest/
│ │ └── engine.py # Walk-forward engine + transaction costs
│ ├── analytics/
│ │ └── performance.py # Sharpe, Sortino, Calmar, Omega, attribution
│ └── plotting/
│ └── charts.py # Matplotlib/Seaborn utilities
├── notebooks/
│ ├── demo.ipynb # Quick-start demonstration
│ └── visualization.ipynb # Full documentation & figure generation
├── tests/ # pytest suite (9 modules)
├── docs/figures/ # Auto-generated figures
├── pyproject.toml # Build config (setuptools, Black, MyPy)
├── setup.py # Package metadata
└── requirements.txt # Pinned dependencies
The historical mean estimator computes the time-average of observed log returns and annualizes by geometric compounding:
The exponentially weighted variant assigns weight
Preferred over the historical mean when idiosyncratic noise dominates and a reliable market proxy is available.
The BL model blends a market-equilibrium prior
The preferred choice for production portfolios — shrinks extreme historical estimates toward equilibrium, reducing estimation error sensitivity.
| Estimator | Method | When to Use |
|---|---|---|
| Sample | Unbiased |
|
| EWM | Exponentially decaying weights |
Regime-changing volatility |
| Ledoit-Wolf OAS | Shrinkage toward scaled identity: |
Default; |
| PCA Factor Model | Known block-correlation structure |
All methods use SLSQP with analytical gradients:
- Maximum Sharpe Ratio — 5 Dirichlet random restarts; best global objective returned
- Minimum Variance — Single convex-quadratic solve from equal-weight initialization
- Efficient Return / Risk — Constraint-based frontier tracing
-
L2 Regularization —
$\gamma\mathbf{w}^\top\mathbf{w}$ penalty drives concentrated portfolios toward equal weight
Minimizes ftol=1e-12. Preferred when the investor is agnostic about expected returns.
Jointly convex in
ConstraintSet provides a fluent API for composing scipy.optimize-compatible constraints:
| Method | Constraint |
|---|---|
.long_only() |
|
.long_short(G, N_e) |
Gross/net exposure bounds |
.max_position(limit) |
Per-asset upper bound |
.sector_neutral(map, dev) |
Sector deviation from benchmark |
.max_turnover(limit, w_curr) |
One-way turnover cap |
.factor_exposure(B, min, max) |
Factor exposure bounds |
The WalkForwardBacktester executes a rolling optimization pipeline: at each rebalance date it extracts the lookback window, constructs and fits the optimizer, applies turnover constraints, and deducts transaction costs. Between rebalance dates, weights drift mark-to-market. Returns a BacktestResult with full diagnostics including cumulative returns, turnover history, realized weights, and dollar value series.
Simulated price paths, correlation structure, return densities, and cross-sectional volatility ordering for the 10-asset GBM universe.
QQ plot of pooled daily log returns vs. standard normal; rolling 63-day cross-asset realized volatility.
True drift vs. historical, EWM, and CAPM estimates; Black-Litterman equilibrium prior vs. posterior scatter.
Correlation matrix heatmaps: Sample, EWM, Ledoit-Wolf OAS, PCA Factor Model.
Eigenvalue spectra; shrinkage intensity; systematic vs. idiosyncratic variance decomposition.
Efficient frontier colored by Sharpe ratio with CML, minimum variance, and tangency portfolio.
Weights for Max Sharpe, Min Variance, and Efficient Return (80% target) portfolios.
Weight and risk contribution comparison; diversification ratio and HHI across strategies.
Loss distribution with VaR/CVaR marks; CVaR-vs-beta curve; tail decomposition; weight comparison.
Net-of-costs cumulative portfolio value for all four strategies (2019–2024).
Drawdown time series and monthly one-way turnover by strategy.
Rolling 63-day Sharpe ratio, realized volatility, and maximum drawdown.
Cross-strategy performance metrics; factor attribution (alpha and market beta).
Six-panel dashboard: return, volatility, Sharpe, max drawdown, CVaR 95%, effective N.
Sharpe ratio and HHI as a function of L2 regularization strength.
Walk-forward backtest on 5 years of synthetic GBM data (seed=42, monthly rebalancing, 252-day lookback, 10 bps transaction costs).
| Strategy | Ann. Return | Ann. Vol | Sharpe | Sortino | Max DD | CVaR 95% |
|---|---|---|---|---|---|---|
| Max Sharpe (BL+LW) | 12.28% | 7.68% | 1.34 | 2.06 | -7.70% | 0.91% |
| Min Volatility | 9.56% | 6.94% | 1.09 | 1.68 | -8.63% | 0.85% |
| Risk Parity (EWM) | 10.33% | 7.41% | 1.12 | 1.72 | -9.37% | 0.89% |
| CVaR Optimizer | 8.04% | 7.31% | 0.83 | 1.26 | -9.81% | 0.90% |
Reproduce:
jupyter notebook notebooks/visualization.ipynb— execute all cells end-to-end.
All four strategies achieve positive risk-adjusted returns on GBM-simulated data. The Max Sharpe (BL+LW) strategy leads with a 1.34 Sharpe ratio and 12.28% annualized return, benefiting from equilibrium-anchored return estimates that prevent the optimizer from acting on noise. The Black-Litterman framework's primary value is structural: by anchoring to market-cap-implied equilibrium, it produces tractable condition numbers across the full rolling window history.
The cross-strategy comparison reveals a clear concentration–diversification tension. Max Sharpe achieves the shallowest drawdown (-7.70%) but concentrates risk budget in 2–3 assets. Risk Parity inverts this logic, achieving the highest diversification ratio at the cost of a lower Sharpe. The practical conclusion: risk parity dominates when drift estimation is unreliable (low
The CVaR optimizer targets expected tail loss rather than variance. On symmetric GBM data the difference from mean-variance is modest; on data with negative skewness and excess kurtosis, the CVaR-optimal solution would further de-risk tail contributors relative to Sharpe-maximizing weights.
Model Assumptions
GBM assumes constant drift and volatility — empirically false for equities which exhibit GARCH clustering, vol mean-reversion, and regime changes. Backtest results on GBM data overstate reliability because the DGP is stationary. The PCA factor model uses statistical (not fundamental) factors that may rotate across windows.
Estimation Error
Per Merton (1980), expected return estimation error dominates covariance error for typical
Optimization
SLSQP is a local solver. Multi-restart reduces but does not eliminate non-global termination risk for
Backtesting
No autocorrelation, vol clustering, or structural breaks in synthetic data. No survivorship bias correction. Transaction cost model is proportional (no market impact / Almgren-Chriss). No slippage or execution delay.
- Hierarchical Risk Parity (HRP) — López de Prado (2016) recursive bisection
- Regime-switching covariance — HMM with Viterbi decoding
- Online covariance updates — Sherman-Morrison-Woodbury
$O(N^2)$ streaming - Cardinality constraints — MIQP via GUROBI / CVXPY SCIP
- Robust optimization — Ben-Tal & Nemirovski ellipsoidal uncertainty sets (SOCP)
- Multi-period dynamic optimization — TC-regularized stochastic DP
Expand full reference list
- Markowitz, H. M. (1952). Portfolio selection. Journal of Finance, 7(1), 77–91.
- Black, F., & Litterman, R. (1992). Global portfolio optimization. Financial Analysts Journal, 48(5), 28–43.
- He, G., & Litterman, R. (1999). The intuition behind Black-Litterman model portfolios. Goldman Sachs.
- Ledoit, O., & Wolf, M. (2004). A well-conditioned estimator for large-dimensional covariance matrices. J. Multivariate Analysis, 88(2), 365–411.
- Chen, Y., Wiesel, A., Eldar, Y. C., & Hero, A. O. (2010). Shrinkage algorithms for MMSE covariance estimation. IEEE Trans. Signal Processing, 58(10), 5016–5029.
- Rockafellar, R. T., & Uryasev, S. (2000). Optimization of conditional value-at-risk. Journal of Risk, 2(3), 21–41.
- Roncalli, T. (2013). Introduction to risk parity and budgeting. CRC Press.
- Merton, R. C. (1980). On estimating the expected return on the market. J. Financial Economics, 8(4), 323–361.
- Ben-Tal, A., & Nemirovski, A. (1998). Robust convex optimization. Math. Operations Research, 23(4), 769–805.
- Almgren, R., & Chriss, N. (2001). Optimal execution of portfolio transactions. Journal of Risk, 3(2), 5–39.
- MSCI Barra. (2011). Barra risk model handbook. MSCI.
- López de Prado, M. (2016). Building diversified portfolios that outperform out-of-sample. J. Portfolio Management, 42(4), 59–69.
Built by Felipe Cardozo














