financialkit.options#

Closed-form European prices: Bachelier’s normal model, Black-Scholes-Merton with a dividend yield, Black-76 on forwards and futures, and Garman-Kohlhagen for currencies, all broadcasting over array inputs; put-call parity and Merton’s no-arbitrage bounds; analytic Greeks checked against finite differences, and a discretely rebalanced delta hedge on paths from financialkit.integrators.gbm_paths(); implied volatility (scipy.optimize.brentq after a bounds check), the Brenner-Subrahmanyam approximation, the smile and the Breeden-Litzenberger density; Cox-Ross-Rubinstein, Rendleman-Bartter, Leisen-Reimer and Boyle trinomial trees with American early exercise, and the Barone-Adesi-Whaley approximation.

financialkit.options: vanilla option pricing, Greeks, implied volatility and lattices.

Closed forms: Bachelier’s normal model, Black-Scholes-Merton with a dividend yield, Black-76 on forwards (the one implementation the rest of financialkit calls) and Garman-Kohlhagen for currencies, all broadcasting over array inputs; put-call parity and Merton’s no-arbitrage bounds; analytic Greeks, checked against finite differences, and a simulated discrete delta hedge built on financialkit.integrators.gbm_paths(). Implied volatility by scipy.optimize.brentq() after a bounds check, the Brenner-Subrahmanyam at-the-money shortcut, the smile, and the Breeden-Litzenberger risk-neutral density. Lattices: Cox-Ross-Rubinstein, Rendleman-Bartter and Leisen-Reimer binomial trees and Boyle’s trinomial tree (hand-written backward induction, with American early exercise and the exercise boundary), plus the Barone-Adesi-Whaley approximation.

class financialkit.options.AmericanApproximationResult(price, european_price, critical_price)[source]#

Bases: object

Output of the Barone-Adesi-Whaley quadratic approximation.

Parameters:
critical_price: float#

Spot at which immediate exercise becomes optimal (inf for a call, 0 for a put, when early exercise is never optimal).

european_price: float#

The European (Black-Scholes-Merton) value it adds the premium to.

price: float#

Approximate American option value.

class financialkit.options.BinomialTree(n_steps=200)[source]#

Bases: LatticePricer

A recombining binomial tree; subclasses choose u, d and p.

Parameters:

n_steps (int)

price(S, K, T, r, sigma, q=0.0, option_type='call', american=False)[source]#

Value a vanilla option by backward induction on the tree.

