Skip to content

Black Pricing

Here we define the log strike k as

\[\begin{equation} k = \log{\frac{K}{F_\tau}} \end{equation}\]

where \(K\) is the strike price and \(F_\tau\) is the forward price of the underlying asset at time to maturity \(\tau\).

quantflow.options.bs.black_price

black_price(k, sigma, ttm, s)

Calculate the undiscounted Black call/put option prices in forward terms from the following params

\[\begin{equation} \begin{aligned} c &= \frac{C}{D_\tau F_\tau} = N(d_1) - e^k N(d_2) \\ p &= \frac{P}{D_\tau F_\tau} = -N(-d_1) + e^k N(-d_2) \\ d_1 &= \frac{-k + \frac{\sigma^2 \tau}{2}}{\sigma \sqrt{\tau}} \\ d_2 &= d_1 - \sigma \sqrt{\tau} \end{aligned} \end{equation}\]

where \(N\) is the CDF of the standard normal distribution.

The results are option prices divided by the forward price also known as option prices in forward terms. These are non-dimensional prices that can be easily converted to actual option prices by multiplying with the forward price of the underlying asset at time to maturity \(\tau\) and a suitable discount factor if interest rates are non-zero.

PARAMETER DESCRIPTION
k

Vector or single value of log-strikes

TYPE: FloatArrayLike

sigma

Corresponding vector or single value of implied volatilities (0.2 for 20%)

TYPE: FloatArrayLike

ttm

Corresponding vector or single value of Time to Maturity

TYPE: FloatArrayLike

s

Corresponding vector or single value of call/put flag (1 for call, -1 for put)

TYPE: FloatArrayLike

Source code in quantflow/options/bs.py
def black_price(
    k: Annotated[
        FloatArrayLike,
        Doc("Vector or single value of log-strikes"),
    ],
    sigma: Annotated[
        FloatArrayLike,
        Doc(
            (
                "Corresponding vector or single value of "
                "implied volatilities (0.2 for 20%)"
            )
        ),
    ],
    ttm: Annotated[
        FloatArrayLike, Doc("Corresponding vector or single value of Time to Maturity")
    ],
    s: Annotated[
        FloatArrayLike,
        Doc(
            "Corresponding vector or single value of call/put flag "
            "(1 for call, -1 for put)"
        ),
    ],
) -> FloatArray:
    r"""Calculate the undiscounted Black call/put option prices in forward terms
    from the following params

    \begin{equation}
    \begin{aligned}
        c &= \frac{C}{D_\tau F_\tau} = N(d_1) - e^k N(d_2) \\
        p &= \frac{P}{D_\tau F_\tau} = -N(-d_1) + e^k N(-d_2) \\
        d_1 &= \frac{-k + \frac{\sigma^2 \tau}{2}}{\sigma \sqrt{\tau}} \\
        d_2 &= d_1 - \sigma \sqrt{\tau}
    \end{aligned}
    \end{equation}

    where $N$ is the CDF of the
    [standard normal distribution](../../glossary.md#standard-normal-distribution).

    The results are option prices divided by the forward price also known as
    option prices in forward terms. These are non-dimensional prices
    that can be easily converted to actual option prices by multiplying with the
    forward price of the underlying asset at time to maturity $\tau$
    and a suitable discount factor if interest rates are non-zero.
    """
    sig2 = sigma * sigma * ttm
    sig = np.sqrt(sig2)
    d1 = (-k + 0.5 * sig2) / sig
    d2 = d1 - sig
    return s * norm.cdf(s * d1) - s * np.exp(k) * norm.cdf(s * d2)

quantflow.options.bs.black_call

black_call(k, sigma, ttm)
PARAMETER DESCRIPTION
k

Vector or single value of log-strikes

TYPE: FloatArrayLike

sigma

Corresponding vector or single value of implied volatilities (0.2 for 20%)

TYPE: FloatArrayLike

ttm

Corresponding vector or single value of Time to Maturity

TYPE: FloatArrayLike

