9/10/24, 8:21 p.m.
FM 9587 Rodrigo Gonzalez Assignment 2
FM 9578 Assignment 2- week 3
Unit 1: Computational Finance - week 2
Rodrigo Ernesto Gonzalez Eslava
In [1]: ##Activate the next line in case you don't have yahooquery installed
#pip install yahooquery
import pandas as pd
import numpy as np
import seaborn as sns
import glob
import os
import matplotlib.pyplot as plt
import plotly.express as px
from yahooquery import Ticker
import warnings
import statistics
import math
warnings.filterwarnings('ignore')
pd.set_option('display.max_columns', 500)
%matplotlib inline
data_path = os.getcwd()+'\\'
files = os.listdir(data_path)
data_path
#files
Out[1]: 'C:\\Users\\Usuario\\Documents\\2024\\Western\\Fall\\Mathematics of Financial Opti
ons\\Assignment 2\\'
First let's get the data from yahoo finance
In [10]: name='^GSPTSE'
tickers=[name]
folder= data_path
nosuccess=[]
for ticker in tickers:
print('Working on: ', ticker)
try:
dfprice=Ticker(ticker)
df=dfprice.history(period='5y',interval='1d')
thefile=folder+ticker+'.csv'
df.to_csv(thefile)
print(ticker+' has been saved to: '+folder)
except FileNotFoundError:
nosuccess.append(ticker)
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment 2.ipynb 1/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
print('Found no price for: ' +ticker)
continue
tickers=[name]
folder= data_path
Working on: ^GSPTSE
^GSPTSE has been saved to: C:\Users\Usuario\Documents\2024\Western\Fall\Mathematics
of Financial Options\Assignment 2\
First view to the data
In [39]: #We're reading the data extracted from yahoo finance
raw_data = pd.read_csv(data_path + name +'.csv')
raw_data.head(100)
Out[39]: symbol date open high low close volume
2019-
0 ^GSPTSE 16347.299805 16409.099609 16308.700195 16379.900391 168389300 1
10-09
2019-
1 ^GSPTSE 16375.400391 16444.400391 16358.099609 16422.699219 186014600 1
10-10
2019-
2 ^GSPTSE 16487.199219 16516.699219 16413.400391 16415.199219 200746600 1
10-11
2019-
3 ^GSPTSE 16433.699219 16510.699219 16417.800781 16418.400391 212333000 1
10-15
2019-
4 ^GSPTSE 16435.099609 16445.599609 16408.699219 16427.199219 165968300 1
10-16
... ... ... ... ... ... ... ...
2020-
95 ^GSPTSE 17140.900391 17304.599609 17029.900391 17041.900391 303632000 1
02-26
2020-
96 ^GSPTSE 16803.400391 16816.199219 16456.800781 16717.400391 232685900 1
02-27
2020-
97 ^GSPTSE 16184.400391 16276.900391 15896.400391 16263.099609 590219300 1
02-28
2020-
98 ^GSPTSE 16325.000000 16566.699219 16166.299805 16553.300781 374721500 1
03-02
2020-
99 ^GSPTSE 16674.900391 16798.199219 16378.299805 16423.599609 393127900 1
03-03
100 rows × 8 columns
In [40]: raw_data.isnull().values.any()
Out[40]: False
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment 2.ipynb 2/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
In [41]: pd.isnull(raw_data).count()
Out[41]: symbol 1256
date 1256
open 1256
high 1256
low 1256
close 1256
volume 1256
adjclose 1256
dtype: int64
In [42]: raw_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1256 entries, 0 to 1255
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 symbol 1256 non-null object
1 date 1256 non-null object
2 open 1256 non-null float64
3 high 1256 non-null float64
4 low 1256 non-null float64
5 close 1256 non-null float64
6 volume 1256 non-null int64
7 adjclose 1256 non-null float64
dtypes: float64(5), int64(1), object(2)
memory usage: 78.6+ KB
As we can see there are no missing values on the original data
Next, we will extract the "adjclose" column
In [51]: data=pd.DataFrame(raw_data[['date','adjclose']])
data.columns=['date', 'adjclose']
data.drop(1255, inplace= True)
data
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment 2.ipynb 3/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
Out[51]: date adjclose
0 2019-10-09 16379.900391
1 2019-10-10 16422.699219
2 2019-10-11 16415.199219
3 2019-10-15 16418.400391
4 2019-10-16 16427.199219
... ... ...
1250 2024-10-02 24001.599609
1251 2024-10-03 23968.500000
1252 2024-10-04 24162.800781
1253 2024-10-07 24102.699219
1254 2024-10-08 24072.500000
1255 rows × 2 columns
In [53]: # Calculate the weekly 3-day Moving Average
data['3d avg'] = data['adjclose'].rolling(3).mean()
data.fillna(value=data['3d avg'][2], inplace=True)
data['weekly 3d avg'] = data['3d avg'].rolling(5).mean()
data.fillna(value=data['weekly 3d avg'][4], inplace=True)
data
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment 2.ipynb 4/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
Out[53]: date adjclose 3d avg weekly 3d avg
0 2019-10-09 16379.900391 16405.932943 16411.366276
1 2019-10-10 16422.699219 16405.932943 16411.366276
2 2019-10-11 16415.199219 16405.932943 16411.366276
3 2019-10-15 16418.400391 16418.766276 16411.366276
4 2019-10-16 16427.199219 16420.266276 16411.366276
... ... ... ... ...
1250 2024-10-02 24001.599609 24012.000000 23987.107031
1251 2024-10-03 23968.500000 24001.366536 23994.586979
1252 2024-10-04 24162.800781 24044.300130 24010.346875
1253 2024-10-07 24102.699219 24078.000000 24026.546745
1254 2024-10-08 24072.500000 24112.666667 24049.666667
1255 rows × 4 columns
Now let's make a series for the log returns
In [58]: data['log_ret'] = np.log(data['weekly 3d avg']/data['weekly 3d avg'].shift(1))
data.fillna(value=0, inplace=True)
data
Out[58]: date adjclose 3d avg weekly 3d avg log_ret
0 2019-10-09 16379.900391 16405.932943 16411.366276 0.000000
1 2019-10-10 16422.699219 16405.932943 16411.366276 0.000000
2 2019-10-11 16415.199219 16405.932943 16411.366276 0.000000
3 2019-10-15 16418.400391 16418.766276 16411.366276 0.000000
4 2019-10-16 16427.199219 16420.266276 16411.366276 0.000000
... ... ... ... ... ...
1250 2024-10-02 24001.599609 24012.000000 23987.107031 0.000787
1251 2024-10-03 23968.500000 24001.366536 23994.586979 0.000312
1252 2024-10-04 24162.800781 24044.300130 24010.346875 0.000657
1253 2024-10-07 24102.699219 24078.000000 24026.546745 0.000674
1254 2024-10-08 24072.500000 24112.666667 24049.666667 0.000962
1255 rows × 5 columns
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment 2.ipynb 5/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
Now let´s compute the mean and standar deviation
In [79]: print(f"The mean of the log returns is: {sum(data['log_ret'])/(len(data['log_ret'])
The mean of the log returns is: 0.00030474240515029357
In [68]: print("The standard deviation of the log returns is: " + str(statistics.stdev(data[
The standard deviation of the log returns is: 0.004565571240670406
Now let´s find the annualized values for the mean and standard deviation (drift and volatility)
In [70]: print("The annualized mean of the log returns is: " + str((252/1254)*sum(data['log_
The annualized mean of the log returns is: 0.07679508609787399
In [74]: print("The annualized standard deviation of the log returns is: " + str((math.sqrt
The annualized standard deviation of the log returns is: 0.0724761965745751
Bonus
Ussing the code seen in the python labs
Writing the StockOption base class
In [81]: import math
"""
Stores common attributes of a stock option
"""
class StockOption(object):
def __init__(
self, S0, K, r=0.05, T=1, N=2, pu=0, pd=0,
div=0, sigma=0, is_put=False, is_am=False):
"""
Initialize the stock option base class.
Defaults to European call unless specified.
:param S0: initial stock price
:param K: strike price
:param r: risk-free interest rate
:param T: time to maturity
:param N: number of time steps
:param pu: probability at up state [Su-S0/S0] % increase rate
:param pd: probability at down state [Sd-S0/S0] % decrease rate
:param div: Dividend yield
:param is_put: True for a put option,
False for a call option
:param is_am: True for an American option,
False for a European option
"""
self.S0 = S0
self.K = K
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment 2.ipynb 6/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
self.r = r
self.T = T
self.N = max(1, N)
self.STs = [] # Declare the stock prices tree
""" Optional parameters used by derived classes """
self.pu, self.pd = pu, pd
self.div = div
self.sigma = sigma
self.is_call = not is_put
self.is_european = not is_am
@property
def dt(self):
""" Single time step, in years """
return self.T/float(self.N)
@property
def df(self):
""" The discount factor """
return math.exp(-(self.r-self.div)*self.dt)
A class for European options using a binomial tree
In [82]: import math
import numpy as np
from decimal import Decimal
"""
Price a European option by the binomial tree model
"""
class BinomialEuropeanOption(StockOption):
def setup_parameters(self):
# Required calculations for the model
self.M = self.N+1 # Number of terminal nodes of tree
self.u = 1+self.pu # Expected value in the up state
self.d = 1-self.pd # Expected value in the down state
self.qu = (math.exp(
(self.r-self.div)*self.dt)-self.d)/(self.u-self.d)
self.qd = 1-self.qu # Probability up is qu and down dq
def init_stock_price_tree(self):
# Initialize terminal price nodes to zeros
self.STs = np.zeros(self.M)
# Calculate expected stock prices for each node
for i in range(self.M):
self.STs[i] = self.S0 * \
(self.u**(self.N-i)) * (self.d**i)
def init_payoffs_tree(self):
"""
Returns the payoffs when the option
expires at terminal nodes
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment 2.ipynb 7/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
"""
if self.is_call:
return np.maximum(0, self.STs-self.K)
else:
return np.maximum(0, self.K-self.STs)
def traverse_tree(self, payoffs):
"""
Starting from the time the option expires, traverse
backwards and calculate discounted payoffs at each node
"""
for i in range(self.N):
payoffs = (payoffs[:-1]*self.qu +
payoffs[1:]*self.qd)*self.df
return payoffs
def begin_tree_traversal(self):
payoffs = self.init_payoffs_tree()
return self.traverse_tree(payoffs)
def price(self):
""" Entry point of the pricing implementation """
self.setup_parameters()
self.init_stock_price_tree()
payoffs = self.begin_tree_traversal()
# Option value converges to first node
return payoffs[0]
Using the values for the problems in this assignment
In [87]: eu_option = BinomialEuropeanOption(
80, 80, r=0.05, T=4/12, N=1, pu=0.0625, pd=0.0625, is_put=True)
print('European put option price for the exercise 1 is:', eu_option.price())
European put option price for the exercise 1 is: 1.7975367874187467
In [90]: eu_option = BinomialEuropeanOption(
40, 40, r=0.079210509, T=3/12, N=1, pu=0.125, pd=0.125, is_put=True)
print('European put option price for the exercise 2 is:', eu_option.price())
European put option price for the exercise 2 is: 2.0588235304304368
In [93]: eu_option = BinomialEuropeanOption(
50, 51, r=0.05, T=6/12, N=2, pu=0.06, pd=0.05, is_put=False)
print('European call option price for the exercise 3 is:', eu_option.price())
European call option price for the exercise 3 is: 1.6350711385184167
In [95]: eu_option = BinomialEuropeanOption(
50, 51, r=0.05, T=6/12, N=2, pu=0.06, pd=0.05, is_put=True)
print('European put option price for the exercise 4 is:', eu_option.price())
European put option price for the exercise 4 is: 1.37587665196338
In [103… (78*math.exp(0.3*math.sqrt(1/6))-80)/80
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment 2.ipynb 8/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
Out[103… 0.10203302569827084
In [109… (80-78*math.exp(-0.3*math.sqrt(1/6)))/78*math.exp(-0.3*math.sqrt(1/6))
Out[109… 0.12466934483941076
In [112… eu_option = BinomialEuropeanOption(
78, 80, r=0.03, T=2/12, N=2, pu=0.10203302569827084, pd=00.12466934483941076, i
print('European call option price for the exercise 5 is:', eu_option.price())
European call option price for the exercise 5 is: 4.611917044425747
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment 2.ipynb 9/9