Parameters:
  • S (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • K (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • T (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • r (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • sigma (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • q (float) – Continuous dividend yield.

  • option_type (str)

  • american (bool) – Allow early exercise at every node.

Return type:

TreeResult

Returns:

TreeResult

class financialkit.options.CRRTree(n_steps=200)[source]#

Bases: BinomialTree

Cox-Ross-Rubinstein (1979) binomial tree, \(u = e^{\sigma\sqrt{\Delta t}} = 1/d\).

Parameters:

n_steps (int)

Examples

Hull, 10th ed., Example 21.1 (Figure 21.3): an American put with S = K = 50, r = 10%, sigma = 40% and five months, on a five-step tree.

>>> round(CRRTree(5).price(50.0, 50.0, 5 / 12, 0.10, 0.40, option_type="put", american=True).price, 2)
4.49
method: str = 'crr'#
class financialkit.options.DensityResult(strikes, density)[source]#

Bases: object

A risk-neutral density recovered from option prices.

Parameters:
density: ndarray#

Risk-neutral density of \(S_T\) there.

Type:

ndarray, shape (m,)

strikes: ndarray#

Strikes at which the density is estimated.

Type:

ndarray, shape (m,)

class financialkit.options.Greeks(delta, gamma, vega, theta, rho, epsilon, vanna, volga)[source]#

Bases: object

Sensitivities of an option price, each broadcast like the inputs.

Theta is \(\partial V / \partial t = -\partial V / \partial T\), per year; vega, rho and epsilon are per unit (not per percentage point) of volatility, rate and dividend yield.

Parameters:
delta: ndarray | float#

Sensitivity to the spot, \(\partial V / \partial S\).

epsilon: ndarray | float#

Sensitivity to the dividend yield, \(\partial V / \partial q\).

gamma: ndarray | float#

Curvature in the spot, \(\partial^2 V / \partial S^2\).

rho: ndarray | float#

Sensitivity to the risk-free rate, \(\partial V / \partial r\).

theta: ndarray | float#

Time decay, \(\partial V / \partial t\), per year.

vanna: ndarray | float#

Cross derivative \(\partial^2 V / \partial S \partial \sigma\).

vega: ndarray | float#

Sensitivity to the volatility, \(\partial V / \partial \sigma\).

volga: ndarray | float#

Second derivative in the volatility, \(\partial^2 V / \partial \sigma^2\).

class financialkit.options.HedgeResult(times, spot, pnl, option_price)[source]#

Bases: object

Output of a discretely rebalanced delta-hedging simulation.

Parameters:
option_price: float#

Premium received for the option at time 0.

pnl: ndarray#

Terminal profit of the short, hedged option position, in time-T money.

Type:

ndarray, shape (n_paths,)

spot: ndarray#

Simulated spot paths.

Type:

ndarray, shape (n_paths, n_rebalance + 1)

times: ndarray#

Rebalancing dates.

Type:

ndarray, shape (n_rebalance + 1,)

class financialkit.options.LatticePricer(n_steps=200)[source]#

Bases: ABC

A recombining lattice for European and American vanilla options.

Parameters:

n_steps (int) – Number of time steps.

method: str = ''#
abstractmethod price(S, K, T, r, sigma, q=0.0, option_type='call', american=False)[source]#

Value a vanilla option on the lattice.

Parameters:
  • S (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • K (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • T (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • r (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • sigma (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • q (float) – Continuous dividend yield.

  • option_type (str)

  • american (bool) – Allow early exercise at every node.

Return type:

TreeResult

Returns:

TreeResult

class financialkit.options.LeisenReimerTree(n_steps=200)[source]#

Bases: BinomialTree

Leisen-Reimer (1996) binomial tree with Peizer-Pratt probabilities.

\(p = h(d_2)\), \(p' = h(d_1)\), \(u = e^{(r-q)\Delta t} p'/p\) and \(d = (e^{(r-q)\Delta t} - p u)/(1 - p)\), with \(h\) the Peizer-Pratt inversion. An even n_steps is rounded up to the next odd number, as the method requires.

Parameters:

n_steps (int)

Examples

>>> from financialkit.options import black_scholes_price
>>> lr = LeisenReimerTree(51).price(100.0, 100.0, 1.0, 0.05, 0.2).price
>>> bool(abs(lr - black_scholes_price(100.0, 100.0, 1.0, 0.05, 0.2)) < 1e-3)
True
method: str = 'leisen_reimer'#
class financialkit.options.RendlemanBartterTree(n_steps=200)[source]#

Bases: BinomialTree

Rendleman-Bartter (1979) drift-centred binomial tree.

\(\ln u, \ln d = (r - q - \sigma^2/2)\Delta t \pm \sigma\sqrt{\Delta t}\), with the no-arbitrage probability \(p = (e^{(r-q)\Delta t} - d)/(u - d)\), which is \(1/2 + O(\Delta t^{3/2})\).

Parameters:

n_steps (int)

Examples

>>> res = RendlemanBartterTree(500).price(100.0, 100.0, 1.0, 0.05, 0.2)
>>> round(res.price, 1)
10.5
method: str = 'rendleman_bartter'#
class financialkit.options.SmileResult(strikes, vols, forward, T)[source]#

Bases: object

Implied volatilities across strikes at one maturity.

Parameters:
T: float#

Maturity in years.

forward: float#

Forward price \(S e^{(r - q) T}\).

property log_moneyness: ndarray#

\(\ln(K / F)\) for each strike.

Type:

ndarray

strikes: ndarray#

Strikes.

Type:

ndarray, shape (n,)

vols: ndarray#

Black-Scholes implied volatilities.

Type:

ndarray, shape (n,)

class financialkit.options.TreeResult(price, delta, gamma, n_steps, method, times=<factory>, exercise_boundary=<factory>)[source]#

Bases: object

Output of a lattice (binomial or trinomial) valuation.

Parameters:
delta: float#

Delta read off the first time step of the lattice.

exercise_boundary: ndarray#

For an American put, the highest node price at each time where early exercise is optimal (for a call, the lowest); nan where no node is exercised. Empty for European options.

Type:

ndarray, shape (n_steps + 1,)

gamma: float#

Gamma read off the second time step of the lattice.

method: str#

"crr", "rendleman_bartter", "leisen_reimer" or "trinomial".

n_steps: int#

Number of time steps actually used (Leisen-Reimer rounds up to odd).

price: float#

Option value at the root of the lattice.

times: ndarray#

The lattice’s time grid.

Type:

ndarray, shape (n_steps + 1,)

class financialkit.options.TrinomialTree(n_steps=200, stretch=1.224744871391589)[source]#

Bases: LatticePricer

Boyle’s (1986) trinomial tree, \(u = e^{\lambda\sigma\sqrt{\Delta t}}\), middle branch flat.

With \(M = e^{(r-q)\Delta t}\) and \(V = M^2(e^{\sigma^2\Delta t} - 1)\),

\[p_u = \frac{(V + M^2 - M)u - (M - 1)}{(u - 1)(u^2 - 1)}, \quad p_d = \frac{(V + M^2 - M)u^2 - (M - 1)u^3}{(u - 1)(u^2 - 1)}, \quad p_m = 1 - p_u - p_d,\]

which match the mean and variance of the next price exactly.

Parameters:
  • n_steps (int)

  • stretch (float) – Boyle’s \(\lambda > 1\); the default \(\sqrt{3/2}\) puts about a third of the probability on the middle branch.

Examples

>>> from financialkit.options import black_scholes_price
>>> tri = TrinomialTree(200).price(100.0, 100.0, 1.0, 0.05, 0.2).price
>>> bool(abs(tri - black_scholes_price(100.0, 100.0, 1.0, 0.05, 0.2)) < 0.01)
True
method: str = 'trinomial'#
price(S, K, T, r, sigma, q=0.0, option_type='call', american=False)[source]#

Value a vanilla option by backward induction on the trinomial tree.

Parameters:
Return type:

TreeResult

Returns:

TreeResult

probabilities(T, r, sigma, q=0.0)[source]#

Return (u, p_u, p_m, p_d) for one step of this tree.

Parameters:
Return type:

tuple[float, float, float, float]

Returns:

tuple of float

financialkit.options.bachelier_price(S, K, T, r, sigma_n, q=0.0, option_type='call')[source]#

Bachelier’s (1900) normal-model price of a European option.

The forward follows an arithmetic Brownian motion with absolute volatility \(\sigma_N\) (price units per square-root year), so

\[c = e^{-rT}\left[(F - K) N(d) + \sigma_N\sqrt{T}\, n(d)\right], \quad d = \frac{F - K}{\sigma_N \sqrt{T}},\]

and the put follows from parity. At the money, \(c = e^{-rT}\sigma_N\sqrt{T/(2\pi)}\). Bachelier’s own setting is r = q = 0.

Parameters:
  • S (ArrayLike) – Spot, strike, maturity (years) and risk-free rate.

  • K (ArrayLike) – Spot, strike, maturity (years) and risk-free rate.

  • T (ArrayLike) – Spot, strike, maturity (years) and risk-free rate.

  • r (ArrayLike) – Spot, strike, maturity (years) and risk-free rate.

  • sigma_n (ArrayLike) – Normal (absolute) volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

Return type:

ndarray | float

Returns:

float or ndarray

Examples

>>> import numpy as np
>>> price = bachelier_price(100.0, 100.0, 1.0, 0.0, 20.0)
>>> bool(np.isclose(price, 20.0 / np.sqrt(2 * np.pi)))
True
financialkit.options.barone_adesi_whaley(S, K, T, r, sigma, q=0.0, option_type='call')[source]#

Approximate American option value by Barone-Adesi and Whaley (1987).

Parameters:
  • S (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • K (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • T (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • r (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • sigma (float) – Spot, strike, maturity (years), risk-free rate and volatility.

  • q (float) – Continuous dividend yield (cost of carry b = r - q).

  • option_type (str)

Return type:

AmericanApproximationResult

Returns:

AmericanApproximationResult

Examples

An American put is worth more than its European counterpart:

>>> res = barone_adesi_whaley(100.0, 100.0, 1.0, 0.08, 0.25, option_type="put")
>>> bool(res.price > res.european_price), round(res.critical_price, 2) < 100
(True, True)
financialkit.options.black76_price(F, K, T, r, sigma, option_type='call')[source]#

Black’s (1976) price of a European option on a forward or futures price.

\[c = e^{-rT}[F N(d_1) - K N(d_2)], \quad p = e^{-rT}[K N(-d_2) - F N(-d_1)].\]

This is the single Black-76 implementation in financialkit: caps, floors and swaptions in financialkit.interest_rate_models call it.

Parameters:
  • F (ArrayLike) – Forward (or futures) price for delivery at T.

  • K (ArrayLike) – Strike, maturity (years), discount rate and lognormal volatility.

  • T (ArrayLike) – Strike, maturity (years), discount rate and lognormal volatility.

  • r (ArrayLike) – Strike, maturity (years), discount rate and lognormal volatility.

  • sigma (ArrayLike) – Strike, maturity (years), discount rate and lognormal volatility.

  • option_type (str)

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Hull, 10th ed., Example 18.7: a 4-month put on futures at 20 with a 20 strike, 9% rate and 25% volatility.

>>> round(black76_price(20.0, 20.0, 4 / 12, 0.09, 0.25, "put"), 2)
1.12
financialkit.options.black_d1_d2(F, K, sigma, T)[source]#

Black’s \(d_{1,2} = [\ln(F/K) \pm \tfrac12 \sigma^2 T] / (\sigma\sqrt{T})\).

Parameters:
  • F (ArrayLike) – Forward, strike, volatility and maturity; sigma * sqrt(T) must be positive.

  • K (ArrayLike) – Forward, strike, volatility and maturity; sigma * sqrt(T) must be positive.

  • sigma (ArrayLike) – Forward, strike, volatility and maturity; sigma * sqrt(T) must be positive.

  • T (ArrayLike) – Forward, strike, volatility and maturity; sigma * sqrt(T) must be positive.

Return type:

tuple[ndarray, ndarray]

Returns:

d1, d2 (ndarray)

financialkit.options.black_scholes_greeks(S, K, T, r, sigma, q=0.0, option_type='call')[source]#

All first-order and the main second-order Black-Scholes-Merton Greeks.

With \(\phi = \pm 1\) for a call/put:

\[\begin{split}\Delta = \phi e^{-qT} N(\phi d_1), \quad \Gamma = \frac{e^{-qT} n(d_1)}{S \sigma \sqrt{T}}, \quad \mathcal{V} = S e^{-qT} n(d_1) \sqrt{T}, \\ \Theta = -\frac{S e^{-qT} n(d_1) \sigma}{2\sqrt{T}} - \phi r K e^{-rT} N(\phi d_2) + \phi q S e^{-qT} N(\phi d_1), \\ \rho = \phi K T e^{-rT} N(\phi d_2), \quad \epsilon = -\phi S T e^{-qT} N(\phi d_1), \quad \text{vanna} = -e^{-qT} n(d_1) \frac{d_2}{\sigma}, \quad \text{volga} = \mathcal{V} \frac{d_1 d_2}{\sigma}.\end{split}\]
Parameters:
  • S (ArrayLike) – Spot, strike, maturity (years, positive), risk-free rate and volatility (positive).

  • K (ArrayLike) – Spot, strike, maturity (years, positive), risk-free rate and volatility (positive).

  • T (ArrayLike) – Spot, strike, maturity (years, positive), risk-free rate and volatility (positive).

  • r (ArrayLike) – Spot, strike, maturity (years, positive), risk-free rate and volatility (positive).

  • sigma (ArrayLike) – Spot, strike, maturity (years, positive), risk-free rate and volatility (positive).

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

Return type:

Greeks

Returns:

Greeks – Each field broadcast like the inputs.

Examples

Hull, 10th ed., Example 19.1 (S = 49, K = 50, r = 5%, sigma = 20%, 20 weeks):

>>> g = black_scholes_greeks(49.0, 50.0, 0.3846, 0.05, 0.20)
>>> round(g.delta, 3), round(g.gamma, 3), round(g.vega, 1), round(g.theta, 2), round(g.rho, 2)
(0.522, 0.066, 12.1, -4.31, 8.91)
financialkit.options.black_scholes_price(S, K, T, r, sigma, q=0.0, option_type='call')[source]#

Black-Scholes-Merton price of a European option on a dividend-paying stock.

\[c = S e^{-qT} N(d_1) - K e^{-rT} N(d_2), \quad p = K e^{-rT} N(-d_2) - S e^{-qT} N(-d_1),\]

with \(d_{1,2} = [\ln(S/K) + (r - q \pm \sigma^2/2) T] / (\sigma\sqrt{T})\). Black and Scholes (1973) derived it for q = 0; Merton (1973) added the continuous dividend yield. At T = 0 or sigma = 0 it returns the discounted intrinsic value of the forward.

Parameters:
  • S (ArrayLike) – Spot, strike, maturity (years), risk-free rate and volatility.

  • K (ArrayLike) – Spot, strike, maturity (years), risk-free rate and volatility.

  • T (ArrayLike) – Spot, strike, maturity (years), risk-free rate and volatility.

  • r (ArrayLike) – Spot, strike, maturity (years), risk-free rate and volatility.

  • sigma (ArrayLike) – Spot, strike, maturity (years), risk-free rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Hull, 10th ed., Example 15.6 (S = 42, K = 40, r = 10%, sigma = 20%, six months):

>>> round(black_scholes_price(42.0, 40.0, 0.5, 0.10, 0.20), 2)
4.76
>>> round(black_scholes_price(42.0, 40.0, 0.5, 0.10, 0.20, option_type="put"), 2)
0.81
financialkit.options.breeden_litzenberger_density(strikes, call_prices, T, r)[source]#

Risk-neutral density of \(S_T\) from European call prices.

Parameters:
  • strikes (ArrayLike) – Strictly increasing strikes, n >= 3.

  • call_prices (ArrayLike) – Call prices at those strikes, same maturity.

  • T (float) – Maturity and risk-free rate.

  • r (float) – Maturity and risk-free rate.

Return type:

DensityResult

Returns:

DensityResult – The density at the n - 2 interior strikes.

Examples

Black-Scholes prices recover the lognormal density:

>>> import numpy as np
>>> from scipy.stats import lognorm
>>> from financialkit.options import black_scholes_price
>>> K = np.linspace(50.0, 200.0, 601)
>>> res = breeden_litzenberger_density(K, black_scholes_price(100.0, K, 1.0, 0.05, 0.2), 1.0, 0.05)
>>> exact = lognorm.pdf(res.strikes, s=0.2, scale=100 * np.exp(0.05 - 0.02))
>>> bool(np.max(np.abs(res.density - exact)) < 1e-5)
True
financialkit.options.brenner_subrahmanyam_vol(price, S, T, r=0.0, q=0.0)[source]#

Brenner-Subrahmanyam (1988) approximation for at-the-money-forward options.

For \(K = F\), \(c = S e^{-qT}[2N(\sigma\sqrt{T}/2) - 1] \approx S e^{-qT} \sigma \sqrt{T / (2\pi)}\), so

\[\sigma \approx \sqrt{\frac{2\pi}{T}} \frac{c}{S e^{-qT}},\]

with a relative error of order \(\sigma^2 T / 24\). At the forward, calls and puts have the same price, so either can be passed.

Parameters:
  • price (ArrayLike) – At-the-money-forward call (or put) price.

  • S (ArrayLike) – Spot, maturity, risk-free rate and dividend yield (r is not used by the formula and is accepted for a uniform signature).

  • T (ArrayLike) – Spot, maturity, risk-free rate and dividend yield (r is not used by the formula and is accepted for a uniform signature).

  • r (ArrayLike) – Spot, maturity, risk-free rate and dividend yield (r is not used by the formula and is accepted for a uniform signature).

  • q (ArrayLike) – Spot, maturity, risk-free rate and dividend yield (r is not used by the formula and is accepted for a uniform signature).

Return type:

ndarray | float

Returns:

float or ndarray

Examples

>>> c = black_scholes_price(100.0, 100.0, 0.25, 0.0, 0.2)
>>> round(brenner_subrahmanyam_vol(c, 100.0, 0.25), 4)
0.1999
financialkit.options.delta_hedge(S0, K, T, r, sigma, n_rebalance, n_paths, mu=None, q=0.0, option_type='call', hedge_sigma=None, rng=None)[source]#

Simulate a short option hedged with the Black-Scholes delta.

Parameters:
  • S0 (float) – Initial spot, strike, maturity (years) and risk-free rate.

  • K (float) – Initial spot, strike, maturity (years) and risk-free rate.

  • T (float) – Initial spot, strike, maturity (years) and risk-free rate.

  • r (float) – Initial spot, strike, maturity (years) and risk-free rate.

  • sigma (float) – Realized volatility of the simulated paths.

  • n_rebalance (int) – Number of equally spaced hedge rebalances (the hedge is set at time 0 and adjusted at each later date before T).

  • n_paths (int) – Number of simulated paths.

  • mu (float | None) – Real-world drift of the stock; defaults to the risk-neutral r - q.

  • q (float) – Continuous dividend yield, paid to the stock holder.

  • option_type (str)

  • hedge_sigma (float | None) – Volatility used to price and delta-hedge the option; defaults to sigma. A mismatch shows the cost of hedging at the wrong vol.

  • rng (Generator | int | None)

Return type:

HedgeResult

Returns:

HedgeResult

Examples

>>> res = delta_hedge(100.0, 100.0, 1.0, 0.05, 0.2, n_rebalance=52, n_paths=2000, rng=0)
>>> bool(abs(res.pnl.mean()) < 4 * res.pnl.std() / 2000**0.5)
True
financialkit.options.finite_difference_greeks(pricer, S, K, T, r, sigma, q=0.0, option_type='call', h=0.0001)[source]#

Greeks by central finite differences of pricer.

Used to check analytic Greeks, and to get Greeks from any pricer with the pricer(S, K, T, r, sigma, q, option_type) signature.

Parameters:
Return type:

Greeks

Returns:

Greeks

financialkit.options.forward_price(S, T, r, q=0.0)[source]#

Forward price \(F = S e^{(r - q) T}\).

Parameters:
  • S (ArrayLike) – Spot, maturity, risk-free rate and dividend yield.

  • T (ArrayLike) – Spot, maturity, risk-free rate and dividend yield.

  • r (ArrayLike) – Spot, maturity, risk-free rate and dividend yield.

  • q (ArrayLike) – Spot, maturity, risk-free rate and dividend yield.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

>>> round(forward_price(100.0, 1.0, 0.05), 4)
105.1271
financialkit.options.garman_kohlhagen_price(S, K, T, r_d, r_f, sigma, option_type='call')[source]#

Garman-Kohlhagen (1983) price of a European currency option.

The foreign currency is an asset paying the foreign interest rate, so this is black_scholes_price() with q = r_f:

\[c = S e^{-r_f T} N(d_1) - K e^{-r_d T} N(d_2).\]
Parameters:
  • S (ArrayLike) – Spot exchange rate, domestic units per unit of foreign currency.

  • K (ArrayLike) – Strike (same units) and maturity in years.

  • T (ArrayLike) – Strike (same units) and maturity in years.

  • r_d (ArrayLike) – Domestic and foreign continuously compounded rates.

  • r_f (ArrayLike) – Domestic and foreign continuously compounded rates.

  • sigma (ArrayLike) – Exchange-rate volatility.

  • option_type (str)

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Haug, The Complete Guide to Option Pricing Formulas, 2nd ed., Section 1.1.6: S = 1.56, K = 1.60, six months, r_d = 6%, r_f = 8%, sigma = 12%.

>>> round(garman_kohlhagen_price(1.56, 1.60, 0.5, 0.06, 0.08, 0.12), 4)
0.0291
financialkit.options.implied_vol_smile(prices, strikes, S, T, r, q=0.0, option_type='call')[source]#

Implied volatilities across a strip of strikes at one maturity.

Parameters:
  • prices (ArrayLike) – Observed European option prices and their strikes.

  • strikes (ArrayLike) – Observed European option prices and their strikes.

  • S (float) – Spot, maturity, risk-free rate and dividend yield.

  • T (float) – Spot, maturity, risk-free rate and dividend yield.

  • r (float) – Spot, maturity, risk-free rate and dividend yield.

  • q (float) – Spot, maturity, risk-free rate and dividend yield.

  • option_type (str)

Return type:

SmileResult

Returns:

SmileResult

Examples

Prices from a single Black-Scholes volatility give a flat smile:

>>> import numpy as np
>>> K = np.array([80.0, 100.0, 120.0])
>>> smile = implied_vol_smile(black_scholes_price(100.0, K, 1.0, 0.03, 0.25), K, 100.0, 1.0, 0.03)
>>> np.round(smile.vols, 6)
array([0.25, 0.25, 0.25])
financialkit.options.implied_volatility(price, S, K, T, r, q=0.0, option_type='call', tol=1e-10, max_iter=1000)[source]#

Black-Scholes implied volatility by Brent’s method.

For the Black-76 implied volatility of an option on a forward F, pass S = F and q = r.

Parameters:
  • price (ArrayLike) – Observed European option prices.

  • S (ArrayLike) – Spot, strike, maturity, risk-free rate and dividend yield.

  • K (ArrayLike) – Spot, strike, maturity, risk-free rate and dividend yield.

  • T (ArrayLike) – Spot, strike, maturity, risk-free rate and dividend yield.

  • r (ArrayLike) – Spot, strike, maturity, risk-free rate and dividend yield.

  • q (ArrayLike) – Spot, strike, maturity, risk-free rate and dividend yield.

  • option_type (str)

  • tol (float) – Absolute tolerance on the volatility.

  • max_iter (int) – Iteration cap passed to scipy.optimize.brentq().

Return type:

ndarray | float

Returns:

float or ndarray

Raises:

ValueError – If any price lies outside Merton’s open no-arbitrage interval, where no volatility can reproduce it.

Examples

Hull, 10th ed., Section 15.11: a call worth 1.875 with S = 21, K = 20, r = 10% and three months has an implied volatility of 23.5%.

>>> round(implied_volatility(1.875, 21.0, 20.0, 0.25, 0.10), 3)
0.235
financialkit.options.no_arbitrage_bounds(S, K, T, r, q=0.0, option_type='call')[source]#

Merton’s (1973) model-free bounds on a European option price.

Calls: \(\max(S e^{-qT} - K e^{-rT}, 0) \le c \le S e^{-qT}\). Puts: \(\max(K e^{-rT} - S e^{-qT}, 0) \le p \le K e^{-rT}\).

Parameters:
Return type:

tuple[ndarray | float, ndarray | float]

Returns:

lower, upper (float or ndarray)

Examples

>>> lo, hi = no_arbitrage_bounds(100.0, 90.0, 1.0, 0.05)
>>> round(lo, 4), hi
(14.3894, 100.0)
financialkit.options.parity_residual(call, put, S, K, T, r, q=0.0)[source]#

Violation of put-call parity, \(c - p - (S e^{-qT} - K e^{-rT})\).

Zero for any arbitrage-free pair; a nonzero residual is the profit of the conversion (or reversal) that exploits it.

Parameters:
Return type:

ndarray | float

Returns:

float or ndarray

financialkit.options.payoff(S, K, option_type='call')[source]#

Exercise value \(\max(\pm(S - K), 0)\).

Parameters:
Return type:

ndarray | float

Returns:

float or ndarray

financialkit.options.put_call_parity(price, S, K, T, r, q=0.0, option_type='call')[source]#

The other leg of a European call-put pair, by put-call parity.

Parameters:
  • price (ArrayLike) – Price of the known option.

  • S (ArrayLike) – Spot, strike, maturity, risk-free rate and dividend yield.

  • K (ArrayLike) – Spot, strike, maturity, risk-free rate and dividend yield.

  • T (ArrayLike) – Spot, strike, maturity, risk-free rate and dividend yield.

  • r (ArrayLike) – Spot, strike, maturity, risk-free rate and dividend yield.

  • q (ArrayLike) – Spot, strike, maturity, risk-free rate and dividend yield.

  • option_type (str) – Type of the known option; the other type’s price is returned.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Hull, 10th ed., Example 15.6: a 4.76 call with S = 42, K = 40, r = 10% and six months implies a 0.81 put.

>>> round(put_call_parity(4.7594, 42.0, 40.0, 0.5, 0.10), 2)
0.81