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 [Link] as plt
import [Link] as px
from yahooquery import Ticker
import warnings
import statistics
import math
[Link]('ignore')
pd.set_option('display.max_columns', 500)
%matplotlib inline
data_path = [Link]()+'\\'
files = [Link](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=[Link](period='5y',interval='1d')
thefile=folder+ticker+'.csv'
df.to_csv(thefile)
print(ticker+' has been saved to: '+folder)
except FileNotFoundError:
[Link](ticker)
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment [Link] 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().[Link]()
Out[40]: False
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment [Link] 2/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
In [41]: [Link](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 '[Link]'>
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=[Link](raw_data[['date','adjclose']])
[Link]=['date', 'adjclose']
[Link](1255, inplace= True)
data
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment [Link] 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()
[Link](value=data['3d avg'][2], inplace=True)
data['weekly 3d avg'] = data['3d avg'].rolling(5).mean()
[Link](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 [Link] 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'] = [Link](data['weekly 3d avg']/data['weekly 3d avg'].shift(1))
[Link](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 [Link] 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([Link](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(([Link]
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 [Link] 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)
[Link] = [] # Declare the stock prices tree
""" Optional parameters used by derived classes """
[Link], [Link] = pu, pd
[Link] = div
[Link] = 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 [Link](-([Link])*[Link])
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+[Link] # Expected value in the up state
self.d = [Link] # Expected value in the down state
[Link] = ([Link](
([Link])*[Link])-self.d)/(self.u-self.d)
[Link] = [Link] # Probability up is qu and down dq
def init_stock_price_tree(self):
# Initialize terminal price nodes to zeros
[Link] = [Link](self.M)
# Calculate expected stock prices for each node
for i in range(self.M):
[Link][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 [Link] 7/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
"""
if self.is_call:
return [Link](0, [Link]-self.K)
else:
return [Link](0, [Link])
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]*[Link] +
payoffs[1:]*[Link])*[Link]
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*[Link](0.3*[Link](1/6))-80)/80
localhost:8985/doc/tree/Documents/2024/Western/Fall/Mathematics of Financial Options/Assignment 2/FM 9587 Rodrigo Gonzalez Assignment [Link] 8/9
9/10/24, 8:21 p.m. FM 9587 Rodrigo Gonzalez Assignment 2
Out[103… 0.10203302569827084
In [109… (80-78*[Link](-0.3*[Link](1/6)))/78*[Link](-0.3*[Link](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 [Link] 9/9