Metadata-Version: 2.4
Name: radonlab
Version: 0.1.1
Summary: Monte Carlo Greeks for discontinuous payoffs: likelihood-ratio, smoothing and conditional Monte Carlo, benchmarked against closed-form references.
Project-URL: Homepage, https://github.com/quantsingularity/radonlab
Project-URL: Source, https://github.com/quantsingularity/radonlab
Project-URL: Issues, https://github.com/quantsingularity/radonlab/issues
Author: Abrar Ahmed
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: greeks,likelihood-ratio,malliavin,monte-carlo,option-pricing,quantitative-finance,sensitivities
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: numpy>=1.22
Requires-Dist: scipy>=1.8
Provides-Extra: benchmark
Requires-Dist: matplotlib>=3.5; extra == 'benchmark'
Requires-Dist: pandas>=1.4; extra == 'benchmark'
Provides-Extra: dev
Requires-Dist: matplotlib>=3.5; extra == 'dev'
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pandas>=1.4; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1; extra == 'dev'
Provides-Extra: test
Requires-Dist: pandas>=1.4; extra == 'test'
Requires-Dist: pytest>=7.0; extra == 'test'
Description-Content-Type: text/markdown

# RadonLab

**Monte Carlo Greeks for discontinuous payoffs**, done correctly and benchmarked
against closed-form references.

When an option's payoff is discontinuous (a digital that pays a fixed amount if
the underlying finishes above a strike, a barrier that knocks out on touch), the
textbook _pathwise_ (infinitesimal perturbation) estimator for its sensitivities
quietly breaks: the derivative of the payoff is zero almost everywhere, so the
estimate collapses toward zero. `radonlab` implements the established remedies,
tests each one against a known analytic answer, and benchmarks them side by side
so you can see _which method to use and what it costs_.

Pure NumPy/SciPy. Apache-2.0. No copyleft dependencies.

## Companion C++ implementation

A header-only C++20 port lives at
[github.com/quantsingularity/radonlab-cpp](https://github.com/quantsingularity/radonlab-cpp).
The two libraries share the same methods and the same closed-form references, and
the C++ analytics are cross-checked against this library's values (which are
verified to machine precision by complex-step differentiation), so the two agree
to 1e-9 across languages.

|              | Python (`radonlab`)                                       | C++ (`radonlab-cpp`)                                                               |
| ------------ | --------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Role         | Reference implementation; carries the research extensions | Header-only production port                                                        |
| Validated by | Complex-step differentiation, to machine precision        | Cross-checked against this library, to 1e-9                                        |
| Extras       | Merton jump-diffusion, path-dependent Malliavin delta     | Adjoint algorithmic differentiation, exact second-order Greeks, threaded estimator |

## What's inside

All estimators return a value together with its per-path standard error.

| Estimator                    | Differentiates                                                                              | Notes                                                                                                                                       |
| ---------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Likelihood-ratio (LRM)       | the density, never the payoff                                                               | Unbiased for digitals and other discontinuous payoffs. Covers delta, vega, gamma.                                                           |
| Smoothing                    | a $C^1$ approximation $f_h$ of bandwidth $h$                                                | A tunable bias/variance trade-off.                                                                                                          |
| Conditional Monte Carlo      | integrates out the barrier-crossing event with the Brownian-bridge non-crossing probability | Unbiased for the continuously-monitored price at any number of steps, and Lipschitz in spot, so its delta comes out by the pathwise method. |
| Pathwise / finite-difference | baselines                                                                                   | Kept so their failure modes on discontinuous payoffs are visible and measurable.                                                            |

Closed-form Black-Scholes analytics (the ground truth) are provided for European
calls/puts, cash-or-nothing and asset-or-nothing digitals, and continuously-monitored
down-and-out / down-and-in calls. Every explicit Greek formula is checked against
complex-step differentiation of the price to machine precision.

## Install

```bash
pip install -e ".[benchmark,test]"
```

| Package      | Needed for      |
| ------------ | --------------- |
| `numpy`      | core (required) |
| `scipy`      | core (required) |
| `pandas`     | benchmark table |
| `matplotlib` | benchmark plots |

## Quick start

```python
from radonlab import GeometricBrownianMotion, CashOrNothingCall, Greek
from radonlab import likelihood_ratio, pathwise, analytics

model = GeometricBrownianMotion(S0=100, r=0.03, sigma=0.20, T=1.0)
payoff = CashOrNothingCall(strike=100)

# Ground truth
ref = analytics.cash_or_nothing_call(100, 100, 0.03, 0.20, 1.0, greek="delta")

# Likelihood ratio: unbiased for the digital
lrm = likelihood_ratio(model, payoff, Greek.DELTA, n_paths=1_000_000)
print(lrm)                    # delta=... +/- ...  [likelihood-ratio]
print(lrm.confidence_interval(0.95))

# Naive pathwise: collapses to ~0, badly biased
pw = pathwise(model, payoff, Greek.DELTA, n_paths=1_000_000)
print(pw.value, "vs analytic", ref)
```

## The math, briefly

Under geometric Brownian motion, the terminal spot is

