Collect stock and option data, price with BSM, compare accuracy

```python
import pandas as pd

from math import sqrt
import numpy as np
from scipy.stats import norm

import seaborn as sns

from datetime import datetime, timezone, timedelta

```

```python
# initial parameters

ticker = 'GOOG'
risk_free_rate = 0.08
option_type = 'put'

# 2 weeks, 1m, 3m, 6m
# expiration_datetime = datetime(2020, 12, 4, 0, 0, tzinfo=timezone.utc)
# expiration_datetime = datetime(2020, 12, 11, 0, 0, tzinfo=timezone.utc)
# expiration_datetime = datetime(2020, 12, 24, 0, 0, tzinfo=timezone.utc)
# expiration_datetime = datetime(2021, 2, 19, 0, 0, tzinfo=timezone.utc)
expiration_datetime = datetime(2021, 6, 18, 0, 0, tzinfo=timezone.utc)

```

```python
# get a UTC timestamp from a date. This is used to scrape data from Yahoo Finance.

expiration_timestamp = int(expiration_datetime.timestamp())

print(expiration_timestamp)
print(datetime.fromtimestamp(expiration_timestamp)) # this is in UTC, add 5 hours to this to get EST

# feb 19 2021
```

    1623974400
    2021-06-18 00:00:00

```python
# !pip install lxml
```

Download historical stock price data for Google (GOOG). I get a past year's worth from (https://finance.yahoo.com/quote/GOOG/history?p=GOOG)

```python
# download annual historical data for the stock

# stock_price_path = "~/documents/quant_finance/price_data/{}.csv".format(ticker)
stock_price_path = "price_data/{}.csv".format(ticker)
options_data_path = 'https://finance.yahoo.com/quote/{}/options?date={}&p={}'.format(ticker, expiration_timestamp, ticker)

df = pd.read_csv(stock_price_path)
df = df.sort_values(by="Date")
df = df.dropna()

# calculate returns
df = df.assign(close_day_before=df['Adj Close'].shift(1))
df['returns'] = ((df['Adj Close'] - df.close_day_before)/df.close_day_before)

# get options data
options_data = pd.read_html(options_data_path) # returns two dataframes, for calls and puts

if option_type == 'call':
    r = options_data[0]
elif option_type == 'put':
    r = options_data[1]

print(df)
print(r)
```

               Date         Open         High          Low        Close  \
    0    2019-12-02  1301.000000  1305.829956  1281.000000  1289.920044
    1    2019-12-03  1279.569946  1298.461060  1279.000000  1295.280029
    2    2019-12-04  1307.010010  1325.800049  1304.869995  1320.540039
    3    2019-12-05  1328.000000  1329.358032  1316.439941  1328.130005
    4    2019-12-06  1333.439941  1344.000000  1333.439941  1340.619995
    ..          ...          ...          ...          ...          ...
    249  2020-11-25  1772.890015  1778.540039  1756.540039  1771.430054
    250  2020-11-27  1773.089966  1804.000000  1772.439941  1793.189941
    251  2020-11-30  1781.183960  1788.064941  1755.000000  1760.739990
    252  2020-12-01  1774.369995  1824.829956  1769.369995  1798.099976
    253  2020-12-02  1798.099976  1835.650024  1789.472534  1827.949951

           Adj Close   Volume  close_day_before   returns
    0    1289.920044  1510900               NaN       NaN
    1    1295.280029  1143800       1289.920044  0.004155
    2    1320.540039  1537500       1295.280029  0.019502
    3    1328.130005  1212700       1320.540039  0.005748
    4    1340.619995  1314800       1328.130005  0.009404
    ..           ...      ...               ...       ...
    249  1771.430054  1045800       1768.880005  0.001442
    250  1793.189941   884900       1771.430054  0.012284
    251  1760.739990  1823800       1793.189941 -0.018096
    252  1798.099976  1736900       1760.739990  0.021218
    253  1827.949951  1179809       1798.099976  0.016601

    [254 rows x 9 columns]
               Contract Name         Last Trade Date  Strike  Last Price     Bid  \
    0    GOOG210618P00520000  2020-12-02 11:32AM EST   520.0        0.15    0.05
    1    GOOG210618P00540000  2020-12-01 12:29PM EST   540.0        0.20    0.00
    2    GOOG210618P00560000  2020-10-29 11:18AM EST   560.0        1.25    0.00
    3    GOOG210618P00580000   2020-07-01 8:54AM EST   580.0        5.50    3.00
    4    GOOG210618P00600000   2020-11-25 1:39PM EST   600.0        0.24    0.05
    ..                   ...                     ...     ...         ...     ...
    134  GOOG210618P01860000  2020-11-16 12:56PM EST  1860.0      205.00  171.90
    135  GOOG210618P01900000   2020-10-12 8:47AM EST  1900.0      407.40  215.50
    136  GOOG210618P02000000   2020-10-30 2:57PM EST  2000.0      436.00  275.90
    137  GOOG210618P02100000   2020-06-24 2:02PM EST  2100.0      673.60  602.00
    138  GOOG210618P02200000   2020-11-06 2:29PM EST  2200.0      469.60  405.70

            Ask  Change % Change Volume  Open Interest Implied Volatility
    0      0.20   -0.15  -50.00%    111            899             55.96%
    1      0.35    0.00        -      1            164             55.86%
    2      0.85    0.00        -      1            217             59.01%
    3      4.50    0.00        -      1             31             74.88%
    4      0.65    0.00        -      1            228             54.64%
    ..      ...     ...      ...    ...            ...                ...
    134  177.60    0.00        -      4              4             29.80%
    135  221.10    0.00        -      1              2             33.48%
    136  282.60    0.00        -      1              1             32.82%
    137  611.10    0.00        -      2              2             80.63%
    138  413.70    0.00        -      1              2             28.06%

    [139 rows x 11 columns]

