Skip to content

Parity

The parity package provides models and utilities based on put-call parity.

ImpliedRateModel

ImpliedRateModel(params: dict[str, float])

Bases: BaseModel

Calculates the risk-free rate implied by put-call parity for European options.

This model solves for the risk-free rate r that satisfies the equation: C - P = Sexp(-qT) - Kexp(-rT)

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()

ParityModel

ParityModel(params: dict[str, float])

Bases: BaseModel

A utility model providing calculations based on Put-Call Parity.

This class is not a traditional pricing model but uses the BaseModel interface to provide parity-based calculations, such as finding a complementary option price.

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()

lower_bound_rate

lower_bound_rate(
    *,
    call_price: float,
    put_price: float,
    spot: float,
    strike: float,
    t: float,
) -> float

Calculates the minimum risk-free rate r to avoid arbitrage.

Parameters:

Name Type Description Default
call_price float

The price of the call option.

required
put_price float

The price of the put option.

required
spot float

The current price of the underlying asset.

required
strike float

The strike price of the option.

required
t float

The time to maturity.

required

Returns:

Type Description
float

The minimum continuously compounded risk-free rate to avoid arbitrage.

Raises:

Type Description
ValueError

If an arbitrage opportunity already exists (S - C + P >= K).

Source code in src/optpricing/parity/parity_model.py
def lower_bound_rate(
    self,
    *,
    call_price: float,
    put_price: float,
    spot: float,
    strike: float,
    t: float,
) -> float:
    """
    Calculates the minimum risk-free rate `r` to avoid arbitrage.

    Parameters
    ----------
    call_price : float
        The price of the call option.
    put_price : float
        The price of the put option.
    spot : float
        The current price of the underlying asset.
    strike : float
        The strike price of the option.
    t : float
        The time to maturity.

    Returns
    -------
    float
        The minimum continuously compounded risk-free rate to avoid arbitrage.

    Raises
    ------
    ValueError
        If an arbitrage opportunity already exists (S - C + P >= K).
    """
    val = strike / (spot - call_price + put_price)
    if val <= 0:
        raise ValueError(
            "Arbitrage exists (S - C + P >= K). Cannot compute implied rate."
        )
    return math.log(val) / t

price_bounds

price_bounds(
    *,
    spot: float,
    strike: float,
    r: float,
    t: float,
    call: bool,
    option_price: float,
) -> tuple[float, float]

Return absolute (lower, upper) no-arbitrage bounds for an option.

Parameters:

Name Type Description Default
spot float

The current price of the underlying asset.

required
strike float

The strike price of the option.

required
r float

The risk-free rate.

required
t float

The time to maturity.

required
call bool

True if option_price is for a call, False if it's for a put.

required
option_price float

The price of the known option. In this case it is a place holder.

required

Returns:

Type Description
tuple[float, float]

A tuple containing the (lower_bound, upper_bound) for the option price.

Source code in src/optpricing/parity/parity_model.py
def price_bounds(
    self,
    *,
    spot: float,
    strike: float,
    r: float,
    t: float,
    call: bool,
    option_price: float,  # noqa: F841
) -> tuple[float, float]:
    """
    Return absolute (lower, upper) no-arbitrage bounds for an option.

    Parameters
    ----------
    spot : float
        The current price of the underlying asset.
    strike : float
        The strike price of the option.
    r : float
        The risk-free rate.
    t : float
        The time to maturity.
    call : bool
        True if `option_price` is for a call, False if it's for a put.
    option_price : float
        The price of the known option. In this case it is a place holder.

    Returns
    -------
    tuple[float, float]
        A tuple containing the (lower_bound, upper_bound) for the option price.
    """
    discounted_strike = strike * math.exp(-r * t)

    if call:
        lower_bound = max(0, spot - discounted_strike)
        upper_bound = spot
    else:  # Put
        lower_bound = max(0, discounted_strike - spot)
        upper_bound = discounted_strike

    return lower_bound, upper_bound