Source code in quantflow/options/bs.py
def black_call(
    k: Annotated[FloatArrayLike, Doc("Vector or single value of log-strikes")],
    sigma: Annotated[
        FloatArrayLike,
        Doc(
            "Corresponding vector or single value of implied volatilities "
            "(0.2 for 20%)"
        ),
    ],
    ttm: Annotated[
        FloatArrayLike, Doc("Corresponding vector or single value of Time to Maturity")
    ],
) -> FloatArrayLike:
    kk = np.asarray(k)
    return black_price(kk, np.asarray(sigma), np.asarray(ttm), np.ones(kk.shape))

quantflow.options.bs.black_vega

black_vega(k, sigma, ttm)

Calculate the Black option vega from the log-strikes, volatility and time to maturity. The vega is the same for calls and puts.

\[\begin{equation} \begin{aligned} \nu &= \frac{\partial c}{\partial \sigma} \\ &= \frac{\partial p}{\partial \sigma}\\ &= n(d_1) \sqrt{\tau} \end{aligned} \end{equation}\]

where \(n\) is the PDF of the standard normal distribution and \(d_1\) is defined in black_price.

Same formula for both calls and puts.

PARAMETER DESCRIPTION
k

Vector or single value of log-strikes

TYPE: FloatArrayLike

sigma

Corresponding vector or single value of implied volatilities (0.2 for 20%)

TYPE: FloatArrayLike

ttm

Corresponding vector or single value of Time to Maturity

TYPE: FloatArrayLike

Source code in quantflow/options/bs.py
def black_vega(
    k: Annotated[
        FloatArrayLike,
        Doc("Vector or single value of log-strikes"),
    ],
    sigma: Annotated[
        FloatArrayLike,
        Doc(
            "Corresponding vector or single value of implied volatilities (0.2 for 20%)"
        ),
    ],
    ttm: Annotated[
        FloatArrayLike, Doc("Corresponding vector or single value of Time to Maturity")
    ],
) -> FloatArrayLike:
    r"""Calculate the Black option vega from the log-strikes,
    volatility and time to maturity. The vega is the same for calls and puts.

    \begin{equation}
    \begin{aligned}
        \nu &= \frac{\partial c}{\partial \sigma} \\
            &= \frac{\partial p}{\partial \sigma}\\
            &= n(d_1) \sqrt{\tau}
    \end{aligned}
    \end{equation}

    where $n$ is the PDF of the
    [standard normal distribution](../../glossary.md#standard-normal-distribution)
    and $d_1$ is defined in [black_price][..black_price].

    Same formula for both calls and puts.
    """
    sig2 = sigma * sigma * ttm
    sig = np.sqrt(sig2)
    d1 = (-k + 0.5 * sig2) / sig
    return norm.pdf(d1) * np.sqrt(ttm)

quantflow.options.bs.BlackSensitivities pydantic-model

Bases: BaseModel

Black model sensitivities (Greeks) for a single option or array of options

Fields:

iv pydantic-field

iv

Implied Black volatility (0.1 for 10%)

price pydantic-field

price

Option price in forward space

delta pydantic-field

delta

Change in price per unit change in forward

gamma pydantic-field

gamma

Change in delta per unit relative change in forward

vega pydantic-field

vega

Change in price per unit change in volatility

volga pydantic-field

volga

Change in vega per unit change in volatility

vanna pydantic-field

vanna

Change in delta per unit change in volatility

theta pydantic-field

theta

Change in price per unit time (negative = time decay)

calculate classmethod

calculate(k, ttm, s, iv=None, price=None)

Calculate Black model sensitivities (Greeks) in forward space. Either the implied volatility iv or the option price must be provided. If both are provided, the implied volatility will be used.

