Monte Carlo Part 2
Solution
strike1 = 90
strike2 = 95
strike3 = 100
#Build a function to find the payoff distribution for three calls with different strike prices
#Remember that you have to discount them for time value of money
def call_payoffs(strike1, strike2, strike3, periods, rf):
time_step = 1/252
paths = data_dictionary['Data']
payoffs1 = paths.iloc[-1].apply(lambda x: max(x-strike1, 0)) / np.exp(time_step * periods * rf)
payoffs2 = paths.iloc[-1].apply(lambda x: max(x-strike2, 0)) / np.exp(time_step * periods * rf)
payoffs3 = paths.iloc[-1].apply(lambda x: max(x-strike3, 0)) / np.exp(time_step * periods * rf)
payoffs = pd.concat([payoffs1, payoffs2, payoffs3], axis=1)
payoffs.columns = ["Call Strike {}".format(strike1),
"Call Strike {}".format(strike2),
"Call Strike {}".format(strike3)]
return payoffs
#You will give the current value of the periods and rf widgets so that the two dashboards are aligned
payoffs = call_payoffs(strike1, strike2, strike3, periods.value, rf.value)
print(payoffs.head(5))
The only thing that you might be a little confused on is why I use periods.value and rf.value. The reason for this is because in this case I am taking the value we used in the original path creation dashboard to ensure the payoffs are consistent.
Now, the fun part, we get to create some cool visualizations. Feel free to try your own out, eventually we will make a plot with four visualizations of the payoffs. The first is a kernel density estimation plot. These plots attempt to make the shape of the probability density function for different series. I also cut off negative values because in reality we will never get these values.
import seaborn as sns
#Build out kernel density estimations
sns.kdeplot(payoffs.iloc[:,0].values, label="Call Strike {}".format(strike1))
sns.kdeplot(payoffs.iloc[:,1].values, label="Call Strike {}".format(strike2))
sns.kdeplot(payoffs.iloc[:,2].values, label="Call Strike {}".format(strike3))
plt.xlabel("Payoff")
plt.ylabel("KDE")
plt.title("Call Payoff Distributions")
plt.xlim(0, plt.xlim()[1])
plt.show()
A violin plot is also a nice way to see the distributions.
#Create a violin plot
sns.violinplot(data=payoffs)
plt.ylim([0, plt.ylim()[1]])
plt.ylabel("Payoff")
plt.title("Call Payoff Distributions")
plt.show()
Besides the payoffs, we might be interested in the percent of the time that a strike price was in the money. To achieve this we can turn the dataframe into a boolean one where being greater than 0 is true (or 1). If you take the mean of this you get the percent of paths that were positive.
from matplotlib.ticker import PercentFormatter
#Plot the percent of the runs that are in the money by the end
ax = (payoffs > 0).mean().plot(kind='bar', color='black')
plt.ylim([0,1])
ax.yaxis.set_major_formatter(PercentFormatter(xmax=1))
plt.title("Time in the Money")
plt.ylabel("Percent")
plt.show()
Finally we can visualize the end prices for the paths from the data dictionary in a histogram. With vertical lines we will be able to visualize the cutoffs for each strike price.
#Build a histogram of the ending prices with the strike prices represented with vertical lines
paths = data_dictionary['Data']
paths.iloc[-1].plot.hist(bins=30, color='black',label='Prices')
plt.axvline(strike1,color='red', label='Strike 1')
plt.axvline(strike2,color='green', label='Strike 2')
plt.axvline(strike3,color='blue', label='Strike 3')
plt.legend()
plt.xlabel('Price')
plt.title("Ending Price Histogram")
plt.show()
Finally put them all together. Using subplots we can pick a graph for each of the four plot squares!
#Build a function to graph call option visualization
def graph_call_options(payoffs, strike1, strike2, strike3):
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(15,8))
paths = data_dictionary['Data']
paths.iloc[-1].plot.hist(bins=30, color='black',label='Prices', ax=ax[0][0])
ax[0][0].axvline(strike1,color='red', label='Strike 1')
ax[0][0].axvline(strike2,color='green', label='Strike 2')
ax[0][0].axvline(strike3,color='blue', label='Strike 3')
ax[0][0].legend()
ax[0][0].set_xlabel('Price')
ax[0][0].set_title("Ending Price Histogram")
(payoffs > 0).mean().plot(kind='bar', ax=ax[0][1], color='black')
ax[0][1].set_ylim([0,1])
ax[0][1].yaxis.set_major_formatter(PercentFormatter(xmax=1))
ax[0][1].set_title("Time in the Money")
ax[0][1].set_ylabel("Percent")
ax[0][1].tick_params(labelrotation=45)
sns.kdeplot(payoffs.iloc[:,0].values, label="Call Strike {}".format(strike1),ax=ax[1][0])
sns.kdeplot(payoffs.iloc[:,1].values, label="Call Strike {}".format(strike2),ax=ax[1][0])
sns.kdeplot(payoffs.iloc[:,2].values, label="Call Strike {}".format(strike3),ax=ax[1][0])
ax[1][0].set_xlabel("Payoff")
ax[1][0].set_ylabel("KDE")
ax[1][0].set_title("Call Payoff Distributions")
ax[1][0].set_xlim(0, ax[1][0].get_xlim()[1])
sns.violinplot(data=payoffs, ax=ax[1][1])
ax[1][1].set_ylim([0, ax[1][1].get_ylim()[1]])
ax[1][1].set_ylabel("Payoff")
ax[1][1].set_title("Call Payoff Distributions")
fig.tight_layout()
plt.show()
graph_call_options(payoffs, strike1, strike2, strike3)
Challenge