Greeks demo (European options on futures, Black-76)¶
We start from a single call option instance and, for each Greek,
explain what it means, 2) compute it analytically using
volkit.future,approximate it numerically using a small central difference bump, and
print the values and the approximation error.
import numpy as np
from volkit import (
price_euro_future,
delta_euro_future,
gamma_euro_future,
vega_euro_future,
theta_euro_future,
rho_euro_future,
dual_delta_euro_future,
vanna_euro_future,
vomma_euro_future,
lambda_euro_future,
)
np.set_printoptions(precision=10, suppress=True)
# Base parameters (edit me)
F = 100.0
K = 100.0
T = 0.5 # years
r = 0.02 # cc rate
sigma = 0.20
cp = 'call'
Numerical greeks¶
Below, we compare the analytical greeks from this library with numerical approximation. The goal is to
show alignment between the two methods
illustrate the definitions of various greeks
We use the following numerical approximation schemes:
Central difference for first derivative
Central difference for second derivative
Mixed partial derivatives (vanna)
Baseline price¶
This is the present value (discounted) of the option under Black-76.
P0 = price_euro_future(F, K, T, r, sigma, cp)
print('Price:', P0)
Price: 5.581106724604813
Delta: sensitivity to futures price \(\partial V / \partial F\)¶
How much the option price \(V\) moves per 1-unit change in the futures price \(F\).
Delta_ana = delta_euro_future(F, K, T, r, sigma, cp)
hF = 1e-4 * F
Pp = price_euro_future(F + hF, K, T, r, sigma, cp)
Pm = price_euro_future(F - hF, K, T, r, sigma, cp)
Delta_num = (Pp - Pm) / (2 * hF)
print('Delta analytic :', Delta_ana)
print('Delta numerical:', Delta_num)
print('abs err:', abs(Delta_ana - Delta_num))
Delta analytic : 0.522930450497608
Delta numerical: 0.5229304435332427
abs err: 6.9643653066719935e-09
Gamma: curvature w.r.t. future price¶
Gamma measures how much delta changes when the future price \(F\) moves by a small amount \(dF\): $\( \Gamma \,=\, \frac{\partial^2 V}{\partial F^2}. \)\( Below we compute Gamma **analytically** using `gamma_euro_future`, and **numerically** by bumping \)F\( in `price_euro_future` with a central difference in \)F$.
Gamma_ana = gamma_euro_future(F, K, T, r, sigma, cp)
hF = np.maximum(1e-4 * (np.abs(F) + 1.0), 1e-6)
P0 = price_euro_future(F, K, T, r, sigma, cp)
Pp = price_euro_future(F + hF, K, T, r, sigma, cp)
Pm = price_euro_future(F - hF, K, T, r, sigma, cp)
Gamma_num = (Pp - 2.0*P0 + Pm) / (hF**2)
print('Gamma analytic :', Gamma_ana)
print('Gamma numerical:', Gamma_num)
print('abs err:', abs(Gamma_ana - Gamma_num))
Gamma analytic : 0.0278590553990826
Gamma numerical: 0.027859054269892318
abs err: 1.1291902829446698e-09
Vega: sensitivity to volatility¶
Vega measures how much the option price changes for a small change \(d\sigma\) in volatility:
$\(
\text{Vega} \,=\, \frac{\partial V}{\partial \sigma}.
\)\(
We compare `vega_euro_future` with a central difference bump in \)\sigma$ using price_euro_future.
Vega_ana = vega_euro_future(F, K, T, r, sigma, cp)
hs = np.maximum(1e-4 * (np.abs(sigma) + 1.0), 1e-6)
Pp = price_euro_future(F, K, T, r, sigma + hs, cp)
Pm = price_euro_future(F, K, T, r, sigma - hs, cp)
Vega_num = (Pp - Pm) / (2.0 * hs)
print('Vega analytic :', Vega_ana)
print('Vega numerical:', Vega_num)
print('abs err:', abs(Vega_ana - Vega_num))
Vega analytic : 27.85905539908261
Vega numerical: 27.85905539074799
abs err: 8.33462010518815e-09
Theta: sensitivity to the passage of time¶
Theta is the rate at which the option price changes with a small change \(dT\) in time-to-expiry \(T\): $\( \Theta \,=\, \frac{\partial V}{\partial T}. \)\( We compare `theta_euro_future` with a central difference bump in \)T\( using `price_euro_future` (ensuring \)T>0$).
Theta_ana = theta_euro_future(F, K, T, r, sigma, cp)
hT = 1e-4
Pp = price_euro_future(F, K, T+hT, r, sigma, cp)
Pm = price_euro_future(F, K, T-hT, r, sigma, cp)
Theta_num = -(Pp - Pm) / (2.0 * hT)
print(hT, P0, Pm, Pp)
print('Theta analytic :', Theta_ana)
print('Theta numerical:', Theta_num)
print('abs err:', abs(Theta_ana - Theta_num))
0.0001 5.581106724604813 5.5805606766058755 5.581652714400655
Theta analytic : -5.460188945324425
Theta numerical: -5.460188973898106
abs err: 2.8573681198906797e-08
Rho: sensitivity to the interest rate¶
Rho measures how much the option price changes for a small change \(dr\) in the interest rate \(r\):
$\(
\rho \,=\, \frac{\partial V}{\partial r}.
\)\(
We compare `rho_euro_future` with a central difference bump in \)r$ using price_euro_future.
Rho_ana = rho_euro_future(F, K, T, r, sigma, cp)
hr = 1e-6
Pp = price_euro_future(F, K, T, r + hr, sigma, cp)
Pm = price_euro_future(F, K, T, r - hr, sigma, cp)
Rho_num = (Pp - Pm) / (2.0 * hr)
print('Rho analytic :', Rho_ana)
print('Rho numerical:', Rho_num)
print('abs err:', abs(Rho_ana - Rho_num))
Rho analytic : -2.7905533623024064
Rho numerical: -2.790553362164161
abs err: 1.3824541511553434e-10
Dual Delta: sensitivity to the strike¶
Dual delta measures how much the option price changes for a small change \(dK\) in the strike \(K\):
$\(
\text{Dual-}\Delta \,=\, \frac{\partial V}{\partial K}.
\)\(
We compare `dual_delta_euro_future` with a central difference bump in \)K$ using price_euro_future.
Dual_ana = dual_delta_euro_future(F, K, T, r, sigma, cp)
hK = 1e-4 * (np.abs(K) + 1.0)
Pp = price_euro_future(F, K + hK, T, r, sigma, cp)
Pm = price_euro_future(F, K - hK, T, r, sigma, cp)
Dual_num = (Pp - Pm) / (2.0 * hK)
print('DualΔ analytic :', Dual_ana)
print('DualΔ numerical:', Dual_num)
print('abs err:', abs(Dual_ana - Dual_num))
DualΔ analytic : -0.46711938325155994
DualΔ numerical: -0.4671193903562059
abs err: 7.104645982636271e-09
Vanna: mixed sensitivity to \(F\) and \(\sigma\)¶
Vanna is the mixed partial derivative w.r.t. the future price \(F\) and volatility \(\sigma\): $\( \text{Vanna} \,=\, \frac{\partial^2 V}{\partial F\,\partial \sigma}. \)\( We compare `vanna_euro_future` with a **central mixed difference**: bump \)F\( by \)dF\( and \)\sigma\( by \)d\sigma$.
Vanna_ana = vanna_euro_future(F, K, T, r, sigma, cp)
hF = np.maximum(1e-4 * (np.abs(F) + 1.0), 1e-6)
hs = np.maximum(1e-4 * (np.abs(sigma) + 1.0), 1e-6)
Ppp = price_euro_future(F + hF, K, T, r, sigma + hs, cp)
Ppm = price_euro_future(F + hF, K, T, r, sigma - hs, cp)
Pmp = price_euro_future(F - hF, K, T, r, sigma + hs, cp)
Pmm = price_euro_future(F - hF, K, T, r, sigma - hs, cp)
Vanna_num = (Ppp - Ppm - Pmp + Pmm) / (4.0 * hF * hs)
print('Vanna analytic :', Vanna_ana)
print('Vanna numerical:', Vanna_num)
print('abs err:', abs(Vanna_ana - Vanna_num))
Vanna analytic : 0.13929527699541303
Vanna numerical: 0.1392953132744114
abs err: 3.6278998361005677e-08
Vomma (Volga): curvature w.r.t. volatility¶
Vomma (also called Volga) is the second derivative of price with respect to volatility \(\sigma\):
$\(
\text{Vomma} \,=\, \frac{\partial^2 V}{\partial \sigma^2}.
\)\(
We compare `vomma_euro_future` with a central second difference in \)\sigma$ using price_euro_future.
Vomma_ana = vomma_euro_future(F, K, T, r, sigma, cp)
hs = np.maximum(1e-4 * (np.abs(sigma) + 1.0), 1e-6)
P0 = price_euro_future(F, K, T, r, sigma, cp)
Pp = price_euro_future(F, K, T, r, sigma + hs, cp)
Pm = price_euro_future(F, K, T, r, sigma - hs, cp)
Vomma_num = (Pp - 2.0 * P0 + Pm) / (hs**2)
print('Vomma analytic :', Vomma_ana)
print('Vomma numerical:', Vomma_num)
print('abs err:', abs(Vomma_ana - Vomma_num))
Vomma analytic : -0.6964763849770655
Vomma numerical: -0.696476580112441
abs err: 1.9513537541371306e-07
Lambda (elasticity): percentage sensitivity to \(F\)¶
Lambda (elasticity) scales delta by the price-to-future ratio: $\( \Lambda \,=\, \frac{F}{V}\,\frac{\partial V}{\partial F} \;=\; \frac{F}{V}\,\Delta. \)\( We compare `lambda_euro_future` with a numerical version built from a central \)dF$ delta and the price.
Lambda_ana = lambda_euro_future(F, K, T, r, sigma, cp)
hF = np.maximum(1e-4 * (np.abs(F) + 1.0), 1e-6)
P0 = price_euro_future(F, K, T, r, sigma, cp)
Pp = price_euro_future(F + hF, K, T, r, sigma, cp)
Pm = price_euro_future(F - hF, K, T, r, sigma, cp)
Delta_num = (Pp - Pm) / (2.0 * hF)
Lambda_num = np.where(P0 != 0.0, (F * Delta_num) / P0, np.nan)
print('Lambda analytic :', Lambda_ana)
print('Lambda numerical:', Lambda_num)
print('abs err:', abs(Lambda_ana - Lambda_num))
Lambda analytic : 9.369655093535874
Lambda numerical: 9.369654966233444
abs err: 1.273024299308645e-07