import numpy as np
import pandas as pd
from scipy.stats import norm
import matplotlib.pyplot as plt
class option_pricing:
"""
To price European Style options without dividends
"""
def __init__(self, spot, strike, rate, dte, sigma):
# assign our variables
self.spot = spot
self.strike = strike
self.rate = rate
self.dte = dte # days to expiration (in years)
self.sigma = sigma
# to avoid zero division, let not allow strike of 0
if self.strike == 0:
raise ZeroDivisionError('The Strike Price cannot be 0')
else:
self._d1_ = (np.log(self.spot / self.strike) + (self.rate + (self.sigma**2 / 2)) * dte) / (self.sigma * self.dte**0.5)
self._d2_ = self._d1_ - (self.sigma * self.dte**0.5)
for i in ['callPrice', 'putPrice', 'callDelta', 'putDelta', 'gamma']:
self.__dict__[i] = None
self.callPrice, self.putPrice] = self._price()
[self.callDelta, self.putDelta] = self._delta()
[self.gamma = self._gamma()
def _price(self):
if self.sigma == 0 or self.dte == 0:
= maximum(0.0, self.spot - self.strike)
call = maximum(0.0, self.strike - self.spot)
put else:
= (self.spot * norm.cdf(self._d1_)) - (self.strike * np.e**(- self.rate * self.dte) * norm.cdf(self._d2_))
call = (self.strike * np.e**(- self.rate * self.dte) * norm.cdf(-self._d2_)) - (self.spot * norm.cdf(-self._d1_))
put return [call, put]
def _delta(self):
if self.sigma == 0 or self.dte == 0:
= 1.0 if self.spot > self.strike else 0.0
call = -1.0 if self.spot < self.strike else 0.0
put else:
= norm.cdf(self._d1_)
call = -norm.cdf(-self._d1_)
put return [call, put]
def _gamma(self):
return norm.cdf(self._d1_) / (self.spot * self.sigma * self.dte**0.5)
Deriving the Black-Schole Equation
Solving the Black-Schole Equation
Recap
European Vanilla
- V is the option price at the time
. So
- S is the asset spot price
- t is the time to expiry (in years)
is the asset diffusion term (its stochastic element) is the annualized continuously compounded risk-free rate (imaginary friend)
In the case of a European Call Option with no-dividend, the BSE has solution:
And in the case of a European Put Option with no-dividend, the BSE has solution:
where,
- K is the strike price
European Vanilla with Dividends
And in the case of a dividend (ok … assuming continuous dividend yield):
The Greeks
Greek | Description | Formula | Call Option | Put Option |
---|---|---|---|---|
Delta | Sensitivity of option value to changes in asset price | |||
Gamma | Sensitivity of Delta to changes in asset price | |||
Vega | Sensitivity of option value to changes in volatility | |||
Theta | Sensitivity of option value to changes in time | |||
Rho | Sensitivity of option value to change in risk-free rate |
Create a function for numerical computation
Using Python
from tabulate import tabulate
= option_pricing(100, 100, 0, 1, 0.2)
option
= ['Call Price', 'Put Price', 'Call Delta', 'Gamma']
header = [[option.callPrice, option.putPrice, option.callDelta, option.gamma]]
table print(tabulate(table, header))
Call Price Put Price Call Delta Gamma
------------ ----------- ------------ ---------
7.96557 7.96557 0.539828 0.0269914
Retrieving option data using Yahoo finance
import yfinance as yf
= yf.Ticker('AMD')
amd = amd.history(start = '2022-01-01')
amd_hist = amd.option_chain('2023-12-15')
options
from datetime import datetime
= (datetime(2023, 12, 15) - datetime.today()).days
dte
= np.log(amd_hist['Close'] / amd_hist['Close'].shift(1)).dropna()
log_returns = log_returns.std() * np.sqrt(dte)
historical_vol
= 116; strike = 120; rate = 0.05
spot
= option_pricing(spot=spot, strike=strike, rate=rate, dte=dte/365, sigma=historical_vol)
amd_opt
print(f'The BS model for AMD 147 days ahead is {amd_opt.callPrice:0.4f}')
The BS model for AMD 147 days ahead is 2.7995
= options.calls[(options.calls['strike'] >= 90) & (options.calls['strike'] < 150)]
df = True, inplace = True)
df.reset_index(drop
= pd.DataFrame({'strike': df['strike'], 'price': df['lastPrice'], 'impl_vol': df['impliedVolatility']})
df1 'delta'] = df1['gamma'] = 0.
df1[
for i in range(len(df1)):
'delta'].iloc[i] = option_pricing(spot=spot, strike=df['strike'].iloc[i], rate, dte=dte, sigma = df1['impl_vol'].iloc[i]).callDelta df[
for i in range(len(df1)):
'Delta'].iloc[i] = option_pricing(spot,df1['strike'].iloc[i],rate,dte,df1['impl_vol'].iloc[i]).callDelta
df1['Gamma'].iloc[i] = option_pricing(spot,df1['strike'].iloc[i],rate,dte,df1['impl_vol'].iloc[i]).gamma df1[