Metadata-Version: 2.5
Name: investing-quant
Version: 0.1.0
Summary: LP portfolio optimizer using the Rockafellar-Uryasev CVaR framework
Project-URL: Homepage, https://github.com/DatupAI/investing-quant
Author-email: Ramiro Chaparro <ramiro@datup.ai>
License: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: numpy>=1.26
Requires-Dist: pandas>=2.1
Requires-Dist: polars>=1.0
Requires-Dist: pulp>=2.8
Requires-Dist: pyarrow>=14.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: notebook
Requires-Dist: ipykernel>=6.29; extra == 'notebook'
Requires-Dist: jupyterlab>=4.0; extra == 'notebook'
Requires-Dist: matplotlib>=3.8; extra == 'notebook'
Description-Content-Type: text/markdown

# 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](https://www.gnu.org/software/glpk/) system solver (used by both the package and the notebook, via PuLP)

```bash
# 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](https://docs.astral.sh/uv/) for dependency management.

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

Validate the bundled universe and config, then solve:

```bash
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:

```bash
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:

```bash
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

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

### 2. Install Python dependencies

```bash
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

```bash
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](https://github.com/DatupAI/investing-quant/blob/main/LICENSE) for full terms.
