| myst |
|
|---|
Author: Artur Sepp / First recorded: 2023-07-08
Source: OptimalPortfolios. Software citation: CITATION.cff. Analytics and holdings simulation use qis; cite its software record.
Explore the analytics gallery for reproducible synthetic examples with sample dates, conventions, producer links and reviewed provenance.
Production multi-asset portfolio construction and rolling backtesting in Python — from point-in-time covariance and alpha estimation through constrained optimisation, rebalancing, transaction costs, and reporting.
Install: pip install optimalportfolios · Import: optimalportfolios · Status: Stable
Papers: Sepp, A. (2023), Optimal Allocation to Cryptocurrencies in Diversified Portfolios, Risk Magazine — SSRN 4217841 · Sepp, A., Ossa, I. and Kastenholz, M. (2026), Robust Optimization of Strategic and Tactical Asset Allocation for Multi-Asset Portfolios, The Journal of Portfolio Management, 52(4), 86–120 · Sepp, A., Hansen, E. and Kastenholz, M. (2026), Capital Market Assumptions and Strategic Asset Allocation Using Multi-Asset Tradable Factors — SSRN 6785958. See References.
PyPortfolioOpt, Riskfolio-Lib, and skfolio all provide substantial portfolio
optimisation capabilities. Their documented design centres emphasise, respectively,
compact classical allocation, breadth across risk measures and portfolio families,
and scikit-learn-compatible model selection. optimalportfolios is organised around
a different primary abstraction: a dated state transition from estimates and current
holdings to constrained targets and realised backtests.
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.
- Production multi-asset pipeline. Factor-model covariance, risk-budgeted strategic allocation, alpha signals, tracking-error-constrained tactical allocation and a rolling backtest share one dated roll-forward state, with equities rebalancing monthly and alternatives quarterly. See the ROSAA case study.
- HCGL factor covariance. Sparse, structured covariance for heterogeneous universes, fitted
by
factorlassoand assembled point in time byFactorCovarEstimatoracross return frequencies. See factor covariance with HCGL. - Cluster-aware risk allocation. Clusters, sectors or asset classes become asset-level risk budgets, static or dated, and canonical HRP runs from a supplied linkage. See hierarchical risk parity and cluster risk budgets.
- Drift-aware rolling backtests. Turnover limits and cost penalties act on the drifted
holdings rather than the previous target (
OptimiserConfig.use_drifted_weights_0, defaultTrue). See rolling backtests. - Incomplete and illiquid histories. Assets enter when their history suffices, missing prices get zero weight, and rebalancing indicators freeze illiquid positions. See incomplete histories and frozen positions.
- Research-backed. The reference implementation of the ROSAA framework in The Journal of Portfolio Management; every number on the methodology pages is asserted by a script that CI runs. See research papers.
The production quickstart is the authoritative source for the first-use workflow. It runs entirely offline on the multi-asset fixture shipped in the wheel and writes no files:
pip install optimalportfolios
python examples/getting_started/production_quickstart.pyFor a zero-setup trial, open the mechanically checked mirror in Colab. The notebook installs the latest PyPI release, prints its version, and adds no notebook dependency to the package.
The script uses a documented six-asset slice, a point-in-time 24-month EWMA covariance estimator, quarterly constrained minimum-variance weights, a one-month implementation lag, and 10 basis points of transaction costs. It prints the data range, rolling-weight dimensions, final weights, final NAV, and measured runtime. The rendered quickstart documentation includes this same file directly, so the example and documentation cannot drift.
The script above remains the authoritative first-use workflow. The shorter version below exists so that the README's own code is executed rather than trusted:
import qis
from optimalportfolios import (
Constraints,
EwmaCovarEstimator,
PortfolioObjective,
compute_rolling_optimal_weights,
)
from optimalportfolios.tests.data.multiasset import load_multiasset_data
prices = load_multiasset_data().prices.iloc[-120:, :4]
time_period = qis.TimePeriod(prices.index[0], prices.index[-1])
# estimate covariance → optimise → get rolling weights
estimator = EwmaCovarEstimator(returns_freq='ME', span=24, 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.loc[weights.index[0]:], weights=weights,
rebalancing_costs=0.001, ticker='MaxDiv')
print(f"assets: {list(weights.columns)}")
print(f"rebalance dates: {len(weights.index)}")
print(f"long only: {bool((weights >= -1e-6).all().all())}")
print(f"fully invested: {bool(weights.sum(axis=1).round(6).eq(1.0).all())}")
print(f"nav name: {portfolio.nav.name}")assets: ['Global Bonds', 'Global IG Bonds', 'US Treasuries', 'US TIPs']
rebalance dates: 39
long only: True
fully invested: True
nav name: MaxDiv
readme_test.py executes the block above and diffs its output against that
result fence, so this example cannot drift from what the package actually
does. Structural facts are asserted rather than weights: a solver-version change
may move an allocation by 1e-9, but it must not change the rebalance schedule,
break long-only, or stop the book being fully invested.
The committed multi-asset fixture keeps this example offline. The same pipeline supports price panels with NaNs and different start dates, while preserving roll-forward estimation (no hindsight bias) and drift-aware turnover accounting.
The solvers use quadratic and conic objectives (variance, tracking error, Sharpe ratio, diversification ratio, CARA utility); non-quadratic risk measures such as CVaR, MAD or drawdown constraints are out of scope, and Riskfolio-Lib or skfolio cover them. Each solver lives in its own module and plugs into the rolling backtester through one dispatch function: the software-design guide explains these boundaries, and the package comparison records the versioned evidence for the field comparison.
Use optimalportfolios when you need a dated roll-forward pipeline from point-in-time covariance
and alpha estimates to constrained targets, scheduled rebalancing, and drift-aware backtests with
transaction costs across incomplete or mixed-frequency multi-asset panels.
Choose another package when the core problem is a non-quadratic risk measure such as CVaR, MAD,
or drawdown constraints; the package comparison points to Riskfolio-Lib and skfolio for those
workflows. Use factorlasso directly when you need its standalone sparse multi-output factor-model
estimator rather than portfolio construction.
| Subpackage | What it holds | Articles |
|---|---|---|
covar_estimation |
EWMA and HCGL factor covariance estimators, the qis.RiskModel adapter, covariance reports |
covariance estimators, factor covariance, risk contributions and betas |
alphas |
Momentum, low-beta, carry, residual and managers' alpha signals, AlphasData, profiling and diagnostics |
alpha signals, signal diagnostics |
optimization |
The rolling dispatcher, the general, risk-allocation, SAA and TAA solvers, Constraints, OptimiserConfig and solver diagnostics |
choosing an objective, constraints, solver outcomes |
universe |
UniverseData and its transforms, such as unsmoothing |
universe data |
utils |
Risk contributions, benchmark betas, weight drift, NaN filtering, Gaussian mixtures | risk contributions and betas |
reports |
Result plots, marginal backtests and optional PyBloqs reports | analytics gallery |
The software-design guide draws the module imports, and the API reference lists every public object.
| Area | Current user-facing analytics |
|---|---|
| Alpha construction | Momentum, low beta, risk-adjusted carry, managers alpha, residual momentum, residual reversal and rolling EWMA means; fixed-group and time-varying cluster scoring are supported. |
| Alpha evaluation | Rank-portfolio profiling, cross-backtests, AlphasData, IC/IR panels, component diagnostics and comparison tables. |
| Covariance and dependence | Current/rolling EWMA and HCGL sparse factor covariance; Pearson, Spearman and Gerber dependence choices, configurable correlation-distance transforms through factorlasso, and current/rolling covariance diagnostic reports. |
| Risk-cluster analytics | Persistent cluster lineage, births/deaths/splits/merges and report tables/figures through factorlasso.cluster_lineage (analyze_cluster_lineage() and run_cluster_lineage_report()); the analyze_risk_clusters() and run_risk_label_report() aliases in optimalportfolios.covar_estimation.risk_labelling are deprecated. |
| General optimisation | Minimum variance, quadratic utility, maximum Sharpe, maximum diversification, CARA Gaussian-mixture utility and minimum tracking error. |
| Risk allocation | Constrained risk budgeting, point-in-time group risk budgets, date-varying rolling budgets, group Euler-risk attribution and external-linkage hierarchical risk parity. |
| SAA and TAA optimisation | Minimum variance at target return, maximum return at target volatility, alpha over tracking error and alpha at target portfolio return. |
| Constraints and implementation | Instrument/group bounds, exposure, turnover, tracking error, target return/volatility, benchmark-relative sector/style/beta limits, frozen holdings and current-to-model eligibility corridors. |
| Solver controls and diagnostics | One covariance factorization per compatible CVXPY solve, input-contract validation, structured OptimizationOutcome/ConstraintResidual output, infeasibility diagnosis and run-level warning summaries. |
| Portfolio and risk results | PortfolioOptimisationResult provides weights/trades, volatility, turnover, tracking error, factor/residual risk, group attribution, factor exposures, efficient-frontier data and report tables using qis.RiskModel. |
| Universe, backtest and reporting | Validated UniverseData, metadata/group-loadings persistence and transforms, drift-aware rolling weights, transaction-cost backtests through qis, efficient-frontier plots, marginal portfolio backtests and optional PyBloqs HTML/PDF reports. |
This table groups the analytics by workflow. The exact package-root import inventory and callable signatures are maintained in the API reference.
Architecture: factorlasso vs optimalportfolios
factorlasso is the domain-agnostic sparse
factor-model estimator, with sign constraints, prior-centred regularisation and HCGL clustering;
it knows nothing about asset returns, frequencies or rebalancing. optimalportfolios adds the
finance layer: estimate_lasso_factor_covar_data() computes factor returns from prices, fits
the factor model to the asset returns of each frequency and annualises the decomposition, and
FactorCovarEstimator runs it on a rolling schedule. See factor covariance with HCGL.
See hierarchical risk parity and cluster risk budgets for group risk budgets and hierarchical risk parity, including date-by-asset cluster labels, and the risk-budgeting guide for the solver that group budgets feed.
See the alpha signals guide for the signal catalogue, mixed-frequency inputs, cluster scoring, and the AlphasData container.
- Why optimalportfolios
- Package overview
- Cluster-aware risk allocation
- Alpha signals module
- Installation
- Portfolio Optimisers
- Examples
- Updates
- Disclaimer
Install from PyPI:
pip install optimalportfoliosAfter installing pytest, verify the installed wheel with python -m pytest --pyargs optimalportfolios.
Upgrade with:
pip install --upgrade optimalportfoliosClone the repository with:
git clone https://github.com/ArturSepp/OptimalPortfolios.gitThe core package supports Python >=3.10; pyproject.toml is the source of truth for dependency
floors, and the installation guide describes the locked environment.
Optional extras keep network-data and reporting integrations out of the core installation:
| Extra | Adds |
|---|---|
data |
yfinance for free-data example loaders. |
reports |
pybloqs for HTML/PDF report backends. |
docs |
Sphinx, Furo and MyST for documentation builds. |
For both runtime integrations, install optimalportfolios[data,reports]. There is no jupyter
or dev extra; tests and static checks are the PEP 735 test and lint dependency groups, and
CONTRIBUTING.md describes the contributor environment.
See the optimisation module guide for solver architecture, constraints, backends, and configuration. The rolling backtest guide covers rebalancing and transaction costs; supported examples provide complete runnable workflows.
The examples/ folder is organised by purpose. The
examples and recipes guide maps every task to its article, canonical
script and standalone examples, with each example's offline, network or local-data lane:
examples/
├── getting_started/ Canonical offline quickstart and its notebook mirror
├── data/ Shared Yahoo loaders and local universe builders
├── solvers/ Objective-specific examples
├── backtests/ Complete rolling workflows
├── comparisons/ Comparisons of methods or configurations
├── covar_estimation/ Covariance and factor-model examples
├── alphas/ Signal profiling
├── reports/ Manually prepared portfolio reports
├── docs/ Canonical scripts of the documentation pages
└── figures/ Existing documentation previews
- Quickstart — the smallest offline portfolio workflow.
examples/backtests/multiasset_saa.py— another offline workflow with group metadata and objective choices.examples/data/universe.pyandexamples/backtests/minimal_backtest.py— downloaded prices and reporting.examples/solvers/min_variance.pyandexamples/solvers/minimum_tracking_error.py— covariance-based construction.examples/solvers/tracking_error.py— benchmark-relative allocation with a signal.examples/comparisons/optimisers.py— how objectives differ on the same universe.
examples/backtests/minimal_backtest.py fetches eight
ETFs, estimates an EWMA covariance, solves maximum diversification each quarter, backtests with
transaction costs and writes a qis factsheet. The previews in this section are offline teaching
exhibits on a fixed synthetic sample ending 31 December 2025, produced by the scripts named with
each; the shared provenance record records their
inputs, configuration, software versions and visual review.
PortfolioData from qis plots NAV, weights and
return scatters. The preview from portfolio_reports.py
shows quarterly target weights and realised trading costs.
examples/comparisons/parameter_sensitivity.py
backtests one method across estimation parameters; the preview from
span_sensitivity.py compares five EWMA spans.
examples/comparisons/optimisers.py runs several objectives
through compute_rolling_optimal_weights(); the preview from
optimiser_comparison.py compares minimum
variance, maximum diversification and equal risk budgets on shared inputs.
examples/comparisons/covar_estimators.py backtests one
objective with several covariance estimators; the preview from
covariance_comparison.py compares six estimators
on one known-factor simulation, which does not establish an estimator ranking.
examples/comparisons/drift_policy.py compares
OptimiserConfig.use_drifted_weights_0 = True (the default) with False under a binding
turnover budget; see turnover and transaction costs.
The paper's replication code is in
papers/crypto_allocation_risk_2023; the
cryptocurrency case study reports its design and results and runs
the four methods offline.
The paper's example is in
papers/robust_optimisation_jpm_2026; the
ROSAA case study reports the framework, its study
design and results, and runs the same configuration offline.
See the changelog for release history and migration notes.
- Thomas Schmelzer, creator of Jebel-Quant/rhiza, for substantial contributions to test coverage, cross-platform CI/CD, dependency auditing, example validation, packaging, and built-wheel verification.
Sepp A. (2023), "Optimal Allocation to Cryptocurrencies in Diversified Portfolios", Risk Magazine, October 2023, 1-6. Available at https://ssrn.com/abstract=4217841
Sepp A., Ossa I., and Kastenholz M. (2026), "Robust Optimization of Strategic and Tactical Asset Allocation for Multi-Asset Portfolios", The Journal of Portfolio Management, 52(4), 86-120. Paper link
Sepp A., Hansen E., and Kastenholz M. (2026), "Capital Market Assumptions and Strategic Asset Allocation Using Multi-Asset Tradable Factors", Under revision at the Journal of Portfolio Management. Available at https://ssrn.com/abstract=6785958
This package is part of an open-source Python stack for quantitative finance. The ArturSepp profile is the canonical full catalogue:
| Package | Purpose |
|---|---|
qis |
Performance analytics, factsheets, and visualisation |
optimalportfolios (this package) |
Portfolio construction and backtesting |
factorlasso |
Sparse factor models and factor covariance estimation |
bbg-fetch |
Bloomberg data fetching |
option-chain-analytics |
Point-in-time option-chain normalisation, reconstruction, querying, and visualisation |
vanilla-option-pricers |
Vectorised vanilla option pricers and implied volatility fitters |
stochvolmodels |
Stochastic volatility pricing analytics |
trendfollowing |
Trend-following systems: closed-form theory and replication |
privateassets |
Money-weighted multi-factor alpha from private-asset cash flows |
goal-based-allocation |
Dynamic MV allocation under regime-switching jump-diffusions |
Within the stack, optimalportfolios directly depends on qis for analytics and reporting and on
factorlasso for sparse factor covariance estimation. The profile catalogue explains the other
packages and their distinct boundaries.
- Bug: use the bug-report form with the package version, Python/platform, a minimal public-data reproducer, and expected versus actual output.
- Feature: use the feature-request form and describe the user goal, current workaround, and smallest useful API. In particular: which constraint, report, or portfolio workflow cannot be expressed today?
- Question or methodology: search or open an issue and name the paper, example, or convention involved.
- Contribution: follow CONTRIBUTING.md; focused work is listed under
good first issueandhelp wanted.
Project decisions, maintenance expectations, release policy, and best-effort support routes are documented in GOVERNANCE.md.
A machine-readable citation is available in CITATION.cff.
If you use optimalportfolios in your research, please cite it as:
@software{sepp2026optimalportfolios,
author={Sepp, Artur},
title={optimalportfolios: point-in-time multi-asset portfolio construction and rolling backtesting in Python},
year={2026},
version={7.9.0},
url={https://github.com/ArturSepp/OptimalPortfolios}
}@article{sepp2023,
title={Optimal allocation to cryptocurrencies in diversified portfolios},
author={Sepp, Artur},
journal={Risk Magazine},
pages={1--6},
month={October},
year={2023},
url={https://ssrn.com/abstract=4217841}
}@article{sepp2026rosaa,
author={Sepp, Artur and Ossa, Ivan and Kastenholz, Mika},
title={Robust Optimization of Strategic and Tactical Asset Allocation for Multi-Asset Portfolios},
journal={The Journal of Portfolio Management},
volume={52},
number={4},
pages={86--120},
year={2026}
}@article{sepphansenkastenholz2026,
title={Capital Market Assumptions and Strategic Asset Allocation Using Multi-Asset Tradable Factors},
author={Sepp, Artur and Hansen, Emilie H. and Kastenholz, Mika},
journal={Working Paper},
year={2026}
}MIT — see LICENSE.txt.
OptimalPortfolios package is distributed FREE & WITHOUT ANY WARRANTY under the MIT License.
See the LICENSE.txt in the release for details.
Use the dedicated routes in Feedback & contributing for bugs, feature requests, and methodology questions.