Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
{
"cells": [
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import QuantLib as ql \n",
"#QuantLib-Python 1.11\n",
"import csv\n",
"import datetime\n",
"from collections import OrderedDict\n",
"from typing import Dict, List\n",
"import numpy as np\n",
"#numpy 1.11.3\n",
"import pandas as pd\n",
"#pandas 0.18.1 \n",
"from timeit import default_timer as timer\n",
"#Python 3.5.2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Load the Data\n",
"Quandl or the Fed itself are the places to find US interest rate data. Relevant Quandl URL set out below. As for stock data you can still get it from yahoo if you use a Pandas data reader \"fix\" as per the pipi reference below. Failing which simply download manually from Yahoo. Alphavantage is an interesting new free datasource for stocks - unfortunately as far as the S&P 500 is concerned I was only able to get prices back to 2000 from that source."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Date</th>\n",
" <th>TBill</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>15939</th>\n",
" <td>2017-10-17</td>\n",
" <td>0.0107</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Date TBill\n",
"15939 2017-10-17 0.0107"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#https://www.quandl.com/data/FRED/DTB3-3-Month-Treasury-Bill-Secondary-Market-Rate\n",
"tbill = pd.read_csv('..\\\\data\\\\Stocks\\\\FRED_DTB3.csv',parse_dates=[\"Date\"])\n",
"tbill.Value = tbill.Value/100\n",
"tbill = tbill.rename(columns={'Value': 'TBill'})\n",
"tbill.tail(1)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Date</th>\n",
" <th>Open</th>\n",
" <th>High</th>\n",
" <th>Low</th>\n",
" <th>Close</th>\n",
" <th>Adj Close</th>\n",
" <th>Volume</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1985-10-30</td>\n",
" <td>189.229996</td>\n",
" <td>190.089996</td>\n",
" <td>189.139999</td>\n",
" <td>190.070007</td>\n",
" <td>190.070007</td>\n",
" <td>120400000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Date Open High Low Close Adj Close \\\n",
"0 1985-10-30 189.229996 190.089996 189.139999 190.070007 190.070007 \n",
"\n",
" Volume \n",
"0 120400000 "
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#https://finance.yahoo.com/quote/%5EGSPC/history?p=%5EGSPC\n",
"#https://www.alphavantage.co/\n",
"#https://pypi.python.org/pypi/fix-yahoo-finance/0.0.4\n",
"SPX = pd.read_csv('..\\\\data\\\\Stocks\\\\SPX.csv',parse_dates=[\"Date\"])\n",
"SPX.head(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Drop obsolete columns and calculate the daily returns\n",
"I only use the open and closing prices from which I calculate the daily return necessary to calculate volatility"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Date</th>\n",
" <th>Open</th>\n",
" <th>Close</th>\n",
" <th>Return</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>8066</th>\n",
" <td>2017-10-27</td>\n",
" <td>2570.26001</td>\n",
" <td>2581.070068</td>\n",
" <td>0.008073</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Date Open Close Return\n",
"8066 2017-10-27 2570.26001 2581.070068 0.008073"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cols=[2,3,5,6]\n",
"SPX.drop(SPX.columns[cols], axis=1, inplace=True)\n",
"SPX['Return'] =SPX['Close'].pct_change()\n",
"SPX=SPX.fillna(method = 'backfill')\n",
"SPX.tail(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Combine tBill and Stock data into one Dataframe"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Date</th>\n",
" <th>Open</th>\n",
" <th>Close</th>\n",
" <th>Return</th>\n",
" <th>TBill</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>8066</th>\n",
" <td>2017-10-27</td>\n",
" <td>2570.26001</td>\n",
" <td>2581.070068</td>\n",
" <td>0.008073</td>\n",
" <td>0.0107</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Date Open Close Return TBill\n",
"8066 2017-10-27 2570.26001 2581.070068 0.008073 0.0107"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"combineddata = SPX.merge(tbill, how='left', left_on='Date', right_on='Date').fillna( method='ffill')\n",
"combineddata.tail(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Setting Strike Prices\n",
"My Data ends in 2017. I want to make sure Options can be calulated 24 months out (through to end 2019). For the process of calculated strike prices I need to ensure I include \"forecast\" index levels for 2018, 2019 and 2020"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Close</th>\n",
" </tr>\n",
" <tr>\n",
" <th>Date</th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2018</th>\n",
" <td>2581.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019</th>\n",
" <td>2581.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2020</th>\n",
" <td>2581.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Close\n",
"Date \n",
"2018 2581.0\n",
"2019 2581.0\n",
"2020 2581.0"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#forecast index levels for 2018,2019 ad 2020\n",
"future_calc = OrderedDict([ ('Date', [2018, 2019, 2020]), ('Close', [2581.0, 2581.0, 2581.0])]) \n",
"future_calc = pd.DataFrame.from_dict(future_calc)\n",
"future_calc.set_index(keys='Date', drop=True, append=False, inplace=True, verify_integrity=False)\n",
"future_calc"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Record each year's index high\n",
"I base strike prices for a given maturity on the index high for the relevant year they start trading.\n",
"So I need to extract each year's high"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Close</th>\n",
" </tr>\n",
" <tr>\n",
" <th>Date</th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2016</th>\n",
" <td>2271.719971</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017</th>\n",
" <td>2581.070068</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2018</th>\n",
" <td>2581.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019</th>\n",
" <td>2581.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2020</th>\n",
" <td>2581.000000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Close\n",
"Date \n",
"2016 2271.719971\n",
"2017 2581.070068\n",
"2018 2581.000000\n",
"2019 2581.000000\n",
"2020 2581.000000"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#record historic index highs for each year on my data\n",
"strike_start = 1985\n",
"strike_end = 2018\n",
"strike_calc={}\n",
"a=0 \n",
"#take the historic high of the closing price for each year\n",
"for i in range(strike_start,strike_end): \n",
" strike_calc[a]=(i,combineddata['Close'][combineddata.Date.dt.year ==i].max() ) \n",
" a=a+1\n",
"strike_roll = pd.DataFrame.from_dict(strike_calc, orient='index', dtype=None)\n",
"strike_roll.columns = ['Date', 'Close']\n",
"strike_roll.set_index(keys='Date', drop=True, append=False, inplace=True, verify_integrity=False)\n",
"\n",
"#add in my forecast index levels for 2018,2019 and 2020\n",
"strike_roll = pd.concat([strike_roll,future_calc])\n",
"strike_roll.tail()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Strike Prices\n",
"You may well wish to achieve finer granularity around the index level on a given day for ATM options, especially for those with short maturities. Adjust \"strike_percentage\" accordingly. Here is the code used later on to calculate the strike prices from the then current index level: \"strike = round(strike_percentage*strike_roll.ix[x.year,'Close'])\""
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(67, 1)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"strike_percentage = [0.05,0.1,0.15,0.2,0.25,0.3,0.32,0.34,0.36,0.38,0.4,0.42,0.44,0.46,0.48,0.5,\n",
" 0.52,0.54,0.56,0.58,0.6,0.62,0.64,0.66,0.68,0.7,0.72,0.74,\n",
" 0.76,0.78,0.8,0.82,0.84,0.86,0.88,0.9,0.92,0.94,0.96,0.98,1,\n",
" 1.02,1.04,1.06,1.08,1.1,1.12,1.14,1.16,1.18,1.2,1.22,1.24,\n",
" 1.26,1.28,1.3,1.32,1.34,1.36,1.38,1.4,1.50,1.60,1.70,1.80,\n",
" 1.90, 2.0]\n",
"strike_percentage = pd.DataFrame({'Strike':strike_percentage})\n",
"strike_percentage.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Dividend Data\n",
"Record historic SPX dividend data (percentage) and add forecasts for 2018, 2019 and 2020. These will be used in the Black Scoles model to calculate option prices"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,\n",
" 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,\n",
" 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,\n",
" 2018, 2019, 2020])"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"divs = [0.04,0.0333,0.0276,0.0345,0.0314,0.0325,0.03,0.0289,0.0268,0.027498879,0.023359532,\n",
" 0.020047344,0.01547191,0.012328052,0.010463432,0.010241579,0.011514514,0.015464024,\n",
" 0.016593709,0.019454529,0.017992297,0.01910639,0.017776754,0.021176745,0.023578469,\n",
" 0.022055675,0.019228185,0.022724277,0.020750464,0.019436528,0.020244296,0.021584811,\n",
" 0.0195,0.0195,0.0195,0.0195]\n",
"div_year = pd.date_range(start = '1985', periods=36, freq = 'A').year\n",
"div_year"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Combine Dividend data with relevant year\n",
"into a dataframe for later use"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Dividends</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2016</th>\n",
" <td>0.021585</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017</th>\n",
" <td>0.019500</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2018</th>\n",
" <td>0.019500</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019</th>\n",
" <td>0.019500</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2020</th>\n",
" <td>0.019500</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Dividends\n",
"2016 0.021585\n",
"2017 0.019500\n",
"2018 0.019500\n",
"2019 0.019500\n",
"2020 0.019500"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"div =pd.DataFrame({'Dividends':divs},index=div_year)\n",
"div.tail()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Cut down the data to a specific year\n",
"You can of course calculate options for the entire price series in one go but it take a whie and produces a very large csv file!"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Date</th>\n",
" <th>Open</th>\n",
" <th>Close</th>\n",
" <th>Return</th>\n",
" <th>TBill</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>8062</th>\n",
" <td>2017-10-23</td>\n",
" <td>2578.080078</td>\n",
" <td>2564.979980</td>\n",
" <td>-0.003972</td>\n",
" <td>0.0107</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8063</th>\n",
" <td>2017-10-24</td>\n",
" <td>2568.659912</td>\n",
" <td>2569.129883</td>\n",
" <td>0.001618</td>\n",
" <td>0.0107</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8064</th>\n",
" <td>2017-10-25</td>\n",
" <td>2566.520020</td>\n",
" <td>2557.149902</td>\n",
" <td>-0.004663</td>\n",
" <td>0.0107</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8065</th>\n",
" <td>2017-10-26</td>\n",
" <td>2560.080078</td>\n",
" <td>2560.399902</td>\n",
" <td>0.001271</td>\n",
" <td>0.0107</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8066</th>\n",
" <td>2017-10-27</td>\n",
" <td>2570.260010</td>\n",
" <td>2581.070068</td>\n",
" <td>0.008073</td>\n",
" <td>0.0107</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Date Open Close Return TBill\n",
"8062 2017-10-23 2578.080078 2564.979980 -0.003972 0.0107\n",
"8063 2017-10-24 2568.659912 2569.129883 0.001618 0.0107\n",
"8064 2017-10-25 2566.520020 2557.149902 -0.004663 0.0107\n",
"8065 2017-10-26 2560.080078 2560.399902 0.001271 0.0107\n",
"8066 2017-10-27 2570.260010 2581.070068 0.008073 0.0107"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"start = 2017\n",
"end = 2018\n",
"test = combineddata[(combineddata.Date.dt.year >=start) & \n",
" (combineddata.Date.dt.year <=end)].loc[:,['Date','Open','Close','Return','TBill']]\n",
"test.tail()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Writing a dataframe for the trade dates and expiries\n",
"For each trading day, up to 24 expiries are listed. \n",
"\n",
"Cut this right down if you want to look at shorter term options only: \n",
"\"(if x >=pd.Timestamp(row.Date) and x<= pd.Timestamp(row.Date) + pd.DateOffset(months=24))\"\n",
"\n",
"For each expiry on each day I calculate a differnt volatility for use in the Black Scholes formula later on. If the days to expiry is 5 I have used 5 days for the standard deviation calculation, if 252 days until expiry then historic volatility is calulated for 252 days.\n",
"\n",
"In otherwords differing periods of \"historic\" volatility are used to calculate price. It does not make sense (to me at least) to use 20 day volatility to estimate future volatility over the next 700 days. This still takes no account of the fact that implied volatility is often higher than historic and therefore the option prices calculated here are likley to be too low. I may attempt to estimate the premium of implied volatility over historic in a later draft. \n",
"\n",
"Note also that no account is taken of the usual \"volatility smile\" prevalent in the US equity markets post 1987. In other words I have asumed the same volatility for all strikes. In practice, OTM and ITM strikes tend to have a higher implied volatility hence higher prices than shown here. Possibly because of the realisation after Black Monday 1987 that fat tails need to be priced in."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"48.64829184252221 335871\n"
]
}
],
"source": [
"starttimer = timer()\n",
"data_dict = {}\n",
"expiries_list_dict={}\n",
"calendar = ql.UnitedStates(ql.UnitedStates.NYSE)\n",
"a=0\n",
"b=0\n",
"c=0\n",
"d=0\n",
"x=0\n",
"us_busdays=0\n",
"\n",
"for h,row in enumerate(test.itertuples(),0): \n",
" for i in range(start, end+2): \n",
" for j in range(1,13): \n",
" x=pd.Timestamp(str(calendar.adjust(ql.Date.nthWeekday(3,6,j,i),ql.Preceding )))\n",
" \n",
" if x >=pd.Timestamp(row.Date) and x<= pd.Timestamp(row.Date) + pd.DateOffset(months=24): \n",
" strike = round(strike_percentage*strike_roll.ix[x.year,'Close'])\n",
" us_busdays = calendar.businessDaysBetween(ql.Date(row.Date.day, row.Date.month, \n",
" row.Date.year),ql.Date(x.day, x.month, x.year) )\n",
" b = test.iloc[[h]].index.values\n",
" c=b[0]-us_busdays\n",
" vol=SPX.Return.loc[c:b[0]].std()*np.sqrt(252)\n",
" expiries_list_dict[d]=(row.Date,x)\n",
" d=d+1\n",
" for k, line in enumerate(strike.itertuples(),0): \n",
" data_dict[a]=(row.Date,x, row.Open, row.Close, row.TBill,div.ix[row.Date.year,'Dividends'],vol,line.Strike)\n",
" a=a+1\n",
"endtimer = timer()\n",
"print(endtimer - starttimer,a) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The expiries list\n",
"Simply so you can see what expiries have been listed for each trading day. A smaller file that data file which inclused strikes."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>0</th>\n",
" <th>1</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>5008</th>\n",
" <td>2017-10-27</td>\n",
" <td>2019-06-21</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5009</th>\n",
" <td>2017-10-27</td>\n",
" <td>2019-07-19</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5010</th>\n",
" <td>2017-10-27</td>\n",
" <td>2019-08-16</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5011</th>\n",
" <td>2017-10-27</td>\n",
" <td>2019-09-20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5012</th>\n",
" <td>2017-10-27</td>\n",
" <td>2019-10-18</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" 0 1\n",
"5008 2017-10-27 2019-06-21\n",
"5009 2017-10-27 2019-07-19\n",
"5010 2017-10-27 2019-08-16\n",
"5011 2017-10-27 2019-09-20\n",
"5012 2017-10-27 2019-10-18"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"expiries_list =pd.DataFrame.from_dict(data = expiries_list_dict ,orient ='index') \n",
"expiries_list.to_csv('expiries_list.csv')\n",
"expiries_list.tail()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Produce the complete dataframe for Black Scoles Calculations"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Date</th>\n",
" <th>Expiration</th>\n",
" <th>Open</th>\n",
" <th>Close</th>\n",
" <th>TBill</th>\n",
" <th>Dividends</th>\n",
" <th>Volatility</th>\n",
" <th>Strike</th>\n",
" <th>Call</th>\n",
" <th>Put</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2017-01-03</td>\n",
" <td>2017-01-20</td>\n",
" <td>2251.570068</td>\n",
" <td>2257.830078</td>\n",
" <td>0.0053</td>\n",
" <td>0.0195</td>\n",
" <td>0.076404</td>\n",
" <td>129.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2017-01-03</td>\n",
" <td>2017-01-20</td>\n",
" <td>2251.570068</td>\n",
" <td>2257.830078</td>\n",
" <td>0.0053</td>\n",
" <td>0.0195</td>\n",
" <td>0.076404</td>\n",
" <td>258.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2017-01-03</td>\n",
" <td>2017-01-20</td>\n",
" <td>2251.570068</td>\n",
" <td>2257.830078</td>\n",
" <td>0.0053</td>\n",
" <td>0.0195</td>\n",
" <td>0.076404</td>\n",
" <td>387.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2017-01-03</td>\n",
" <td>2017-01-20</td>\n",
" <td>2251.570068</td>\n",
" <td>2257.830078</td>\n",
" <td>0.0053</td>\n",
" <td>0.0195</td>\n",
" <td>0.076404</td>\n",
" <td>516.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2017-01-03</td>\n",
" <td>2017-01-20</td>\n",
" <td>2251.570068</td>\n",
" <td>2257.830078</td>\n",
" <td>0.0053</td>\n",
" <td>0.0195</td>\n",
" <td>0.076404</td>\n",
" <td>645.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Date Expiration Open Close TBill Dividends \\\n",
"0 2017-01-03 2017-01-20 2251.570068 2257.830078 0.0053 0.0195 \n",
"1 2017-01-03 2017-01-20 2251.570068 2257.830078 0.0053 0.0195 \n",
"2 2017-01-03 2017-01-20 2251.570068 2257.830078 0.0053 0.0195 \n",
"3 2017-01-03 2017-01-20 2251.570068 2257.830078 0.0053 0.0195 \n",
"4 2017-01-03 2017-01-20 2251.570068 2257.830078 0.0053 0.0195 \n",
"\n",
" Volatility Strike Call Put \n",
"0 0.076404 129.0 0.0 0.0 \n",
"1 0.076404 258.0 0.0 0.0 \n",
"2 0.076404 387.0 0.0 0.0 \n",
"3 0.076404 516.0 0.0 0.0 \n",
"4 0.076404 645.0 0.0 0.0 "
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tmp_data = pd.DataFrame.from_dict(data = data_dict,orient ='index') \n",
"tmp_data.rename(columns={0: 'Date',1:'Expiration', 2:'Open',3:'Close',4:'TBill',5:'Dividends',6: 'Volatility',7:'Strike'},inplace=True)\n",
"tmp_data['Call']=np.zeros(len(tmp_data))\n",
"tmp_data['Put']=np.zeros(len(tmp_data))\n",
"\n",
"tmp_data.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Option Price Calculations\n",
"Probably at some stage I should think about vectorisation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Calculate Calls"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"129.2285610853312\n"
]
}
],
"source": [
"start = timer()\n",
"\n",
"option_type = ql.Option.Call\n",
"day_count = ql.Actual365Fixed()\n",
"calendar = ql.UnitedStates()\n",
"tmp_price={}\n",
"for h,row in enumerate(tmp_data.itertuples(),0): \n",
" maturity_date = ql.Date(row.Expiration.day, row.Expiration.month, row.Expiration.year)\n",
" spot_price = row.Close\n",
" strike_price = row.Strike\n",
" volatility = row.Volatility\n",
" dividend_rate = row.Dividends\n",
" risk_free_rate = row.TBill\n",
" calculation_date = ql.Date(row.Date.day, row.Date.month, row.Date.year)\n",
" ql.Settings.instance().evaluationDate = calculation_date\n",
" payoff = ql.PlainVanillaPayoff(option_type, strike_price)\n",
" exercise = ql.EuropeanExercise(maturity_date)\n",
" european_option = ql.VanillaOption(payoff, exercise)\n",
" spot_handle = ql.QuoteHandle(ql.SimpleQuote(spot_price))\n",
" flat_ts = ql.YieldTermStructureHandle(ql.FlatForward(calculation_date, risk_free_rate, day_count))\n",
" dividend_yield = ql.YieldTermStructureHandle(ql.FlatForward(calculation_date, dividend_rate, day_count))\n",
" flat_vol_ts = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(calculation_date, calendar, volatility, day_count))\n",
" bsm_process = ql.BlackScholesMertonProcess(spot_handle,dividend_yield,flat_ts,flat_vol_ts)\n",
" european_option.setPricingEngine(ql.AnalyticEuropeanEngine(bsm_process))\n",
" bs_price = european_option.NPV()\n",
" tmp_price[h]=ql.ClosestRounding(2)(bs_price+0.0001)\n",
" #print(tmp_price[h])\n",
"#tmp_price\n",
"tmp_data['Call']=pd.Series(tmp_price)\n",
"#tmp_data.tail()\n",
"end = timer()\n",
"print(end - start) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Calculate Puts"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"117.60858093075694\n"
]
}
],
"source": [
"start = timer()\n",
"\n",
"option_type = ql.Option.Put\n",
"day_count = ql.Actual365Fixed()\n",
"calendar = ql.UnitedStates()\n",
"tmp_price={}\n",
"for h,row in enumerate(tmp_data.itertuples(),0): \n",
" maturity_date = ql.Date(row.Expiration.day, row.Expiration.month, row.Expiration.year)\n",
" spot_price = row.Close\n",
" strike_price = row.Strike\n",
" volatility = row.Volatility\n",
" dividend_rate = row.Dividends\n",
" risk_free_rate = row.TBill\n",
" calculation_date = ql.Date(row.Date.day, row.Date.month, row.Date.year)\n",
" ql.Settings.instance().evaluationDate = calculation_date\n",
" payoff = ql.PlainVanillaPayoff(option_type, strike_price)\n",
" exercise = ql.EuropeanExercise(maturity_date)\n",
" european_option = ql.VanillaOption(payoff, exercise)\n",
" spot_handle = ql.QuoteHandle(ql.SimpleQuote(spot_price))\n",
" flat_ts = ql.YieldTermStructureHandle(ql.FlatForward(calculation_date, risk_free_rate, day_count))\n",
" dividend_yield = ql.YieldTermStructureHandle(ql.FlatForward(calculation_date, dividend_rate, day_count))\n",
" flat_vol_ts = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(calculation_date, calendar, volatility, day_count))\n",
" bsm_process = ql.BlackScholesMertonProcess(spot_handle,dividend_yield,flat_ts,flat_vol_ts)\n",
" european_option.setPricingEngine(ql.AnalyticEuropeanEngine(bsm_process))\n",
" bs_price = european_option.NPV()\n",
" tmp_price[h]=ql.ClosestRounding(2)(bs_price+0.0001)\n",
" #tmp_price[h]=bs_price+0.001\n",
"#tmp_price\n",
"tmp_data['Put']=pd.Series(tmp_price)\n",
"tmp_data.tail()\n",
"end = timer()\n",
"print(end - start) "
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(335871, 10)"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tmp_data.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Set option prices for the expiration day \n",
"This can not done by Black Scholes"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [],
"source": [
"#Set option prices for the expiration day - this is not done by Quanlib / Black Sscholes\n",
"mask = (tmp_data['Date']==tmp_data['Expiration']) & (tmp_data['Strike']>=tmp_data['Open'])\n",
"tmp_data.loc[mask,'Put']=(tmp_data.loc[mask,'Strike']-tmp_data.loc[mask,'Open']).round(2)\n",
"mask1 = (tmp_data['Date']==tmp_data['Expiration']) & (tmp_data['Strike']<=tmp_data['Open'])\n",
"tmp_data.loc[mask1,'Call']=(tmp_data.loc[mask1,'Open']-tmp_data.loc[mask1,'Strike']).round(2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Final Product\n",
"And here are the results"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [],
"source": [
"tmp_data.to_csv('tmp.csv')"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Date</th>\n",
" <th>Expiration</th>\n",
" <th>Open</th>\n",
" <th>Close</th>\n",
" <th>TBill</th>\n",
" <th>Dividends</th>\n",
" <th>Volatility</th>\n",
" <th>Strike</th>\n",
" <th>Call</th>\n",
" <th>Put</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>335866</th>\n",
" <td>2017-10-27</td>\n",
" <td>2019-10-18</td>\n",
" <td>2570.26001</td>\n",
" <td>2581.070068</td>\n",
" <td>0.0107</td>\n",
" <td>0.0195</td>\n",
" <td>0.11207</td>\n",
" <td>4130.0</td>\n",
" <td>0.14</td>\n",
" <td>1560.22</td>\n",
" </tr>\n",
" <tr>\n",
" <th>335867</th>\n",
" <td>2017-10-27</td>\n",
" <td>2019-10-18</td>\n",
" <td>2570.26001</td>\n",
" <td>2581.070068</td>\n",
" <td>0.0107</td>\n",
" <td>0.0195</td>\n",
" <td>0.11207</td>\n",
" <td>4388.0</td>\n",
" <td>0.03</td>\n",
" <td>1812.72</td>\n",
" </tr>\n",
" <tr>\n",
" <th>335868</th>\n",
" <td>2017-10-27</td>\n",
" <td>2019-10-18</td>\n",
" <td>2570.26001</td>\n",
" <td>2581.070068</td>\n",
" <td>0.0107</td>\n",
" <td>0.0195</td>\n",
" <td>0.11207</td>\n",
" <td>4646.0</td>\n",
" <td>0.01</td>\n",
" <td>2065.30</td>\n",
" </tr>\n",
" <tr>\n",
" <th>335869</th>\n",
" <td>2017-10-27</td>\n",
" <td>2019-10-18</td>\n",
" <td>2570.26001</td>\n",
" <td>2581.070068</td>\n",
" <td>0.0107</td>\n",
" <td>0.0195</td>\n",
" <td>0.11207</td>\n",
" <td>4904.0</td>\n",
" <td>0.00</td>\n",
" <td>2317.90</td>\n",
" </tr>\n",
" <tr>\n",
" <th>335870</th>\n",
" <td>2017-10-27</td>\n",
" <td>2019-10-18</td>\n",
" <td>2570.26001</td>\n",
" <td>2581.070068</td>\n",
" <td>0.0107</td>\n",
" <td>0.0195</td>\n",
" <td>0.11207</td>\n",
" <td>5162.0</td>\n",
" <td>0.00</td>\n",
" <td>2570.50</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Date Expiration Open Close TBill Dividends \\\n",
"335866 2017-10-27 2019-10-18 2570.26001 2581.070068 0.0107 0.0195 \n",
"335867 2017-10-27 2019-10-18 2570.26001 2581.070068 0.0107 0.0195 \n",
"335868 2017-10-27 2019-10-18 2570.26001 2581.070068 0.0107 0.0195 \n",
"335869 2017-10-27 2019-10-18 2570.26001 2581.070068 0.0107 0.0195 \n",
"335870 2017-10-27 2019-10-18 2570.26001 2581.070068 0.0107 0.0195 \n",
"\n",
" Volatility Strike Call Put \n",
"335866 0.11207 4130.0 0.14 1560.22 \n",
"335867 0.11207 4388.0 0.03 1812.72 \n",
"335868 0.11207 4646.0 0.01 2065.30 \n",
"335869 0.11207 4904.0 0.00 2317.90 \n",
"335870 0.11207 5162.0 0.00 2570.50 "
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tmp_data.tail()\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.5"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment