Skip to content

Models

The models package contains all financial models for valuing options and rates.

It provides the abstract BaseModel and a suite of concrete implementations, from the standard Black-Scholes-Merton to advanced stochastic volatility and jump-diffusion models.

BSMModel

BSMModel(params: dict[str, float] | None = None)

Bases: BaseModel

Black-Scholes-Merton (BSM) model for pricing European options.

This model assumes the underlying asset follows a geometric Brownian motion with constant volatility and risk-free rate.

Initializes the BSM model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used. Must contain 'sigma'.

None
Source code in src/optpricing/models/bsm.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the BSM model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
        Must contain 'sigma'.
    """
    super().__init__(params or self.default_params)

delta_analytic

delta_analytic(
    *,
    spot: float,
    strike: float,
    r: float,
    q: float,
    t: float,
    call: bool = True,
) -> float

Analytic delta for the BSM model.

Source code in src/optpricing/models/bsm.py
def delta_analytic(
    self,
    *,
    spot: float,
    strike: float,
    r: float,
    q: float,
    t: float,
    call: bool = True,
) -> float:
    """Analytic delta for the BSM model."""
    sigma = self.params["sigma"]
    d1 = (np.log(spot / strike) + (r - q + 0.5 * sigma**2) * t) / (
        sigma * np.sqrt(t)
    )
    df_div = np.exp(-q * t)
    return df_div * norm.cdf(d1) if call else -df_div * norm.cdf(-d1)

gamma_analytic

gamma_analytic(
    *,
    spot: float,
    strike: float,
    r: float,
    q: float,
    t: float,
) -> float

Analytic gamma for the BSM model.

Source code in src/optpricing/models/bsm.py
def gamma_analytic(
    self,
    *,
    spot: float,
    strike: float,
    r: float,
    q: float,
    t: float,
) -> float:
    """Analytic gamma for the BSM model."""
    sigma = self.params["sigma"]
    sqrt_t = np.sqrt(t)
    d1 = (np.log(spot / strike) + (r - q + 0.5 * sigma**2) * t) / (sigma * sqrt_t)
    df_div = np.exp(-q * t)
    return df_div * norm.pdf(d1) / (spot * sigma * sqrt_t)

rho_analytic

rho_analytic(
    *,
    spot: float,
    strike: float,
    r: float,
    q: float,
    t: float,
    call: bool = True,
) -> float

Analytic rho for the BSM model.

Source code in src/optpricing/models/bsm.py
def rho_analytic(
    self,
    *,
    spot: float,
    strike: float,
    r: float,
    q: float,
    t: float,
    call: bool = True,
) -> float:
    """Analytic rho for the BSM model."""
    sigma = self.params["sigma"]
    sqrt_t = np.sqrt(t)
    d2 = (np.log(spot / strike) + (r - q - 0.5 * sigma**2) * t) / (sigma * sqrt_t)
    df_rate = np.exp(-r * t)
    return strike * t * df_rate * (norm.cdf(d2) if call else -norm.cdf(-d2))

theta_analytic

theta_analytic(
    *,
    spot: float,
    strike: float,
    r: float,
    q: float,
    t: float,
    call: bool = True,
) -> float

Analytic theta for the BSM model.

Source code in src/optpricing/models/bsm.py
def theta_analytic(
    self,
    *,
    spot: float,
    strike: float,
    r: float,
    q: float,
    t: float,
    call: bool = True,
) -> float:
    """Analytic theta for the BSM model."""
    sigma = self.params["sigma"]
    sqrt_t = np.sqrt(t)
    d1 = (np.log(spot / strike) + (r - q + 0.5 * sigma**2) * t) / (sigma * sqrt_t)
    d2 = d1 - sigma * sqrt_t
    term1 = -spot * np.exp(-q * t) * norm.pdf(d1) * sigma / (2 * sqrt_t)
    if call:
        term2 = q * spot * np.exp(-q * t) * norm.cdf(d1)
        term3 = -r * strike * np.exp(-r * t) * norm.cdf(d2)
    else:
        term2 = -q * spot * np.exp(-q * t) * norm.cdf(-d1)
        term3 = r * strike * np.exp(-r * t) * norm.cdf(-d2)
    return term1 + term2 + term3

vega_analytic

vega_analytic(
    *,
    spot: float,
    strike: float,
    r: float,
    q: float,
    t: float,
) -> float

Analytic vega for the BSM model.

Source code in src/optpricing/models/bsm.py
def vega_analytic(
    self,
    *,
    spot: float,
    strike: float,
    r: float,
    q: float,
    t: float,
) -> float:
    """Analytic vega for the BSM model."""
    sigma = self.params["sigma"]
    sqrt_t = np.sqrt(t)
    d1 = (np.log(spot / strike) + (r - q + 0.5 * sigma**2) * t) / (sigma * sqrt_t)
    return spot * np.exp(-q * t) * norm.pdf(d1) * sqrt_t

BaseModel

BaseModel(params: dict[str, float])

Bases: ABC

Abstract base class for all financial pricing models.

This class defines a common interface for all models, including parameter validation, metadata flags for supported features (e.g., characteristic function, SDE), and methods for creating modified model instances.

Attributes:

Name Type Description
name str

A string identifier for the model (e.g., "Black-Scholes-Merton").

params dict[str, float]

A dictionary holding the model's parameters.

supports_cf bool

Flag indicating if the model implements a characteristic function.

supports_sde bool

Flag indicating if the model implements an SDE simulation path.

supports_pde bool

Flag indicating if the model provides PDE coefficients.

has_closed_form bool

Flag indicating if a closed-form solution is available.

has_variance_process bool

Flag for stochastic volatility models (e.g., Heston, SABR).

is_pure_levy bool

Flag for pure Levy models where the terminal value can be sampled directly.

has_jumps bool

Flag for models that include a jump component.

Initializes the model and validates its parameters.

Parameters:

Name Type Description Default
params dict[str, float]

A dictionary of parameter names to values for the model.

required
Source code in src/optpricing/models/base/base_model.py
def __init__(self, params: dict[str, float]) -> None:
    """
    Initializes the model and validates its parameters.

    Parameters
    ----------
    params : dict[str, float]
        A dictionary of parameter names to values for the model.
    """
    self.params = params
    self._validate_params()

__eq__

__eq__(other: object) -> bool

Check for equality based on class type and parameters.

Source code in src/optpricing/models/base/base_model.py
def __eq__(self, other: object) -> bool:
    """
    Check for equality based on class type and parameters.
    """
    if not isinstance(other, self.__class__):
        return NotImplemented
    return self.params == other.params

__hash__

__hash__() -> int

Provide a hash for the model, making it usable in sets and dict keys.

Source code in src/optpricing/models/base/base_model.py
def __hash__(self) -> int:
    """
    Provide a hash for the model, making it usable in sets and dict keys.
    """
    return hash((self.__class__, tuple(sorted(self.params.items()))))

__repr__

__repr__() -> str

Provide a formal string representation of the model.

Source code in src/optpricing/models/base/base_model.py
def __repr__(self) -> str:
    """Provide a formal string representation of the model."""
    param_str = ", ".join(f"{k}={v:.4f}" for k, v in self.params.items())
    return f"{self.__class__.__name__}({param_str})"

cf

cf(**kwargs: Any) -> CF

Return the characteristic function of the log-price process.

Raises:

Type Description
NotImplementedError

If the model does not support a characteristic function.

Source code in src/optpricing/models/base/base_model.py
def cf(self, **kwargs: Any) -> CF:
    """
    Return the characteristic function of the log-price process.

    Raises
    ------
    NotImplementedError
        If the model does not support a characteristic function.
    """
    if not self.supports_cf:
        raise NotImplementedError(
            f"{self.name} does not support characteristic functions."
        )
    return self._cf_impl(**kwargs)

get_pde_coeffs

get_pde_coeffs(**kwargs: Any) -> PDECoeffs

Return the coefficients for the pricing PDE.

Raises:

Type Description
NotImplementedError

If the model does not support PDE solving.

Source code in src/optpricing/models/base/base_model.py
def get_pde_coeffs(self, **kwargs: Any) -> PDECoeffs:
    """
    Return the coefficients for the pricing PDE.

    Raises
    ------
    NotImplementedError
        If the model does not support PDE solving.
    """
    if not self.supports_pde:
        raise NotImplementedError(f"{self.name} does not support PDE solving.")
    return self._pde_impl(**kwargs)

get_sde_sampler

get_sde_sampler(**kwargs: Any) -> Callable

Return a function that can be used to sample paths from the model's SDE.

Raises:

Type Description
NotImplementedError

If the model does not support SDE sampling.

Source code in src/optpricing/models/base/base_model.py
def get_sde_sampler(self, **kwargs: Any) -> Callable:
    """
    Return a function that can be used to sample paths from the model's SDE.

    Raises
    ------
    NotImplementedError
        If the model does not support SDE sampling.
    """
    if not self.supports_sde:
        raise NotImplementedError(f"{self.name} does not support SDE sampling.")
    return self._sde_impl(**kwargs)

price_closed_form

price_closed_form(*args, **kwargs) -> float

Compute the option price using a closed-form solution, if available.

Raises:

Type Description
NotImplementedError

If the model does not have a closed-form solution.

Source code in src/optpricing/models/base/base_model.py
def price_closed_form(self, *args, **kwargs) -> float:
    """
    Compute the option price using a closed-form solution, if available.

    Raises
    ------
    NotImplementedError
        If the model does not have a closed-form solution.
    """
    if not self.has_closed_form:
        raise NotImplementedError(
            f"{self.name} does not have a closed-form solution."
        )
    return self._closed_form_impl(*args, **kwargs)

with_params

with_params(**updated_params: float) -> BaseModel

Create a new model instance with updated parameters.

This is useful for calibration and sensitivity analysis.

Parameters:

Name Type Description Default
**updated_params float

Keyword arguments for the parameters to update.

{}

Returns:

Type Description
BaseModel

A new instance of the model with the updated parameters.

Source code in src/optpricing/models/base/base_model.py
def with_params(self, **updated_params: float) -> BaseModel:
    """
    Create a new model instance with updated parameters.

    This is useful for calibration and sensitivity analysis.

    Parameters
    ----------
    **updated_params : float
        Keyword arguments for the parameters to update.

    Returns
    -------
    BaseModel
        A new instance of the model with the updated parameters.
    """
    new_params = {**self.params, **updated_params}
    return self.__class__(params=new_params)

BatesModel

BatesModel(params: dict[str, float] | None = None)

Bases: BaseModel

Bates stochastic volatility jump-diffusion model.

This model combines the Heston stochastic volatility process with Merton's log-normal jump-diffusion process, providing a rich framework for capturing both volatility smiles and sudden market shocks.

Initializes the Bates model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used.

None
Source code in src/optpricing/models/bates.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the Bates model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
    """
    super().__init__(params or self.default_params)

