# Python Binding Update Plan (post v2.0.0 stable)

**Status:** drafted 2026-05-10 after the rc.2 fix-pass closure (audit
§1.5). The Python ecosystem is the highest-leverage growth lever for
`stochastic-rs` — most quant practitioners use Python, and the existing
PyO3 surface already exposes ~210 entries.

This plan is **what we want to land between v2.0.0 stable and the first
PyPI release** (target window: ~2 weeks of focused work post-stable).

---

## Goals (in order of leverage)

1. **First-class PyPI presence.** Wheels for Linux x86_64, macOS x86_64,
   macOS aarch64, Windows x86_64. `pip install stochastic-rs` works.
2. **Pythonic API.** Rename camelCase / abbreviated parameters to
   snake_case where Python conventions clash with current names.
   Add `__repr__` / `__eq__` on all `#[pyclass]` types.
3. **Type stubs (`.pyi`).** IDE autocompletion, mypy / pyright support.
4. **Documentation site.** `mkdocs-material` with API reference scraped
   from rustdoc + hand-written tutorials.
5. **Notebook gallery.** Real-market calibration, Heston vs QuantLib
   benchmark, fBM/rBergomi sampling, vol-surface fit.
6. **Coverage gaps.** Python wrappers for the rc.1 / rc.2 surface that
   currently have no Python exposure (HscmCalibrator, HullWhite, SsviSurface
   full API, empirical_cvar function, multi_period_kyle, factor models).

---

## §1 — Coverage gaps (rc.1 / rc.2 audit residuals)

These are the items from `docs/V2_RC2_TODO.md` §1.5 where the Rust API
exists but the Python wrapper is missing:

| # | Rust type | Action |
|---|---|---|
| 1.1 | `HscmCalibrator` | Add `PyHscmCalibrator` exposing the unified `Calibrator` API (`calibrate() -> PyResult<...>`) |
| 1.2 | `HscmModel` | Add `PyHscmModel` with `q` parameter (note: rc.1 fix-pass made `q` non-`_q`-prefixed) |
| 1.3 | `bonds::HullWhite::from_curve` | Add `PyHullWhite` accepting `PyDiscountCurve` argument |
| 1.4 | `multi_period_kyle` | Add `py_multi_period_kyle` `#[pyfunction]` returning a list of equilibria |
| 1.5 | `SsviSurface::is_calendar_spread_free` | New signature takes `ks: &[T]` — wrapper accepts numpy-array argument |
| 1.6 | `empirical_cvar` | Add as `#[pyfunction]` so users can compute CVaR without going through portfolio optimization |
| 1.7 | `optimize_with_method` | Update `PyOptimizerEngine` (or add) to accept `OptimizerConfig` (`periods_per_year`, `lambda`) |
| 1.8 | `factors::pca_decompose` / `try_pca_decompose` | Expose for direct PCA on returns |
| 1.9 | `factors::fama_macbeth` | Expose for cross-sectional regression |
| 1.10 | `factors::pairs_signals` | Expose for pairs-trading signal generation |

Effort estimate: ~1 focused day (mostly mechanical, follows the
`python-bindings` SKILL).

## §2 — Pythonic API polish

| Area | Action |
|---|---|
| Parameter naming | Audit `pyo3(signature = ...)` vs PEP 8: rename `r_d`/`r_f` to `domestic_rate`/`foreign_rate`, `tau` to `time_to_maturity`, `s` to `spot`, `k` to `strike`, etc. — only where the Python idiom is clearer. Keep the short names as keyword aliases. |
| `__repr__` | Auto-generate via `derive(Debug)` + a macro `py_repr!` that prints `BSMPricer(s=100, k=100, sigma=0.2, ...)`. |
| `__eq__` | For value types (params, results), derive PartialEq and expose. |
| `__copy__` / `__deepcopy__` | For pricers that hold scalars/arrays, derive Clone and expose `__copy__`. |
| Iterables | Where Rust returns `Vec<T>`, return numpy arrays via `into_pyarray` so users get `.shape`/`.dtype` for free. |

Effort: ~1.5 days. Most items are macro-able once the patterns settle.

## §3 — Type stubs (`.pyi`)

Two viable paths:

- **(A)** Hand-write `stochastic_rs.pyi` once (mirrors all 210+ classes).
  ~1 day to write, but stale-by-default — every PR that touches the
  cdylib needs to update the stub.
- **(B)** Generate stubs at build time. PyO3 has experimental support;
  alternative: write a Rust binary `gen_stubs.rs` that parses
  `python.rs` macros and emits `.pyi`. ~2 days to build, ~0 maintenance.

**Recommendation:** start with (A) (gets us `pip install` working
quickly), then migrate to (B) in 2.0.x.

Stub items: `__init__`, `sample`, `price`, `calibrate`, all parameters
typed (`float`, `int`, `bool`, `numpy.ndarray`, `Optional[T]`).