\[\begin{equation} \begin{aligned} \Delta &= \frac{\partial O_s}{\partial F} = N(d_1) - \frac{1-s}{2} \\ \Gamma &= F \frac{\partial^2 O_s}{\partial F^2} = \frac{n(d_1)}{\sigma\sqrt{\tau}} \\ \nu &= \frac{\partial o_s}{\partial \sigma} = n(d_1)\sqrt{\tau} \\ \text{volga} &= \frac{\partial \nu}{\partial \sigma} = \nu \cdot \frac{d_1 d_2}{\sigma} \\ \text{vanna} &= \frac{\partial \Delta}{\partial \sigma} = -\frac{n(d_1) d_2}{\sigma} \\ \Theta &= -\frac{\partial o_s}{\partial \tau} = -\frac{n(d_1)\sigma}{2\sqrt{\tau}} \end{aligned} \end{equation}\]

where \(o_s\) is the option price in forward space indexed by the call/put flag \(s\) (\(o_1 = c\) the call, \(o_{-1} = p\) the put), \(O_s = F\, o_s\) is the undiscounted option price in currency units for forward \(F\), with the derivative in \(\Delta\) taken at fixed strike \(K\). \(N\) and \(n\) are the CDF and PDF of the standard normal distribution and \(d_1\), \(d_2\) are defined in black_price.

PARAMETER DESCRIPTION
k

Vector or single value of log-strikes

TYPE: FloatArrayLike

ttm

Time to maturity

TYPE: FloatArrayLike

s

Call/put flag (1 for call, -1 for put)

TYPE: FloatArrayLike

iv

Implied volatility (0.2 for 20%)

TYPE: FloatArrayLike | None DEFAULT: None

price

Option price in forward space

TYPE: FloatArrayLike | None DEFAULT: None

Source code in quantflow/options/bs.py
@classmethod
def calculate(
    cls,
    k: Annotated[FloatArrayLike, Doc("Vector or single value of log-strikes")],
    ttm: Annotated[FloatArrayLike, Doc("Time to maturity")],
    s: Annotated[FloatArrayLike, Doc("Call/put flag (1 for call, -1 for put)")],
    iv: Annotated[
        FloatArrayLike | None, Doc("Implied volatility (0.2 for 20%)")
    ] = None,
    price: Annotated[
        FloatArrayLike | None, Doc("Option price in forward space")
    ] = None,
) -> BlackSensitivities:
    r"""Calculate Black model sensitivities (Greeks) in forward space.
    Either the implied volatility `iv` or the option `price` must be provided.
    If both are provided, the implied volatility will be used.

    \begin{equation}
    \begin{aligned}
        \Delta &= \frac{\partial O_s}{\partial F}
            = N(d_1) - \frac{1-s}{2} \\
        \Gamma &= F \frac{\partial^2 O_s}{\partial F^2}
            = \frac{n(d_1)}{\sigma\sqrt{\tau}} \\
        \nu &= \frac{\partial o_s}{\partial \sigma}
            = n(d_1)\sqrt{\tau} \\
        \text{volga} &= \frac{\partial \nu}{\partial \sigma}
            = \nu \cdot \frac{d_1 d_2}{\sigma} \\
        \text{vanna} &= \frac{\partial \Delta}{\partial \sigma}
            = -\frac{n(d_1) d_2}{\sigma} \\
        \Theta &= -\frac{\partial o_s}{\partial \tau}
            = -\frac{n(d_1)\sigma}{2\sqrt{\tau}}
    \end{aligned}
    \end{equation}

    where $o_s$ is the option price in
    [forward space](../../glossary.md#forward-space) indexed by the
    call/put flag $s$ ($o_1 = c$ the call, $o_{-1} = p$ the put),
    $O_s = F\, o_s$ is the undiscounted option price in currency units
    for forward $F$, with the derivative in $\Delta$ taken at fixed
    strike $K$.
    $N$ and $n$ are the CDF and PDF of the
    [standard normal distribution](../../glossary.md#standard-normal-distribution)
    and $d_1$, $d_2$ are defined in [black_price][...black_price].
    """
    if iv is None:
        if price is None:
            raise ValueError(
                "Either implied volatility or option price must be provided"
            )
        result = implied_black_volatility(
            k=k,
            price=price,
            ttm=ttm,
            initial_sigma=0.2,
            call_put=s,
        )
        iv = result.single().value if np.isscalar(price) else result.values
    else:
        price = black_price(k, iv, ttm, s)
    sig2 = iv * iv * ttm
    sig = np.sqrt(sig2)
    d1 = (-k + 0.5 * sig2) / sig
    d2 = d1 - sig
    npd1 = norm.pdf(d1)
    nd1 = norm.cdf(d1)
    vega = npd1 * np.sqrt(ttm)
    return cls(
        iv=iv,
        price=price,
        delta=nd1 - 0.5 * (1 - s),
        gamma=npd1 / sig,
        vega=vega,
        volga=vega * d1 * d2 / iv,
        vanna=-npd1 * d2 / iv,
        theta=-npd1 * iv / (2 * np.sqrt(ttm)),
    )