get_sde_stepper

get_sde_stepper() -> Callable

Returns the SDE stepper function for the Bates model.

Source code in src/optpricing/models/bates.py
def get_sde_stepper(self) -> Callable:
    """Returns the SDE stepper function for the Bates model."""
    p = self.params
    kappa, theta, _, vol_of_vol = (
        p["kappa"],
        p["theta"],
        p["rho"],
        p["vol_of_vol"],
    )
    lambda_, mu_j, sigma_j = p["lambda"], p["mu_j"], p["sigma_j"]
    compensator = lambda_ * (np.exp(mu_j + 0.5 * sigma_j**2) - 1)

    def stepper(
        log_s_t: np.ndarray,
        v_t: np.ndarray,
        r: float,
        q: float,
        dt: float,
        dw_s: np.ndarray,
        dw_v: np.ndarray,
        jump_counts: np.ndarray,
        rng: np.random.Generator,
    ) -> tuple[np.ndarray, np.ndarray]:
        v_t_pos = np.maximum(v_t, 0)
        v_sqrt = np.sqrt(v_t_pos)
        # Evolve variance
        v_t_next = v_t + kappa * (theta - v_t_pos) * dt + vol_of_vol * v_sqrt * dw_v
        # Evolve log-spot (Heston part + jump compensator)
        s_drift = (r - q - 0.5 * v_t_pos - compensator) * dt
        next_log_s = log_s_t + s_drift + v_sqrt * dw_s
        # Add jumps
        paths_with_jumps = np.where(jump_counts > 0)[0]
        for path_idx in paths_with_jumps:
            num_jumps = jump_counts[path_idx]
            jump_size = np.sum(rng.normal(loc=mu_j, scale=sigma_j, size=num_jumps))
            next_log_s[path_idx] += jump_size
        return next_log_s, np.maximum(v_t_next, 0)

    return stepper

