Installation and a first result

Install

Install the released package from PyPI:

python -m pip install vanilla-option-pricers

For a source checkout, install the package and the build-only documentation tools separately:

python -m pip install -e ".[docs]"

The package’s runtime dependencies remain NumPy and Numba. Sphinx, MyST Parser, and Furo belong only to the optional docs extra.

Run the authoritative offline example

Open In Colab

The repository’s pricing and IV script is the single source for first success. It uses only the installed package, NumPy, Numba, and deterministic generated inputs; it requires no network, credentials, pandas, or SciPy and creates no files.

From a source checkout with the package installed, run:

python examples/getting_started/pricing_and_iv.py

The wheel intentionally excludes repository examples. After installing from PyPI, download or copy the linked script and run it from any directory.

For a zero-local-setup trial, the Colab notebook installs the latest released distribution from the official PyPI index, prints its distribution version and import path, and runs the exact same workflow below. The committed notebook contains no execution output.

  1"""Offline first-success workflow for vanilla-option-pricers."""
  2
  3from enum import Enum
  4from importlib.metadata import version
  5from pathlib import Path
  6from time import perf_counter
  7
  8import numpy as np
  9from numba.typed import List
 10
 11import vanilla_option_pricers as vop
 12
 13forward = 100.0
 14discfactor = 0.98
 15ttm = 0.50
 16strikes = np.array([90.0, 100.0, 100.0, 110.0])
 17option_types = np.array(["P", "P", "C", "C"])
 18solve_types = List(option_types.tolist())
 19lognormal_vols = np.full(strikes.shape, 0.20)
 20
 21
 22class Locals(Enum):
 23    """Available local pricing and implied-volatility workflows."""
 24
 25    PRICING_AND_IV = 1
 26
 27
 28def run_bsm_workflow() -> tuple[np.ndarray, np.ndarray]:
 29    """Price one aligned BSM slice and recover its implied volatilities."""
 30    prices = vop.compute_bsm_vanilla_slice_prices(
 31        ttm,
 32        forward,
 33        strikes,
 34        lognormal_vols,
 35        option_types,
 36        discfactor,
 37    )
 38    implied_vols = vop.infer_bsm_ivols_from_slice_prices(
 39        ttm,
 40        forward,
 41        discfactor,
 42        strikes,
 43        solve_types,
 44        prices,
 45    )
 46    return prices, implied_vols
 47
 48
 49def run_local(local: Locals) -> None:
 50    """Run the selected pricing and implied-volatility workflow."""
 51    if local != Locals.PRICING_AND_IV:
 52        raise ValueError(f"Unsupported local case: {local}")
 53
 54    cold_started = perf_counter()
 55    prices, implied_vols = run_bsm_workflow()
 56    cold_seconds = perf_counter() - cold_started
 57
 58    warm_repetitions = 10
 59    warm_started = perf_counter()
 60    for _ in range(warm_repetitions):
 61        warm_prices, warm_implied_vols = run_bsm_workflow()
 62    warm_mean_seconds = (perf_counter() - warm_started) / warm_repetitions
 63
 64    max_iv_error = float(np.max(np.abs(implied_vols - lognormal_vols)))
 65    parity_error = float(prices[2] - prices[1] - discfactor * (forward - strikes[1]))
 66
 67    absolute_normal_vol = 5.0
 68    normal_strike = 102.0
 69    normal_price = vop.compute_normal_price(
 70        forward,
 71        normal_strike,
 72        ttm,
 73        absolute_normal_vol,
 74        discfactor,
 75        "C",
 76    )
 77    normal_implied_vol = vop.infer_normal_implied_vol(
 78        forward,
 79        ttm,
 80        normal_strike,
 81        normal_price,
 82        discfactor,
 83        "C",
 84    )
 85
 86    if max_iv_error > 1e-7:
 87        raise RuntimeError(f"BSM implied-volatility round trip failed: {max_iv_error}")
 88    if abs(parity_error) > 1e-10:
 89        raise RuntimeError(f"BSM put-call parity failed: {parity_error}")
 90    if abs(normal_implied_vol - absolute_normal_vol) > 1e-7:
 91        raise RuntimeError("Bachelier implied-volatility round trip failed")
 92    if not (
 93        np.array_equal(prices, warm_prices)
 94        and np.array_equal(implied_vols, warm_implied_vols)
 95    ):
 96        raise RuntimeError("Warm BSM workflow changed the deterministic result")
 97
 98    print(f"distribution_version={version('vanilla-option-pricers')}")
 99    print(f"import_path={Path(vop.__file__).resolve()}")
100    print(
101        "array_shapes="
102        f"strikes{strikes.shape} option_types{option_types.shape} "
103        f"prices{prices.shape} implied_vols{implied_vols.shape}"
104    )
105    print("option_types=" + str(option_types.tolist()))
106    print("prices=" + np.array2string(prices, precision=8))
107    print(f"max_iv_error={max_iv_error:.3e} parity_error={parity_error:.3e}")
108    print(
109        f"cold_first_call_seconds={cold_seconds:.6f} "
110        f"warm_mean_seconds={warm_mean_seconds:.6f} "
111        f"warm_repetitions={warm_repetitions}"
112    )
113    print(
114        f"bachelier_absolute_vol={absolute_normal_vol:.6f} "
115        f"price={normal_price:.8f} iv_error="
116        f"{normal_implied_vol - absolute_normal_vol:.3e}"
117    )
118    print("bsm_convention=forward, discount factor, years, annualised lognormal volatility")
119    print("bachelier_convention=annualised absolute normal volatility in forward units")
120    print(
121        "change_first=forward, discfactor, ttm, strikes, option_types, model_convention"
122    )
123
124
125if __name__ == "__main__":
126    run_local(local=Locals.PRICING_AND_IV)

The script prices one aligned BSM slice, recovers all four input volatilities, and checks the same-strike call/put pair against forward put-call parity. It also prices and inverts one Bachelier call with annualised absolute normal volatility in the same units as the forward and strike.

Successful output reports package version and import path; input/output shapes; option codes and prices; maximum IV and parity errors; one cold first-call duration; the mean of ten warm repeats; and the Bachelier result. Timing values are machine-dependent and are not a benchmark. The verified numerical errors are required to remain below the explicit thresholds in the script.

Start adaptation with the final change_first line: forward, discfactor, ttm, strikes, option_types, and model_convention. Preserve the stated units and alignment contracts when substituting market inputs.

Next steps and boundaries

Use the package-root functions for stable user examples. Array helpers have distinct scalar, aligned-array, grid, and per-expiry contracts; arbitrary broadcasting is not promised. The first call to a Numba-compiled signature includes compilation time.

Continue with pricing and Greeks, implied volatility, Bachelier units, and array/Numba behavior. Use the issue tracker for support.