```python
# get risk free rate data

rfr_data_path = 'https://www.treasury.gov/resource-center/data-chart-center/interest-rates/Pages/TextView.aspx?data=yield'
rfr_df = pd.read_html(rfr_data_path)[1] # returns two dataframes, for calls and puts
rfr_df
```

      Date
      1 mo
      2 mo
      3 mo
      6 mo
      1 yr
      2 yr
      3 yr
      5 yr
      7 yr
      10 yr
      20 yr
      30 yr

      0
      12/01/20
      0.07
      0.07
      0.09
      0.1
      0.12
      0.17
      0.22
      0.42
      0.68
      0.92
      1.46
      1.66

      1
      12/02/20
      0.07
      0.08
      0.09
      0.1
      0.11
      0.16
      0.22
      0.42
      0.69
      0.95
      1.50
      1.70

```python
# plot the rfr
rfr_date = rfr_df.iloc[-1][0]

cur_rfr = rfr_df.iloc[-1][1:].astype(float)
cur_rfr.plot(title="Treasury Curve for {}".format(rfr_date))
```

```python
# BSM model

# S is the spot price
# K is the strike price
# T is the fraction of days to strike date divided by 252 (stock market days)
# r is the risk free rate
# sigma is the annual volatility of the returns of the stock

def black_scholes(S, K, T, r, sigma, option_type='call'):

    d1 = (np.log(S/K) + (r + sigma**2/2)*T)/(sigma * sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)

    if option_type=='call':
        return S * norm.cdf(d1) - K*np.exp(-r*T) * norm.cdf(d2)
    elif option_type=='put':
        return K*np.exp(-r*T) * norm.cdf(-d2) - S * norm.cdf(-d1)

# get the (num days to expiration) / (trading days in a year)
def get_time_to_expiration(expiration_datetime_utc):
    return (expiration_datetime_utc - datetime.now(timezone.utc)).days / 252

```

```python
value_s = black_scholes(S = 69,
                        K = 70,
                        T = 6/12,
                        r = .05,
                        sigma = .35,
                        option_type='put')
print(value_s)
```

    6.401407649076464

```python
# the rfr should vary with time, not be constant.

cur_stock_price = df.iloc[-1]['Adj Close']
time_to_expiration = get_time_to_expiration(expiration_datetime)

# Calculate the volatility as the annualized standard deviation of the stock returns
sigma = np.sqrt(252) * df['returns'].std()

print('cur_stock_price: {}, time to expiration: {}, rfr: {}, vol: {}'.format(cur_stock_price, time_to_expiration, risk_free_rate, sigma))
list_estimates = []

strike_start_idx, strike_end_idx = 0, r.shape[0]
# strike_start_idx, strike_end_idx = 0, 21

# run BSM for different strikes
for x in range(strike_start_idx,strike_end_idx):
    value_s = black_scholes(S = cur_stock_price,
                            K = r['Strike'][x],
                            T = time_to_expiration,
                            r = risk_free_rate,
                            sigma = sigma,
                            option_type = option_type)
    list_estimates.append(value_s)

# merge the real and computed dataframes to compare results
df_list = pd.DataFrame(data=list_estimates, index=r.index[strike_start_idx:strike_end_idx])
df_list['estimate'] = df_list[0]
del df_list[0]
df_estimate = r.merge(df_list, right_index = True, left_index = True)
```

    cur_stock_price: 1827.949951, time to expiration: 0.7777777777777778, rfr: 0.08, vol: 0.38212781123440914