## §4 — Documentation site

Tools: `mkdocs-material` + `mkdocstrings` (Python-side) + a sphinx-like
scraper for rustdoc (or `cargo doc` HTML embedded under
`/api/rust/`).

Sections:

- **Getting started** — `pip install stochastic-rs`, hello-world.
- **Tutorials** — 5–8 task-driven walkthroughs:
  1. Black-Scholes pricing & implied vol
  2. Heston calibration to a real-market chain
  3. fBM / rBergomi simulation & rough-vol calibration
  4. SSVI surface fit & arbitrage check
  5. Multi-asset basket pricing under Heston
  6. Bond pricing under Hull-White / G2++
  7. Kyle's model & multi-period equilibrium
  8. Almgren-Chriss optimal liquidation
- **API reference** — auto-generated from rustdoc + pyi.
- **Performance** — benchmarks vs QuantLib / scipy.stats / numpy.

Hosted on GitHub Pages or readthedocs. Effort: ~2 days for skeleton +
one tutorial; the remaining tutorials accrete over time.

## §5 — Notebook gallery

`examples/notebooks/`:

- `01_black_scholes.ipynb`
- `02_heston_calibration_aapl.ipynb` — pulls a real AAPL chain, calibrates
- `03_rough_bergomi_skew_term_structure.ipynb`
- `04_quantlib_vs_stochastic_rs_benchmark.ipynb` — same Heston/Bates
  calibration, side-by-side timing
- `05_multi_asset_basket.ipynb`
- `06_pairs_trading_kalman.ipynb`

These do double duty as user-facing tutorials AND regression checks
(Run-All in CI catches API drift).

Effort: ~2 days for the first 3 notebooks; rest can be community-driven.

## §6 — PyPI release mechanics

Already mostly wired:

- `pyproject.toml` has `[tool.maturin]` pointing at the cdylib crate.
- `maturin build --release --strip` produces wheels.

Still needed:

- **CI matrix** — GitHub Actions for {ubuntu-22.04, macos-13, macos-14,
  windows-2022} × {python 3.10, 3.11, 3.12, 3.13}. Use `cibuildwheel` or
  `maturin-action`.
- **TestPyPI dry run** — publish to TestPyPI first, install from
  TestPyPI, run smoke tests, then publish to PyPI.
- **Versioning** — every Rust crate version bump triggers a Python
  version bump. Hook into the `release-checklist` SKILL.

Effort: ~1 day for the matrix, ~0 maintenance after.

---

## Phase ordering

**Phase A (target: 2026-05-17 / 1 week post-stable):**
- §1 coverage gaps (1 day)
- §3 hand-written `.pyi` stubs (1 day)
- §6 PyPI matrix + first wheel publish (1 day)
- Pythonic-naming audit on pricers (0.5 day)

**Phase B (target: 2026-05-31 / 3 weeks post-stable):**
- §2 full pythonic polish (1 day)
- §4 docs site skeleton + 2 tutorials (2 days)
- §5 first 3 notebooks (1.5 days)

**Phase C (post-2.0.1):**
- Auto-generated stubs (§3 path B)
- More tutorials, more notebooks
- pyi linter integration in CI

---

## Effort summary

| Phase | Item | Days |
|---|---|---|
| A | §1 coverage gaps | 1 |
| A | §3 stubs hand-written | 1 |
| A | §6 PyPI matrix | 1 |
| A | §2 partial pythonic naming | 0.5 |
| B | §2 full polish | 1 |
| B | §4 docs skeleton + 2 tutorials | 2 |
| B | §5 3 notebooks | 1.5 |
| **Phase A total** | | **~3.5 days** |
| **Phase B total** | | **~4.5 days** |
| **A + B** | | **~8 days** |

Plus ~2 days slack for CI debugging / cross-platform wheel issues.

---

## Out of scope (defer to 2.x or later)

- AI-side Python bindings — the `stochastic-rs-ai` neural surrogates are
  rc.1-experimental and not yet ready for Python exposure.
- jax / torch interop. Returning `numpy.ndarray` is enough for v2.
- Async Python API. The Rust side is sync; bridging async would require
  `pyo3-asyncio` and a full review.
- Wasm builds. Not requested.

---

## Maintenance: SKILL

`.claude/skills/python-bindings/SKILL.md` (created alongside this plan)
codifies the wrapper / registration / testing rules so subsequent
binding work follows the same conventions. Reference it when:

- Adding a new distribution / process / pricer / calibrator with Python
  exposure
- Updating a Python wrapper after a Rust API change
- Investigating a "module has no attribute X" import error (almost
  always: forgot `m.add_class::<PyXxx>()`)
- Cutting a PyPI release (delegates to `release-checklist` SKILL for the
  workspace-wide bump steps)
