financialkit.stochastic_volatility#
Local volatility: Cox’s constant elasticity of variance with Schroder’s
noncentral chi-square prices, Dupire’s local volatility from a price
surface, and its simulation through
financialkit.integrators.euler_maruyama(). Stochastic volatility:
the Hull-White series and mixing Monte Carlo, and the Heston model with
its two-probability formula. Jumps: Merton’s jump diffusion (series
formula), variance gamma (Madan-Seneta, Madan-Carr-Chang), Kou’s
double-exponential jumps and Bates. Fourier pricing for every
characteristic-function model: Carr-Madan’s FFT, Lewis’s single
integral and the COS method. Smiles: Hagan’s SABR formula and
calibration, Gatheral’s SVI with Durrleman’s condition, and Lee’s moment
formula. Simulation: full-truncation, partial-truncation and Higham-Mao
Euler schemes and Andersen’s quadratic-exponential scheme for Heston.
financialkit.stochastic_volatility: beyond constant volatility.
Local volatility: Cox’s constant elasticity of variance with Schroder’s
noncentral chi-square prices, and Dupire’s local volatility recovered
from a price surface and simulated through
financialkit.integrators.euler_maruyama(). Stochastic volatility:
Hull and White’s mixing formula, and the Heston model with its
two-probability formula. Jumps: Merton’s jump diffusion (series formula),
the Madan-Seneta and Madan-Carr-Chang variance gamma process, Kou’s
double-exponential jumps and Bates’s Heston-with-jumps. Fourier
pricing for every characteristic-function model: Carr-Madan’s FFT,
Lewis’s single integral and Fang-Oosterlee’s COS method. Smiles: Hagan’s
SABR formula and calibration, Gatheral’s SVI with Durrleman’s arbitrage
check, and Lee’s moment formula for the wings. Simulation: truncated
Euler schemes (Lord-Koekkoek-van Dijk) and Andersen’s
quadratic-exponential scheme for Heston.
- class financialkit.stochastic_volatility.BatesModel(v0, kappa, theta, xi, rho, lam, mu_j, delta)[source]#
Bases:
CharacteristicFunctionModelBates’s (1996) model: Heston’s stochastic variance plus Merton’s lognormal jumps.
- Parameters:
v0 (
float) – Heston parameters, seeHestonModel.kappa (
float) – Heston parameters, seeHestonModel.theta (
float) – Heston parameters, seeHestonModel.xi (
float) – Heston parameters, seeHestonModel.rho (
float) – Heston parameters, seeHestonModel.lam (
float) – Merton jump parameters, seeMertonJumpModel.mu_j (
float) – Merton jump parameters, seeMertonJumpModel.delta (
float) – Merton jump parameters, seeMertonJumpModel.
Examples
With no jumps it is Heston:
>>> b = BatesModel(0.04, 2.0, 0.04, 0.3, -0.7, lam=0.0, mu_j=-0.1, delta=0.1) >>> h = HestonModel(0.04, 2.0, 0.04, 0.3, -0.7) >>> bool(abs(b.price(100.0, 95.0, 1.0, 0.03) - h.price(100.0, 95.0, 1.0, 0.03)) < 1e-12) True
- class financialkit.stochastic_volatility.BlackScholesModel(sigma)[source]#
Bases:
CharacteristicFunctionModelBlack-Scholes as a characteristic-function model, the reference for the Fourier pricers.
\(X_T = -\tfrac12\sigma^2 T + \sigma W_T\), so \(\phi(u) = \exp(-\tfrac12\sigma^2 T (i u + u^2))\).
- Parameters:
sigma (
float) – Volatility.
Examples
>>> from financialkit.options import black_scholes_price >>> m = BlackScholesModel(0.2) >>> round(m.price(100.0, 100.0, 1.0, 0.05), 6) == round(black_scholes_price(100.0, 100.0, 1.0, 0.05, 0.2), 6) True
- class financialkit.stochastic_volatility.CharacteristicFunctionModel[source]#
Bases:
ABCA model for \(X_T = \ln(S_T / F_T)\) known through its characteristic function.
Subclasses implement
char_func(); pricing, implied volatilities and cumulants follow from it.- cumulants(T, h=0.02)[source]#
First, second and fourth cumulants of \(X_T\).
By central differences of the cumulant generating function \(\kappa(s) = \ln \phi(-i s)\) at \(s = 0\). The COS method uses them to size its truncation interval.
- implied_vol(S, K, T, r, q=0.0, method='cos')[source]#
Black-Scholes implied volatilities of the model’s prices.
Out-of-the-money options are inverted (puts below the forward, calls above), where the time value is the whole price.
- price(S, K, T, r, q=0.0, option_type='call', method='cos')[source]#
European option prices under the model.
- Parameters:
S (
float) – Spot.K (
ArrayLike) – Strikes.T (
float) – Maturity, risk-free rate and dividend yield.r (
float) – Maturity, risk-free rate and dividend yield.q (
float) – Maturity, risk-free rate and dividend yield.option_type (
str)method (
str) –cos_price(),lewis_price(),heston_price()orcarr_madan_price().
- Return type:
- Returns:
float or ndarray
- class financialkit.stochastic_volatility.FFTResult(strikes, calls, alpha, eta)[source]#
Bases:
objectCall prices on the Carr-Madan log-strike grid.
- class financialkit.stochastic_volatility.HestonModel(v0, kappa, theta, xi, rho)[source]#
Bases:
CharacteristicFunctionModelHeston’s (1993) square-root stochastic volatility model.
- Parameters:
Examples
>>> m = HestonModel(v0=0.04, kappa=2.0, theta=0.04, xi=0.3, rho=-0.7) >>> m.feller_satisfied True >>> complex(m.char_func(-1j, 1.0)).real # E[S_T / F_T] = 1 1.0
- char_func(u, T)[source]#
Characteristic function of \(\ln(S_T/F_T)\), in the “little trap” form of Albrecher et al. (2007).
That form keeps the complex logarithm on its principal branch for all maturities, unlike Heston’s original expression.
- class financialkit.stochastic_volatility.HestonPaths(times, spot, variance, scheme)[source]#
Bases:
objectSimulated spot and variance paths under the Heston model.
- class financialkit.stochastic_volatility.KouModel(sigma, lam, p, eta1, eta2)[source]#
Bases:
CharacteristicFunctionModelKou’s (2002) double-exponential jump diffusion.
Log jump sizes are \(\mathrm{Exp}(\eta_1)\) upward with probability \(p\) and \(-\mathrm{Exp}(\eta_2)\) downward otherwise.
- Parameters:
Examples
>>> m = KouModel(0.16, 1.0, 0.4, 10.0, 5.0) >>> m.critical_moments() (9.0, 5.0)
- class financialkit.stochastic_volatility.LocalVolSurface(strikes, maturities, vol)[source]#
Bases:
objectA local volatility \(\sigma(K, T)\) on a strike-maturity grid.
- class financialkit.stochastic_volatility.MertonJumpModel(sigma, lam, mu_j, delta)[source]#
Bases:
CharacteristicFunctionModelMerton’s (1976) jump diffusion: GBM plus compound Poisson lognormal jumps.
\(dS/S_- = (r - q - \lambda\bar k)\,dt + \sigma\,dW + (J - 1)\,dN\), with \(\ln J \sim N(\mu_J, \delta^2)\) and \(\bar k = E[J] - 1 = e^{\mu_J + \delta^2/2} - 1\).
- Parameters:
Examples
>>> m = MertonJumpModel(0.2, 1.0, -0.1, 0.15) >>> round(m.mean_jump, 6) -0.084926
- class financialkit.stochastic_volatility.SABRFit(alpha, beta, rho, nu, F, T, rmse)[source]#
Bases:
objectSABR parameters fitted to a smile at one expiry.
- class financialkit.stochastic_volatility.SVIFit(a, b, rho, m, sigma, T, rmse)[source]#
Bases:
objectRaw SVI parameters fitted to total implied variance at one expiry.
- class financialkit.stochastic_volatility.VarianceGammaModel(sigma, nu, theta=0.0)[source]#
Bases:
CharacteristicFunctionModelVariance gamma: Brownian motion with drift run on a gamma clock (Madan-Seneta 1990, Madan-Carr-Chang 1998).
\(X_T = \omega T + \theta G_T + \sigma W_{G_T}\), with \(G_T \sim \Gamma(T/\nu, \nu)\) and \(\omega = \ln(1 - \theta\nu - \tfrac12\sigma^2\nu)/\nu\) so that \(E[e^{X_T}] = 1\).
theta = 0is the symmetric Madan-Seneta process;theta < 0skews it to the left.- Parameters:
Examples
>>> m = VarianceGammaModel(sigma=0.12, nu=0.2, theta=-0.14) >>> round(m.price(100.0, 90.0, 1.0, 0.1), 6) # Fang-Oosterlee (2008), Table 7 19.099355
- critical_moments()[source]#
Lee’s critical moments: the largest \(p\) with \(E[S_T^{1+p}] < \infty\) and \(q\) with \(E[S_T^{-q}] < \infty\).
The moment generating function \(E[e^{sX}]\) is finite exactly while \(1 - \theta\nu s - \tfrac12\sigma^2\nu s^2 > 0\), for every maturity.
- pdf(x, T)[source]#
Closed-form density of \(X_T\) (Madan, Carr and Chang 1998, eq. 23).
\[f(z) = \frac{2 e^{\theta z/\sigma^2}}{\nu^{T/\nu}\sqrt{2\pi}\,\sigma\,\Gamma(T/\nu)} \left(\frac{z^2}{2\sigma^2/\nu + \theta^2}\right)^{\frac{T}{2\nu} - \frac14} K_{\frac{T}{\nu} - \frac12}\!\left(\frac{\sqrt{z^2 (2\sigma^2/\nu + \theta^2)}}{\sigma^2}\right),\]with \(z = x - \omega T\) and \(K\) the modified Bessel function of the second kind.
- financialkit.stochastic_volatility.carr_madan_fft(model, S, T, r, q=0.0, alpha=1.5, N=4096, eta=0.25)[source]#
Call prices on a grid of log strikes by the Carr-Madan (1999) FFT.
The damped call \(c(k) = e^{\alpha k} C(k)\) has Fourier transform
\[\psi(v) = \frac{e^{-rT} \phi_{\ln S_T}(v - (\alpha + 1) i)}{\alpha^2 + \alpha - v^2 + i (2\alpha + 1) v},\]and the inverse transform at \(N\) log strikes spaced \(\lambda = 2\pi/(N\eta)\) around \(\ln F\) is one FFT of \(\psi\) sampled at \(v_j = j\eta\) with Simpson weights.
- Parameters:
- Return type:
- Returns:
FFTResult
Examples
>>> from financialkit.options import black_scholes_price >>> res = carr_madan_fft(BlackScholesModel(0.2), 100.0, 1.0, 0.05) >>> i = abs(res.strikes - 100.0).argmin() >>> bool(abs(res.calls[i] - black_scholes_price(100.0, res.strikes[i], 1.0, 0.05, 0.2)) < 1e-6) True
- financialkit.stochastic_volatility.carr_madan_price(model, S, K, T, r, q=0.0, option_type='call', alpha=1.5, N=4096, eta=0.25)[source]#
European option prices at arbitrary strikes, by cubic interpolation of
carr_madan_fft().- Parameters:
model (
CharacteristicFunctionModel)S (
float)K (
ArrayLike)T (
float)r (
float)q (
float)option_type (
str)alpha (
float) – As incarr_madan_fft().N (
int) – As incarr_madan_fft().eta (
float) – As incarr_madan_fft().
- Return type:
- Returns:
float or ndarray
- financialkit.stochastic_volatility.cev_local_vol(S, sigma, beta)[source]#
CEV local volatility \(\sigma S^{\beta - 1}\) (proportional, as in Black-Scholes).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> cev_local_vol(100.0, 2.0, 0.5) 0.2
- financialkit.stochastic_volatility.cev_price(S, K, T, r, sigma, beta, q=0.0, option_type='call')[source]#
European option prices under Cox’s CEV model (Schroder 1989).
For \(dS = (r - q) S\,dt + \sigma S^{\beta}\,dW\) with \(0 < \beta < 1\), writing \(Q(z; k, \lambda)\) for the noncentral chi-square CDF,
\[C = S e^{-qT}\left[1 - Q(a; b + 2, c)\right] - K e^{-rT} Q(c; b, a),\]with \(a = (K e^{-(r-q)T})^{2(1-\beta)} / ((1-\beta)^2 v)\), \(b = 1/(1 - \beta)\), \(c = S^{2(1-\beta)} / ((1-\beta)^2 v)\) and \(v = \sigma^2 [e^{2(r-q)(\beta-1)T} - 1] / (2(r-q)(\beta-1))\) (\(\sigma^2 T\) when \(r = q\)). For \(\beta > 1\) the roles swap: \(C = S e^{-qT}[1 - Q(c; -b, a)] - K e^{-rT} Q(a; 2 - b, c)\). Puts follow by parity.
- Parameters:
S (
ArrayLike) – Spot, strike, maturity and risk-free rate.K (
ArrayLike) – Spot, strike, maturity and risk-free rate.T (
ArrayLike) – Spot, strike, maturity and risk-free rate.r (
ArrayLike) – Spot, strike, maturity and risk-free rate.sigma (
float) – Volatility scale; the local volatility is \(\sigma S^{\beta - 1}\).beta (
float) – Elasticity exponent, positive and not 1 (that is Black-Scholes).q (
ArrayLike) – Dividend yield.option_type (
str)
- Return type:
- Returns:
float or ndarray
Examples
Hull, 10th ed., Section 27.1, uses the same formula; with \(\beta = 0.5\) and \(\sigma = 2\) the at-the-money local volatility is 20%, and the price is close to Black-Scholes at 20%:
>>> round(cev_price(100.0, 100.0, 1.0, 0.05, 2.0, 0.5), 4) 10.4539
- financialkit.stochastic_volatility.cos_density(model, x, T, N=None, L=10.0)[source]#
Density of \(X_T = \ln(S_T / F_T)\) by the cosine expansion.
\[f(x) \approx \frac{2}{b - a} \sum_{k=0}^{N-1}{}' \operatorname{Re}\!\left[\phi(u_k) e^{-i u_k a}\right] \cos(u_k (x - a))\]- Parameters:
model (
CharacteristicFunctionModel)x (
ArrayLike) – Points inside the truncation interval (the density is 0 outside).T (
float)N (
int|None) – Number of cosine terms; by defaultcos_terms()of the interval.L (
float)
- Return type:
- Returns:
ndarray
Examples
>>> from scipy.stats import norm >>> f = cos_density(BlackScholesModel(0.2), [0.0], 1.0) >>> bool(abs(f[0] - norm.pdf(0.0, -0.02, 0.2)) < 1e-10) True
- financialkit.stochastic_volatility.cos_price(model, S, K, T, r, q=0.0, option_type='call', N=None, L=10.0)[source]#
European option prices by the Fang-Oosterlee (2008) COS method.
With \(y = \ln(S_T/K)\) truncated to \([a_y, b_y]\) and \(u_k = k\pi/(b_y - a_y)\),
\[V = e^{-rT} \sum_{k=0}^{N-1}{}' \operatorname{Re}\!\left[\phi(u_k) e^{-i u_k a}\right] V_k,\]where \(a = a_y - \ln(F/K)\) is the interval’s left end for \(X_T\), the prime halves the first term, and \(V_k\) are the cosine coefficients of the payoff, in closed form. Puts are priced by the series (their payoff is bounded) and calls by parity, as Fang and Oosterlee recommend.
- Parameters:
model (
CharacteristicFunctionModel)S (
float) – Spot.K (
ArrayLike) – Strikes.T (
float) – Maturity, risk-free rate and dividend yield.r (
float) – Maturity, risk-free rate and dividend yield.q (
float) – Maturity, risk-free rate and dividend yield.option_type (
str)N (
int|None) – Number of cosine terms; by defaultcos_terms()of the interval.L (
float) – Truncation width, seetruncation_range().
- Return type:
- Returns:
float or ndarray
Examples
Fang and Oosterlee (2008), Section 5.2: Heston with S = K = 100, T = 1, r = 0 has the reference call price 5.785155450.
>>> from financialkit.stochastic_volatility import HestonModel >>> m = HestonModel(v0=0.0175, kappa=1.5768, theta=0.0398, xi=0.5751, rho=-0.5711) >>> round(cos_price(m, 100.0, 100.0, 1.0, 0.0), 6) 5.785155
- financialkit.stochastic_volatility.cos_terms(a, b, per_unit=40.0, minimum=256)[source]#
Default number of COS terms: a power of two of at least
per_unitper unit of interval width.A fixed count is not enough: the fourth cumulant of a long-dated Heston return with a large volatility of variance widens the truncation interval tenfold, and the series needs proportionally more terms to resolve the density on it.
- Parameters:
- Return type:
- Returns:
int
Examples
>>> cos_terms(-3.0, 3.0), cos_terms(-37.0, 37.0) (256, 4096)
- financialkit.stochastic_volatility.dupire_local_vol(strikes, maturities, call_prices, r, q=0.0)[source]#
Dupire’s (1994) local volatility from a grid of call prices.
\[\sigma^2(K, T) = \frac{\partial_T C + (r - q) K\,\partial_K C + q C}{\tfrac12 K^2\,\partial_{KK} C}\]Derivatives are second-order finite differences (
numpy.gradient(), non-uniform grids allowed). Nodes where the numerator or the butterfly \(\partial_{KK}C\) is not positive (an arbitrage, or numerical noise in the wings) getnan.- Parameters:
- Return type:
- Returns:
LocalVolSurface
Examples
Black-Scholes prices give back the flat volatility:
>>> import numpy as np >>> from financialkit.options import black_scholes_price >>> K, T = np.linspace(60, 140, 81), np.linspace(0.5, 1.5, 21) >>> C = black_scholes_price(100.0, K[None, :], T[:, None], 0.03, 0.25) >>> lv = dupire_local_vol(K, T, C, 0.03) >>> bool(np.nanmax(np.abs(lv.vol[5:-5, 20:-20] - 0.25)) < 2e-3) True
- financialkit.stochastic_volatility.durrleman_g(k, a, b, rho, m, sigma)[source]#
Durrleman’s condition for SVI: the implied density is \(g(k)\,n(d_2)/\sqrt{w}\).
\[g(k) = \left(1 - \frac{k w'}{2 w}\right)^2 - \frac{w'^2}{4}\left(\frac1w + \frac14\right) + \frac{w''}{2},\]so a slice is free of butterfly arbitrage exactly when \(g \ge 0\) (Gatheral and Jacquier 2014).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> bool(durrleman_g(0.0, 0.04, 0.1, -0.3, 0.0, 0.2) > 0) True
- financialkit.stochastic_volatility.heston_mc_price(model, S, K, T, r, q=0.0, option_type='call', n_steps=100, n_paths=10000, scheme='qe', rng=None)[source]#
European option price under Heston by Monte Carlo on
heston_paths().The standard error measures only the statistical error; the discretisation bias of the scheme comes on top of it.
- Parameters:
- Return type:
- Returns:
MonteCarloResult
- financialkit.stochastic_volatility.heston_paths(model, S, T, r, q=0.0, n_steps=100, n_paths=10000, scheme='qe', rng=None, psi_c=1.5)[source]#
Simulate Heston spot and variance paths.
- Parameters:
model (
HestonModel)S (
float) – Spot, horizon, risk-free rate and dividend yield.T (
float) – Spot, horizon, risk-free rate and dividend yield.r (
float) – Spot, horizon, risk-free rate and dividend yield.q (
float) – Spot, horizon, risk-free rate and dividend yield.n_steps (
int)n_paths (
int)scheme (
str) – Andersen’s QE scheme, or an Euler scheme on \((\ln S, v)\) with \(v\) in the coefficients replaced by \(v^+\) in both drift and diffusion (full truncation, Lord et al. 2010), in the diffusion only (partial truncation, Deelstra and Delbaen 1998), or by \(|v|\) in the diffusion (Higham and Mao 2005).psi_c (
float) – QE switching threshold between the quadratic and exponential branches.
- Return type:
- Returns:
HestonPaths
Examples
>>> m = HestonModel(v0=0.04, kappa=1.5, theta=0.04, xi=0.5, rho=-0.7) >>> res = heston_paths(m, 100.0, 1.0, 0.03, n_steps=20, n_paths=20_000, rng=0) >>> bool(abs(res.spot[:, -1].mean() / (100 * np.exp(0.03)) - 1) < 5e-3) True
- financialkit.stochastic_volatility.heston_price(model, S, K, T, r, q=0.0, option_type='call')[source]#
European prices by Heston’s (1993) two-probability formula.
\[C = S e^{-qT} P_1 - K e^{-rT} P_2, \qquad P_j = \frac12 + \frac1\pi \int_0^\infty \operatorname{Re}\!\left[\frac{e^{i u \ln(F/K)} f_j(u)}{i u}\right] du,\]with \(f_2 = \phi\) (the exercise probability under the risk-neutral measure) and \(f_1(u) = \phi(u - i)\) (under the stock measure). It works for any characteristic-function model, not only Heston’s.
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
Andersen (2008), Case I (Feller condition badly violated): the at-the-money call over ten years is worth 13.0847.
>>> m = HestonModel(v0=0.04, kappa=0.5, theta=0.04, xi=1.0, rho=-0.9) >>> round(heston_price(m, 100.0, 100.0, 10.0, 0.0), 4) 13.0847
- financialkit.stochastic_volatility.hull_white_mixing_mc(S, K, T, r, sigma0, xi, q=0.0, option_type='call', n_steps=100, n_paths=20000, rng=None)[source]#
Hull-White price by mixing: average Black-Scholes prices over simulated average variances.
Variance paths \(dV = \xi V\,dW\) come from
financialkit.integrators.gbm_paths(); each path’s time-average variance (trapezoidal rule) sets a Black-Scholes volatility, and the price is the mean of those Black-Scholes prices. This conditional Monte Carlo has far less variance than simulating the stock.- Parameters:
S (
float) – As inhull_white_price().K (
float) – As inhull_white_price().T (
float) – As inhull_white_price().r (
float) – As inhull_white_price().sigma0 (
float) – As inhull_white_price().xi (
float) – As inhull_white_price().q (
float) – As inhull_white_price().option_type (
str) – As inhull_white_price().n_steps (
int) – Time steps and variance paths.n_paths (
int) – Time steps and variance paths.
- Return type:
- Returns:
MonteCarloResult
Examples
>>> res = hull_white_mixing_mc(100.0, 100.0, 1.0, 0.0, 0.2, 0.3, n_paths=4000, rng=0) >>> bool(abs(res.estimate - hull_white_price(100.0, 100.0, 1.0, 0.0, 0.2, 0.3)) < 4 * res.std_error) True
- financialkit.stochastic_volatility.hull_white_price(S, K, T, r, sigma0, xi, q=0.0, option_type='call')[source]#
Hull-White (1987) series for an option under uncorrelated lognormal variance.
With \(dV = \xi V\,dW\) independent of the stock and \(\bar V = \frac1T\int_0^T V_t\,dt\), the price is \(E[C_{BS}(\bar V)]\); expanding about \(V_0 = \sigma_0^2\),
\[f \approx C_{BS}(V_0) + \tfrac12 C''(V_0)\,\operatorname{Var}\bar V + \tfrac16 C'''(V_0)\,E[(\bar V - V_0)^3],\]with the derivatives in the variance in closed form. The truncated series is accurate while \(\xi^2 T\) is small (below about 0.1) and drifts away from the exact mixing price beyond that; compare
hull_white_mixing_mc().- Parameters:
S (
ArrayLike) – Spot, strike, maturity and risk-free rate.K (
ArrayLike) – Spot, strike, maturity and risk-free rate.T (
ArrayLike) – Spot, strike, maturity and risk-free rate.r (
ArrayLike) – Spot, strike, maturity and risk-free rate.sigma0 (
float) – Initial volatility, \(\sqrt{V_0}\).xi (
float) – Volatility of the variance.q (
ArrayLike) – Dividend yield.option_type (
str)
- Return type:
- Returns:
float or ndarray
Examples
Without volatility of variance it is Black-Scholes:
>>> hull_white_price(100.0, 100.0, 1.0, 0.05, 0.2, 0.0) == black_scholes_price(100.0, 100.0, 1.0, 0.05, 0.2) True
- financialkit.stochastic_volatility.lee_wing_slope(p)[source]#
Lee’s (2004) moment formula: the limiting wing slope of \(w(k)/|k|\).
\[\beta = 2 - 4\left(\sqrt{p^2 + p} - p\right) \in [0, 2],\]with \(p\) the critical moment: \(\tilde p\) for the right wing (\(E[S_T^{1+p}] < \infty\)), \(\tilde q\) for the left (\(E[S_T^{-q}] < \infty\)). Infinite moments of all orders give a slope of 0; no moments beyond the first give the maximum, 2.
- Parameters:
p (
ArrayLike) – Critical moment, non-negative (infallowed).- Return type:
- Returns:
float or ndarray
Examples
>>> lee_wing_slope([0.0, np.inf]) array([2., 0.])
- financialkit.stochastic_volatility.lewis_price(model, S, K, T, r, q=0.0, option_type='call')[source]#
European option prices by Lewis’s (2001) single-integral formula.
\[C = S e^{-qT} - \frac{\sqrt{F K}\, e^{-rT}}{\pi} \int_0^\infty \operatorname{Re}\!\left[e^{i u \ln(F/K)} \phi\!\left(u - \tfrac{i}{2}\right)\right] \frac{du}{u^2 + \tfrac14}\]The integrand decays like \(u^{-2}\) even when \(\phi\) does not decay, so the formula is robust for pure-jump models. Puts by parity.
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> from financialkit.options import black_scholes_price >>> m = BlackScholesModel(0.25) >>> abs(lewis_price(m, 100.0, 110.0, 0.5, 0.03) - black_scholes_price(100.0, 110.0, 0.5, 0.03, 0.25)) < 1e-8 True
- financialkit.stochastic_volatility.local_vol_paths(surface, S, T, r, q=0.0, n_steps=100, n_paths=10000, rng=None, dW=None)[source]#
Simulate \(dS = (r - q) S\,dt + \sigma(S, t) S\,dW\) on a local-volatility grid.
Euler-Maruyama on \(\ln S\), through
financialkit.integrators.euler_maruyama(); the volatility is interpolated bilinearly and held flat outside the grid. Nodes that arenaninsurfaceare filled from their nearest finite neighbour in strike first.- Parameters:
- Return type:
- Returns:
times (ndarray, shape (n_steps + 1,))
spot (ndarray, shape (n_paths, n_steps + 1))
Examples
>>> import numpy as np >>> flat = LocalVolSurface(np.array([50.0, 150.0]), np.array([0.0, 1.0]), np.full((2, 2), 0.2)) >>> t, S = local_vol_paths(flat, 100.0, 1.0, 0.05, n_steps=50, n_paths=20_000, rng=0) >>> bool(abs(S[:, -1].mean() - 100 * np.exp(0.05)) < 0.6) True
- financialkit.stochastic_volatility.merton_jump_price(S, K, T, r, model, q=0.0, option_type='call', n_terms=80)[source]#
Merton’s (1976) series: a Poisson mixture of Black-Scholes prices.
\[C = \sum_{n \ge 0} \frac{e^{-\lambda' T} (\lambda' T)^n}{n!}\, C_{BS}(S, K, T, r_n, \sigma_n, q),\]with \(\lambda' = \lambda(1 + \bar k)\), \(\sigma_n^2 = \sigma^2 + n\delta^2/T\) and \(r_n = r - \lambda\bar k + n \ln(1 + \bar k)/T\).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
Without jumps the series is Black-Scholes:
>>> m = MertonJumpModel(0.2, 0.0, -0.1, 0.1) >>> merton_jump_price(100.0, 100.0, 1.0, 0.05, m) == black_scholes_price(100.0, 100.0, 1.0, 0.05, 0.2) True
- financialkit.stochastic_volatility.sabr_calibrate(F, strikes, T, vols, beta=0.5)[source]#
Fit \((\alpha, \rho, \nu)\) to a smile with \(\beta\) fixed, by
scipy.optimize.least_squares().- Parameters:
- Return type:
- Returns:
SABRFit
Examples
>>> import numpy as np >>> K = np.linspace(70, 130, 13) >>> fit = sabr_calibrate(100.0, K, 1.0, sabr_implied_vol(100.0, K, 1.0, 2.0, 0.5, -0.3, 0.4)) >>> round(fit.alpha, 4), round(fit.rho, 4), round(fit.nu, 4) (2.0, -0.3, 0.4)
- financialkit.stochastic_volatility.sabr_implied_vol(F, K, T, alpha, beta, rho, nu)[source]#
Hagan et al.’s (2002) Black implied volatility of the SABR model.
\[\sigma_B = \frac{\alpha}{(FK)^{(1-\beta)/2}\left[1 + \frac{(1-\beta)^2}{24}\ell^2 + \frac{(1-\beta)^4}{1920}\ell^4\right]} \frac{z}{x(z)}\left[1 + \left(\frac{(1-\beta)^2 \alpha^2}{24 (FK)^{1-\beta}} + \frac{\rho\beta\nu\alpha}{4 (FK)^{(1-\beta)/2}} + \frac{2 - 3\rho^2}{24}\nu^2\right) T\right],\]with \(\ell = \ln(F/K)\), \(z = (\nu/\alpha)(FK)^{(1-\beta)/2}\ell\) and \(x(z) = \ln[(\sqrt{1 - 2\rho z + z^2} + z - \rho)/(1 - \rho)]\) (\(z/x(z) = 1\) at the money).
- Parameters:
F (
ArrayLike) – Forward, strike and expiry.K (
ArrayLike) – Forward, strike and expiry.T (
ArrayLike) – Forward, strike and expiry.alpha (
float) – SABR parameters: initial volatility, CEV exponent in [0, 1], correlation and volatility of volatility.beta (
float) – SABR parameters: initial volatility, CEV exponent in [0, 1], correlation and volatility of volatility.rho (
float) – SABR parameters: initial volatility, CEV exponent in [0, 1], correlation and volatility of volatility.nu (
float) – SABR parameters: initial volatility, CEV exponent in [0, 1], correlation and volatility of volatility.
- Return type:
- Returns:
float or ndarray
Examples
With \(\beta = 1\) and no volatility of volatility it is lognormal with volatility \(\alpha\):
>>> sabr_implied_vol(100.0, [80.0, 100.0, 120.0], 1.0, 0.2, 1.0, 0.0, 0.0) array([0.2, 0.2, 0.2])
- financialkit.stochastic_volatility.svi_fit(k, w, T)[source]#
Fit raw SVI to total implied variances by
scipy.optimize.least_squares().Several starting points are tried and the best fit kept. Bounds keep \(b \ge 0\), \(|\rho| < 1\) and \(\sigma > 0\).
- Parameters:
k (
ArrayLike) – Log-moneyness and total implied variance \(\sigma_{BS}^2 T\).w (
ArrayLike) – Log-moneyness and total implied variance \(\sigma_{BS}^2 T\).T (
float) – Expiry, stored forSVIFit.implied_vol().
- Return type:
- Returns:
SVIFit
Examples
>>> import numpy as np >>> k = np.linspace(-0.5, 0.5, 21) >>> fit = svi_fit(k, svi_total_variance(k, 0.01, 0.12, -0.6, 0.05, 0.15), 1.0) >>> bool(fit.rmse < 1e-8) True
- financialkit.stochastic_volatility.svi_total_variance(k, a, b, rho, m, sigma)[source]#
Gatheral’s raw SVI total implied variance \(w(k)\).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> round(svi_total_variance(0.0, 0.02, 0.1, -0.5, 0.0, 0.2), 12) 0.04