```python
df_estimate
```

      Contract Name
      Last Trade Date
      Strike
      Last Price
      Bid
      Ask
      Change
      % Change
      Volume
      Open Interest
      Implied Volatility
      estimate

      0
      GOOG210618P00520000
      2020-12-02 11:32AM EST
      520.0
      0.15
      0.05
      0.20
      -0.15
      -50.00%
      111
      899
      55.96%
      0.003265

      1
      GOOG210618P00540000
      2020-12-01 12:29PM EST
      540.0
      0.20
      0.00
      0.35
      0.00
      -
      1
      164
      55.86%
      0.005388

      2
      GOOG210618P00560000
      2020-10-29 11:18AM EST
      560.0
      1.25
      0.00
      0.85
      0.00
      -
      1
      217
      59.01%
      0.008636

      3
      GOOG210618P00580000
      2020-07-01 8:54AM EST
      580.0
      5.50
      3.00
      4.50
      0.00
      -
      1
      31
      74.88%
      0.013480

      4
      GOOG210618P00600000
      2020-11-25 1:39PM EST
      600.0
      0.24
      0.05
      0.65
      0.00
      -
      1
      228
      54.64%
      0.020534

      5
      GOOG210618P00620000
      2020-08-19 1:00PM EST
      620.0
      3.00
      0.00
      5.00
      0.00
      -
      1
      32
      66.75%
      0.030584

      6
      GOOG210618P00640000
      2020-08-18 2:48PM EST
      640.0
      3.24
      1.50
      2.65
      0.00
      -
      1
      22
      63.27%
      0.044617

      7
      GOOG210618P00660000
      2020-08-10 12:02PM EST
      660.0
      5.65
      3.80
      5.20
      0.00
      -
      15
      28
      68.85%
      0.063853

      8
      GOOG210618P00680000
      2020-11-06 11:21AM EST
      680.0
      1.30
      0.05
      0.85
      0.00
      -
      1
      153
      50.00%
      0.089771

      9
      GOOG210618P00700000
      2020-12-02 3:15PM EST
      700.0
      0.53
      0.15
      0.95
      -0.17
      -24.29%
      1
      71
      52.76%
      0.124141

      10
      GOOG210618P00720000
      2020-10-15 12:42PM EST
      720.0
      3.29
      0.50
      1.95
      0.00
      -
      4
      107
      52.88%
      0.169050

      11
      GOOG210618P00740000
      2020-10-12 8:36AM EST
      740.0
      3.35
      0.70
      2.15
      0.00
      -
      4
      91
      52.41%
      0.226926

      12
      GOOG210618P00760000
      2020-10-28 2:01PM EST
      760.0
      5.30
      0.45
      1.70
      0.00
      -
      1
      74
      52.12%
      0.300563

      13
      GOOG210618P00780000
      2020-10-08 11:04AM EST
      780.0
      5.60
      1.85
      2.70
      0.00
      -
      2
      38
      52.72%
      0.393135

      14
      GOOG210618P00800000
      2020-11-24 3:54PM EST
      800.0
      1.65
      0.75
      1.55
      0.00
      -
      1
      79
      48.70%
      0.508215

      15
      GOOG210618P00820000
      2020-10-20 10:11AM EST
      820.0
      5.10
      1.00
      3.20
      0.00
      -
      2
      5
      52.39%
      0.649779

      16
      GOOG210618P00840000
      2020-11-18 10:28AM EST
      840.0
      2.40
      1.10
      1.90
      0.00
      -
      1
      26
      47.30%
      0.822214

      17
      GOOG210618P00860000
      2020-11-17 11:49AM EST
      860.0
      3.00
      1.30
      2.15
      0.00
      -
      5
      35
      46.77%
      1.030318

      18
      GOOG210618P00880000
      2020-09-21 11:11AM EST
      880.0
      12.50
      5.80
      7.00
      0.00
      -
      2
      24
      53.94%
      1.279290

      19
      GOOG210618P00900000
      2020-11-06 1:55PM EST
      900.0
      3.50
      1.75
      2.65
      0.00
      -
      6
      38
      45.52%
      1.574725

      20
      GOOG210618P00920000
      2020-11-19 10:43AM EST
      920.0
      3.80
      2.05
      2.90
      0.00
      -
      1
      26
      44.83%
      1.922596

      21
      GOOG210618P00940000
      2020-10-13 11:21AM EST
      940.0
      10.70
      3.80
      5.50
      0.00
      -
      1
      33
      48.29%
      2.329232

      22
      GOOG210618P00960000
      2020-11-23 10:40AM EST
      960.0
      4.80
      2.70
      3.60
      0.00
      -
      6
      71
      43.75%
      2.801297

      23
      GOOG210618P00980000
      2020-11-04 9:59AM EST
      980.0
      5.75
      3.00
      4.00
      0.00
      -
      2
      130
      43.22%
      3.345760

      24
      GOOG210618P01000000
      2020-12-02 3:20PM EST
      1000.0
      3.81
      3.40
      4.40
      -1.94
      -33.74%
      1
      586
      42.63%
      3.969864

      25
      GOOG210618P01020000
      2020-10-28 8:49AM EST
      1020.0
      19.70
      4.50
      5.90
      0.00
      -
      1
      26
      43.55%
      4.681093

      26
      GOOG210618P01040000
      2020-11-05 12:35PM EST
      1040.0
      10.37
      4.40
      5.40
      0.00
      -
      1
      53
      41.61%
      5.487132

      27
      GOOG210618P01060000
      2020-10-20 11:13AM EST
      1060.0
      21.40
      6.60
      9.30
      0.00
      -
      21
      40
      44.75%
      6.395833

      28
      GOOG210618P01080000
      2020-11-04 10:05AM EST
      1080.0
      12.40
      5.50
      6.50
      0.00
      -
      1
      385
      40.52%
      7.415170

      29
      GOOG210618P01100000
      2020-11-30 11:38AM EST
      1100.0
      8.30
      6.10
      7.20
      0.00
      -
      1
      300
      40.06%
      8.553204

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      109
      GOOG210618P01600000
      2020-12-02 12:04PM EST
      1600.0
      67.73
      67.70
      71.30
      -4.47
      -6.19%
      1
      239
      31.92%
      96.626534

      110
      GOOG210618P01605000
      2020-11-24 10:28AM EST
      1605.0
      96.30
      69.10
      72.70
      0.00
      -
      1
      6
      31.86%
      98.233881

      111
      GOOG210618P01610000
      2020-11-20 10:18AM EST
      1610.0
      93.60
      70.50
      74.20
      0.00
      -
      1
      3
      31.83%
      99.857210

      112
      GOOG210618P01615000
      2020-11-24 2:36PM EST
      1615.0
      90.40
      72.00
      75.70
      0.00
      -
      1
      28
      31.78%
      101.496528

      113
      GOOG210618P01620000
      2020-12-02 3:49PM EST
      1620.0
      76.20
      73.50
      77.20
      -16.00
      -17.35%
      3
      2
      31.73%
      103.151844

      114
      GOOG210618P01625000
      2020-11-18 11:18AM EST
      1625.0
      99.00
      75.00
      78.70
      0.00
      -
      2
      8
      31.68%
      104.823164

      115
      GOOG210618P01630000
      2020-11-20 10:18AM EST
      1630.0
      100.60
      76.50
      80.20
      0.00
      -
      1
      1
      31.62%
      106.510492

      116
      GOOG210618P01635000
      2020-11-17 11:18AM EST
      1635.0
      98.40
      78.00
      81.80
      0.00
      -
      1
      2
      31.58%
      108.213832

      117
      GOOG210618P01640000
      2020-12-02 3:49PM EST
      1640.0
      80.90
      79.60
      83.40
      -23.50
      -22.51%
      1
      33
      31.54%
      109.933186

      118
      GOOG210618P01645000
      2020-11-16 1:01PM EST
      1645.0
      103.40
      81.10
      85.00
      0.00
      -
      1
      3
      31.49%
      111.668555

      119
      GOOG210618P01650000
      2020-10-21 12:32PM EST
      1650.0
      205.20
      106.70
      118.70
      0.00
      -
      -
      2
      38.31%
      113.419938

      120
      GOOG210618P01655000
      2020-11-23 10:06AM EST
      1655.0
      115.70
      84.40
      88.30
      0.00
      -
      2
      14
      31.40%
      115.187334

      121
      GOOG210618P01660000
      2020-11-19 9:47AM EST
      1660.0
      118.40
      86.00
      90.00
      0.00
      -
      2
      7
      31.36%
      116.970740

      122
      GOOG210618P01665000
      2020-11-20 3:32PM EST
      1665.0
      116.90
      87.70
      91.70
      0.00
      -
      2
      8
      31.31%
      118.770151

      123
      GOOG210618P01670000
      2020-12-01 12:25PM EST
      1670.0
      93.59
      89.40
      93.50
      0.00
      -
      1
      17
      31.28%
      120.585561

      124
      GOOG210618P01675000
      2020-11-24 2:36PM EST
      1675.0
      112.90
      91.00
      95.20
      0.00
      -
      1
      7
      31.22%
      122.416964

      125
      GOOG210618P01680000
      2020-12-02 11:51AM EST
      1680.0
      94.00
      92.70
      97.00
      -6.99
      -6.92%
      1
      17
      31.18%
      124.264352

      126
      GOOG210618P01700000
      2020-12-02 3:03PM EST
      1700.0
      100.10
      100.30
      104.40
      -8.00
      -7.40%
      11
      54
      31.02%
      131.813543

      127
      GOOG210618P01720000
      2020-11-30 10:51AM EST
      1720.0
      132.70
      107.70
      112.10
      0.00
      -
      2
      39
      30.84%
      139.617528

      128
      GOOG210618P01740000
      2020-12-02 3:14PM EST
      1740.0
      115.91
      115.60
      120.30
      -27.29
      -19.06%
      3
      67
      30.69%
      147.675221

      129
      GOOG210618P01760000
      2020-12-02 2:06PM EST
      1760.0
      125.21
      124.00
      128.80
      -23.89
      -16.02%
      2
      88
      30.52%
      155.985258

      130
      GOOG210618P01780000
      2020-11-25 10:13AM EST
      1780.0
      164.30
      132.70
      137.80
      0.00
      -
      1
      47
      30.38%
      164.546007

      131
      GOOG210618P01800000
      2020-12-02 3:58PM EST
      1800.0
      144.70
      142.00
      147.20
      -3.80
      -2.56%
      5
      25
      30.24%
      173.355582

      132
      GOOG210618P01820000
      2020-12-01 3:19PM EST
      1820.0
      166.50
      151.60
      156.90
      0.00
      -
      2
      22
      30.08%
      182.411860

      133
      GOOG210618P01840000
      2020-11-05 10:32AM EST
      1840.0
      210.60
      161.40
      167.00
      0.00
      -
      3
      4
      29.93%
      191.712495

      134
      GOOG210618P01860000
      2020-11-16 12:56PM EST
      1860.0
      205.00
      171.90
      177.60
      0.00
      -
      4
      4
      29.80%
      201.254934

      135
      GOOG210618P01900000
      2020-10-12 8:47AM EST
      1900.0
      407.40
      215.50
      221.10
      0.00
      -
      1
      2
      33.48%
      221.054049

      136
      GOOG210618P02000000
      2020-10-30 2:57PM EST
      2000.0
      436.00
      275.90
      282.60
      0.00
      -
      1
      1
      32.82%
      274.570866

      137
      GOOG210618P02100000
      2020-06-24 2:02PM EST
      2100.0
      673.60
      602.00
      611.10
      0.00
      -
      2
      2
      80.63%
      333.472840

      138
      GOOG210618P02200000
      2020-11-06 2:29PM EST
      2200.0
      469.60
      405.70
      413.70
      0.00
      -
      1
      2
      28.06%
      397.271434

