10. Greeks under Black-Scholes#

closed form

Hide code cell source

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.stats import norm

import ipywidgets as widgets
from ipywidgets import interactive

10.1. Black-Scholes Greeks Calculation and Visualization#

e First we need to define the Greeks formulas for Black-Scholes model:

  • \(\Delta = \displaystyle \frac{\partial C}{\partial S}\)

    • \(\displaystyle \Delta_{\text{call}} = \Phi(\mathrm{d}1)\)

    • \(\displaystyle \Delta_{\text{put}} = -\Phi(-d1)\)

  • \(\Gamma = \displaystyle \frac{\partial^2 C}{\partial S^2}\)

    • \(\displaystyle \Gamma_{\text{call}} = \frac{\phi(\mathrm{d}1)}{S\sigma \sqrt{T}} = \Gamma_{\text{put}}\)

  • \(\nu = \displaystyle \frac{\partial C}{\partial \sigma}\)

    • \(\displaystyle \nu_{\text{call}} = S \phi(\mathrm{d}1)\sqrt{T} = \nu_{\text{put}}\)

  • \(\Theta = -\displaystyle \frac{\partial C}{\partial \tau}\), where \(\tau = T-t\), i.e. time to maturity.

    • \(\displaystyle \Theta_{\text{call}} = -\frac{S\phi(\mathrm{d}1)\sigma}{2\tau} - rK \exp{(-rT)}\Phi(\mathrm{d}2)\)

    • \(\displaystyle \Theta_{\text{put}} = -\frac{S\phi(\mathrm{d}1)\sigma}{2\tau} + rK \exp{(-rT)}\Phi(-\mathrm{d}2)\)

  • \(\rho = \displaystyle \frac{\partial C}{\partial r}\)

    • \(\displaystyle \rho_{\text{call}} = K\tau \exp{(-rT)}\Phi(\mathrm{d}2)\)

    • \(\displaystyle \rho_{\text{put}} = -K\tau \exp{(-rT)}\Phi(-\mathrm{d}2)\)

Hide code cell source

# Black-Scholes pricing function for a European Call option
def bsPricer(S, K, T, r, sigma, type='call'):
    if type == 'call':
        d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
        d2 = d1 - sigma * np.sqrt(T)
        price = S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)
        delta = norm.cdf(d1)  # Delta of the call option
        gamma = norm.pdf(d1, 0, 1) / (S * sigma * np.sqrt(T))
        vega = S * norm.pdf(d1, 0, 1) * np.sqrt(T)
        theta = -S * norm.pdf(d1, 0, 1) * sigma/ (2 * np.sqrt(T)) - r * K * np.exp(-r * T) * norm.cdf(d2, 0, 1)
        rho = K * T * np.exp(-r * T) * norm.cdf(d2, 0, 1)

    elif type == 'put':
        d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
        d2 = d1 - sigma * np.sqrt(T)
        price = K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)
        delta = -norm.cdf(-d1)
        gamma = norm.pdf(d1, 0, 1) / (S * sigma * np.sqrt(T))
        vega = S * norm.pdf(d1, 0, 1) * np.sqrt(T)
        theta = -S * norm.pdf(d1, 0, 1) * sigma/ (2 * np.sqrt(T)) + r * K * np.exp(-r * T) * norm.cdf(-d2, 0, 1)
        rho = -K * T * np.exp(-r * T) * norm.cdf(-d2, 0, 1)
    return price, delta, gamma*0.01, vega*0.01, theta/365, rho*0.01

Hide code cell source

# Test
# Define variables
r = 0.01
S = 30
K = 40
T = 240/365
sigma = 0.30
type = 'put'  # 'call' or 'put'

# Calculate option price and Greeks
result = bsPricer(S, K, T, r, sigma, type)
columns = ['Price', 'Delta', 'Gamma', 'Vega', 'Theta', 'Rho']
df = pd.DataFrame([result], columns=columns)
df
Price Delta Gamma Vega Theta Rho
0 10.251133 -0.849414 0.00032 0.056867 -0.002575 -0.23496

Hide code cell source

# Interactive plot to select which Greek to display alongside option price

def update(K=100, T=1, r=0.05, sigma=0.2, type='call', greek='Delta'):
    S_range = np.linspace(50, 150, 100)
    prices, greeks = [], []
    for s in S_range:
        price, delta, gamma, vega, theta, rho = bsPricer(s, K, T, r, sigma, type)
        prices.append(price)
        if greek == 'Delta':
            greeks.append(delta)
        elif greek == 'Gamma':
            greeks.append(gamma)
        elif greek == 'Vega':
            greeks.append(vega)
        elif greek == 'Theta':
            greeks.append(theta)
        elif greek == 'Rho':
            greeks.append(rho)
    fig, ax1 = plt.subplots(figsize=(7, 6))
    ax2 = ax1.twinx()
    ax1.plot(S_range, prices, 'b-', label=f'{type.capitalize()} Price')
    ax2.plot(S_range, greeks, 'r-', label=greek)
    ax1.axvline(K, color='k', linestyle='--', label='K')
    ax1.set_xlabel('Stock Price (S)')
    ax1.set_ylabel(f'{type.capitalize()} Price', color='b')
    ax2.set_ylabel(greek, color='r')
    ax1.tick_params(axis='y', colors='b')
    ax2.tick_params(axis='y', colors='r')
    ax1.grid()
    lines1, labels1 = ax1.get_legend_handles_labels()
    lines2, labels2 = ax2.get_legend_handles_labels()
    ax1.legend(lines1 + lines2, labels1 + labels2)
    plt.title(f'Black-Scholes {type.capitalize()} Option and {greek}')
    plt.tight_layout()
    plt.show()

greek_options = ['Delta', 'Gamma', 'Vega', 'Theta', 'Rho']
type_options = ['call', 'put']

interactive = interactive(
    update,
    K=widgets.FloatSlider(min=50, max=150, step=1, value=100, description='K'),
    T=widgets.FloatSlider(min=0.1, max=5, step=0.1, value=1, description='T'),
    r=widgets.FloatSlider(min=0, max=0.2, step=0.01, value=0.05, description='r'),
    sigma=widgets.FloatSlider(min=0.05, max=1, step=0.05, value=0.2, description='sigma'),
    type=widgets.Dropdown(options=type_options, value='call', description='Type'),
    greek=widgets.Dropdown(options=greek_options, value='Delta', description='Greek')
)

display(interactive)

10.1.1. What happens if the reality has stochastic volatility instead?#

  • we move to the next notebook.