Skip to content

Black Pricing

Here we define the log strike k as

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

where \(K\) is the strike price and \(F\) is the forward price of the underlying asset.

quantflow.options.bs.black_price

black_price(k, sigma, ttm, s)

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

\[ \begin{align} c &= \frac{C}{F} = N(d1) - e^k N(d2) \\ p &= \frac{P}{F} = -N(-d1) + e^k N(-d2) \\ d1 &= \frac{-k + \frac{\sigma^2 t}{2}}{\sigma \sqrt{t}} \\ d2 &= d1 - \sigma \sqrt{t} \end{align} \]

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.

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 Black call/put option prices in forward terms
    from the following params

    $$
    \begin{align}
        c &= \frac{C}{F} = N(d1) - e^k N(d2) \\
        p &= \frac{P}{F} = -N(-d1) + e^k N(-d2) \\
        d1 &= \frac{-k + \frac{\sigma^2 t}{2}}{\sigma \sqrt{t}} \\
        d2 &= d1 - \sigma \sqrt{t}
    \end{align}
    $$

    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.
    """
    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{align} \nu &= \frac{\partial c}{\partial \sigma} \\ &= \frac{\partial p}{\partial \sigma}\\ &= N'(d1) \sqrt{t} \end{align} \]

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{align}
        \nu &= \frac{\partial c}{\partial \sigma} \\
            &= \frac{\partial p}{\partial \sigma}\\
            &= N'(d1) \sqrt{t}
    \end{align}
    $$

    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 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{align} d_1 &= \frac{-k + \frac{1}{2}\sigma^2 t}{\sigma\sqrt{t}} \\ \Delta &= N(d_1) - \frac{1-s}{2} \\ \Gamma &= \frac{N'(d_1)}{\sigma\sqrt{t}} \\ \nu &= N'(d_1)\sqrt{t} \\ \text{volga} &= \nu \cdot \frac{d_1 d_2}{\sigma\sqrt{t}} \\ \text{vanna} &= -\frac{N'(d_1) d_2}{\sigma\sqrt{t}} \\ \Theta &= -\frac{N'(d_1)\sigma}{2\sqrt{t}} \end{align}\]
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{align}
        d_1 &= \frac{-k + \frac{1}{2}\sigma^2 t}{\sigma\sqrt{t}} \\
        \Delta &= N(d_1) - \frac{1-s}{2} \\
        \Gamma &= \frac{N'(d_1)}{\sigma\sqrt{t}} \\
        \nu &= N'(d_1)\sqrt{t} \\
        \text{volga} &= \nu \cdot \frac{d_1 d_2}{\sigma\sqrt{t}} \\
        \text{vanna} &= -\frac{N'(d_1) d_2}{\sigma\sqrt{t}} \\
        \Theta &= -\frac{N'(d_1)\sigma}{2\sqrt{t}}
    \end{align}
    """
    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 / sig,
        vanna=-npd1 * d2 / sig,
        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