Cvxportfolio: portfolio optimisation and back-testing that keeps the optimiser in the loop
Portfolio optimization and back-testing.
At a glance
- What is it?
- Cvxportfolio is an object-oriented Python library for portfolio optimisation and back-testing, built on CVXPY and Pandas, where the policy is re-solved at every step of the simulation using only past data. It is GPL-3.0 licensed, developed by the CVXPY group, and its daily example strategies have been committing live target weights since the end of 2023.
- Who is it for?
- Cvxportfolio fits a research or prototyping team that wants a portfolio model expressed as an objective and constraints, simulated honestly with costs and causality enforced, rather than a production trading system you can point at a broker today. It does not fit a team wanting a hosted allocation service, and it does not fit a shop that cannot accept GPL-3.0.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 179 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The policy is re-solved at every point in the back-test
Cvxportfolio is an object-oriented library for portfolio optimisation and back-testing, and it implements the models described in the accompanying paper. The design decision that defines it is that the optimisation is not run once at the start of a back-test. It is run again at every point in the simulation, each time from past data only.
The simple example makes the mechanics visible:
import cvxportfolio as cvx
gamma = 3 # risk aversion parameter (Chapter 4.2)
kappa = 0.05 # covariance forecast error risk parameter (Chapter 4.3)
objective = cvx.ReturnsForecast() - gamma * (
cvx.FullCovariance() + kappa * cvx.RiskForecastError()
) - cvx.StocksTransactionCost()
constraints = [cvx.LeverageLimit(3)]
policy = cvx.MultiPeriodOptimization(objective, constraints, planning_horizon=2)
simulator = cvx.StockMarketSimulator(['AAPL', 'AMZN', 'TSLA', 'GM', 'CVX', 'NKE'])
result = simulator.backtest(policy, start_time='2020-01-01')Read the objective rather than the syntax. The thing being maximised is a forecast of returns, minus a risk penalty built from the full covariance estimate plus a term for covariance forecast error, minus a transaction cost model. The constraint list holds a single exposure cap, written as `cvx.LeverageLimit(3)`. The policy wraps all of that in a `MultiPeriodOptimization` with a planning horizon of 2, and the simulator runs the whole thing over six tickers from a start date.
That is the mechanism: a policy object that knows how to make a decision given the state of the world so far, and a simulator that repeatedly asks it to decide. The two comments naming the paper chapters for `gamma` and `kappa` are a useful hint that this is a teaching implementation of a specific framework, not a bag of heuristics.
Causality is enforced by the simulator, not by discipline
The claim that makes this library different from a hand-rolled back-test is stated plainly: at each point in the back-test, the policy object only operates on past data, and thus the result you get is a realistic simulation of what the strategy would have performed in the market.
The forecasters are the part of the design that carries that guarantee. Returns are forecast as the historical mean returns and covariances as historical covariances, both ignoring `np.nan` values. So the default is a naïve statistical forecast, and the machinery around it is what makes the forecast available at the right point in time rather than in hindsight.
The user-provided forecasters example extends this. You can define custom classes to forecast the expected returns and covariances, and these provide callbacks executed at each point in time during the back-test. The system enforces causality and safety against numerical errors, and the project recommends always including the default forecasters in any analysis, since the project describes them as very well tested.
That last point is a design opinion worth taking seriously. It says the defaults are the baseline and your custom forecaster is the addition, not the replacement, and it says the project has seen enough custom forecasters to have an opinion about which parts carry the risk.
Cost handling is on by default as well. The simulator includes holding and transaction costs using the models described in the paper, with default parameters that are typical for the US stock market. That default matters, because a back-test without costs is the standard way to get a spectacular and wrong result.
The daily example strategies are the project's own regression test
The most distinctive practice in this repository is not visible in the API at all. Since the end of 2023 the maintainers have been running daily example strategies using the development, master branch, and each day they commit target weights and initial holdings to the repository. All the code that runs them, including the cron script, is in the repository.
Read that as a testing strategy. Alongside a CI matrix that runs the test suite, the project runs live strategies every day against real market data and records what the optimiser decided. A change that breaks the optimiser shows up as an absurd target weight in tomorrow's commit rather than as a green tick. The unit test suite still exists and is run in CI, but the daily run is the harder check.
The strategies live under `examples/strategies/`, and the surrounding examples directory is organised as a set of papers, one folder per topic. There are `paper_examples/`, `user_contributed/`, and standalone scripts including `case_shiller.py`, `data_cleaning.py`, `dow30.py`, `etfs.py`, `hello_world.py`, `market_neutral.py`, `market_neutral_nocosts.py`, `multiperiod_tcost.py`, `regression_covariance.py`, `risk_models.py`, `sp500_ndx100.py`, `timing.py`, `universes.py`, and `user_provided_forecasters.py`.
The naming of `market_neutral.py` and `market_neutral_nocosts.py` as a pair is instructive. Running the same strategy with and without costs is exactly the diagnostic you want, and having both as first-class examples means the difference is one command rather than an afternoon of editing.
For the same reason, the documentation points at a sweep over hyper-parameters, using the parallel back-test machinery, to find the best strategy for a given selection of assets, and shows this on DOW30 components and on wide asset-class ETFs.
Pinning cvxpy below 1.7 is a stated compromise, not an oversight
The dependency situation in pyproject.toml is unusually candid, and reading the comments tells you more about the project than the version numbers do.
The dependencies list names pandas, numpy, matplotlib, and `cvxpy<1.7`, and the comment on that last one says it is temporary to make CI tests pass, with a link to a pull request in the scs-python repository. So the ceiling on the solver stack is a known, tracked compromise rather than an accident.
Then there is a longer note about the lack of pinning. The project says it tests in GitHub CI against various combinations of dependency versions, back to versions that were current around 2021, and that most probably older ones work too if they install. It notes that the reason some old versions cannot be tested is that they cannot be installed from wheel or source, since that code tends to break more easily over time, especially across platforms. It names Pandas and Cvxpy as the important dependencies and says it is not using any particularly new feature nor any part of their APIs that is not very well documented.
The recommendation at the end is the practical takeaway: install Cvxportfolio in a fresh environment with recent versions of the dependencies, or, if you are installing into a pre-existing environment, run `python -m cvxportfolio.tests` to check that your dependency versions work correctly. And please report bugs that come from a combination of dependency versions that break the library.
That is the right way to handle an unpinned numerical stack, and the fact that it is written down is a mark of a project that has been burned by it.
Install and run the test suite before you trust the model
Installation is a single pip command, and the project points to a video explaining it:
pip install -U cvxportfolioAnaconda installs are also supported, through the conda-forge channel. The two main dependencies are CVXPY, for interfacing with numerical solvers, and Pandas, for interfacing with databases, and the project states it does not require any specific version of its dependencies.
If you want the development version rather than a release, that is a direct install from the master branch:
pip install --upgrade --force-reinstall git+https://github.com/cvxgrp/cvxportfolio@masterThe test suite runs as a module, which is the check the project itself recommends for a pre-existing environment:
python -m cvxportfolio.testsThere is a second form, and it exists for a specific reason. If your environment has no internet access, you can run the suite ignoring the `cvx.errors.DownloadError` raised by the few unit tests that use the internet:
python -m cvxportfolio.tests --ignore-download-errorsThat flag tells you something about the test suite's shape. A handful of tests fetch data, which means the suite is not hermetic, which means a CI runner without network access fails for reasons that have nothing to do with the library. The flag is the escape hatch for that.
The supported Python versions are 3.8 through 3.12, and testing runs against recent versions of the main dependencies, from Pandas 1.4 and CVXPY 1.1 up to the current ones, on all major operating systems. The Makefile drives the same workflows locally, with an `env` target that creates a virtual environment and installs the editable package with its extras, and separate `test`, `lint`, `docs`, and `coverage` targets.
Where Cvxportfolio is the wrong tool
The boundaries are worth drawing clearly, because the library's reputation outruns its scope.
First, this is a research and prototyping library. It downloads data from public sources, it models costs with default parameters typical for the US stock market, and it produces back-test statistics. Nothing in the documentation describes connecting it to a broker, placing orders, or handling live position reconciliation. A team looking for a production allocation service is looking for a different product.
Second, the modelling is opinionated in a way that matters. Returns are forecast as historical means and covariances as historical covariances. That is a deliberate, defensible choice for a library, and it is also a weak forecast in absolute terms. Anyone who wants a better forecaster has to write one, and the project is clear that the default forecasters should stay in the analysis as a baseline.
Third, the licensing is GPL-3.0. The copyright header covers 2023 to 2024 for Enzo Busseti and 2016 for Enzo Busseti, Stephen Boyd, Steven Diamond, and BlackRock Inc., which tells you this is academic and industry work carried forward rather than a product. GPL-3.0 on a library that you link from proprietary code is a legal question for your side, and the difference between linking and modifying matters, so it deserves a real answer rather than an assumption.
Fourth, the solver ceiling is a live constraint. The `cvxpy<1.7` pin exists to keep CI green, and when the project eventually lifts it, the numerical behaviour of existing strategies may shift. A team with a research pipeline that must reproduce published numbers should treat a solver upgrade as a change requiring re-validation.
Fifth, and most practically, the back-test is only as good as the data. The library's job is to make the decision logic honest about causality, and it does that well. It cannot make the price series clean, and the presence of an example called `data_cleaning.py` in the examples directory is a quiet admission of that.
Editorial conclusion
Cvxportfolio fits a research or prototyping team that wants a portfolio model expressed as an objective and constraints, simulated honestly with costs and causality enforced, rather than a production trading system you can point at a broker today. It does not fit a team wanting a hosted allocation service, and it does not fit a shop that cannot accept GPL-3.0. Check three things before you build on it: that your solver stack is compatible, since the package pins `cvxpy<1.7` with a comment about making CI tests pass, that your data source is something the simulator can read, and that the version you install is the release you intend to cite, because the newest tagged release is 1.5.1 from 2025-07-06 while the last push was on 2026-04-27.
Frequently asked questions
How do I install cvxportfolio and run its tests?
Install it with `pip install -U cvxportfolio`, then run `python -m cvxportfolio.tests` in your environment. Anaconda installs are supported through the conda-forge channel, and the development version can be installed directly from the master branch with pip.
How does cvxportfolio avoid look-ahead bias in its back-tests?
The policy object only operates on past data at each point in the back-test, and the forecasts come from historical mean returns and historical covariances computed up to that point. Custom forecasters run as callbacks at each point in time, and the system enforces causality and safety against numerical errors.
What are the main dependencies of cvxportfolio and are version ranges pinned?
The main dependencies are CVXPY for interfacing with numerical solvers and Pandas for interfacing with databases, and the project states it does not require any specific version. The package does pin `cvxpy<1.7` temporarily to make CI tests pass, and the project asks you to run `python -m cvxportfolio.tests` in a pre-existing environment to check that your dependency versions work.
Does cvxportfolio include transaction costs in its back-tests?
Yes. The simulator by default includes holding and transaction costs, using the models described in the paper, with default parameters that are typical for the US stock market. The example set includes a market neutral strategy run both with and without costs for comparison.
What licence is cvxportfolio released under?
GPL-3.0. The pyproject.toml records the licence as GPLv3 with a comment that the GPL-3.0-or-later syntax is not yet used because it breaks the Python 3.8 build, and the README carries the full GNU General Public License text.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/cvxgrp-cvxportfolio)Was this useful?