139 rows × 12 columns

```python
# plot option prices across strikes

df_estimate.plot(x='Strike',y=['Last Price','estimate'], title='Computed v Market prices for {}, Expiry {}'.format(ticker, expiration_datetime.date()))
```

```python

```

```python
df_estimate['estimate_error'] = ((df_estimate['Last Price'] - df_estimate['estimate'])/df_estimate['estimate'])*100
ax = sns.distplot(df_estimate['estimate_error'])
print(df_estimate['estimate_error'].describe())

# across strikes, the median error is 34%, but there is a huge spread and outliers,
# which mainly seem to be due to the higher strikes being very close to 0.

# plot the error across strikes

df_estimate.plot(x='Strike',y='estimate_error',title='estimate error across strikes')
```

```python
# what is a measure of error?
# MSE: average of the sum of squared errors

mse = np.sum((df_estimate['Last Price'] - df_estimate['estimate'])**2) / df_estimate.shape[0]
rmse = np.sqrt(mse)

print(mse, rmse)
# could also try MAD
```

    1555.921413793013 39.44516971433908

```python
# examine outliers

df_estimate[df_estimate['estimate_error'].abs() > 1]

# the model tends to undervalue puts especially at high strikes (deep out of the money).
```

      Contract Name
      Last Trade Date
      Strike
      Last Price
      Bid
      Ask
      Change
      % Change
      Volume
      Open Interest
      Implied Volatility
      estimate
      estimate_error

      0
      GOOG210618P00520000
      2020-12-02 11:32AM EST
      520.0
      0.15
      0.05
      0.20
      -0.15
      -50.00%
      111
      899
      55.96%
      0.003265
      4494.040577

      1
      GOOG210618P00540000
      2020-12-01 12:29PM EST
      540.0
      0.20
      0.00
      0.35
      0.00
      -
      1
      164
      55.86%
      0.005388
      3612.055727

      2
      GOOG210618P00560000
      2020-10-29 11:18AM EST
      560.0
      1.25
      0.00
      0.85
      0.00
      -
      1
      217
      59.01%
      0.008636
      14373.731205

      3
      GOOG210618P00580000
      2020-07-01 8:54AM EST
      580.0
      5.50
      3.00
      4.50
      0.00
      -
      1
      31
      74.88%
      0.013480
      40700.062332

      4
      GOOG210618P00600000
      2020-11-25 1:39PM EST
      600.0
      0.24
      0.05
      0.65
      0.00
      -
      1
      228
      54.64%
      0.020534
      1068.786308

      5
      GOOG210618P00620000
      2020-08-19 1:00PM EST
      620.0
      3.00
      0.00
      5.00
      0.00
      -
      1
      32
      66.75%
      0.030584
      9709.082805

      6
      GOOG210618P00640000
      2020-08-18 2:48PM EST
      640.0
      3.24
      1.50
      2.65
      0.00
      -
      1
      22
      63.27%
      0.044617
      7161.751414

      7
      GOOG210618P00660000
      2020-08-10 12:02PM EST
      660.0
      5.65
      3.80
      5.20
      0.00
      -
      15
      28
      68.85%
      0.063853
      8748.417437

      8
      GOOG210618P00680000
      2020-11-06 11:21AM EST
      680.0
      1.30
      0.05
      0.85
      0.00
      -
      1
      153
      50.00%
      0.089771
      1348.124059

      9
      GOOG210618P00700000
      2020-12-02 3:15PM EST
      700.0
      0.53
      0.15
      0.95
      -0.17
      -24.29%
      1
      71
      52.76%
      0.124141
      326.933101

      10
      GOOG210618P00720000
      2020-10-15 12:42PM EST
      720.0
      3.29
      0.50
      1.95
      0.00
      -
      4
      107
      52.88%
      0.169050
      1846.172059

      11
      GOOG210618P00740000
      2020-10-12 8:36AM EST
      740.0
      3.35
      0.70
      2.15
      0.00
      -
      4
      91
      52.41%
      0.226926
      1376.252176

      12
      GOOG210618P00760000
      2020-10-28 2:01PM EST
      760.0
      5.30
      0.45
      1.70
      0.00
      -
      1
      74
      52.12%
      0.300563
      1663.358584

      13
      GOOG210618P00780000
      2020-10-08 11:04AM EST
      780.0
      5.60
      1.85
      2.70
      0.00
      -
      2
      38
      52.72%
      0.393135
      1324.445620

      14
      GOOG210618P00800000
      2020-11-24 3:54PM EST
      800.0
      1.65
      0.75
      1.55
      0.00
      -
      1
      79
      48.70%
      0.508215
      224.665625

      15
      GOOG210618P00820000
      2020-10-20 10:11AM EST
      820.0
      5.10
      1.00
      3.20
      0.00
      -
      2
      5
      52.39%
      0.649779
      684.882061

      16
      GOOG210618P00840000
      2020-11-18 10:28AM EST
      840.0
      2.40
      1.10
      1.90
      0.00
      -
      1
      26
      47.30%
      0.822214
      191.894651

      17
      GOOG210618P00860000
      2020-11-17 11:49AM EST
      860.0
      3.00
      1.30
      2.15
      0.00
      -
      5
      35
      46.77%
      1.030318
      191.172284

      18
      GOOG210618P00880000
      2020-09-21 11:11AM EST
      880.0
      12.50
      5.80
      7.00
      0.00
      -
      2
      24
      53.94%
      1.279290
      877.104393

      19
      GOOG210618P00900000
      2020-11-06 1:55PM EST
      900.0
      3.50
      1.75
      2.65
      0.00
      -
      6
      38
      45.52%
      1.574725
      122.260961

      20
      GOOG210618P00920000
      2020-11-19 10:43AM EST
      920.0
      3.80
      2.05
      2.90
      0.00
      -
      1
      26
      44.83%
      1.922596
      97.649421

      21
      GOOG210618P00940000
      2020-10-13 11:21AM EST
      940.0
      10.70
      3.80
      5.50
      0.00
      -
      1
      33
      48.29%
      2.329232
      359.378901

      22
      GOOG210618P00960000
      2020-11-23 10:40AM EST
      960.0
      4.80
      2.70
      3.60
      0.00
      -
      6
      71
      43.75%
      2.801297
      71.349217

      23
      GOOG210618P00980000
      2020-11-04 9:59AM EST
      980.0
      5.75
      3.00
      4.00
      0.00
      -
      2
      130
      43.22%
      3.345760
      71.859325

      24
      GOOG210618P01000000
      2020-12-02 3:20PM EST
      1000.0
      3.81
      3.40
      4.40
      -1.94
      -33.74%
      1
      586
      42.63%
      3.969864
      -4.026941

      25
      GOOG210618P01020000
      2020-10-28 8:49AM EST
      1020.0
      19.70
      4.50
      5.90
      0.00
      -
      1
      26
      43.55%
      4.681093
      320.841889

      26
      GOOG210618P01040000
      2020-11-05 12:35PM EST
      1040.0
      10.37
      4.40
      5.40
      0.00
      -
      1
      53
      41.61%
      5.487132
      88.987604

      27
      GOOG210618P01060000
      2020-10-20 11:13AM EST
      1060.0
      21.40
      6.60
      9.30
      0.00
      -
      21
      40
      44.75%
      6.395833
      234.592852

      28
      GOOG210618P01080000
      2020-11-04 10:05AM EST
      1080.0
      12.40
      5.50
      6.50
      0.00
      -
      1
      385
      40.52%
      7.415170
      67.224751

      29
      GOOG210618P01100000
      2020-11-30 11:38AM EST
      1100.0
      8.30
      6.10
      7.20
      0.00
      -
      1
      300
      40.06%
      8.553204
      -2.960340

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      107
      GOOG210618P01590000
      2020-11-24 2:36PM EST
      1590.0
      82.00
      65.10
      68.40
      0.00
      -
      1
      4
      32.00%
      93.459739
      -12.261685

      108
      GOOG210618P01595000
      2020-12-01 10:00AM EST
      1595.0
      78.00
      66.30
      69.80
      0.00
      -
      2
      2
      31.95%
      95.035157
      -17.925110

      109
      GOOG210618P01600000
      2020-12-02 12:04PM EST
      1600.0
      67.73
      67.70
      71.30
      -4.47
      -6.19%
      1
      239
      31.92%
      96.626534
      -29.905382

      110
      GOOG210618P01605000
      2020-11-24 10:28AM EST
      1605.0
      96.30
      69.10
      72.70
      0.00
      -
      1
      6
      31.86%
      98.233881
      -1.968650

      111
      GOOG210618P01610000
      2020-11-20 10:18AM EST
      1610.0
      93.60
      70.50
      74.20
      0.00
      -
      1
      3
      31.83%
      99.857210
      -6.266157

      112
      GOOG210618P01615000
      2020-11-24 2:36PM EST
      1615.0
      90.40
      72.00
      75.70
      0.00
      -
      1
      28
      31.78%
      101.496528
      -10.932914

      113
      GOOG210618P01620000
      2020-12-02 3:49PM EST
      1620.0
      76.20
      73.50
      77.20
      -16.00
      -17.35%
      3
      2
      31.73%
      103.151844
      -26.128320

      114
      GOOG210618P01625000
      2020-11-18 11:18AM EST
      1625.0
      99.00
      75.00
      78.70
      0.00
      -
      2
      8
      31.68%
      104.823164
      -5.555226

      115
      GOOG210618P01630000
      2020-11-20 10:18AM EST
      1630.0
      100.60
      76.50
      80.20
      0.00
      -
      1
      1
      31.62%
      106.510492
      -5.549211

      116
      GOOG210618P01635000
      2020-11-17 11:18AM EST
      1635.0
      98.40
      78.00
      81.80
      0.00
      -
      1
      2
      31.58%
      108.213832
      -9.068925

      117
      GOOG210618P01640000
      2020-12-02 3:49PM EST
      1640.0
      80.90
      79.60
      83.40
      -23.50
      -22.51%
      1
      33
      31.54%
      109.933186
      -26.409847

      118
      GOOG210618P01645000
      2020-11-16 1:01PM EST
      1645.0
      103.40
      81.10
      85.00
      0.00
      -
      1
      3
      31.49%
      111.668555
      -7.404551

      119
      GOOG210618P01650000
      2020-10-21 12:32PM EST
      1650.0
      205.20
      106.70
      118.70
      0.00
      -
      -
      2
      38.31%
      113.419938
      80.920571

      121
      GOOG210618P01660000
      2020-11-19 9:47AM EST
      1660.0
      118.40
      86.00
      90.00
      0.00
      -
      2
      7
      31.36%
      116.970740
      1.221895

      122
      GOOG210618P01665000
      2020-11-20 3:32PM EST
      1665.0
      116.90
      87.70
      91.70
      0.00
      -
      2
      8
      31.31%
      118.770151
      -1.574597

      123
      GOOG210618P01670000
      2020-12-01 12:25PM EST
      1670.0
      93.59
      89.40
      93.50
      0.00
      -
      1
      17
      31.28%
      120.585561
      -22.387059

      124
      GOOG210618P01675000
      2020-11-24 2:36PM EST
      1675.0
      112.90
      91.00
      95.20
      0.00
      -
      1
      7
      31.22%
      122.416964
      -7.774220

      125
      GOOG210618P01680000
      2020-12-02 11:51AM EST
      1680.0
      94.00
      92.70
      97.00
      -6.99
      -6.92%
      1
      17
      31.18%
      124.264352
      -24.354814

      126
      GOOG210618P01700000
      2020-12-02 3:03PM EST
      1700.0
      100.10
      100.30
      104.40
      -8.00
      -7.40%
      11
      54
      31.02%
      131.813543
      -24.059397

      127
      GOOG210618P01720000
      2020-11-30 10:51AM EST
      1720.0
      132.70
      107.70
      112.10
      0.00
      -
      2
      39
      30.84%
      139.617528
      -4.954627

      128
      GOOG210618P01740000
      2020-12-02 3:14PM EST
      1740.0
      115.91
      115.60
      120.30
      -27.29
      -19.06%
      3
      67
      30.69%
      147.675221
      -21.510190

      129
      GOOG210618P01760000
      2020-12-02 2:06PM EST
      1760.0
      125.21
      124.00
      128.80
      -23.89
      -16.02%
      2
      88
      30.52%
      155.985258
      -19.729594

      131
      GOOG210618P01800000
      2020-12-02 3:58PM EST
      1800.0
      144.70
      142.00
      147.20
      -3.80
      -2.56%
      5
      25
      30.24%
      173.355582
      -16.529945

      132
      GOOG210618P01820000
      2020-12-01 3:19PM EST
      1820.0
      166.50
      151.60
      156.90
      0.00
      -
      2
      22
      30.08%
      182.411860
      -8.723040

      133
      GOOG210618P01840000
      2020-11-05 10:32AM EST
      1840.0
      210.60
      161.40
      167.00
      0.00
      -
      3
      4
      29.93%
      191.712495
      9.851995

      134
      GOOG210618P01860000
      2020-11-16 12:56PM EST
      1860.0
      205.00
      171.90
      177.60
      0.00
      -
      4
      4
      29.80%
      201.254934
      1.860857

      135
      GOOG210618P01900000
      2020-10-12 8:47AM EST
      1900.0
      407.40
      215.50
      221.10
      0.00
      -
      1
      2
      33.48%
      221.054049
      84.298818

      136
      GOOG210618P02000000
      2020-10-30 2:57PM EST
      2000.0
      436.00
      275.90
      282.60
      0.00
      -
      1
      1
      32.82%
      274.570866
      58.793249

      137
      GOOG210618P02100000
      2020-06-24 2:02PM EST
      2100.0
      673.60
      602.00
      611.10
      0.00
      -
      2
      2
      80.63%
      333.472840
      101.995461

      138
      GOOG210618P02200000
      2020-11-06 2:29PM EST
      2200.0
      469.60
      405.70
      413.70
      0.00
      -
      1
      2
      28.06%
      397.271434
      18.206334

