Skip to main content

investing-quant

A linear programming (LP) based quantitative portfolio optimization system using the Rockafellar-Uryasev CVaR framework.


Purpose

This project implements a multi-constraint portfolio optimizer that allocates capital across a universe of financial instruments — including equity ETFs, fixed income ETFs, and individual stocks — spanning US, EU, and Emerging Markets regions.

The optimizer maximizes expected net return (after transaction fees) subject to a layered set of risk and diversification constraints:

  • Budget constraint — fully invested capital
  • Concentration cap — per-instrument maximum allocation
  • Asset-class bands — minimum and maximum exposure to ETFs, bonds, and stocks
  • Geographic bands — minimum and maximum exposure to US, EU, and Emerging Markets
  • Beta ceiling — portfolio-level market sensitivity limit
  • Weighted volatility ceiling — linear proxy for portfolio risk
  • Liquidity constraint — weighted average days to full liquidation
  • CVaR bound — tail risk limit via Conditional Value at Risk (95% confidence), linearized using Monte Carlo scenarios and the Rockafellar-Uryasev reformulation

The full mathematical formulation (constraints C-1 through C-9) is documented in docs/Investment Portfolio Optimization.pdf.

Disclaimer: This project is intended for educational and analytical purposes only. It does not constitute financial advice.


Two ways to run this: package or notebook

There are two independent, self-contained ways to run the optimizer, each with its own instrument universe and tuned parameters:

  • The iq CLI package (src/investing_quant/) — headless, reads its universe from data/universe.csv (39 instruments) and its parameters from a YAML config (configs/base.yaml). Has a test suite. This is the one to script against or automate.
  • The notebook (notebooks/quant-lp-model.ipynb) — self-contained and Colab-ready, with its own hardcoded 43-instrument universe, sensitivity-sweep experiments, and matplotlib charts that the package does not (yet) reproduce.

They share the same model (ModelParameters, the LP constraints, CVaR scenario generation) but are maintained in parallel, not kept in sync — a correctness fix to one (like the C-9 slack-variable fix below) needs to be applied to the other by hand. A data/parameter change to one (e.g. editing data/universe.csv) does not affect the other.


Content Structure

investing-quant/
├── docs/
│   ├── Investment Portfolio Optimization.pdf    # Technical/math reference (constraints C-1..C-9)
│   └── Investment Portfolio Optimization.pptx
├── notebooks/
│   └── quant-lp-model.ipynb        # Self-contained notebook, its own 43-instrument universe
├── data/
│   └── universe.csv                # Package's instrument universe (39 instruments)
├── configs/
│   └── base.yaml                   # Package's tuned model parameters
├── src/investing_quant/            # The iq package (see below)
├── tests/                          # pytest suite for the package
├── scripts/
│   └── extract_universe.py         # One-off script used to generate data/universe.csv
├── LICENSE
└── README.md

Notebook Sections (quant-lp-model.ipynb)

Section Description
1 — Setup Imports, solver configuration (GLPK via PuLP)
2 — Instrument Universe Definition of 43 instruments with expected return, beta, volatility, liquidity, and transaction fee
3 — Model Parameters ModelParameters dataclass: capital, concentration caps, asset-class/region bands, risk ceilings
4 — CVaR Scenario Generation Monte Carlo correlated monthly return scenarios
5 — LP Formulation Decision variables, objective function, and constraints C-1 through C-9
6 — Solve GLPK solver execution via PuLP
7 — Extract Results Parsing optimal allocations, VaR/CVaR, and risk metrics from solver output
8 — Portfolio Reports Styled tables, allocation bar and pie charts, constraint compliance, P&L distribution
9 — Sensitivity Analysis CVaR limit, beta ceiling, and concentration cap sweep experiments
10 — Custom Sandbox Template for user-defined parameter experiments
11 — Export Writes portfolio_allocations.csv and portfolio_full_results.csv

Requirements

  • Python 3.11+
  • GLPK system solver (used by both the package and the notebook, via PuLP)
# Debian / Ubuntu
sudo apt install glpk-utils

# macOS (Homebrew)
brew install glpk

# Windows
# Download from https://winglpk.sourceforge.net or use WSL

Running the package (iq CLI)

The package uses uv for dependency management.

uv sync                     # installs into .venv, using uv.lock
uv run iq --help

Validate the bundled universe and config, then solve:

uv run iq validate -u data/universe.csv -c configs/base.yaml
uv run iq optimize -c configs/base.yaml -u data/universe.csv

iq optimize prints the parameter summary, executive summary, active positions, allocation breakdown, constraint compliance, and risk analysis. Add -o DIR to also write DIR/portfolio_allocations.csv and DIR/portfolio_full_results.csv. Override individual parameters without editing the YAML:

uv run iq optimize -c configs/base.yaml -u data/universe.csv --budget 5000 --seed 7 -o out/

To use your own universe or parameters, edit data/universe.csv (same columns as the shipped file) and/or configs/base.yaml, or point -u/-c at different files entirely.

Run the test suite:

uv run pytest              # fast suite
uv run pytest -m slow       # + the full-size golden regression test
uv run ruff check .

Running the notebook

1. Create and activate a virtual environment

python -m venv .venv
source .venv/bin/activate        # Linux / macOS
.venv\Scripts\activate           # Windows

2. Install Python dependencies

pip install numpy pandas polars pulp matplotlib jupyterlab

(Alternatively, uv sync --extra notebook installs these into the package's own .venv.)

3. Launch Jupyter and open the notebook

jupyter lab notebooks/quant-lp-model.ipynb

4. Configure the model

Edit the ModelParameters dataclass in Section 3 to set:

  • W — total capital to allocate
  • U_default — per-instrument concentration cap
  • Asset-class and region allocation bands
  • Risk ceilings: beta_max, sigma_max, liquidity_max_days, cvar_max

5. Run all cells

Execute all notebook cells in order. The solver will find the optimal allocation and render portfolio reports.

6. Export results

Section 11 writes two CSV files to the working directory:

  • portfolio_allocations.csv — active positions with weights and risk metrics
  • portfolio_full_results.csv — full instrument universe with solver outputs

7. Sensitivity experiments (optional)

Sections 9 and 10 contain pre-built and template experiments for exploring how the optimal portfolio changes as individual parameters vary. (These sweeps, and the notebook's matplotlib charts, are not yet reproduced by the iq package.)


Contact

Ramiro Chaparro ramiro@datup.ai


License

MIT License — Copyright 2026 Datup AI

See LICENSE for full terms.

Metadata

Release files for investing-quant 0.1.0

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

Source distribution (sdist)

Source distribution for investing-quant 0.1.0
File Size Uploaded
investing_quant-0.1.0.tar.gz 1.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for investing-quant 0.1.0
File Interpreter ABI Platform
investing_quant-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.1 MB

Release files / investing_quant-0.1.0.tar.gz

Download URL investing_quant-0.1.0.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
2c8ae144da59bb254161ed11cc53c6f09da5b439eb5009de4b96f327e79e69b5
BLAKE2b-256 checksum
How to use checksums
252c49460642f6c7509e2b0f6a6a315ebd5c63d98ca63a93696a5eedde2abdd6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / investing_quant-0.1.0-py3-none-any.whl

Download URL investing_quant-0.1.0-py3-none-any.whl
Size 18.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f0d97054365d7875a6c94ae50aa8119da666b87d2ed9e7d9ccc1e3c1cb53c1b0
BLAKE2b-256 checksum
How to use checksums
1408fdf29f25f5784932f919c922a4499af890acdeeb9ed6c05d35d9d7e7d326
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

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