BlacksApproxModel

BlacksApproxModel(params: dict[str, float])

Bases: BaseModel

Black's approximation for an American call on a stock with discrete dividends.

Initializes the Black's Approximation model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used. Must contain 'sigma'.

required
Source code in src/optpricing/models/blacks_approx.py
def __init__(self, params: dict[str, float]):
    """
    Initializes the Black's Approximation model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
        Must contain 'sigma'.
    """
    super().__init__(params)
    self.bsm_solver = BSMModel(params={"sigma": self.params["sigma"]})

CEVModel

CEVModel(params: dict[str, float] | None = None)

Bases: BaseModel

Constant Elasticity of Variance (CEV) model.

Initializes the CEV model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used. Must contain 'sigma' and 'gamma'. Defaults to None.

None
Source code in src/optpricing/models/cev.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the CEV model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
        Must contain 'sigma' and 'gamma'. Defaults to None.
    """
    super().__init__(params or self.default_params)

sample_terminal_spot

sample_terminal_spot(
    S0: float, r: float, T: float, size: int
) -> np.ndarray

Exact simulation of the terminal spot price via Non-Central Chi-Squared.

This method provides a way to sample directly from the terminal distribution of the CEV process, which is more efficient than path-based simulation for pricing European options.

Parameters:

Name Type Description Default
S0 float

The initial spot price of the asset.

required
r float

The continuously compounded risk-free rate.

required
T float

The time to maturity, in years.

required
size int

The number of samples to generate.

required

Returns:

Type Description
ndarray

An array of simulated terminal spot prices.

Source code in src/optpricing/models/cev.py
def sample_terminal_spot(
    self,
    S0: float,
    r: float,
    T: float,
    size: int,
) -> np.ndarray:
    """
    Exact simulation of the terminal spot price via Non-Central Chi-Squared.

    This method provides a way to sample directly from the terminal
    distribution of the CEV process, which is more efficient than
    path-based simulation for pricing European options.

    Parameters
    ----------
    S0 : float
        The initial spot price of the asset.
    r : float
        The continuously compounded risk-free rate.
    T : float
        The time to maturity, in years.
    size : int
        The number of samples to generate.

    Returns
    -------
    np.ndarray
        An array of simulated terminal spot prices.
    """
    from scipy.stats import ncx2

    p = self.params
    sigma, gamma = p["sigma"], p["gamma"]

    # Handle the boundary case where gamma -> 1 (which is BSM)
    if abs(1.0 - gamma) < 1e-6:
        drift = (r - 0.5 * sigma**2) * T
        diffusion = sigma * np.sqrt(T) * np.random.standard_normal(size)  # noqa: NPY002
        return S0 * np.exp(drift + diffusion)

    df = 2 + 2 / (1 - gamma)
    k = 2 * r / (sigma**2 * (1 - gamma) * (np.exp(r * (1 - gamma) * T) - 1))
    nc = k * S0 ** (2 * (1 - gamma)) * np.exp(-r * (1 - gamma) * T)

    chi2_draws = ncx2.rvs(df=df, nc=nc, size=size)
    ST = (chi2_draws / k) ** (1 / (2 * (1 - gamma)))
    return ST

CGMYModel

CGMYModel(params: dict[str, float] | None = None)

Bases: BaseModel

CGMY (Carr, Geman, Madan, Yor, 2002) pure-jump Levy model.

This is a flexible four-parameter model that can capture skewness and kurtosis. It has a known characteristic function for all valid parameters. Monte Carlo simulation is supported for the special case Y=1 (Variance Gamma process).

Initializes the CGMY model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used. Defaults to None.

None
Source code in src/optpricing/models/cgmy.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the CGMY model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
        Defaults to None.
    """
    super().__init__(params or self.default_params)

