financialkit.exotic_options#

Barrier options: Merton’s down-and-out call, the Reiner-Rubinstein closed forms for all eight single barriers with rebates, Ikeda-Kunitomo double barriers, the Broadie-Glasserman-Kou correction for discrete monitoring and Monte Carlo with Brownian-bridge crossing probabilities; binary options (cash-or-nothing, asset-or-nothing, gap and one-touch); Asian options (Kemna-Vorst geometric closed forms, arithmetic Monte Carlo with the geometric control variate, Turnbull-Wakeman moment matching); floating and fixed lookbacks with bridge-sampled extrema; variance swaps replicated by a strip of options; Geske compound options, forward starts and choosers; Margrabe exchange options, Stulz options on the maximum and minimum and Reiner quantos; and American options by Longstaff-Schwartz least-squares Monte Carlo. Paths come from financialkit.integrators and Monte Carlo results from financialkit.monte_carlo.

financialkit.exotic_options: barrier, binary, Asian, lookback, compound, multi-asset and American options.

Barriers: Merton’s down-and-out call by the method of images, the Reiner-Rubinstein closed forms for all eight single-barrier options with rebates, Ikeda-Kunitomo double barriers, the Broadie-Glasserman-Kou correction for discrete monitoring, and Monte Carlo with Brownian-bridge crossing probabilities. Binaries: cash-or-nothing, asset-or-nothing, gap and one-touch options. Asians: Kemna-Vorst geometric closed forms, the arithmetic average by Monte Carlo with the geometric control variate, and Turnbull-Wakeman moment matching. Lookbacks: Goldman-Sosin-Gatto floating and Conze-Viswanathan fixed strikes, with exact bridge-sampled extrema for Monte Carlo. Variance swaps replicated by a strip of out-of-the-money options (Demeterfi-Derman-Kamal-Zou). Geske compound options, Rubinstein forward starts and choosers, Margrabe exchange options, Stulz options on the maximum or minimum of two assets, Reiner quantos, and American options by Longstaff-Schwartz least-squares Monte Carlo. Paths are simulated through financialkit.integrators, and Monte Carlo results are financialkit.monte_carlo result objects.

class financialkit.exotic_options.LongstaffSchwartzResult(estimate, std_error, n_samples, confidence=0.95, method='crude', times=<factory>, exercise_boundary=<factory>, exercise_probability=0.0)[source]#

Bases: MonteCarloResult

An American (Bermudan) option value by least-squares Monte Carlo (Longstaff-Schwartz 2001).

estimate is the mean discounted cash flow of the regression exercise policy and std_error its standard error; the policy is fitted on the same paths, so the estimate carries a small in-sample bias.

Parameters:
exercise_boundary: ndarray#

For a put, the highest price in the simulated in-the-money range at which the fitted policy exercises on each date (for a call, the lowest); nan where it never does, and the strike at expiry.

Type:

ndarray, shape (n_exercise + 1,)

exercise_probability: float = 0.0#

Fraction of paths exercised before maturity.

Type:

float

times: ndarray#

The exercise dates, starting at 0.

Type:

ndarray, shape (n_exercise + 1,)

class financialkit.exotic_options.VarianceSwapResult(fair_variance, strikes, weights, option_prices, forward, K0, T)[source]#

Bases: object

The fair strike of a variance swap from a strip of out-of-the-money options (Demeterfi et al. 1999).

The fair variance is \(\sum_i w_i Q(K_i) - (F/K_0 - 1)^2 / T\), with \(Q\) the out-of-the-money option price at strike \(K_i\) and \(w_i = 2 e^{rT} \Delta K_i / (T K_i^2)\).

Parameters:
K0: float#

Largest strike at or below the forward, separating puts from calls.

Type:

float

T: float#

Maturity in years.

Type:

float

property contributions: ndarray#

Each option’s share \(w_i Q(K_i)\) of the fair variance.

Type:

ndarray

fair_variance: float#

Annualized fair variance strike \(K_{\mathrm{var}}\).

Type:

float

forward: float#

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

Type:

float

option_prices: ndarray#

Out-of-the-money option prices \(Q(K_i)\) (the average of put and call at \(K_0\)).

Type:

ndarray, shape (n,)

strikes: ndarray#

Strikes of the replicating strip.

Type:

ndarray, shape (n,)

property vol_strike: float#

The volatility \(\sqrt{K_{\mathrm{var}}}\) quoted for the swap.

Type:

float

weights: ndarray#

Number of options held per unit of variance notional, \(w_i\).

Type:

ndarray, shape (n,)

financialkit.exotic_options.arithmetic_asian_mc(S, K, T, r, sigma, q=0.0, option_type='call', n_fixings=12, n_paths=50000, control_variate=True, rng=None, confidence=0.95)[source]#

Arithmetic-average option by Monte Carlo, with Kemna and Vorst’s geometric control variate.

The paths come from financialkit.integrators.gbm_paths() on the fixing grid. The discounted geometric-average payoff on the same paths has the known mean geometric_asian_price() and a correlation with the arithmetic payoff above 0.99, so financialkit.monte_carlo.control_variate_estimate() removes almost all the variance.

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

  • K (float) – Spot, strike, maturity, rate and volatility.

  • T (float) – Spot, strike, maturity, rate and volatility.

  • r (float) – Spot, strike, maturity, rate and volatility.

  • sigma (float) – Spot, strike, maturity, rate and volatility.

  • q (float) – Continuous dividend yield.

  • option_type (str)

  • n_fixings (int) – Number of equally spaced fixings \(t_i = iT/n\).

  • n_paths (int) – Number of paths.

  • control_variate (bool) – Use the geometric control; otherwise plain Monte Carlo.

  • rng (Generator | int | None) – Generator or seed.

  • confidence (float) – Coverage of the confidence interval.

Return type:

ControlVariateResult | MonteCarloResult

Returns:

ControlVariateResult or MonteCarloResult – A ControlVariateResult (which also reports the crude estimate) with the control, a MonteCarloResult without.

Examples

>>> res = arithmetic_asian_mc(100.0, 100.0, 1.0, 0.05, 0.2, n_paths=20_000, rng=0)
>>> bool(res.std_error < res.crude_std_error / 10)
True
financialkit.exotic_options.asset_or_nothing_price(S, K, T, r, sigma, q=0.0, option_type='call')[source]#