quantflow.options.bs.implied_black_volatility

implied_black_volatility(
    k, price, ttm, initial_sigma, call_put
)

Calculate the implied black volatility via Newton's method

It returns a ImpliedVols object which contains the implied volatility and convergence status. Implied volatility is in decimals (0.2 for 20%).

PARAMETER DESCRIPTION
k

Vector or scalar of log strikes

TYPE: FloatArrayLike

price

Corresponding vector or scalar of option price in forward terms (price divided by forward price)

TYPE: FloatArrayLike

ttm

Corresponding vector or single value of Time to Maturity

TYPE: FloatArrayLike

initial_sigma

Corresponding vector or single value of initial volatility

TYPE: FloatArrayLike

call_put

Corresponding vector or single value of call/put flag (1 for call, -1 for put)

TYPE: FloatArrayLike

Source code in quantflow/options/bs.py
def implied_black_volatility(
    k: Annotated[
        FloatArrayLike,
        Doc("Vector or scalar of log strikes"),
    ],
    price: Annotated[
        FloatArrayLike,
        Doc(
            "Corresponding vector or scalar of option price in forward terms "
            "(price divided by forward price)"
        ),
    ],
    ttm: Annotated[
        FloatArrayLike,
        Doc("Corresponding vector or single value of Time to Maturity"),
    ],
    initial_sigma: Annotated[
        FloatArrayLike,
        Doc("Corresponding vector or single value of initial volatility"),
    ],
    call_put: Annotated[
        FloatArrayLike,
        Doc(
            "Corresponding vector or single value of call/put flag "
            "(1 for call, -1 for put)"
        ),
    ],
) -> ImpliedVols:
    """Calculate the implied black volatility via Newton's method

    It returns a [ImpliedVols][quantflow.options.bs.ImpliedVols] object which
    contains the implied volatility and convergence status.
    Implied volatility is in decimals (0.2 for 20%).
    """
    if not np.isscalar(k) and np.isscalar(initial_sigma):
        initial_sigma = np.full_like(k, initial_sigma)
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", RuntimeWarning)
        result = newton(
            lambda x: black_price(k, x, ttm, call_put) - price,
            initial_sigma,
            fprime=lambda x: black_vega(k, x, ttm),
            full_output=True,
        )
    if hasattr(result, "root"):
        return ImpliedVols(values=result.root, converged=result.converged)
    else:
        return ImpliedVols(
            values=np.asarray([result[0]]), converged=np.asarray([result[1]])
        )

quantflow.options.bs.ImpliedVols

Bases: NamedTuple

Result of root finding algorithm

values instance-attribute

values

Array of implied volatilities in decimals (0.2 for 20%)

converged instance-attribute

converged

Array indicating whether the root finding algorithm converged for each implied volatility

single

single()

Return the first implied volatility and convergence status a a single ImpliedVol

Source code in quantflow/options/bs.py
def single(self) -> ImpliedVol:
    """Return the first implied volatility and convergence status a
    a single ImpliedVol"""
    if len(self.values) != 1 or len(self.converged) != 1:
        raise ValueError("Expected exactly one root and convergence status")
    return ImpliedVol(value=self.values[0], converged=self.converged[0])

quantflow.options.bs.ImpliedVol

Bases: NamedTuple

Result of implied volatility calculation

value instance-attribute

value

The implied volatility in decimals (0.2 for 20%)

converged instance-attribute

converged

Whether the root finding algorithm converged