raw_cf

raw_cf(*, t: float) -> Callable

Returns the CF of the raw CGMY process, without drift or spot.

This function represents the characteristic function of the process before the risk-neutral drift adjustment is applied.

Parameters:

Name Type Description Default
t float

The time horizon.

required

Returns:

Type Description
Callable

The raw characteristic function.

Source code in src/optpricing/models/cgmy.py
def raw_cf(self, *, t: float) -> Callable:
    """
    Returns the CF of the raw CGMY process, without drift or spot.

    This function represents the characteristic function of the process
    before the risk-neutral drift adjustment is applied.

    Parameters
    ----------
    t : float
        The time horizon.

    Returns
    -------
    Callable
        The raw characteristic function.
    """
    p = self.params
    C, G, M, Y = p["C"], p["G"], p["M"], p["Y"]

    def phi_raw(u: np.ndarray | complex) -> np.ndarray | complex:
        term = (
            C
            * t
            * gamma_func(-Y)
            * ((M - 1j * u) ** Y - M**Y + (G + 1j * u) ** Y - G**Y)
        )
        return np.exp(term)

    return phi_raw

sample_terminal_log_return

sample_terminal_log_return(
    T: float, size: int, rng: Generator
) -> np.ndarray

Generates exact samples of the terminal log-return for the CGMY process.

NOTE: This is only implemented for the special case Y=1, where the process is a difference of two time-changed Brownian motions.

Parameters:

Name Type Description Default
T float

The time to maturity, in years.

required
size int

The number of samples to generate.

required
rng Generator

A random number generator instance for reproducibility.

required

Returns:

Type Description
ndarray

An array of simulated terminal log-returns.

Raises:

Type Description
NotImplementedError

If the model parameter Y is not equal to 1.

Source code in src/optpricing/models/cgmy.py
def sample_terminal_log_return(
    self,
    T: float,
    size: int,
    rng: np.random.Generator,
) -> np.ndarray:
    """
    Generates exact samples of the terminal log-return for the CGMY process.

    NOTE: This is only implemented for the special case Y=1, where the
    process is a difference of two time-changed Brownian motions.

    Parameters
    ----------
    T : float
        The time to maturity, in years.
    size : int
        The number of samples to generate.
    rng : np.random.Generator
        A random number generator instance for reproducibility.

    Returns
    -------
    np.ndarray
        An array of simulated terminal log-returns.

    Raises
    ------
    NotImplementedError
        If the model parameter Y is not equal to 1.
    """
    p = self.params
    C, G, M, Y = p["C"], p["G"], p["M"], p["Y"]

    if not np.isclose(Y, 1.0):
        raise NotImplementedError(
            "Monte Carlo sampling for CGMY is only implemented for Y=1."
        )

    # For Y=1, CGMY is a scaled difference of two Gamma processes.
    # This is equivalent to a Variance Gamma process.
    # Can sample this by time-changing a Brownian motion.
    g_up = rng.gamma(shape=C * T, scale=1 / M, size=size)
    g_down = rng.gamma(shape=C * T, scale=1 / G, size=size)

    return g_up - g_down

CIRModel

CIRModel(params: dict[str, float] | None = None)

Bases: BaseModel