136 rows × 13 columns

```python
# how accurate are our vol estimates?

df_estimate['Implied Volatility'] = df_estimate['Implied Volatility'].str.replace(',','').str.slice(stop=-1).astype('float') / 100

df_estimate['Historical Vol'] = sigma
ax = df_estimate.plot(x='Strike',y=['Implied Volatility','Historical Vol'],title='volatility across strikes')
```

```python
df_estimate['Volume'] = df_estimate['Volume'].replace(to_replace='-',value=0)
df_estimate['Volume'] = df_estimate['Volume'].astype('int')

df_estimate['Open Interest'] = df_estimate['Open Interest'].replace(to_replace='-',value=0)
df_estimate['Open Interest'] = df_estimate['Open Interest'].astype('int')

df_estimate.plot(x='Strike',y=['Volume','Open Interest'],title='volume and open interest')
```

```python
sns.distplot(df_estimate['Open Interest'])
print(df_estimate['Open Interest'].describe())
```

```python

```

```python

```

```python
# filter out low volume options

# lower_bound, upper_bound = cur_stock_price * .5, cur_stock_price * 1.5
# price_estimate_filtered = df_estimate[(df_estimate['Strike'] > lower_bound) & (df_estimate['Strike'] < upper_bound)].reset_index()

price_estimate_filtered = df_estimate[(df_estimate['Open Interest'] > 15)].reset_index()
ax = sns.distplot(price_estimate_filtered['estimate_error'])
print(price_estimate_filtered['estimate_error'].describe())

price_estimate_filtered.plot(x='Strike',y=['Last Price','estimate'], title='Option prices for {}, Expiry {} (Filtered Strikes)'.format(ticker, expiration_datetime.date()))

```

