Systematically Pricing Financial Options With Black Scholes | by Brunna Torino | Towards Data Science

There are two types of vanilla financial options that are traded in the financial markets: American — where you can exercise the option any time until the exercise date — and European — where you have to wait until the exercise date to exercise the option.
The names “American” and “European” don’t actually relate to where the option is traded, or what nationality the company is. The two types of options will only have a significant different in valuation if the stock pays dividends and the time to the exercise date includes the ex-dividend date — when buying a stock will not grant you a right to receive the dividend anymore.
Black-Scholes assumes an European option, but it can be used for American-style options that don’t pay dividends.
The Nobel-winning original Black-Scholes formula states that the price of a call option depends on the cumulative normal distribution, denoted here by N, of a function of the stock’s spot price S, the present value of a risk-free bond trading at a value K (which equals the strike price), the volatility of the stock’s annualised returns and the time from today to the exercise date divided by the number of days in a financial year, denoted here by T.
If you would like to know more about the derivation of the formula, here is a really good resource. In this article, I will focus more on the systematic application of the formula.
To calculate our potential payoffs from the option, we need the historical data of the stock’s price for one year (in this case, June 12th 2018 to June 12th 2019) and data on the price level that the options are currently being traded at so we can compare our results.
The historical data can be obtained from Yahoo Finance: go to Historical Data and download the dataset for one year.
First we need to calculate the volatility input to the Black Scholes model. Traditionally, we use the volatility of the annualised returns of the stock, as calculated below:
The last line will retrieve the table for the call options data with an exercise date of July 24th for the stock.
Below is the same Black Scholes formula described in the start of the article, but now represented by a Python function named black_scholes . Note that the present value formula of the risk-free bond is just the price of the bond K divided by (1 + risk free rate) to the power of the fraction of time T, and we are using the Python function cdf to calculate the cumulative distribution function value of our variables d1 and d2.
Having calculated the volatility (square root of financial market days times the standard deviation of the annualised returns), we can make estimates for all the strike prices we currently have available contracts for. In Tesla’s case, we have 35 different strike prices for call options.
For each strike price, we input the spot price of the stock (972.84 at the moment), loop through the strike prices in the data frame r, the risk-free rate which I am using the 10-year U.S. treasury yield currently at 0.69%.
We output the data frame df_estimate that we can use to calculate how close the Black Scholes model estimates were to the actual price that the options are being traded at in the market:
Has Black Scholes correctly predicted the market value of the options?
Our mean estimation error is 0.49%, with a standard deviation of 2.46 percentage points. Our median is even more promising at around 0.29%. Our standard deviation still seems a bit high, but looking closer at the dataset we can see that there are a few contracts that haven’t been traded at all today which skews the dataset since these prices aren’t as highly updated.
If we only included contracts that have been traded at least once today:
df1 = df_estimate[(df_estimate.Volume != '-')]
We can actually produce much better results for the estimation error:
Here we have a mean estimation error of -0.13% which means the options prices are on average underpricing the options by 0.13% compared to our estimates and our standard deviation is decreased to only 1.8 percentage points.
For curiosity, let’s analyse the few contracts where the black Scholes has made an error larger than 1%:
df1[df1['estimate_error'] > 1]
Most of the contracts have small volumes, being traded only once during the stock market day. Contract number 4 however, has an estimation error of 1.4% with a high volume of 21 contracts and 466 open interests. The strike price is at 850, which means it will make a profit if the stock price gets higher than 850 + 174.65 = $1021.65 whereas at the moment the stock is currently trading at 972.84. That’s only 5.325% climb up in about 31 stock market days which is not a difficult feat for Tesla.
Our estimate is a bit lower, but one reason for the difference could be due to the very large spread on these options, where market makers are exiting with a 6% risk-free profit. This indicates that risk is a lot higher at the moment regarding Tesla options for July 24th, which makes sense considering the low volume of these trades and the proximity of the exercise date.
An important feat of the Yahoo Finance options data is that it shows us the implied volatility of the options prices, which can be calculated by solving the Black Scholes equation backwards for the volatility starting with the option trading price. For this analysis, I didn’t use the implied volatility as an input because I wanted to compare how market players are pricing options vs. the Black Scholes model estimates.
The implied volatility is forward-looking, meaning it can give us a sense of how much future volatility traders are currently pricing in the option, if traders are rational. This last assumption is important when talking about implied volatility since other factors, such as behavioural economics (Tesla is the most shorted stock in the market due to this belief), an irrational risk-averseness during the current pandemic, and other external factors, can all play a role into the pricing of the option that doesn’t necessarily reflect the expected volatility of the stock.