Skip to main content

🔄 cvxsimulator

PyPI version Apache 2.0 License Downloads Coverage Status Renovate enabled

Open in GitHub Codespaces

A simple yet powerful simulator for investment strategies and portfolio backtesting.

Given a universe of $m$ assets we are given prices for each of them at time $t_1, t_2, \ldots t_n$, e.g. we operate using an $n \times m$ matrix where each column corresponds to a particular asset.

In a backtest we iterate in time (e.g. row by row) through the matrix and allocate positions to all or some of the assets. This tool helps to simplify the accounting. It keeps track of the available cash, the profits achieved, etc.

Analytics

📥 Installation

Install cvxsimulator via pip:

pip install cvxsimulator

📊 Creating Portfolios

The simulator is completely agnostic to the trading policy/strategy. Our approach follows a rather common pattern:

We demonstrate these steps with simple example policies. They are never good strategies, but are always valid ones.

Create the builder object

The user defines a builder object by loading prices and initializing the amount of cash used in an experiment:

import pandas as pd
from cvx.simulator import Builder

# For doctest, we'll create a small DataFrame instead of reading from a file
dates = pd.date_range('2020-01-01', periods=5)
prices = pd.DataFrame({
    'A': [100, 102, 104, 103, 105],
    'B': [50, 51, 52, 51, 53],
    'C': [200, 202, 198, 205, 210],
    'D': [75, 76, 77, 78, 79]
}, index = dates)
b = Builder(prices=prices, initial_aum=1e6)

Prices have to be valid, there may be NaNs only at the beginning and the end of each column in the frame. There can be no NaNs hiding in the middle of any time series.

It is also possible to specify a model for trading costs. The builder helps to fill up the frame of positions. Only once done we construct the actual portfolio.

Loop through time

We have overloaded the __iter__ and __setitem__ methods to create a custom loop. Let's start with a first strategy. Each day we choose two names from the universe at random. Buy one (say 0.1 of your portfolio wealth) and short one the same amount.

import numpy as np

np.random.seed(42)  # Set seed for reproducibility

for t, state in b:
    # pick two assets deterministically for doctest
    pair = ['A', 'B']  # Use first two assets instead of random choice
    # compute the pair
    units = pd.Series(index=state.assets, data=0.0)
    units[pair] = [state.nav, -state.nav] / state.prices[pair].values
    # update the position
    b.position = 0.1 * units
    # Do not apply trading costs
    b.aum = state.aum
    # Check the final positions
    b.units.iloc[-1][['A', 'B']]

Here t is the growing list of timestamps, e.g. in the first iteration t is $t1$, in the second iteration it will be $t1, t2$ etc.

A lot of magic is hidden in the state variable. The state gives access to the currently available cash, the current prices and the current valuation of all holdings.

Here's a slightly more realistic loop. Given a set of $4$ assets we want to implement the popular $1/n$ strategy.

b2 = Builder(prices=prices, initial_aum=1e6)

for t, state in b2:
    # each day we invest a quarter of the capital in the assets
    b2.position = 0.25 * state.nav / state.prices
    b2.aum = state.aum

# Check the final positions
b2.units.iloc[-1]

Note that we update the position at the last element in the t list using a series of actual units rather than weights or cashpositions. The builder class also exposes setters for such alternative conventions.

b3 = Builder(prices=prices, initial_aum=1e6)

for t, state in b3:
    # each day we invest a quarter of the capital in the assets
    b3.weights = np.ones(4) * 0.25
    b3.aum = state.aum

    # Check the final positions
b3.units.iloc[-1]

Build the portfolio

Once finished it is possible to build the portfolio object:

b3 = Builder(prices=prices, initial_aum=1e6)

for t, state in b3:
    b3.weights = np.ones(4) * 0.25
    b3.aum = state.aum

# Build the portfolio from one of our builders
portfolio = b3.build()
# Verify the portfolio was created successfully
type(portfolio).__name__

📈 Analytics

The portfolio object supports further analysis and exposes a number of properties, e.g.:

b3 = Builder(prices=prices, initial_aum=1e6)

for t, state in b3:
    b3.weights = np.ones(4) * 0.25
    b3.aum = state.aum

portfolio = b3.build()

It is possible to generate a snapshot of the portfolio:

b3 = Builder(prices=prices, initial_aum=1e6)
for t, state in b3:
    b3.weights = np.ones(4) * 0.25
    b3.aum = state.aum
portfolio = b3.build()

# Generate a snapshot (returns a plotly figure)
fig = portfolio.snapshot()

🛠️ Development

UV Package Manager

Start with:

make install

This will install uv and create the virtual environment defined in pyproject.toml and locked in uv.lock.

Marimo Notebooks

We install marimo on the fly within the aforementioned virtual environment. Execute:

make marimo

This will install and start marimo for interactive notebook development.

📚 Documentation

  • Full documentation is available at cvxgrp.org/simulator/book
  • API reference can be found in the documentation
  • Example notebooks are included in the repository under the book directory

🤝 Contributing

Contributions are welcome! Here's how you can contribute:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature-name
  3. Commit your changes: git commit -m 'Add some feature'
  4. Push to the branch: git push origin feature-name
  5. Open a pull request

Please make sure to update tests as appropriate and follow the code style of the project.

📄 License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Copyright 2023 Stanford University Convex Optimization Group

Metadata

Release files for cvxsimulator 1.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cvxsimulator 1.5.1
File Size Uploaded Provenance Actions
cvxsimulator-1.5.1.tar.gz 7.7 MB With provenance Details for cvxsimulator-1.5.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cvxsimulator 1.5.1
File Interpreter ABI Platform Actions
cvxsimulator-1.5.1-py3-none-any.whl Python 3 none any Details for cvxsimulator-1.5.1-py3-none-any.whl

Total release size: 7.7 MB

Release files / cvxsimulator-1.5.1.tar.gz

Download URL cvxsimulator-1.5.1.tar.gz
Size 7.7 MB
Tags Source
SHA-256 checksum
How to use checksums
484f91c11eacaa8763f06c379fb016710eed97bf28e9ecf8f7f04085f730f340
BLAKE2b-256 checksum
How to use checksums
b534ceb60bcaab7eb5608b8cef01b94e38cfaacc36708ecb8384a0ed354a0f64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 16, 2026.

Transparency log

Release files / cvxsimulator-1.5.1-py3-none-any.whl

Download URL cvxsimulator-1.5.1-py3-none-any.whl
Size 20.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7fd62ebb88a4dda6c8e1d417bb8150771934aeee5dbc0460bf50574c1cd81354
BLAKE2b-256 checksum
How to use checksums
48d8c5e9fed4f951e1ce9c3b584601b785be045537bc868f20f05bed818831d8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 16, 2026.

Transparency log

Security for cvxsimulator 1.5.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for cvxsimulator-1.5.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 16, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.5.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page