```python

```

```python
# if we use implied vol instead of historical vol, our estimate should improve

def calculate_prices_across_strikes(options_df, cur_stock_price, time_to_expiration, risk_free_rate, sigma, option_type):

    print('cur_stock_price: {}, time to expiration: {}, rfr: {}, vol: {}'.format(cur_stock_price, time_to_expiration, risk_free_rate, sigma))
    list_estimates = []
    estimates_with_implied = []

    strike_start_idx, strike_end_idx = 0, options_df.shape[0]

    # run BSM for different strikes
    for x in range(strike_start_idx,strike_end_idx):
#         print(options_df['Strike'][x])
        value_s = black_scholes(S = cur_stock_price,
                                K = options_df['Strike'][x],
                                T = time_to_expiration,
                                r = risk_free_rate,
                                sigma = sigma,
                                option_type = option_type)
        list_estimates.append(value_s)

    for x in range(strike_start_idx,strike_end_idx):
#         print(options_df['Strike'][x])
        value_s = black_scholes(S = cur_stock_price,
                                K = options_df['Strike'][x],
                                T = time_to_expiration,
                                r = risk_free_rate,
                                sigma = options_df['Implied Volatility'][x],
                                option_type = option_type)
        estimates_with_implied.append(value_s)

    # merge the real and computed dataframes to compare results
    df_estimate = options_df[['Strike','Last Price']]
    df_estimate['estimate'] = list_estimates
    df_estimate['estimate_with_implied'] = estimates_with_implied

    return df_estimate

df_estimate_european = calculate_prices_across_strikes(price_estimate_filtered, cur_stock_price, time_to_expiration, risk_free_rate, sigma, option_type)
df_estimate_european.plot(x='Strike',y=['Last Price','estimate','estimate_with_implied'], title='Option Prices for {}, Expiry {}, with Implied Vol'.format(ticker, expiration_datetime.date()))
```