Cox-Ingersoll-Ross (1985) mean-reverting short rate model. dr_t = kappa * (theta - r_t) * dt + sigma * sqrt(r_t) * dW_t

Initializes the CIR model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used.

None
Source code in src/optpricing/models/cir.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the CIR model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
    """
    super().__init__(params or self.default_params)

DupireLocalVolModel

DupireLocalVolModel(params: dict[str, Any] | None = None)

Bases: BaseModel

Dupire Local Volatility model.

Initializes the Dupire Local Volatility model.

The key parameter for this model is 'vol_surface', a callable function that takes maturity (T) and strike (K) and returns the local volatility.

Parameters:

Name Type Description Default
params dict[str, Any] | None

A dictionary of model parameters. If None, a default constant volatility surface is used. Defaults to None.

None
Source code in src/optpricing/models/dupire_local.py
def __init__(self, params: dict[str, Any] | None = None):
    """
    Initializes the Dupire Local Volatility model.

    The key parameter for this model is 'vol_surface', a callable function
    that takes maturity (T) and strike (K) and returns the local volatility.

    Parameters
    ----------
    params : dict[str, Any] | None, optional
        A dictionary of model parameters. If None, a default constant
        volatility surface is used. Defaults to None.
    """
    super().__init__(params or self.default_params)

__repr__

__repr__() -> str

Custom representation to handle the vol_surface function.

Source code in src/optpricing/models/dupire_local.py
def __repr__(self) -> str:
    """Custom representation to handle the vol_surface function."""
    # Get the name of the function or show its type
    vol_surface_repr = getattr(
        self.params["vol_surface"],
        "__name__",
        str(type(self.params["vol_surface"])),
    )
    return f"{self.__class__.__name__}(vol_surface={vol_surface_repr})"

HestonModel

HestonModel(params: dict[str, float] | None = None)

Bases: BaseModel

Heston stochastic volatility model.

This model describes the evolution of an asset's price where the volatility itself is a random process, following a Cox-Ingersoll-Ross (CIR) process. It is widely used as it can capture volatility smiles and skews.

Initializes the Heston model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used.

None
Source code in src/optpricing/models/heston.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the Heston model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
    """
    super().__init__(params or self.default_params)

get_sde_stepper

get_sde_stepper() -> Callable

Returns the SDE stepper function for the Heston model.

This is not used by the JIT-compiled kernel but is kept for potential future use with non-compiled or more complex simulation loops.

Source code in src/optpricing/models/heston.py
def get_sde_stepper(self) -> Callable:
    """
    Returns the SDE stepper function for the Heston model.

    This is not used by the JIT-compiled kernel but is kept for potential
    future use with non-compiled or more complex simulation loops.
    """
    p = self.params
    kappa, theta, _, vol_of_vol = (
        p["kappa"],
        p["theta"],
        p["rho"],
        p["vol_of_vol"],
    )

    def stepper(
        log_s_t: np.ndarray,
        v_t: np.ndarray,
        r: float,
        q: float,
        dt: float,
        dw_s: np.ndarray,
        dw_v: np.ndarray,
    ) -> tuple[np.ndarray, np.ndarray]:
        v_t_pos = np.maximum(v_t, 0)
        v_sqrt = np.sqrt(v_t_pos)
        log_s_t_next = log_s_t + (r - q - 0.5 * v_t_pos) * dt + v_sqrt * dw_s
        v_t_next = v_t + kappa * (theta - v_t_pos) * dt + vol_of_vol * v_sqrt * dw_v
        return log_s_t_next, np.maximum(v_t_next, 0)  # Reflection scheme

    return stepper

HyperbolicModel

HyperbolicModel(params: dict[str, float] | None = None)

Bases: BaseModel

Hyperbolic pure-jump Levy model.

Initializes the Hyperbolic model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used. Defaults to None.