Asset-or-nothing digital: one share at expiry if the option finishes in the money.

\[c = S e^{-qT} N(d_1), \qquad p = S e^{-qT} N(-d_1).\]
Parameters:
  • S (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • K (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • T (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • r (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • sigma (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Haug (2007), Section 4.19.3: S = 70, K = 65, T = 0.5, r = 7%, b = 2%, sigma = 27%:

>>> round(asset_or_nothing_price(70.0, 65.0, 0.5, 0.07, 0.27, q=0.05, option_type="put"), 4)
20.2069
financialkit.exotic_options.barrier_crossing_probability(x0, x1, b, sigma, dt)[source]#

Probability that a Brownian bridge from x0 to x1 crosses the level b.

For a log-price with volatility \(\sigma\) over a step \(\Delta t\), with both endpoints on the same side of \(b\),

\[P = \exp\!\left(-\frac{2 (x_0 - b)(x_1 - b)}{\sigma^2 \Delta t}\right);\]

it is 1 if either endpoint is on the far side. The drift does not matter once both endpoints are fixed (Glasserman 2004, Sec. 6.4).

Parameters:
  • x0 (ArrayLike) – Log-prices at the start and end of the step.

  • x1 (ArrayLike) – Log-prices at the start and end of the step.

  • b (float) – Log of the barrier.

  • sigma (float) – Volatility.

  • dt (float) – Step length in years.

Return type:

ndarray

Returns:

ndarray

Examples

>>> import numpy as np
>>> float(barrier_crossing_probability(0.1, 0.1, 0.0, 0.2, 0.25))
0.1353352832366127
financialkit.exotic_options.barrier_option_mc(S, K, H, T, r, sigma, q=0.0, option_type='call', barrier_type='down-and-out', n_steps=50, n_paths=100000, continuous=True, rng=None, confidence=0.95)[source]#

Monte Carlo price of a single-barrier option without rebate.

Paths come from financialkit.integrators.gbm_paths(). With continuous=False the barrier is checked only at the n_steps grid dates, which prices a discretely monitored option exactly. With continuous=True each step’s Brownian-bridge crossing probability \(p_i\) (barrier_crossing_probability()) weights the payoff by the survival probability \(\prod_i (1 - p_i)\), which prices continuous monitoring without discretisation bias (Glasserman 2004, Sec. 6.4).

Parameters:
  • S (float) – Spot, strike, barrier, maturity, rate and volatility.

  • K (float) – Spot, strike, barrier, maturity, rate and volatility.

  • H (float) – Spot, strike, barrier, maturity, rate and volatility.

  • T (float) – Spot, strike, barrier, maturity, rate and volatility.

  • r (float) – Spot, strike, barrier, maturity, rate and volatility.

  • sigma (float) – Spot, strike, barrier, maturity, rate and volatility.

  • q (float) – Continuous dividend yield.

  • option_type (str)

  • barrier_type (str)

  • n_steps (int) – Time steps (monitoring dates when continuous=False).

  • n_paths (int) – Number of paths.

  • continuous (bool) – Continuous monitoring through the bridge correction, or discrete.

  • rng (Generator | int | None) – Generator or seed.

  • confidence (float) – Coverage of the confidence interval.

Return type:

MonteCarloResult

Returns:

MonteCarloResult

Examples

>>> res = barrier_option_mc(100.0, 100.0, 90.0, 1.0, 0.05, 0.2, n_steps=20, n_paths=40_000, rng=0)
>>> bool(abs(res.estimate - barrier_price(100.0, 100.0, 90.0, 1.0, 0.05, 0.2)) < 4 * res.std_error)
True
financialkit.exotic_options.barrier_price(S, K, H, T, r, sigma, q=0.0, option_type='call', barrier_type='down-and-out', rebate=0.0)[source]#

Reiner-Rubinstein (1991) price of a continuously monitored single-barrier option.

With \(b = r - q\), \(\mu = (b - \sigma^2/2)/\sigma^2\) and \(\lambda = \sqrt{\mu^2 + 2r/\sigma^2}\), every one of the eight calls and puts is a sum of the building blocks \(A, \dots, F\) of Haug (2007, Sec. 4.17.1): \(A\) is the vanilla option, \(B\) the vanilla struck at the barrier, \(C\) and \(D\) their reflections through the barrier, \(E\) a knock-in’s rebate paid at expiry if the barrier is never hit and \(F\) a knock-out’s rebate paid when it is hit. For example, for \(K > H\),

\[C_{do} = A - C + F, \qquad C_{di} = C + E.\]

In-out parity holds when the rebate is zero: knock-in + knock-out = vanilla. If the spot is already through the barrier, a knock-out is worth its rebate and a knock-in the vanilla option.

Parameters:
  • S (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • K (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • H (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • T (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • r (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • sigma (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

  • barrier_type (str)

  • rebate (ArrayLike) – Cash paid when a knock-out is knocked out, or at expiry when a knock-in never knocks in.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Haug (2007), Table 4-13: S = 100, H = 95, rebate 3, T = 0.5, r = 8%, b = 4%, sigma = 25%:

>>> round(barrier_price(100.0, 90.0, 95.0, 0.5, 0.08, 0.25, q=0.04, barrier_type="down-and-out", rebate=3.0), 4)
9.0246
financialkit.exotic_options.bivariate_normal_cdf(h, k, rho)[source]#

Standard bivariate normal distribution \(M(h, k; \rho) = P(X \le h, Y \le k)\).

Computed exactly from Owen’s T function (scipy.special.owens_t()):

\[M(h, k; \rho) = \tfrac12 N(h) + \tfrac12 N(k) - T\!\left(h, \frac{k - \rho h}{h \sqrt{1 - \rho^2}}\right) - T\!\left(k, \frac{h - \rho k}{k \sqrt{1 - \rho^2}}\right) - \beta,\]

with \(\beta = 0\) when \(h\) and \(k\) have the same sign and \(\tfrac12\) otherwise (Owen 1956). It broadcasts over its inputs, which the compound and rainbow option formulas rely on.

Parameters:
  • h (ArrayLike) – Upper integration limits.

  • k (ArrayLike) – Upper integration limits.

  • rho (ArrayLike) – Correlation, \(|\rho| < 1\).

Return type:

ndarray | float

Returns:

float or ndarray

Examples

>>> import numpy as np
>>> bool(abs(bivariate_normal_cdf(0.0, 0.0, 0.5) - (0.25 + np.arcsin(0.5) / (2 * np.pi))) < 1e-12)
True
financialkit.exotic_options.bridge_extremum(log_paths, sigma, dt, kind='max', rng=None)[source]#

Exact samples of each path’s continuous maximum or minimum given its grid values.

Within a step from \(x_0\) to \(x_1\) the maximum of the Brownian bridge is

\[M = \tfrac12\left[x_0 + x_1 + \sqrt{(x_1 - x_0)^2 - 2\sigma^2 \Delta t \ln U}\right], \quad U \sim U(0, 1],\]

and the minimum is the mirror image (Glasserman 2004, Sec. 6.4). The path’s extremum is the extremum over its steps.

Parameters:
Return type:

ndarray

Returns:

ndarray, shape (n_paths,) – The log of each path’s running maximum (or minimum) over the whole interval.

financialkit.exotic_options.cash_or_nothing_price(S, K, T, r, sigma, q=0.0, option_type='call', cash=1.0)[source]#

Cash-or-nothing digital: cash at expiry if \(S_T > K\) (call) or \(S_T < K\) (put).

\[c = X e^{-rT} N(d_2), \qquad p = X e^{-rT} N(-d_2).\]
Parameters:
  • S (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • K (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • T (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • r (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • sigma (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

  • cash (ArrayLike) – Amount paid \(X\).

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Haug (2007), Section 4.19.2: S = 100, K = 80, T = 0.75, r = 6%, b = 0, sigma = 35%, cash 10:

>>> round(cash_or_nothing_price(100.0, 80.0, 0.75, 0.06, 0.35, q=0.06, option_type="put", cash=10.0), 3)
2.671
financialkit.exotic_options.chooser_price(S, K, t_choose, T, r, sigma, q=0.0)[source]#

Rubinstein (1991) simple chooser: at \(t\) the holder picks a call or a put, both struck at \(K\) and expiring at \(T\).

At \(t\) the holder takes \(\max(c, p) = c + (p - c)^+\), and by put-call parity \(p - c = K e^{-r(T-t)} - S_t e^{-q(T-t)}\), so

\[V = c(S, K, T) + e^{-q(T-t)}\, p\big(S, K e^{-(r-q)(T-t)}, t\big).\]
Parameters:
  • S (ArrayLike) – Spot and strike.

  • K (ArrayLike) – Spot and strike.

  • t_choose (ArrayLike) – Choice date and expiry, t_choose <= T.

  • T (ArrayLike) – Choice date and expiry, t_choose <= T.

  • r (ArrayLike) – Rate and volatility.

  • sigma (ArrayLike) – Rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Haug (2007), Section 4.4.1: S = 50, K = 50, t = 0.25, T = 0.5, r = 8%, b = 8%, sigma = 25%:

>>> round(chooser_price(50.0, 50.0, 0.25, 0.5, 0.08, 0.25), 4)
6.1071
financialkit.exotic_options.compound_option_price(S, K1, K2, T1, T2, r, sigma, q=0.0, option_type='call', underlying_type='call')[source]#

Geske (1979) compound option: an option expiring at \(T_1\) with strike \(K_1\) on an option to \(T_2\) struck at \(K_2\).

With \(I\) the critical spot (critical_price()), \(\rho = \sqrt{T_1/T_2}\), \(y_{1,2}\) the Black-Scholes \(d_{1,2}\) for strike \(I\) and horizon \(T_1\), and \(z_{1,2}\) those for \(K_2\) and \(T_2\), the call on a call is

\[S e^{-q T_2} M(z_1, y_1; \rho) - K_2 e^{-r T_2} M(z_2, y_2; \rho) - K_1 e^{-r T_1} N(y_2),\]

and the other three combinations follow by flipping signs (Haug 2007, Sec. 4.6). They satisfy the compound put-call parity call on V - put on V = V - K1 exp(-r T1).

Parameters:
  • S (ArrayLike) – Spot.

  • K1 (float) – Strikes of the compound option and of the underlying option.

  • K2 (float) – Strikes of the compound option and of the underlying option.

  • T1 (float) – Expiries of the compound option and of the underlying, T1 < T2.

  • T2 (float) – Expiries of the compound option and of the underlying, T1 < T2.

  • r (float) – Rate and volatility.

  • sigma (float) – Rate and volatility.

  • q (float) – Continuous dividend yield.

  • option_type (str) – The compound option.

  • underlying_type (str) – The option it is written on.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Haug (2007), Section 4.6: a put on a call with S = 500, K1 = 50, K2 = 520, T1 = 0.25, T2 = 0.5, r = 8%, q = 3%, sigma = 35%:

>>> round(compound_option_price(500.0, 50.0, 520.0, 0.25, 0.5, 0.08, 0.35, q=0.03, option_type="put"), 3)
21.196
financialkit.exotic_options.critical_price(K1, K2, tau, r, sigma, q=0.0, underlying_type='call')[source]#

Spot \(I\) at which the underlying option is worth the compound strike: \(V(I, K_2, \tau) = K_1\).

Solved by scipy.optimize.brentq() on the Black-Scholes-Merton price of the underlying option with \(\tau = T_2 - T_1\) to run.

Parameters:
  • K1 (float) – Strike of the compound option (the price paid for the underlying option).

  • K2 (float) – Strike of the underlying option.

  • tau (float) – Remaining life of the underlying option at \(T_1\), rate and volatility.

  • r (float) – Remaining life of the underlying option at \(T_1\), rate and volatility.

  • sigma (float) – Remaining life of the underlying option at \(T_1\), rate and volatility.

  • q (float) – Continuous dividend yield.

  • underlying_type (str)

Return type:

float

Returns:

float

Examples

>>> from financialkit.options import black_scholes_price
>>> I = critical_price(5.0, 100.0, 0.5, 0.05, 0.2)
>>> round(black_scholes_price(I, 100.0, 0.5, 0.05, 0.2), 10)
5.0
financialkit.exotic_options.discrete_barrier_price(S, K, H, T, r, sigma, q=0.0, option_type='call', barrier_type='down-and-out', n_monitor=252, rebate=0.0)[source]#

Broadie-Glasserman-Kou (1997) price of a barrier monitored at n_monitor equally spaced dates.

A path can cross a discretely monitored barrier between two dates without being caught, so the option behaves like a continuously monitored one with the barrier moved away from the spot:

\[H \to H e^{\pm \beta \sigma \sqrt{\Delta t}}, \qquad \beta = -\zeta(1/2)/\sqrt{2\pi} \approx 0.5826,\]

with + for an up barrier and - for a down barrier. The error is \(o(1/\sqrt{m})\), against \(O(1/\sqrt{m})\) for ignoring the difference.

Parameters:
  • S (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • K (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • H (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • T (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • r (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • sigma (ArrayLike) – Spot, strike, barrier, maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

  • barrier_type (str)

  • n_monitor (int) – Number of monitoring dates \(m\), the last at expiry.

  • rebate (ArrayLike) – Cash rebate, as in barrier_price().

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Daily monitoring is worth more to the holder of a knock-out than continuous monitoring:

>>> args = (100.0, 100.0, 85.0, 1.0, 0.05, 0.3)
>>> bool(discrete_barrier_price(*args, n_monitor=252) > barrier_price(*args))
True
financialkit.exotic_options.double_barrier_price(S, K, L, U, T, r, sigma, q=0.0, option_type='call', knock='out', n_terms=5)[source]#

Ikeda-Kunitomo (1992) double-barrier option with flat barriers \(L < S < U\).

The knock-out call is a series of images reflected alternately through both barriers (Haug 2007, Sec. 4.17.3, with zero curvature):

\[c = S e^{(b - r)T} \sum_{n=-N}^{N} \left[\left(\tfrac{U^n}{L^n}\right)^{\mu} (N(d_1) - N(d_2)) - \left(\tfrac{L^{n+1}}{U^n S}\right)^{\mu} (N(d_3) - N(d_4))\right] - K e^{-rT} \sum_{n=-N}^{N} [\dots]_{\mu - 2,\; d_i - \sigma\sqrt T},\]

with \(\mu = 2b/\sigma^2 + 1\). The terms decay like \(e^{-2n^2 \ln^2(U/L)/\sigma^2 T}\), so a handful suffice. The knock-in is the vanilla option minus the knock-out.

Parameters:
  • S (ArrayLike) – Spot, strike, lower and upper barriers, maturity, rate and volatility.

  • K (ArrayLike) – Spot, strike, lower and upper barriers, maturity, rate and volatility.

  • L (ArrayLike) – Spot, strike, lower and upper barriers, maturity, rate and volatility.

  • U (ArrayLike) – Spot, strike, lower and upper barriers, maturity, rate and volatility.

  • T (ArrayLike) – Spot, strike, lower and upper barriers, maturity, rate and volatility.

  • r (ArrayLike) – Spot, strike, lower and upper barriers, maturity, rate and volatility.

  • sigma (ArrayLike) – Spot, strike, lower and upper barriers, maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

  • knock (str)

  • n_terms (int) – The series runs over \(n = -N, \dots, N\).

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Far-away barriers leave the vanilla option:

>>> from financialkit.options import black_scholes_price
>>> bool(abs(double_barrier_price(100.0, 100.0, 20.0, 500.0, 0.5, 0.05, 0.2) - black_scholes_price(100.0, 100.0, 0.5, 0.05, 0.2)) < 1e-8)
True
financialkit.exotic_options.fixed_lookback_price(S, K, T, r, sigma, q=0.0, option_type='call', S_extreme=None)[source]#

Conze-Viswanathan (1991) fixed-strike lookback: \((\max S - K)^+\) (call) or \((K - \min S)^+\) (put).

For the call with \(K \ge S_{\max}\),

\[c = S e^{(b-r)T} N(d_1) - K e^{-rT} N(d_2) + S e^{-rT} \frac{\sigma^2}{2b}\left[-\left(\frac{S}{K}\right)^{-2b/\sigma^2} N\!\left(d_1 - \frac{2b\sqrt T}{\sigma}\right) + e^{bT} N(d_1)\right];\]

for \(K < S_{\max}\) the call locks in \(e^{-rT}(S_{\max} - K)\) and adds the same expression with \(S_{\max}\) in place of \(K\). The put mirrors it in the minimum.

Parameters:
  • S (ArrayLike) – Spot, strike, remaining maturity, rate and volatility.

  • K (ArrayLike) – Spot, strike, remaining maturity, rate and volatility.

  • T (ArrayLike) – Spot, strike, remaining maturity, rate and volatility.

  • r (ArrayLike) – Spot, strike, remaining maturity, rate and volatility.

  • sigma (ArrayLike) – Spot, strike, remaining maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

  • S_extreme (ArrayLike | None) – Maximum (call) or minimum (put) observed so far; defaults to S.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

A lookback call is worth more than the vanilla call:

>>> from financialkit.options import black_scholes_price
>>> bool(fixed_lookback_price(100.0, 100.0, 1.0, 0.05, 0.2) > black_scholes_price(100.0, 100.0, 1.0, 0.05, 0.2))
True
financialkit.exotic_options.floating_lookback_price(S, T, r, sigma, q=0.0, option_type='call', S_extreme=None)[source]#

Goldman-Sosin-Gatto (1979) floating-strike lookback: \(S_T - \min S\) (call) or \(\max S - S_T\) (put).

With \(a_1 = [\ln(S/S_{\min}) + (b + \sigma^2/2)T]/(\sigma\sqrt T)\), \(a_2 = a_1 - \sigma\sqrt T\) and \(b = r - q\), the call is

\[c = S e^{(b-r)T} N(a_1) - S_{\min} e^{-rT} N(a_2) + S e^{-rT} \frac{\sigma^2}{2b}\left[\left(\frac{S}{S_{\min}}\right)^{-2b/\sigma^2} N\!\left(-a_1 + \frac{2b\sqrt T}{\sigma}\right) - e^{bT} N(-a_1)\right],\]

and the put is its mirror image in the maximum.

Parameters:
  • S (ArrayLike) – Spot, remaining maturity, rate and volatility.

  • T (ArrayLike) – Spot, remaining maturity, rate and volatility.

  • r (ArrayLike) – Spot, remaining maturity, rate and volatility.

  • sigma (ArrayLike) – Spot, remaining maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

  • S_extreme (ArrayLike | None) – Minimum (call) or maximum (put) observed so far; defaults to S for a newly written option.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Haug (2007), Section 4.15.1: S = 120, S_min = 100, T = 0.5, r = 10%, b = 4%, sigma = 30%:

>>> round(floating_lookback_price(120.0, 0.5, 0.10, 0.30, q=0.06, S_extreme=100.0), 3)
25.353
financialkit.exotic_options.forward_start_price(S, alpha, t_start, T, r, sigma, q=0.0, option_type='call')[source]#

Rubinstein (1990) forward-start option: struck at \(\alpha S_{t}\) at the future date \(t\).

At time \(t\) the option is worth \(S_t\) times a Black-Scholes-Merton option on a unit spot with strike \(\alpha\), and the risk-neutral value of \(S_t\) is \(S e^{-qt}\):

\[V = S e^{-q t}\, \mathrm{BS}(1, \alpha, T - t, r, q, \sigma).\]

The value does not depend on the path before \(t\), which is why employee stock options granted at the money in the future and the legs of a cliquet are priced this way.

Parameters:
  • S (ArrayLike) – Spot.

  • alpha (ArrayLike) – Strike as a fraction of the spot at the start date (1 for at the money).

  • t_start (ArrayLike) – Start date and expiry, t_start < T.

  • T (ArrayLike) – Start date and expiry, t_start < T.

  • r (ArrayLike) – Rate and volatility.

  • sigma (ArrayLike) – Rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Haug (2007), Section 4.3: S = 60, alpha = 1.1, t = 0.25, T = 1, r = 8%, b = 4%, sigma = 30%:

>>> round(forward_start_price(60.0, 1.1, 0.25, 1.0, 0.08, 0.30, q=0.04), 3)
4.406
financialkit.exotic_options.gap_option_price(S, K1, K2, T, r, sigma, q=0.0, option_type='call')[source]#

Gap option: pays \(S_T - K_2\) if \(S_T > K_1\) (call), \(K_2 - S_T\) if \(S_T < K_1\) (put).

It is an asset-or-nothing option minus \(K_2\) cash-or-nothing options, both triggered at \(K_1\):

\[c = S e^{-qT} N(d_1) - K_2 e^{-rT} N(d_2), \quad d_{1,2} \text{ at } K_1.\]
Parameters:
  • S (ArrayLike) – Spot.

  • K1 (ArrayLike) – Trigger and payoff strikes.

  • K2 (ArrayLike) – Trigger and payoff strikes.

  • T (ArrayLike) – Maturity, rate and volatility.

  • r (ArrayLike) – Maturity, rate and volatility.

  • sigma (ArrayLike) – Maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Haug (2007), Section 4.19.1: S = 50, K1 = 50, K2 = 57, T = 0.5, r = 9%, b = 9%, sigma = 20%; the value is negative:

>>> round(gap_option_price(50.0, 50.0, 57.0, 0.5, 0.09, 0.20), 4)
-0.0053
financialkit.exotic_options.geometric_asian_price(S, K, T, r, sigma, q=0.0, option_type='call', n_fixings=None)[source]#

Kemna-Vorst (1990) price of an option on the geometric average \(G\).

\(\ln G\) is normal with mean \(m\) and variance \(v\), so the option is Black’s formula on the forward \(e^{m + v/2}\) with volatility \(\sqrt{v/T}\). For continuous averaging \(m = \ln S + (b - \sigma^2/2) T/2\) and \(v = \sigma^2 T/3\); for \(n\) equally spaced fixings

\[m = \ln S + (b - \tfrac{\sigma^2}{2}) \frac{(n+1) T}{2n}, \qquad v = \sigma^2 T \frac{(n+1)(2n+1)}{6 n^2}.\]
Parameters:
  • S (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • K (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • T (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • r (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • sigma (ArrayLike) – Spot, strike, maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

  • n_fixings (int | None) – Number of fixings; None for continuous averaging.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

Haug (2007), Section 4.20.1: S = 80, K = 85, T = 0.25, r = 5%, b = 8%, sigma = 20%:

>>> round(geometric_asian_price(80.0, 85.0, 0.25, 0.05, 0.20, q=-0.03, option_type="put"), 4)
4.6922
financialkit.exotic_options.longstaff_schwartz(S, K, T, r, sigma, q=0.0, option_type='put', n_exercise=50, n_paths=50000, degree=3, basis='laguerre', antithetic=True, rng=None, confidence=0.95)[source]#

American (Bermudan) option value by least-squares Monte Carlo (Longstaff-Schwartz 2001).

Exercise is allowed on n_exercise equally spaced dates up to \(T\). At each date \(t_i\), working backward, the discounted future cash flow \(Y\) of each in-the-money path is regressed on regression_basis() of \(S_{t_i}/K\), and the path is exercised where the payoff exceeds the fitted \(E[Y \mid S_{t_i}]\). The paths come from financialkit.integrators.gbm_paths(), optionally with antithetic increments.

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

  • K (float) – Spot, strike, maturity, rate and volatility.

  • T (float) – Spot, strike, maturity, rate and volatility.

  • r (float) – Spot, strike, maturity, rate and volatility.

  • sigma (float) – Spot, strike, maturity, rate and volatility.

  • q (float) – Continuous dividend yield.

  • option_type (str)

  • n_exercise (int) – Number of exercise dates (time steps).

  • n_paths (int) – Number of paths (rounded up to even with antithetic).

  • degree (int) – Number of non-constant regressors.

  • basis (str)

  • antithetic (bool) – Simulate antithetic pairs; the standard error then uses pair means.

  • rng (Generator | int | None) – Generator or seed.

  • confidence (float) – Coverage of the confidence interval.

Return type:

LongstaffSchwartzResult

Returns:

LongstaffSchwartzResult

Examples

Longstaff and Schwartz (2001), Table 1, first row: S = 36, K = 40, r = 6%, sigma = 20%, T = 1, 50 exercise dates; the finite-difference value is 4.478:

>>> res = longstaff_schwartz(36.0, 40.0, 1.0, 0.06, 0.2, n_paths=20_000, rng=0)
>>> bool(abs(res.estimate - 4.478) < 4 * res.std_error + 0.02)
True
financialkit.exotic_options.lookback_option_mc(S, T, r, sigma, q=0.0, option_type='call', K=None, n_steps=50, n_paths=100000, continuous=True, rng=None, confidence=0.95)[source]#

Monte Carlo price of a newly written lookback, floating (K=None) or fixed strike.

Paths come from financialkit.integrators.gbm_paths(). With continuous=True the path extremum is sampled exactly from the Brownian bridge in each step (bridge_extremum()), so the estimator is unbiased for continuous monitoring at any step size; with continuous=False it is the extremum over the grid dates, which prices a discretely monitored lookback.

Parameters:
  • S (float) – Spot, maturity, rate and volatility.

  • T (float) – Spot, maturity, rate and volatility.

  • r (float) – Spot, maturity, rate and volatility.

  • sigma (float) – Spot, maturity, rate and volatility.

  • q (float) – Continuous dividend yield.

  • option_type (str)

  • K (float | None) – Fixed strike; None for a floating-strike lookback.

  • n_steps (int) – Time steps (monitoring dates when continuous=False).

  • n_paths (int) – Number of paths.

  • continuous (bool) – Continuous monitoring through the bridge, or discrete.

  • rng (Generator | int | None) – Generator or seed.

  • confidence (float) – Coverage of the confidence interval.

Return type:

MonteCarloResult

Returns:

MonteCarloResult

Examples

>>> res = lookback_option_mc(100.0, 1.0, 0.05, 0.2, n_steps=10, n_paths=40_000, rng=0)
>>> bool(abs(res.estimate - floating_lookback_price(100.0, 1.0, 0.05, 0.2)) < 4 * res.std_error)
True
financialkit.exotic_options.margrabe_price(S1, S2, T, sigma1, sigma2, rho, q1=0.0, q2=0.0)[source]#

Margrabe (1978) value of the right to receive asset 1 in exchange for asset 2 at \(T\): \((S_1 - S_2)^+\).

Measured in units of asset 2 the payoff is a call on \(S_1/S_2\) struck at 1, and the ratio has volatility \(\sigma = \sqrt{\sigma_1^2 + \sigma_2^2 - 2\rho\sigma_1\sigma_2}\):

\[V = S_1 e^{-q_1 T} N(d_1) - S_2 e^{-q_2 T} N(d_2), \qquad d_{1,2} = \frac{\ln(S_1 e^{-q_1 T} / S_2 e^{-q_2 T}) \pm \sigma^2 T/2}{\sigma\sqrt T}.\]

The interest rate drops out. This is financialkit.options.black_scholes_price() with spot \(S_1 e^{-q_1 T}\), strike \(S_2 e^{-q_2 T}\) and zero rate.

Parameters:
  • S1 (ArrayLike) – Prices of the asset received and the asset given up.

  • S2 (ArrayLike) – Prices of the asset received and the asset given up.

  • T (ArrayLike) – Maturity.

  • sigma1 (ArrayLike) – Volatilities.

  • sigma2 (ArrayLike) – Volatilities.

  • rho (ArrayLike) – Correlation of the two assets’ returns.

  • q1 (ArrayLike) – Continuous dividend yields.

  • q2 (ArrayLike) – Continuous dividend yields.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

When asset 2 is riskless cash growing at the rate \(r\) (zero volatility, “dividend yield” \(r\)) the exchange option is the Black-Scholes call:

>>> from financialkit.options import black_scholes_price
>>> m = margrabe_price(42.0, 40.0, 0.5, 0.2, 0.0, 0.0, q2=0.10)
>>> bool(abs(m - black_scholes_price(42.0, 40.0, 0.5, 0.10, 0.2)) < 1e-12)
True
financialkit.exotic_options.merton_down_and_out_call(S, K, H, T, r, sigma, q=0.0)[source]#

Merton’s (1973) down-and-out call with the barrier at or below the strike.

The barrier is an absorbing boundary for the log-price, and the method of images subtracts a call started from the reflected spot \(H^2 / S\):

\[C_{do} = c(S, K) - \left(\frac{H}{S}\right)^{2b/\sigma^2 - 1} c\!\left(\frac{H^2}{S}, K\right), \qquad b = r - q,\]

with \(c\) the Black-Scholes-Merton call. It is zero once \(S \le H\).

Parameters:
  • S (ArrayLike) – Spot, strike, barrier (H <= K), maturity, rate and volatility.

  • K (ArrayLike) – Spot, strike, barrier (H <= K), maturity, rate and volatility.

  • H (ArrayLike) – Spot, strike, barrier (H <= K), maturity, rate and volatility.

  • T (ArrayLike) – Spot, strike, barrier (H <= K), maturity, rate and volatility.

  • r (ArrayLike) – Spot, strike, barrier (H <= K), maturity, rate and volatility.

  • sigma (ArrayLike) – Spot, strike, barrier (H <= K), maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

A barrier far below the spot hardly matters:

>>> from financialkit.options import black_scholes_price
>>> bool(abs(merton_down_and_out_call(100.0, 100.0, 50.0, 1.0, 0.05, 0.2) - black_scholes_price(100.0, 100.0, 1.0, 0.05, 0.2)) < 1e-6)
True
financialkit.exotic_options.one_touch_price(S, H, T, r, sigma, q=0.0, cash=1.0)[source]#

One-touch: cash paid the first time the price touches \(H\) before \(T\).

The barrier is below the spot for \(H < S\) and above it otherwise. With \(\mu = (b - \sigma^2/2)/\sigma^2\), \(\lambda = \sqrt{\mu^2 + 2r/\sigma^2}\) and \(\eta = \pm 1\) for a down or up barrier,

\[V = X\left[\left(\tfrac{H}{S}\right)^{\mu + \lambda} N(\eta z) + \left(\tfrac{H}{S}\right)^{\mu - \lambda} N(\eta z - 2\eta\lambda\sigma\sqrt T)\right], \quad z = \frac{\ln(H/S)}{\sigma\sqrt T} + \lambda\sigma\sqrt T,\]

the rebate term of a knock-out barrier option (Reiner and Rubinstein 1991). As \(T \to \infty\) it tends to the perpetual value \(X (H/S)^{\mu + \eta\lambda}\).

Parameters:
  • S (ArrayLike) – Spot, barrier, maturity, rate and volatility.

  • H (ArrayLike) – Spot, barrier, maturity, rate and volatility.

  • T (ArrayLike) – Spot, barrier, maturity, rate and volatility.

  • r (ArrayLike) – Spot, barrier, maturity, rate and volatility.

  • sigma (ArrayLike) – Spot, barrier, maturity, rate and volatility.

  • q (ArrayLike) – Continuous dividend yield.

  • cash (ArrayLike) – Amount paid at the touch.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

A barrier at the spot is touched at once:

>>> one_touch_price(100.0, 100.0, 1.0, 0.05, 0.2)
1.0
financialkit.exotic_options.parse_barrier_type(barrier_type)[source]#

Split "down-and-out" and friends into (is_down, is_out).

Parameters:

barrier_type (str)

Return type:

tuple[bool, bool]

Returns:

is_down, is_out (bool)

Examples

>>> parse_barrier_type("up-and-in")
(False, False)
financialkit.exotic_options.plot_barrier_paths(times, paths, barriers, ax=None, max_paths=40)[source]#

Simulated price paths, red where they touch a barrier on the grid and blue where they survive.

Parameters:
  • times (ArrayLike)

  • paths (ArrayLike)

  • barriers (Sequence[float]) – Barrier levels; a path is knocked out if it reaches any of them from the starting side.

  • ax (matplotlib.axes.Axes, optional)

  • max_paths (int) – Number of paths drawn.

Returns:

matplotlib.axes.Axes

financialkit.exotic_options.plot_exercise_boundary(result, ax=None, label='Longstaff-Schwartz')[source]#

The early-exercise boundary of a LongstaffSchwartzResult.

Parameters:
Returns:

matplotlib.axes.Axes

financialkit.exotic_options.plot_price_profile(x, curves, ax=None, xlabel='spot S', ylabel='option value', markers=())[source]#

Several pricing curves over a common axis, with dashed vertical markers (strikes, barriers).

Parameters:
  • x (ArrayLike) – Common abscissa (usually the spot).

  • curves (Mapping[str, ArrayLike]) – Label and values of each curve.

  • ax (matplotlib.axes.Axes, optional) – Axes to draw on; a new figure is created if omitted.

  • xlabel (str) – Axis labels.

  • ylabel (str) – Axis labels.

  • markers (Sequence[float]) – Positions of dashed vertical lines.

Returns:

matplotlib.axes.Axes

financialkit.exotic_options.plot_variance_strip(result, ax=None)[source]#

Each option’s contribution \(w_i Q(K_i)\) to a variance swap’s fair strike, puts left of \(K_0\) and calls right.

Parameters:
Returns:

matplotlib.axes.Axes

financialkit.exotic_options.quanto_price(S, K, T, r_d, r_f, sigma_S, sigma_X, rho, q=0.0, fixed_rate=1.0, option_type='call')[source]#

Reiner (1992) quanto option: a foreign-currency payoff \((S_T - K)^+\) paid in domestic currency at a fixed rate \(\bar X\).

Under the domestic risk-neutral measure the foreign asset drifts at \(r_f - q - \rho\sigma_S\sigma_X\), where \(\rho\) is the correlation between the asset and the exchange rate (domestic units per foreign unit). The quanto is Black’s formula on the adjusted forward, discounted domestically:

\[V = \bar X e^{-r_d T}\left[\hat F N(d_1) - K N(d_2)\right], \qquad \hat F = S e^{(r_f - q - \rho\sigma_S\sigma_X)T}.\]
Parameters:
  • S (ArrayLike) – Foreign asset price and strike, in foreign currency.

  • K (ArrayLike) – Foreign asset price and strike, in foreign currency.

  • T (ArrayLike) – Maturity.

  • r_d (ArrayLike) – Domestic and foreign rates.

  • r_f (ArrayLike) – Domestic and foreign rates.

  • sigma_S (ArrayLike) – Volatilities of the asset and of the exchange rate.

  • sigma_X (ArrayLike) – Volatilities of the asset and of the exchange rate.

  • rho (ArrayLike) – Correlation between the asset and the exchange rate.

  • q (ArrayLike) – Continuous dividend yield of the asset.

  • fixed_rate (ArrayLike) – Fixed conversion rate \(\bar X\), domestic per foreign.

  • option_type (str)

Return type:

ndarray | float

Returns:

float or ndarray

Examples

With no correlation the quanto is the foreign option at the fixed rate, re-discounted at the domestic rate:

>>> import numpy as np
>>> from financialkit.options import black_scholes_price
>>> v = quanto_price(100.0, 105.0, 0.5, 0.08, 0.05, 0.2, 0.1, 0.0, q=0.04, fixed_rate=1.5)
>>> bool(abs(v - 1.5 * np.exp(-0.03 * 0.5) * black_scholes_price(100.0, 105.0, 0.5, 0.05, 0.2, q=0.04)) < 1e-12)
True
financialkit.exotic_options.realized_variance(paths, T)[source]#

Annualized realized variance \(\frac{1}{T}\sum_i \ln^2(S_{t_{i+1}}/S_{t_i})\) of each path.

Parameters:
  • paths (ArrayLike) – Prices on the observation dates.

  • T (float) – Length of the observation period in years.

Return type:

ndarray

Returns:

ndarray, shape (n_paths,) – A 0-d array for a single path.

Examples

>>> import numpy as np
>>> round(float(realized_variance(100.0 * np.exp([0.0, 0.1, 0.0]), 0.5)), 10)
0.04
financialkit.exotic_options.regression_basis(x, degree=3, basis='laguerre')[source]#

Regressors for the continuation value: a constant and degree basis functions of x.

Parameters:
  • x (ndarray) – Moneyness \(S/K\) of the in-the-money paths.

  • degree (int) – Number of non-constant basis functions.

  • basis (str) – Longstaff and Schwartz’s weighted Laguerre polynomials \(e^{-x/2} L_k(x)\), \(k = 0, \dots, \text{degree} - 1\), or the powers \(x, \dots, x^{\text{degree}}\).

Return type:

ndarray

Returns:

ndarray, shape (n, degree + 1)

Examples

>>> regression_basis([0.0, 1.0], degree=2).round(4).tolist()
[[1.0, 1.0, 1.0], [1.0, 0.6065, 0.0]]
financialkit.exotic_options.stulz_price(S1, S2, K, T, r, sigma1, sigma2, rho, q1=0.0, q2=0.0, option_type='call', kind='max')[source]#

Stulz (1982) option on the maximum or minimum of two assets: \((\max(S_1, S_2) - K)^+\) and its relatives.

With \(\sigma\) the ratio volatility, \(\rho_1 = (\sigma_1 - \rho\sigma_2)/\sigma\), \(\rho_2 = (\sigma_2 - \rho\sigma_1)/\sigma\), \(d = [\ln(S_1/S_2) + (b_1 - b_2 + \sigma^2/2)T]/(\sigma\sqrt T)\) and \(y_i = [\ln(S_i/K) + (b_i + \sigma_i^2/2)T]/(\sigma_i\sqrt T)\), the call on the minimum is

\[S_1 e^{-q_1 T} M(y_1, -d; -\rho_1) + S_2 e^{-q_2 T} M(y_2, d - \sigma\sqrt T; -\rho_2) - K e^{-rT} M(y_1 - \sigma_1\sqrt T, y_2 - \sigma_2\sqrt T; \rho),\]

the call on the maximum is its counterpart (Haug 2007, Sec. 4.8.4), and the puts follow from parity with the zero-strike call, which is a Margrabe exchange option. Since \(\max + \min = S_1 + S_2\), the calls on the maximum and the minimum add up to the two vanilla calls.

Parameters:
  • S1 (ArrayLike) – Spots, strike, maturity and rate.

  • S2 (ArrayLike) – Spots, strike, maturity and rate.

  • K (ArrayLike) – Spots, strike, maturity and rate.

  • T (ArrayLike) – Spots, strike, maturity and rate.

  • r (ArrayLike) – Spots, strike, maturity and rate.

  • sigma1 (ArrayLike) – Volatilities.

  • sigma2 (ArrayLike) – Volatilities.

  • rho (ArrayLike) – Correlation, \(|\rho| < 1\).

  • q1 (ArrayLike) – Continuous dividend yields.

  • q2 (ArrayLike) – Continuous dividend yields.

  • option_type (str)

  • kind (str)

Return type:

ndarray | float

Returns:

float or ndarray

Examples

>>> from financialkit.options import black_scholes_price
>>> args = (100.0, 105.0, 98.0, 0.5, 0.05, 0.11, 0.16, 0.63)
>>> total = stulz_price(*args, kind="max") + stulz_price(*args, kind="min")
>>> vanilla = black_scholes_price(100.0, 98.0, 0.5, 0.05, 0.11) + black_scholes_price(105.0, 98.0, 0.5, 0.05, 0.16)
>>> bool(abs(total - vanilla) < 1e-10)
True
financialkit.exotic_options.turnbull_wakeman_price(S, K, T, r, sigma, q=0.0, option_type='call', n_fixings=None)[source]#

Turnbull-Wakeman (1991) moment-matching approximation of an arithmetic-average option.

The arithmetic average \(A\) is replaced by a lognormal with the same first two moments \(M_1 = E[A]\), \(M_2 = E[A^2]\), and the option is Black’s formula on the forward \(M_1\) with volatility

\[\sigma_A^2 = \frac{1}{T} \ln \frac{M_2}{M_1^2}.\]

For continuous averaging (Turnbull and Wakeman) \(M_1 = S (e^{bT} - 1)/(bT)\) and \(M_2\) is the double integral of \(E[S_t S_u]\); for discrete fixings the moments are Levy’s (1992) exact sums \(M_1 = \tfrac{S}{n}\sum_i e^{b t_i}\), \(M_2 = \tfrac{S^2}{n^2}\sum_{i,j} e^{b(t_i + t_j) + \sigma^2 \min(t_i, t_j)}\). The approximation is excellent at moderate volatility and drifts as \(\sigma^2 T\) grows.

Parameters:
  • S (ArrayLike) – Spot, strike, maturity, rate and volatility; with discrete fixings T, r, sigma and q must be scalars.

  • K (ArrayLike) – Spot, strike, maturity, rate and volatility; with discrete fixings T, r, sigma and q must be scalars.

  • T (ArrayLike) – Spot, strike, maturity, rate and volatility; with discrete fixings T, r, sigma and q must be scalars.

  • r (ArrayLike) – Spot, strike, maturity, rate and volatility; with discrete fixings T, r, sigma and q must be scalars.

  • sigma (ArrayLike) – Spot, strike, maturity, rate and volatility; with discrete fixings T, r, sigma and q must be scalars.

  • q (ArrayLike) – Continuous dividend yield.

  • option_type (str)

  • n_fixings (int | None) – Number of equally spaced fixings; None for continuous averaging.

Return type:

ndarray | float

Returns:

float or ndarray

Examples

A call struck at zero is worth the discounted mean of the average, which the approximation matches exactly:

>>> import numpy as np
>>> c = turnbull_wakeman_price(100.0, 1e-8, 1.0, 0.05, 0.3)
>>> bool(abs(c - 100.0 * np.exp(-0.05) * np.expm1(0.05) / 0.05) < 1e-6)
True
financialkit.exotic_options.variance_swap_strike(strikes, call_prices, put_prices, S, T, r, q=0.0)[source]#

Fair variance strike from out-of-the-money option prices (Demeterfi, Derman, Kamal and Zou 1999).

With \(K_0\) the largest strike at or below the forward \(F = S e^{(r-q)T}\), puts are used below \(K_0\), calls above and their average at \(K_0\), and

\[K_{\mathrm{var}} = \frac{2 e^{rT}}{T} \sum_i \frac{\Delta K_i}{K_i^2} Q(K_i) - \frac{1}{T}\left(\frac{F}{K_0} - 1\right)^2,\]

with \(\Delta K_i\) half the distance between the neighbouring strikes. The last term is the second-order expansion of the forward adjustment above. The strike range truncates the integrals, so the strip should reach well into both wings.

Parameters:
  • strikes (ArrayLike) – Increasing strikes.

  • call_prices (ArrayLike) – Call and put prices at those strikes.

  • put_prices (ArrayLike) – Call and put prices at those strikes.

  • S (float) – Spot, maturity and rate.

  • T (float) – Spot, maturity and rate.

  • r (float) – Spot, maturity and rate.

  • q (float) – Continuous dividend yield.

Return type:

VarianceSwapResult

Returns:

VarianceSwapResult

Examples

Under Black-Scholes the fair variance is \(\sigma^2\):

>>> import numpy as np
>>> from financialkit.options import black_scholes_price
>>> K = np.arange(20.0, 400.0, 1.0)
>>> c, p = (black_scholes_price(100.0, K, 1.0, 0.05, 0.2, option_type=t) for t in ("call", "put"))
>>> round(variance_swap_strike(K, c, p, 100.0, 1.0, 0.05).vol_strike, 4)
0.2