```python

df_estimate_european['estimate_with_implied_error'] = ((df_estimate_european['Last Price'] - df_estimate_european['estimate_with_implied'])/df_estimate_european['estimate_with_implied'])*100
ax = sns.distplot(df_estimate_european['estimate_with_implied_error'])
print(df_estimate_european['estimate_with_implied_error'].describe())
df_estimate_european.plot(x='Strike',y='estimate_with_implied_error',title='Estimate Percent Error Across Strikes (Filtered Strikes)')

mse = np.sum((df_estimate_european['Last Price'] - df_estimate_european['estimate_with_implied'])**2) / df_estimate_european.shape[0]
rmse = np.sqrt(mse)

print(mse, rmse)
```

```python

```

```python
# American options

from functions.BS_pricer import BS_pricer
from functions.Parameters import Option_param
from functions.Processes import Diffusion_process
from functions.cython.cython_functions import PSOR

import numpy as np
import scipy.stats as ss

import matplotlib.pyplot as plt
%matplotlib inline
from IPython.display import display
import sympy; sympy.init_printing()

def display_matrix(m):
    display(sympy.Matrix(m))

```

```python
# Longstaff Schwartz Method

def price_american(S0, K, T, r, sigma, payoff='call'):

    # Creates the object with the parameters of the option
    opt_param = Option_param(S0=S0, K=K, T=T, exercise="American", payoff=payoff )
    # Creates the object with the parameters of the process
    diff_param = Diffusion_process(r=r, sig=sigma)
    # Creates the pricer object
    BS = BS_pricer(opt_param, diff_param)

#     return BS.LSM(N=1000, paths=1000, order=2)  # Longstaff Schwartz Method
    N_space = 8000
    N_time = 5000
    return BS.PDE_price((N_space,N_time))

# def plot_american(BS, x_start, x_end, y_start, y_end):

#     fig = plt.figure(figsize=(9,5))
#     BS.plot([x_start,x_end,y_start,y_end])
```

```python
# price_american(S0=100, K=100, T=1, r=0.1, sigma=0.2, payoff="put" )
# 1827.949951 1110.0 0.7817460317460317 0.08 0.38212781123440914 put
price_american(S0=1827.949951, K=1110.0, T=0.7817460317460317, r=0.08, sigma=0.38212781123440914, payoff="put" )

```