None
Source code in src/optpricing/models/hyperbolic.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the Hyperbolic model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
        Defaults to None.
    """
    super().__init__(params or self.default_params)

raw_cf

raw_cf(*, t: float) -> Callable

Returns the CF of the raw Hyperbolic process, without drift or spot.

This function represents the characteristic function of the process before the risk-neutral drift adjustment is applied.

Parameters:

Name Type Description Default
t float

The time horizon.

required

Returns:

Type Description
Callable

The raw characteristic function.

Source code in src/optpricing/models/hyperbolic.py
def raw_cf(self, *, t: float) -> Callable:
    """
    Returns the CF of the raw Hyperbolic process, without drift or spot.

    This function represents the characteristic function of the process
    before the risk-neutral drift adjustment is applied.

    Parameters
    ----------
    t : float
        The time horizon.

    Returns
    -------
    Callable
        The raw characteristic function.
    """
    p = self.params
    alpha, beta, delta, mu = p["alpha"], p["beta"], p["delta"], p["mu"]
    gamma_0 = np.sqrt(alpha**2 - beta**2)

    def phi_raw(u: np.ndarray | complex) -> np.ndarray | complex:
        gamma_u = np.sqrt(alpha**2 - (beta + 1j * u) ** 2)
        term1 = np.exp(1j * u * mu * t)
        term2 = (gamma_0 / gamma_u) ** t * (
            kv(1, delta * t * gamma_u) / kv(1, delta * t * gamma_0)
        )
        return term1 * term2

    return phi_raw

sample_terminal_log_return

sample_terminal_log_return(
    T: float, size: int, rng: Generator
) -> np.ndarray

Generates exact samples of the terminal log-return for the Hyperbolic process.

This uses the scipy.stats.genhyperbolic distribution, which is the exact distribution of the process at a given time horizon.

Parameters:

Name Type Description Default
T float

The time to maturity, in years.

required
size int

The number of samples to generate.

required
rng Generator

A random number generator instance for reproducibility.

required

Returns:

Type Description
ndarray

An array of simulated terminal log-returns.

Source code in src/optpricing/models/hyperbolic.py
def sample_terminal_log_return(
    self,
    T: float,
    size: int,
    rng: np.random.Generator,
) -> np.ndarray:
    """
    Generates exact samples of the terminal log-return for the Hyperbolic process.

    This uses the `scipy.stats.genhyperbolic` distribution, which is the
    exact distribution of the process at a given time horizon.

    Parameters
    ----------
    T : float
        The time to maturity, in years.
    size : int
        The number of samples to generate.
    rng : np.random.Generator
        A random number generator instance for reproducibility.

    Returns
    -------
    np.ndarray
        An array of simulated terminal log-returns.
    """
    p = self.params
    return genhyperbolic.rvs(
        p=1.0,
        a=p["alpha"],
        b=p["beta"],
        loc=p["mu"] * T,
        scale=p["delta"] * T,
        size=size,
        random_state=rng,
    )

KouModel

KouModel(params: dict[str, float] | None = None)

Bases: BaseModel

Kou double-exponential jump-diffusion model.

Initializes the Kou Double-Exponential Jump model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used.

None
Source code in src/optpricing/models/kou.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the Kou Double-Exponential Jump model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
    """
    super().__init__(params or self.default_params)

MertonJumpModel

MertonJumpModel(params: dict[str, float])

Bases: BaseModel

Merton Jump-Diffusion model, implemented as a Poisson-weighted sum of BSM prices.

Initializes the Merton Jump-Diffusion model. Uses BSMModel instance for the core diffusion

Parameters:

Name Type Description Default
params dict[str, float]

A dictionary of model parameters.

required
Source code in src/optpricing/models/merton_jump.py
def __init__(self, params: dict[str, float]):
    """
    Initializes the Merton Jump-Diffusion model.
    Uses BSMModel instance for the core diffusion

    Parameters
    ----------
    params : dict[str, float]
        A dictionary of model parameters.
    """
    super().__init__(params)
    self.bsm_solver = BSMModel(params={"sigma": self.params["sigma"]})

NIGModel

NIGModel(params: dict[str, float] | None = None)

Bases: BaseModel

Normal Inverse Gaussian (NIG) model, a pure-jump Levy process.

Initializes the NIG model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used.