$$
S_T = S_0 \exp\!\left(\left(r - q - \tfrac{1}{2}\sigma^2\right)T + \sigma\sqrt{T}\,Z\right),
\qquad Z \sim \mathcal{N}(0,1),
$$

and a price is $P = e^{-rT}\,\mathbb{E}[f(S_T)]$.

| Method                   | Identity                                                                                                                                  | Behaviour on a jump in $f$                                                    |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| Likelihood ratio         | $\dfrac{\partial P}{\partial \theta} = e^{-rT}\,\mathbb{E}\!\left[f(S_T)\cdot \dfrac{\partial}{\partial \theta}\log p(S_T;\theta)\right]$ | Unbiased: $f$ is never differentiated.                                        |
| Pathwise                 | $\dfrac{\partial P}{\partial \theta} = e^{-rT}\,\mathbb{E}\!\left[f'(S_T)\cdot \dfrac{\partial S_T}{\partial \theta}\right]$              | Degenerate: $f'(S_T)=0$ almost everywhere for a digital.                      |
| Conditional MC (barrier) | multiplies the terminal payoff by $\displaystyle\prod_i\left(1-\exp\!\left(\dfrac{-2(X_i-b)(X_{i+1}-b)}{\sigma^2\Delta t}\right)\right)$  | Exact bridge probability of not breaching $b=\ln B$ between simulated points. |

Writing $Z = \dfrac{\ln S_T - \mu}{\sigma\sqrt{T}}$, the likelihood-ratio score functions
are:

| Greek | Score function                                            |
| ----- | --------------------------------------------------------- |
| Delta | $\dfrac{Z}{S_0\,\sigma\sqrt{T}}$                          |
| Vega  | $\dfrac{Z^2-1}{\sigma} - \sqrt{T}\,Z$                     |
| Gamma | $\dfrac{Z^2 - \sigma\sqrt{T}\,Z - 1}{S_0^2\,\sigma^2\,T}$ |

Full references are in each module's docstring: Broadie and Glasserman (1996),
Glasserman (2003), Reiner and Rubinstein (1991), and the smoothed-perturbation and
Malliavin literature.

## Benchmark

Reproduce with `python examples/benchmark_digital_delta.py` (1,000,000 paths,
seed 0). Delta of an at-the-money cash-or-nothing call, $S_0=K=100$, $r=3\%$,
$\sigma=20\%$, $T=1$. Analytic reference delta = **0.019333**. Ranked by standard
error, the metric that actually reflects an estimator's accuracy:

| Method                      | Value   | Std. error | Abs. error vs analytic | Biased? |
| --------------------------- | ------- | ---------- | ---------------------- | ------- |
| Likelihood-ratio            | 0.01939 | 2.84e-05   | 5.9e-05                | No      |
| Smoothing (h = default)     | 0.01921 | 4.56e-05   | 1.2e-04                | No      |
| Finite-difference (1% bump) | 0.01934 | 9.49e-05   | 8.5e-06                | No      |
| Pathwise                    | 0.00000 | 0.00e+00   | 1.9e-02                | **Yes** |

Two things to read off it. First, **pathwise is 100% wrong**: it returns
exactly zero, because the digital's derivative is zero almost everywhere.
Second, among the unbiased methods **the likelihood-ratio estimator has the
lowest variance** (about 3.3 times smaller standard error than the 1% finite
difference) and needs no bump to tune.

Finite difference forces a bad choice: sweeping the bump size in the same run
shows the standard error exploding as the bump shrinks, while a large bump
introduces its own bias:

| Relative bump | Value    | Std. error | Abs. error     |
| ------------- | -------- | ---------- | -------------- |
| 1e-01         | 0.018620 | 2.36e-05   | 7.1e-04 (bias) |
| 1e-02         | 0.019311 | 9.49e-05   | 2.2e-05        |
| 1e-03         | 0.019161 | 3.04e-04   | 1.7e-04        |
| 1e-04         | 0.017274 | 9.15e-04   | 2.1e-03        |

The likelihood-ratio method sidesteps that trade-off entirely. This is the
whole reason the library exists.

## Testing

```bash
pytest
```

The suite validates the analytics by complex-step differentiation, confirms each
Monte Carlo estimator agrees with the analytic Greek within a few standard
errors, and documents the pathwise failure on digitals as an explicit test.

## Scope and honesty

This release covers the Black-Scholes / GBM world and, in `radonlab.merton`, the
Merton jump-diffusion model with a closed-form digital reference: it shows the
likelihood-ratio delta weight is unchanged by jumps and validates the estimator
against the closed form. It is a rigorous, tested core rather than a kitchen
sink: the estimators and their validation are real, and where a method is
expected to be biased (pathwise on a digital), the library shows it rather than
hiding it. It also includes a genuinely path-dependent frontier estimator in
`radonlab.asian`: the likelihood-ratio / Malliavin delta of a geometric
Asian digital, validated against the closed form, with a weight built from
the whole path. Stochastic volatility, arithmetic-Asian and lookback
payoffs, and a JIT/vectorized backend are natural next steps.

## License

Apache-2.0. See [LICENSE](LICENSE).