```python
%%time

# if we use implied vol instead of historical vol, our estimate should improve

def calculate_prices_across_strikes_american(options_df, cur_stock_price, time_to_expiration, risk_free_rate, sigma, option_type):

    print('cur_stock_price: {}, time to expiration: {}, rfr: {}, vol: {}'.format(cur_stock_price, time_to_expiration, risk_free_rate, sigma))
    index = []
    list_estimates = []
#     estimates_with_implied = []

    strike_start_idx, strike_end_idx = 0, options_df.shape[0]
    step = 10

    # run BSM for different strikes
    for x in range(strike_start_idx,strike_end_idx, step):
        index.append(x)
        print('strike', options_df['Strike'][x])
        value_s = price_american(S0 = cur_stock_price,
                                K = options_df['Strike'][x],
                                T = time_to_expiration,
                                r = risk_free_rate,
                                sigma = sigma,
                                payoff = option_type)
        list_estimates.append(value_s)

#         print(cur_stock_price, options_df['Strike'][x], time_to_expiration, risk_free_rate, options_df['Implied Volatility'][x], option_type)
#         value_s = price_american(S0 = cur_stock_price,
#                                 K = options_df['Strike'][x],
#                                 T = time_to_expiration,
#                                 r = risk_free_rate,
#                                 sigma = options_df['Implied Volatility'][x],
#                                 payoff = option_type)
#         estimates_with_implied.append(value_s)

    # merge the real and computed dataframes to compare results
    price_estimates = pd.Series(data=list_estimates, index=index)
    print(price_estimates)
#     df_estimate['estimate_with_implied'] = estimates_with_implied
    df_estimate = options_df[['Strike','Last Price']]
    df_estimate['estimate'] = price_estimates

    return df_estimate

df_estimate_american = calculate_prices_across_strikes_american(price_estimate_filtered, cur_stock_price, time_to_expiration, risk_free_rate, sigma, option_type)
# df_estimate.plot(x='Strike',y=['Last Price','estimate','estimate_with_implied'], title='Option Prices for {}, Expiry {}, with Implied Vol'.format(ticker, expiration_datetime.date()))
# df_estimate.plot(x='Strike',y=['Last Price','estimate'], title='Option Prices for {}, Expiry {}, with Implied Vol'.format(ticker, expiration_datetime.date()))

ax1 = df_estimate_american.plot(kind='scatter', x='Strike',y=['Last Price'], title='Option Prices for {}, Expiry {}, with Implied Vol'.format(ticker, expiration_datetime.date()))
df_estimate_american.plot(kind='scatter', x='Strike',y=['estimate'], ax=ax1, color='r')

```

```python
# compare with european

df_estimate_american['european_estimate'] = df_estimate_european['estimate']
df_estimate_american['european_estimate_with_implied'] = df_estimate_european['estimate_with_implied']

ax1 = df_estimate_american.plot(kind='scatter', x='Strike',y=['Last Price'], label='Actual Price')
df_estimate_american.plot(kind='scatter', x='Strike',y=['estimate'], ax=ax1, color='r', label='PDE solver')
df_estimate_american.plot(kind='scatter', x='Strike',y=['european_estimate'], ax=ax1, color='g', label='European')
# df_estimate.plot(kind='scatter', x='Strike',y=['estimate'], ax=ax1, color='r')
ax1.set_title('Option Prices for {}, Expiry {}, Collected {}'.format(ticker, expiration_datetime.date(), datetime.now().date()))
ax1.legend()
```

```python
datetime.now().date()

```

    datetime.date(2020, 12, 3)

```python
# how accurate is BSM for different expiration dates?

# import os

# data_path = "/home/amao/options_data/2020-11-19 17:00:01.568118"

# ticker = 'TSLA'
# calls_data, puts_data = dict(), dict()

# for filename in os.listdir(data_path):
#     if ticker in filename:
#         df = pd.read_csv(os.path.join(data_path, filename))
#         if 'puts' in filename :
#             puts_data[filename] = df
#         elif 'calls' in filename:
#             calls_data[filename] = df

# print(len(calls_data), len(puts_data))
```

```python

```

```python
# stock_price_df = df

# for k,v in calls_data.items():
#     print(k, v.head())
#     bsm_estimate_df = calculate_prices_across_strikes(v, cur_stock_price, time_to_expiration, risk_free_rate, sigma):

```

```python
# binomial tree
import math

sigma = .2
rfr = .02
delta_t = 1

u = math.exp(sigma * math.sqrt(delta_t))
d = math.exp(-sigma * math.sqrt(delta_t))
print('u and d:',u,d)

# a = math.exp(rfr * delta_t)
a = math.exp((rfr - .01) * delta_t)
p = (a - d)/(u - d)

print('p', p)

```

    u and d: 1.2214027581601699 0.8187307530779818
    p 0.47512469600943963

```python
# N period binomial tree model

def price_option_european(S_0, u, d, K, option_type: str, num_periods: int):

    if num_periods == 0:
        if option_type == 'call':
            return max(S_0-K, 0)
        elif option_type == 'put':
            return max(K-S_0, 0)
    else:
        S_u = S_0 * u
        S_d = S_0 * d

        f_u = price_option_european(S_0 * u,u,d,K,option_type,num_periods-1)
        f_d = price_option_european(S_0 * d,u,d,K,option_type,num_periods-1)

#         print('f_u and f_d:', f_u, f_d)

        return math.exp(-rfr*delta_t) * (p*f_u + (1-p)*f_d)

S_0 = 200
K = 200
option_type = 'put'

for i in range(5):
    price = price_option_european(S_0, u, d, K, option_type, num_periods=i)
    print('price',price)

```

    price 0
    price 18.65195571686643
    price 17.452716560268637
    price 25.695678025946652
    price 24.44307753439426

```python
def price_option_american(S_0, u, d, K, option_type: str, num_periods: int):

    if num_periods == 0:
        if option_type == 'call':
            return max(S_0-K, 0)
        elif option_type == 'put':
            return max(K-S_0, 0)
    else:
        S_u = S_0 * u
        S_d = S_0 * d

        f_u = price_option_american(S_0 * u,u,d,K,option_type,num_periods-1)
        f_d = price_option_american(S_0 * d,u,d,K,option_type,num_periods-1)

        if option_type == 'call':
            f_u_early = max(S_u-K, 0)
            f_d_early = max(S_d-K, 0)
        elif option_type == 'put':
            f_u_early = max(K-S_u, 0)
            f_d_early = max(K-S_d, 0)
#         print('f_u,f_d,f_u_early,f_d_early:',f_u,f_d,f_u_early,f_d_early)
        f_u = max(f_u,f_u_early)
        f_d = max(f_u,f_d_early)

#         print('f_u and f_d:', f_u, f_d)

        return math.exp(-rfr*delta_t) * (p*f_u + (1-p)*f_d)

S_0 = 200
K = 200
option_type = 'put'

for i in range(5):
    price = price_option_american(S_0, u, d, K, option_type, num_periods=i)
    print('price',price)
```

    price 0
    price 18.65195571686643
    price 18.65195571686643
    price 18.65195571686643
    price 18.65195571686643

```python

```

```python

```

```python

```