None
Source code in src/optpricing/models/nig.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the NIG model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
    """
    super().__init__(params or self.default_params)

raw_cf

raw_cf(*, t: float) -> Callable

Returns the CF of the raw NIG process, without drift or spot.

This function represents the characteristic function of the process before the risk-neutral drift adjustment is applied.

Parameters:

Name Type Description Default
t float

The time horizon.

required

Returns:

Type Description
Callable

The raw characteristic function.

Source code in src/optpricing/models/nig.py
def raw_cf(self, *, t: float) -> Callable:
    """
    Returns the CF of the raw NIG process, without drift or spot.

    This function represents the characteristic function of the process
    before the risk-neutral drift adjustment is applied.

    Parameters
    ----------
    t : float
        The time horizon.

    Returns
    -------
    Callable
        The raw characteristic function.
    """
    p = self.params
    alpha, beta, delta = p["alpha"], p["beta"], p["delta"]

    def phi_raw(u: np.ndarray | complex) -> np.ndarray | complex:
        term = (
            delta
            * t
            * (
                np.sqrt(alpha**2 - beta**2)
                - np.sqrt(alpha**2 - (beta + 1j * u) ** 2)
            )
        )
        return np.exp(term)

    return phi_raw

sample_terminal_log_return

sample_terminal_log_return(
    T: float, size: int, rng: Generator
) -> np.ndarray

Generates exact samples of the terminal log-return for the NIG process.

This leverages the representation of the NIG process as a Brownian motion with drift, time-changed by an Inverse Gaussian process.

Parameters:

Name Type Description Default
T float

The time to maturity, in years.

required
size int

The number of samples to generate.

required
rng Generator

A random number generator instance for reproducibility.

required

Returns:

Type Description
ndarray

An array of simulated terminal log-returns.

Source code in src/optpricing/models/nig.py
def sample_terminal_log_return(
    self,
    T: float,
    size: int,
    rng: np.random.Generator,
) -> np.ndarray:
    """
    Generates exact samples of the terminal log-return for the NIG process.

    This leverages the representation of the NIG process as a Brownian
    motion with drift, time-changed by an Inverse Gaussian process.

    Parameters
    ----------
    T : float
        The time to maturity, in years.
    size : int
        The number of samples to generate.
    rng : np.random.Generator
        A random number generator instance for reproducibility.

    Returns
    -------
    np.ndarray
        An array of simulated terminal log-returns.
    """
    from scipy.stats import invgauss

    p = self.params
    alpha, beta, delta = p["alpha"], p["beta"], p["delta"]
    mu_ig = delta * T / np.sqrt(alpha**2 - beta**2)
    ig_time = invgauss.rvs(mu=mu_ig, size=size, random_state=rng)
    bm_drift = beta * ig_time
    bm_diffusion = np.sqrt(ig_time) * rng.standard_normal(size=size)
    return bm_drift + bm_diffusion

ParamValidator

A utility class containing static methods for model parameter validation.

This class is not meant to be instantiated. It serves as a namespace for common validation logic used by BaseModel subclasses.

bounded staticmethod

bounded(
    params: dict[str, float],
    key: str,
    low: float,
    high: float,
    *,
    model: str,
) -> None

Check if a parameter is within a specified inclusive range.

Parameters:

Name Type Description Default
params dict[str, float]

The dictionary of parameters to validate.

required
key str

The name of the parameter to check.

required
low float

The lower bound of the valid range.

required
high float

The upper bound of the valid range.

required
model str

The name of the model performing the validation, for error messages.

required

Raises:

Type Description
ValueError

If the parameter is outside the [low, high] range.

Source code in src/optpricing/models/base/validators.py
@staticmethod
def bounded(
    params: dict[str, float], key: str, low: float, high: float, *, model: str
) -> None:
    """
    Check if a parameter is within a specified inclusive range.

    Parameters
    ----------
    params : dict[str, float]
        The dictionary of parameters to validate.
    key : str
        The name of the parameter to check.
    low : float
        The lower bound of the valid range.
    high : float
        The upper bound of the valid range.
    model : str
        The name of the model performing the validation, for error messages.

    Raises
    ------
    ValueError
        If the parameter is outside the [low, high] range.
    """
    val = params.get(key)
    if val is None or not (low <= val <= high):
        raise ValueError(
            f"{model}: parameter '{key}' must be in [{low}, {high}], got {val}"
        )

positive staticmethod

positive(
    params: dict[str, float], keys: list[str], *, model: str
) -> None

Check if specified parameters are strictly positive.

Parameters:

Name Type Description Default
params dict[str, float]

The dictionary of parameters to validate.

required
keys list[str]

A list of parameter names that must be positive.

required
model str

The name of the model performing the validation, for error messages.

required

Raises:

Type Description
ValueError

If any of the specified parameters are not strictly positive.

Source code in src/optpricing/models/base/validators.py
@staticmethod
def positive(params: dict[str, float], keys: list[str], *, model: str) -> None:
    """
    Check if specified parameters are strictly positive.

    Parameters
    ----------
    params : dict[str, float]
        The dictionary of parameters to validate.
    keys : list[str]
        A list of parameter names that must be positive.
    model : str
        The name of the model performing the validation, for error messages.

    Raises
    ------
    ValueError
        If any of the specified parameters are not strictly positive.
    """
    nonpos = [k for k in keys if params.get(k, 0.0) <= 0.0]
    if nonpos:
        raise ValueError(
            f"{model}: parameters must be positive: {', '.join(nonpos)}"
        )

require staticmethod

require(
    params: dict[str, float],
    required: list[str],
    *,
    model: str,
) -> None

Check for the presence of required parameters.

Parameters:

Name Type Description Default
params dict[str, float]

The dictionary of parameters to validate.

required
required list[str]

A list of parameter names that must be present in params.

required
model str

The name of the model performing the validation, for error messages.

required

Raises:

Type Description
ValueError

If any of the required parameters are missing.

Source code in src/optpricing/models/base/validators.py
@staticmethod
def require(params: dict[str, float], required: list[str], *, model: str) -> None:
    """
    Check for the presence of required parameters.

    Parameters
    ----------
    params : dict[str, float]
        The dictionary of parameters to validate.
    required : list[str]
        A list of parameter names that must be present in `params`.
    model : str
        The name of the model performing the validation, for error messages.

    Raises
    ------
    ValueError
        If any of the required parameters are missing.
    """
    missing = [k for k in required if k not in params]
    if missing:
        raise ValueError(
            f"{model}: missing required parameters: {', '.join(missing)}"
        )

PerpetualPutModel

PerpetualPutModel(params: dict[str, float])

Bases: BaseModel

Provides a closed-form price for a perpetual American put option.

A perpetual option has no expiry date. This model assumes the holder will exercise optimally. The risk-free rate and volatility are considered intrinsic parameters of the model itself.

Initializes the model with its parameters.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary requiring 'sigma' and 'rate'. If None, default_params are used.

required
Source code in src/optpricing/models/perpetual_put.py
def __init__(self, params: dict[str, float]) -> None:
    """
    Initializes the model with its parameters.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary requiring 'sigma' and 'rate'. If None, `default_params`
        are used.
    """
    super().__init__(params=params)

SABRJumpModel

SABRJumpModel(params: dict[str, float] | None = None)

Bases: BaseModel

SABR model with an added log-normal jump component on the spot process.

Initializes the SABR with Jumps model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used. Defaults to None.

None
Source code in src/optpricing/models/sabr_jump.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the SABR with Jumps model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
        Defaults to None.
    """
    super().__init__(params or self.default_params)

