stochastic-rs

Python bindings

stochastic-rs-py — Python coverage for distributions, processes, pricers, calibrators, copulas, stats. NumPy in / out, 210 entries at v2.0.

Python bindings

The stochastic_rs Python package wraps the Rust crates via PyO3. At v2.0 the surface is 210 entries: 198 PyO3 classes plus 12 free functions across distributions, stochastic processes, pricers, calibrators, copulas, and stats. AI bindings are deferred to 2.x.

Installation

See Installation (Python).

NumPy interop

All sample() calls return numpy.ndarray. Bulk samplers (sample_par(m, n)) return (m, n)-shaped arrays. The default dtype is float64; pass dtype="f32" where the underlying sampler supports it.

End-to-end example — simulate, estimate, price

A single Python script that touches four crates:

import stochastic_rs as srs
import numpy as np

# 1. Simulate an OU path (stochastic crate)
ou = srs.Ou(theta=2.0, mu=0.0, sigma=1.0, n=4096, x0=0.0, t=1.0, seed=42)
path = ou.sample()                    # shape (4096,)

# 2. Estimate the Hurst exponent of an fBM path (stats crate)
fgn = srs.Fgn(hurst=0.4, sigma=1.0, n=4096, t=1.0, seed=42)
fbm = np.cumsum(fgn.sample())
hurst = srs.fukasawa_hurst(fbm)
print(f"H = {hurst.point:.3f} (true 0.4)")

# 3. Price a Heston call (quant crate)
pricer = srs.HestonPricer(
    s0=100, k=100, tau=1.0, r=0.03, q=0.0,
    v0=0.04, kappa=2.0, theta=0.04, sigma=0.3, rho=-0.5,
)
print("Heston call =", pricer.price("call"))

# 4. Sample from a Clayton copula (copulas crate)
cop = srs.Clayton(theta=2.0, seed=42)
u, v = cop.sample(10_000)
print("τ̂ =", srs.kendall_tau(u, v))

Bulk sampling — sample_par(m, n)

import stochastic_rs as srs

# 100_000 GBM paths × 252 daily steps in parallel
gbm = srs.Gbm(mu=0.05, sigma=0.2, n=252, x0=100.0, t=1.0, seed=42)
paths = gbm.sample_par(100_000)       # shape (100_000, 252)

# Path-wise terminal payoff
payoffs = np.maximum(paths[:, -1] - 100.0, 0.0)
mc_call = np.exp(-0.05) * payoffs.mean()
print("MC call =", mc_call)

Three pipelines

There are three ways a Rust type ends up in the Python package:

  1. py_distribution! macro — distributions wrap automatically when you invoke the macro at the bottom of the source file.
  2. py_process_*! macros — processes use py_process_1d!, py_process_2x1d!, or py_process_2d! depending on shape.
  3. Hand-written #[pyclass] blocks — pricers, calibrators, vol surfaces, and estimators are wrapped by hand in stochastic-rs-quant/src/python.rs and stochastic-rs-stats/src/python.rs.

For the file-by-file recipe see the python-bindings SKILL (comprehensive) or adding-python-binding (quickstart).

Common gotchas

  • Seed reproducibility. Every wrapper supports __init__(..., seed=None). Pass an explicit seed for tests; rely on the thread-local default for production sampling.
  • Dtype shims. IntoF32 / IntoF64 shims convert Python float parameters to the requested dtype. All distribution parameters are f64-typed in the macro — the shim handles down-conversion to f32 when dtype="f32".
  • sample_par(m, n) shape. The returned array is (m, n), with m independent paths along axis 0. This matches NumPy's row-major default for downstream .mean(axis=0) reductions.

On this page