OptimalPortfolios
Implementation of optimisation analytics for constructing and backtesting optimal portfolios in Python
Install / Use
/learn @ArturSepp/OptimalPortfoliosREADME
🚀 Optimal Portfolios Construction and Backtesting: optimalportfolios
Production-grade multi-asset portfolio construction and backtesting in Python — from covariance estimation to rolling optimisation to factsheet reporting, in a single pipeline that handles real-world data
| 📊 Metric | 🔢 Value |
|-----------|----------|
| PyPI Version | |
| Python Versions |
|
| License |
|
| CI Status |
|
📈 Package Statistics
| 📊 Metric | 🔢 Value |
|-----------|----------|
| Total Downloads | |
| Monthly |
|
| Weekly |
|
| GitHub Stars |
|
| GitHub Forks |
|
Why optimalportfolios <a name="analytics"></a>
Most Python portfolio optimisation packages (PyPortfolioOpt, Riskfolio-Lib, skfolio) solve single-period allocation problems: given a covariance matrix and expected returns, find the optimal weights. This is useful for textbook exercises but insufficient for running a real multi-asset portfolio.
optimalportfolios solves the production problem end-to-end: estimate covariance → compute alpha signals → optimise with constraints → rebalance on schedule → backtest with transaction costs — all in a single roll-forward pipeline that handles incomplete data, mixed-frequency assets, and illiquid positions.
Key differentiators
Production multi-asset portfolio construction.
The package implements the full pipeline from the ROSAA framework: factor model
covariance estimation (via factorlasso)
→ risk-budgeted SAA → alpha signal computation →
TE-constrained TAA → rolling backtest. No other open-source package handles
universes where equities rebalance monthly, alternatives rebalance quarterly,
and private equity enters the allocation set only when sufficient return history
is available. The constraint system (weight bounds, group allocation limits,
tracking error budgets, turnover controls, rebalancing indicators for frozen
positions) matches what real institutional PM teams need.
HCGL factor covariance estimation.
The Hierarchical Clustering Group LASSO factor model (published in JPM, 2026)
produces sparse, structured covariance matrices for heterogeneous multi-asset
universes. The LASSO/Group LASSO/HCGL solver is implemented in the standalone
factorlasso package — a
general-purpose sparse factor model estimator with sign constraints,
prior-centered regularisation, and scikit-learn compatible API.
optimalportfolios builds on top of factorlasso with finance-specific
functionality: FactorCovarEstimator handles multi-frequency asset returns,
rolling estimation schedules, factor covariance assembly
(Σ_y = β Σ_x β' + D), and integration with qis for performance attribution.
The separation means the LASSO solver can be used independently for any
multi-output regression problem (genomics, macro-econometrics), while the
portfolio-specific rolling pipeline stays in optimalportfolios.
NaN-aware rolling backtesting. The three-layer architecture (solver / wrapper / rolling) automatically handles real-world data: assets with missing prices receive zero weight, assets entering the universe mid-sample are included when sufficient history is available, and the rebalancing indicator system freezes illiquid positions at their current weight while re-optimising the liquid portion. No data cleaning or pre-filtering required.
Research-backed methodology. The package is the reference implementation for the ROSAA framework published in The Journal of Portfolio Management (Sepp, Ossa, Kastenholz, 2026). The optimisation solvers, covariance estimators, and alpha signals are battle-tested on live multi-asset portfolios.
Quick-start: rolling backtest in 10 lines
import qis as qis
from optimalportfolios import (EwmaCovarEstimator, Constraints,
PortfolioObjective, compute_rolling_optimal_weights)
prices = ... # pd.DataFrame of asset prices (may have NaNs, different start dates)
time_period = qis.TimePeriod('31Dec2004', '15Mar2026')
# estimate covariance → optimise → get rolling weights
estimator = EwmaCovarEstimator(returns_freq='W-WED', span=52, rebalancing_freq='QE')
covar_dict = estimator.fit_rolling_covars(prices=prices, time_period=time_period)
weights = compute_rolling_optimal_weights(prices=prices,
portfolio_objective=PortfolioObjective.MAX_DIVERSIFICATION,
constraints=Constraints(is_long_only=True),
time_period=time_period,
covar_dict=covar_dict)
# backtest with transaction costs
portfolio = qis.backtest_model_portfolio(prices=prices, weights=weights,
rebalancing_costs=0.001, ticker='MaxDiv')
That's it — from prices to backtested portfolio in 10 lines, with automatic NaN handling, roll-forward estimation (no hindsight bias), and any optimisation objective. Try doing this with PyPortfolioOpt or skfolio — you'll need to write the rolling loop, covariance estimation, NaN filtering, and backtesting yourself.
Design scope
The optimisation solvers use quadratic and conic objective functions (variance,
tracking error, Sharpe ratio, diversification ratio, CARA utility). The package
does not implement non-quadratic risk measures (CVaR, MAD, drawdown constraints).
For these, use Riskfolio-Lib or skfolio. The solver architecture (three-layer:
mathematical / wrapper / rolling) makes it straightforward to add new solvers —
each solver lives in its own module under optimization/ (grouped into
general/, saa/, and taa/ submodules) and plugs into the rolling
backtester via a single dispatch function.
Package overview <a name="overview"></a>
optimalportfolios/
├── alphas/ # Alpha signal computation
│ ├── signals/
│ │ ├── momentum.py # compute_momentum_alpha()
│ │ ├── low_beta.py # compute_low_beta_alpha()
│ │ └── managers_alpha.py # compute_managers_alpha()
│ ├── alpha_data.py # AlphasData container
│ ├── backtest_alphas.py # Signal backtesting tool
│ └── tests/
│ └── signals_test.py
├── covar_estimation/ # Covariance matrix estimation
│ ├── covar_estimator.py # CovarEstimator ABC
│ ├── ewma_covar_estimator.py # EwmaCovarEstimator
│ ├── factor_covar_estimator.py # FactorCovarEstimator (uses factorlasso)
│ ├── factor_covar_data.py # CurrentFactorCovarData, RollingFactorCovarData
│ └── covar_reporting.py # Rolling covariance diagnostics
├── optimization/ # Portfolio optimisation
│ ├── constraints.py # Constraints, GroupLowerUpperConstraints
│ ├── config.py # OptimiserConfig dataclass
│ ├── wrapper_rolling_portfolios.py # compute_rolling_optimal_weights()
│ ├── general/ # Objective-driven solvers (no benchmark semantics)
│ │ ├── quadratic.py # min variance, max quadratic utility
│ │ ├── max_sharpe.py # maximum Sharpe ratio (Charnes-Cooper)
│ │ ├── max_diversification.py # maximum diversification ratio
│ │ ├── risk_budgeting.py # constrained risk budgeting (pyrb)
│ │ └── carra_mixture.py # CARA utility under Gaussian mixture
│ ├── saa/ # Strategic solvers (CMA inputs, return/vol targets)
│ │ ├── min_variance_target_return.py
│ │ └── max_return_target_vol.py
│ ├── taa/ # Tactical solvers (alphas, TE constraints, benchmarks)
│ │ ├── maximise_alpha_over_tre.py
│ │ └── maximise_alpha_with_target_yield.py
│ └── tests/ # One test file per solver
├── utils/ # Auxiliary analytics
│ ├── filter_nans.py # NaN-aware covariance/vector filtering
│ ├── portfolio_funcs.py # Risk contributions, diversification ratio
│ ├── gaussian_mixture.py # Gaussian mixture fitting (pure numpy/scipy EM)
│ └── returns_unsmoother.py # AR(1) return unsmoothing for PE/PD
├── reports/ # Performance reporting
│ └── marginal_backtest.py # Marginal asset contribution analysis
└── examples/ # Worked examples and paper reproductions
# External dependency:
# factorlasso (pip install factorlasso)
# └── LassoModel, solve_lasso_cvx_problem, solve_group_lasso_cvx_problem
# Sign-constrained LASSO/Group LASSO/HCGL solver (domain-agnostic)
# https://github.com/ArturSepp/factorlasso
Architecture: factorlasso vs optimalportfolios
factorlasso is the domain-agnostic
LASSO solver — it estimates sparse factor loadings β in Y_t = α + β X_t + ε_t with sign
constraints, prior-centered regularisation, and HCGL clustering. It provides
LassoModel (scikit-learn compatible estimator), CurrentFactorCovarData
(single-date covariance decomposition Σ_y = β Σ_x β' + D), and
RollingFactorCovarData (time-indexed collection). It knows nothi
