Black-Scholes Part 2
Solution
import pandas as pd
import numpy as np
from scipy.stats import norm
#Pull in the black-scholes call formula from the course
def black_scholes_call(S, X, sigma, rf, t):
d1 = 1/(sigma*t**.5) * (np.log(S/X) + (rf + sigma **2 /2) * t)
d2 = d1 - sigma * t**.5
return norm.cdf(d1) * S - norm.cdf(d2) * X * np.exp(-rf*t)
#Create a function for testing ranges of prices for black-scholes call options
def compute_BS_calls(X, sigma, rf, t):
calls = pd.Series(list(range(101)))
calls = calls.apply(lambda S: black_scholes_call(S, X, sigma, rf, t))
print(calls)
X, t, rf, sigma = build_widgets()
interact(compute_BS_calls,X=X, sigma=sigma, rf=rf, t=t);
Close down once you are done playing with the dashboard. Then we could switch this to be a graphing.
close_widgets([X, t, rf, sigma])
import matplotlib.pyplot as plt
#Change the formula to plot the value of the option
def compute_BS_calls(X, sigma, rf, t):
calls = pd.Series(list(range(101)))
calls = calls.apply(lambda S: black_scholes_call(S, X, sigma, rf, t))
calls.plot(kind='line')
plt.show()
X, t, rf, sigma = build_widgets()
interact(compute_BS_calls,X=X, sigma=sigma, rf=rf, t=t);
Again close the widgets.
close_widgets([X, t, rf, sigma])
Let’s modify the function to be useful for showing two options. We need to modify the widgets function to return two sets. We are also modifying the way we show this to use layouts.
#Change the formula to return two sets of widgets for two different call options
from ipywidgets import HBox, VBox, interactive_output
def build_widgets2():
X1 = widgets.IntSlider(
value=50,
min=1,
max=100,
step=1,
description='Strike Price:',
readout_format='.1f',
)
t1 = widgets.FloatSlider(
value=1,
min=0.01,
max=10,
step=.01,
description='Time to Maturity:',
readout_format='.2f',
)
rf1 = widgets.FloatSlider(
value=.05,
min=0,
max=.20,
step=.0001,
description='Risk-free Rate:',
readout_format='.2%',
)
sigma1 = widgets.FloatSlider(
value=.08,
min=0,
max=.80,
step=.0001,
description='Implied Volatility:',
readout_format='.2%',
)
X2 = widgets.IntSlider(
value=50,
min=1,
max=100,
step=1,
description='Strike Price 2:',
readout_format='.1f',
)
t2 = widgets.FloatSlider(
value=1,
min=0.01,
max=10,
step=.01,
description='Time to Maturity 2:',
readout_format='.2f',
)
rf2 = widgets.FloatSlider(
value=.05,
min=0,
max=.20,
step=.0001,
description='Risk-free Rate 2:',
readout_format='.2%',
)
sigma2 = widgets.FloatSlider(
value=.20,
min=0,
max=.80,
step=.0001,
description='Implied Volatility 2:',
readout_format='.2%',
)
return X1, sigma1, rf1, t1, X2, sigma2, rf2, t2
#Create a function to compare black-scholes options
def compute_BS_calls_comparison(X1, sigma1, rf1, t1,
X2, sigma2, rf2, t2):
calls1 = pd.Series(list(range(101)))
calls1 = calls1.apply(lambda S: black_scholes_call(S, X1, sigma1, rf1, t1))
calls2 = pd.Series(list(range(101)))
calls2 = calls2.apply(lambda S: black_scholes_call(S, X2, sigma2, rf2, t2))
calls = pd.concat([calls1, calls2], axis=1)
calls.columns = ['Call 1', 'Call 2']
calls.plot(kind='line')
plt.xlabel("Stock Price")
plt.ylabel("Option Value")
plt.show()
X1, sigma1, rf1, t1, X2, sigma2, rf2, t2 = build_widgets2()
graph = interactive_output(compute_BS_calls_comparison,{"X1":X1, "sigma1":sigma1, "rf1":rf1, "t1":t1,
"X2":X2, "sigma2":sigma2, "rf2":rf2, "t2":t2})
display(VBox([HBox([VBox([X1, sigma1, rf1, t1]),
VBox([X2, sigma2, rf2, t2])]), graph]));
Close again.
close_widgets([X1, sigma1, rf1, t1, X2, sigma2, rf2, t2])
Finally, we could bring in the delta and gamma functions to add on to this function.
#Create a dashboard for comparing delta and gamma
def black_scholes_delta(S, X, sigma, rf, t):
d1 = 1/(sigma*t**.5) * (np.log(S/X) + (rf + sigma **2 /2) * t)
return norm.cdf(d1)
def black_scholes_gamma(S, X, sigma, rf, t):
d1 = 1/(sigma*t**.5) * (np.log(S/X) + (rf + sigma **2 /2) * t)
dN = np.exp(-d1**2/2) / (2 * np.pi)
return dN / (S * sigma * t **.5)
def compute_BS_calls_comparison(X1, sigma1, rf1, t1,
X2, sigma2, rf2, t2):
fig, ax = plt.subplots(ncols=3, figsize=(15,6))
calls1 = pd.Series(list(range(101)))
calls1 = calls1.apply(lambda S: black_scholes_call(S, X1, sigma1, rf1, t1))
calls2 = pd.Series(list(range(101)))
calls2 = calls2.apply(lambda S: black_scholes_call(S, X2, sigma2, rf2, t2))
calls = pd.concat([calls1, calls2], axis=1)
calls.columns = ['Call 1', 'Call 2']
calls.plot(kind='line', ax=ax[0])
ax[0].set_xlabel("Stock Price")
ax[0].set_ylabel("Option Value")
calls_delta1 = pd.Series(list(range(101)))
calls_delta1 = calls_delta1.apply(lambda S: black_scholes_delta(S, X1, sigma1, rf1, t1))
calls_delta2 = pd.Series(list(range(101)))
calls_delta2 = calls_delta2.apply(lambda S: black_scholes_delta(S, X2, sigma2, rf2, t2))
calls_delta = pd.concat([calls_delta1, calls_delta2], axis=1)
calls_delta.columns = ['Call 1', 'Call 2']
calls_delta.plot(kind='line', ax=ax[1])
ax[1].set_xlabel("Stock Price")
ax[1].set_ylabel("Option Delta")
calls_gamma1 = pd.Series(list(range(101)))
calls_gamma1 = calls_gamma1.apply(lambda S: black_scholes_gamma(S, X1, sigma1, rf1, t1))
calls_gamma2 = pd.Series(list(range(101)))
calls_gamma2 = calls_gamma2.apply(lambda S: black_scholes_gamma(S, X2, sigma2, rf2, t2))
calls_gamma = pd.concat([calls_gamma1, calls_gamma2], axis=1)
calls_gamma.columns = ['Call 1', 'Call 2']
calls_gamma.plot(kind='line', ax=ax[2])
ax[2].set_xlabel("Stock Price")
ax[2].set_ylabel("Option Gamma")
plt.show()
close_widgets([X1, sigma1, rf1, t1, X2, sigma2, rf2, t2])
X1, sigma1, rf1, t1, X2, sigma2, rf2, t2 = build_widgets2()
graph = interactive_output(compute_BS_calls_comparison,{"X1":X1, "sigma1":sigma1, "rf1":rf1, "t1":t1,
"X2":X2, "sigma2":sigma2, "rf2":rf2, "t2":t2})
VBox([HBox([VBox([X1, sigma1, rf1, t1]),
VBox([X2, sigma2, rf2, t2])]), graph])