SABRModel

SABRModel(params: dict[str, float] | None = None)

Bases: BaseModel

Stochastic Alpha, Beta, Rho (SABR) model from Hagan et al. (2002).

Initializes the SABR model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used.

None
Source code in src/optpricing/models/sabr.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the SABR model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
    """
    super().__init__(params or self.default_params)

VarianceGammaModel

VarianceGammaModel(params: dict[str, float] | None = None)

Bases: BaseModel

Variance Gamma (VG) model, a pure-jump Levy process.

Initializes the Variance Gamma (VG) model.

Parameters:

Name Type Description Default
params dict[str, float]

A dictionary of model parameters.

None
Source code in src/optpricing/models/vg.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the Variance Gamma (VG) model.

    Parameters
    ----------
    params : dict[str, float]
        A dictionary of model parameters.
    """
    super().__init__(params or self.default_params)

raw_cf

raw_cf(*, t: float) -> Callable

Returns the raw characteristic function of the VG process itself.

This function represents the characteristic function of the process before the risk-neutral drift adjustment is applied.

Parameters:

Name Type Description Default
t float

The time horizon.

required

Returns:

Type Description
Callable

The raw characteristic function.

Source code in src/optpricing/models/vg.py
def raw_cf(self, *, t: float) -> Callable:
    """
    Returns the raw characteristic function of the VG process itself.

    This function represents the characteristic function of the process
    before the risk-neutral drift adjustment is applied.

    Parameters
    ----------
    t : float
        The time horizon.

    Returns
    -------
    Callable
        The raw characteristic function.
    """
    p = self.params
    sigma, nu, theta = p["sigma"], p["nu"], p["theta"]

    def phi_raw(u: np.ndarray | complex) -> np.ndarray | complex:
        return (1 - 1j * u * theta * nu + 0.5 * u**2 * sigma**2 * nu) ** (-t / nu)

    return phi_raw

sample_terminal_log_return

sample_terminal_log_return(
    T: float, size: int, rng: Generator
) -> np.ndarray

Generates exact samples of the terminal log-return for the VG process.

This leverages the representation of the VG process as a Brownian motion with drift, time-changed by a Gamma process.

Parameters:

Name Type Description Default
T float

The time to maturity, in years.

required
size int

The number of samples to generate.

required
rng Generator

A random number generator instance for reproducibility.

required

Returns:

Type Description
ndarray

An array of simulated terminal log-returns.

Source code in src/optpricing/models/vg.py
def sample_terminal_log_return(
    self,
    T: float,
    size: int,
    rng: np.random.Generator,
) -> np.ndarray:
    """
    Generates exact samples of the terminal log-return for the VG process.

    This leverages the representation of the VG process as a Brownian
    motion with drift, time-changed by a Gamma process.

    Parameters
    ----------
    T : float
        The time to maturity, in years.
    size : int
        The number of samples to generate.
    rng : np.random.Generator
        A random number generator instance for reproducibility.

    Returns
    -------
    np.ndarray
        An array of simulated terminal log-returns.
    """

    p = self.params
    sigma, nu, theta = p["sigma"], p["nu"], p["theta"]
    gamma_time = rng.gamma(shape=T / nu, scale=nu, size=size)
    bm_drift = theta * gamma_time
    bm_diffusion = sigma * np.sqrt(gamma_time) * rng.standard_normal(size=size)
    return bm_drift + bm_diffusion

VasicekModel

VasicekModel(params: dict[str, float] | None = None)

Bases: BaseModel

Vasicek mean-reverting short rate model. dr_t = kappa * (theta - r_t) * dt + sigma * dW_t

Initializes the Vasicek model.

Parameters:

Name Type Description Default
params dict[str, float] | None

A dictionary of model parameters. If None, default_params are used. Defaults to None.

None
Source code in src/optpricing/models/vasicek.py
def __init__(self, params: dict[str, float] | None = None):
    """
    Initializes the Vasicek model.

    Parameters
    ----------
    params : dict[str, float] | None, optional
        A dictionary of model parameters. If None, `default_params` are used.
        Defaults to None.
    """
    super().__init__(params or self.default_params)