Showing preview only (291K chars total). Download the full file or copy to clipboard to get everything.
Repository: LechGrzelak/FinancialEngineering_IR_xVA
Branch: main
Commit: 6b898e5ae7f8
Files: 36
Total size: 275.6 KB
Directory structure:
gitextract__8ev3d3k/
├── LICENSE
├── Lecture 02-Understanding of Filtrations and Measures/
│ └── Materials/
│ ├── Black_Scholes_Jumps.py
│ └── Martingale.py
├── Lecture 03-The HJM Framework/
│ └── Materials/
│ ├── CIR_IR_paths.py
│ ├── Ho-Lee-ZCBs.py
│ ├── Hull-White-Paths.py
│ └── Hull-White-ZCBs.py
├── Lecture 04-Yield Curve Dynamics under Short Rate/
│ └── Materials/
│ ├── Hull-White-CompRateSim.py
│ ├── Hull-White-ZCBs2.py
│ └── Hull_White_1F_2F_Comparison.py
├── Lecture 05-Interest Rate Products/
│ └── Materials/
│ ├── HW_Caplets.py
│ ├── HW_OptionsOnZCBs.py
│ └── Swaps_HW.py
├── Lecture 06-Construction of Yield Curve and Multi-Curve/
│ └── Materials/
│ ├── MultiCurveBuild.py
│ ├── YieldCurveBuildGreeks.py
│ └── YieldCurveBuild_Treasury.py
├── Lecture 07-Pricing of Swaptions and Negative Interest Rates/
│ └── Materials/
│ ├── HW_CapletsAndFloorlets.py
│ ├── JamshidianTrick.py
│ └── ShiftedLognormal.py
├── Lecture 08-Mortgages and Prepayments/
│ └── Materials/
│ ├── AnnuityMortgage.py
│ ├── BulletMortgage.py
│ ├── Incentives.py
│ └── StochasticAmortizingSwap.py
├── Lecture 09-Hybrid Models and Stochastic Interest Rates/
│ └── Materials/
│ ├── BSHW_Comparison.py
│ ├── BSHW_ImpliedVolatility.py
│ ├── H1_HW_COS_vs_MC.py
│ ├── SZHW_ImpliedVolatilities.py
│ └── SZHW_MonteCarlo_DiversificationProduct.py
├── Lecture 10-Foreign Exchange (FX) and Inflation/
│ └── Materials/
│ └── H1_HW_COS_vs_MC_FX.py
├── Lecture 11-Market Model and Convexity Adjustments/
│ └── Materials/
│ ├── ConvexityCorrection.py
│ └── DD_ImpliedVolatility.py
├── Lecture 12-Valuation Adjustments- xVA (CVA,BCVA and FVA)/
│ └── Materials/
│ └── Exposures_HW_Netting.py
├── Lecture 13-Value-at-Risk and Expected Shortfall/
│ └── Materials/
│ ├── HistoricalVaR_Calculation.py
│ ├── MonteCarloVaR.py
│ └── MrktData.xlsx
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: LICENSE
================================================
BSD 3-Clause License
Copyright (c) 2024, leszek
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: Lecture 02-Understanding of Filtrations and Measures/Materials/Black_Scholes_Jumps.py
================================================
#%%
"""
Created on July 05 2021
Impact of conditional expectation pricing (Black-Scholes with Jump volatility)
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
import enum
import scipy.stats as st
# This class defines puts and calls
class OptionType(enum.Enum):
CALL = 1.0
PUT = -1.0
def GeneratePaths(NoOfPaths,NoOfSteps,S0,T,muJ,sigmaJ,r):
# Create empty matrices for Poisson process and for compensated Poisson process
X = np.zeros([NoOfPaths, NoOfSteps+1])
S = np.zeros([NoOfPaths, NoOfSteps+1])
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
X[:,0] = np.log(S0)
S[:,0] = S0
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
J = np.random.normal(muJ,sigmaJ,[NoOfPaths,NoOfSteps])
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
X[:,i+1] = X[:,i] + (r - 0.5*J[:,i]**2.0)*dt +J[:,i]*np.sqrt(dt)* Z[:,i]
time[i+1] = time[i] +dt
S = np.exp(X)
paths = {"time":time,"X":X,"S":S,"J":J}
return paths
def EUOptionPriceFromMCPaths(CP,S,K,T,r):
# S is a vector of Monte Carlo samples at T
if CP == OptionType.CALL:
return np.exp(-r*T)*np.mean(np.maximum(S-K,0.0))
elif CP == OptionType.PUT:
return np.exp(-r*T)*np.mean(np.maximum(K-S,0.0))
def BS_Call_Put_Option_Price(CP,S_0,K,sigma,t,T,r):
K = np.array(K).reshape([len(K),1])
d1 = (np.log(S_0 / K) + (r + 0.5 * np.power(sigma,2.0))
* (T-t)) / (sigma * np.sqrt(T-t))
d2 = d1 - sigma * np.sqrt(T-t)
if CP == OptionType.CALL:
value = st.norm.cdf(d1) * S_0 - st.norm.cdf(d2) * K * np.exp(-r * (T-t))
elif CP == OptionType.PUT:
value = st.norm.cdf(-d2) * K * np.exp(-r * (T-t)) - st.norm.cdf(-d1)*S_0
return value
def CallOption_CondExpectation(NoOfPaths,T,S0,K,J,r):
# Jumps at time T
J_i = J[:,-1]
result = np.zeros([NoOfPaths])
for j in range(0,NoOfPaths):
sigma = J_i[j]
result[j] = BS_Call_Put_Option_Price(OptionType.CALL,S0,[K],sigma,0.0,T,r)
return np.mean(result)
def mainCalculation():
NoOfPaths = 25
NoOfSteps = 500
T = 5
muJ = 0.3
sigmaJ = 0.005
S0 =100
r =0.00
Paths = GeneratePaths(NoOfPaths,NoOfSteps,S0, T,muJ,sigmaJ,r)
timeGrid = Paths["time"]
X = Paths["X"]
S = Paths["S"]
plt.figure(1)
plt.plot(timeGrid, np.transpose(X))
plt.grid()
plt.xlabel("time")
plt.ylabel("X(t)")
plt.figure(2)
plt.plot(timeGrid, np.transpose(S))
plt.grid()
plt.xlabel("time")
plt.ylabel("S(t)")
# Check the convergence for a given strike
K = 80
CP =OptionType.CALL
NGrid = range(100,10000,1000)
NoOfRuns = len(NGrid)
resultMC = np.zeros([NoOfRuns])
resultCondExp = np.zeros([NoOfRuns])
for (i,N) in enumerate(NGrid):
print(N)
Paths = GeneratePaths(N,NoOfSteps,S0, T,muJ,sigmaJ,r)
timeGrid = Paths["time"]
S = Paths["S"]
resultMC[i] = EUOptionPriceFromMCPaths(CP,S[:,-1],K,T,r)
J = Paths["J"]
resultCondExp[i]=CallOption_CondExpectation(N,T,S0,K,J,r)
plt.figure(3)
plt.plot(NGrid,resultMC)
plt.plot(NGrid,resultCondExp)
plt.legend(['MC','Conditional Expectation'])
plt.title('Call Option Price- Convergence')
plt.xlabel('Number of Paths')
plt.ylabel('Option price for a given strike, K')
plt.grid()
mainCalculation()
================================================
FILE: Lecture 02-Understanding of Filtrations and Measures/Materials/Martingale.py
================================================
#%%
"""
Created on July 05 2021
Simulation of, E(W(t)|F(s)) = W(s) using nested Monte Carlo
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
t = 10
s = 5
NoOfPaths=1000
NoOfSteps=10
# First part to caclulate E(W(t)|F(0)) = W(0)=0
def martingaleA():
W_t = np.random.normal(0.0,pow(t,0.5),[NoOfPaths,1])
E_W_t = np.mean(W_t)
print("mean value equals to: %.2f while the expected value is W(0) =%0.2f " %(E_W_t,0.0))
# Second part requiring nested Monte Carlo simulation E(W(t)|F(s)) = W(s)
def martingaleB():
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths,NoOfSteps+1])
# time-step from [t0,s]
dt1 = s / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + pow(dt1,0.5)*Z[:,i]
#W_s is the last column of W
W_s = W[:,-1]
#for every path W(s) we perform sub-simulation until time t and calculate
#the expectation
# time-step from [s,t]
dt2 = (t-s)/float(NoOfSteps);
W_t = np.zeros([NoOfPaths,NoOfSteps+1]);
#Store the results
E_W_t = np.zeros([NoOfPaths])
Error=[]
for i in range(0,NoOfPaths):
#Sub-simulation from time "s" until "t"
W_t[:,0] = W_s[i];
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
for j in range(0,NoOfSteps):
#this is a scaling that ensures that Z has mean 0 and variance 1
Z[:,j] = (Z[:,j]-np.mean(Z[:,j])) / np.std(Z[:,j]);
#path simulation, from "s" until "t"
W_t[:,j+1] = W_t[:,j] + pow(dt2,0.5)*Z[:,j];
E_W_t[i]=np.mean(W_t[:,-1])
Error.append(E_W_t[i]-W_s[i])
#Generate a plot for the first path
if i==0:
plt.plot(np.linspace(0,s,NoOfSteps+1),W[0,:])
for j in range(0,NoOfPaths):
plt.plot(np.linspace(s,t,NoOfSteps+1),W_t[j,:])
plt.xlabel("time")
plt.ylabel("W(t)")
plt.grid()
print(Error)
error = np.max(np.abs(E_W_t-W_s))
print("The error is equal to: %.18f"%(error))
martingaleB()
================================================
FILE: Lecture 03-The HJM Framework/Materials/CIR_IR_paths.py
================================================
#%%
"""
Created on July 11 2021
Monte Carlo Paths for the CIR Process
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
def GeneratePathsCIREuler(NoOfPaths,NoOfSteps,T,lambd,r0,theta,gamma):
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
R[:,0]=r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta - R[:,i]) * dt + gamma* np.sqrt(R[:,i]) * (W[:,i+1]-W[:,i])
# Truncated boundary condition
R[:,i+1] = np.maximum(R[:,i+1],0.0)
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R}
return paths
def mainCalculation():
NoOfPaths = 1
NoOfSteps = 500
T = 50.0
lambd = 0.1
gamma = 0.05
r0 = 0.05
theta = 0.05
# Effect of mean reversion lambda
plt.figure(1)
legend = []
lambdVec = [0.01, 0.2, 5.0]
for lambdTemp in lambdVec:
np.random.seed(2)
Paths = GeneratePathsCIREuler(NoOfPaths,NoOfSteps,T,lambdTemp,r0,theta,gamma)
legend.append('lambda={0}'.format(lambdTemp))
timeGrid = Paths["time"]
R = Paths["R"]
plt.plot(timeGrid, np.transpose(R))
plt.grid()
plt.xlabel("time")
plt.ylabel("R(t)")
plt.legend(legend)
# Effect of the volatility
plt.figure(2)
legend = []
gammaVec = [0.1, 0.2, 0.3]
for gammaTemp in gammaVec:
np.random.seed(2)
Paths = GeneratePathsCIREuler(NoOfPaths,NoOfSteps,T,lambd,r0,theta,gammaTemp)
legend.append('gamma={0}'.format(gammaTemp))
timeGrid = Paths["time"]
R = Paths["R"]
plt.plot(timeGrid, np.transpose(R))
plt.grid()
plt.xlabel("time")
plt.ylabel("R(t)")
plt.legend(legend)
mainCalculation()
================================================
FILE: Lecture 03-The HJM Framework/Materials/Ho-Lee-ZCBs.py
================================================
#%%
"""
Created on July 12 2021
Ho-Lee Model, Simulation of the Model + Computation of ZCBs, P(0,t)
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
def f0T(t,P0T):
# time-step needed for differentiation
dt = 0.01
expr = - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
return expr
def GeneratePathsHoLeeEuler(NoOfPaths,NoOfSteps,T,P0T,sigma):
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.01,P0T)
theta = lambda t: (f0T(t+dt,P0T)-f0T(t-dt,P0T))/(2.0*dt) + sigma**2.0*t
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
M = np.zeros([NoOfPaths, NoOfSteps+1])
M[:,0]= 1.0
R[:,0]=r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + theta(time[i]) * dt + sigma* (W[:,i+1]-W[:,i])
M[:,i+1] = M[:,i] * np.exp((R[:,i+1]+R[:,i])*0.5*dt)
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R,"M":M}
return paths
def mainCalculation():
NoOfPaths = 25000
NoOfSteps = 500
sigma = 0.007
# We define a ZCB curve (obtained from the market)
P0T = lambda T: np.exp(-0.1*T)
# In this experiment we compare ZCB from the Market and Monte Carlo
"Monte Carlo part"
T = 40
paths= GeneratePathsHoLeeEuler(NoOfPaths,NoOfSteps,T,P0T,sigma)
M = paths["M"]
ti = paths["time"]
# Here we compare the price of an option on a ZCB from Monte Carlo and Analytical expression
P_t = np.zeros([NoOfSteps+1])
for i in range(0,NoOfSteps+1):
P_t[i] = np.mean(1.0/M[:,i])
plt.figure(1)
plt.grid()
plt.xlabel('T')
plt.ylabel('P(0,T)')
plt.plot(ti,P0T(ti))
plt.plot(ti,P_t,'--r')
plt.legend(['P(0,t) market','P(0,t) Monte Carlo'])
plt.title('ZCBs from Ho-Lee Model')
mainCalculation()
================================================
FILE: Lecture 03-The HJM Framework/Materials/Hull-White-Paths.py
================================================
#%%
"""
Created on July 12 2021
Monte Carlo paths for the Hull-White model
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + \
eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
R[:,0]=r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta(time[i]) - R[:,i]) * dt + eta* (W[:,i+1]-W[:,i])
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R}
return paths
def mainCalculation():
NoOfPaths = 1
NoOfSteps = 5000
T = 50.0
lambd = 0.5
eta = 0.01
# We define a ZCB curve (obtained from the market)
P0T = lambda T: np.exp(-0.05*T)
# Effect of mean reversion lambda
plt.figure(1)
legend = []
lambdVec = [-0.01, 0.2, 5.0]
for lambdTemp in lambdVec:
np.random.seed(2)
Paths = GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambdTemp, eta)
legend.append('lambda={0}'.format(lambdTemp))
timeGrid = Paths["time"]
R = Paths["R"]
plt.plot(timeGrid, np.transpose(R))
plt.grid()
plt.xlabel("time")
plt.ylabel("R(t)")
plt.legend(legend)
# Effect of the volatility
plt.figure(2)
legend = []
etaVec = [0.1, 0.2, 0.3]
for etaTemp in etaVec:
np.random.seed(2)
Paths = GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, etaTemp)
legend.append('eta={0}'.format(etaTemp))
timeGrid = Paths["time"]
R = Paths["R"]
plt.plot(timeGrid, np.transpose(R))
plt.grid()
plt.xlabel("time")
plt.ylabel("R(t)")
plt.legend(legend)
mainCalculation()
================================================
FILE: Lecture 03-The HJM Framework/Materials/Hull-White-ZCBs.py
================================================
#%%
"""
Created on July 12 2021
Hull-White Model, Simulation of the Model
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
def f0T(t,P0T):
# time-step needed for differentiation
dt = 0.01
expr = - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
return expr
def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta):
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.01,P0T)
theta = lambda t: 1.0/lambd * (f0T(t+dt,P0T)-f0T(t-dt,P0T))/(2.0*dt) + f0T(t,P0T) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#theta = lambda t: 0.1 +t -t
#print("changed theta")
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
M = np.zeros([NoOfPaths, NoOfSteps+1])
M[:,0]= 1.0
R[:,0]= r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta(time[i]) - R[:,i]) * dt + eta* (W[:,i+1]-W[:,i])
M[:,i+1] = M[:,i] * np.exp((R[:,i+1]+R[:,i])*0.5*dt)
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R,"M":M}
return paths
def HW_theta(lambd,eta,P0T):
dt = 0.01
theta = lambda t: 1.0/lambd * (f0T(t+dt,P0T)-f0T(t-dt,P0T))/(2.0*dt) + f0T(t,P0T) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#print("CHANGED THETA")
return theta#lambda t: 0.1+t-t
def mainCalculation():
NoOfPaths = 25000
NoOfSteps = 25
lambd = 0.02
eta = 0.02
# We define a ZCB curve (obtained from the market)
P0T = lambda T: np.exp(-0.1*T)
# In this experiment we compare ZCB from the Market and Monte Carlo
"Monte Carlo part"
T = 40
paths= GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta)
M = paths["M"]
ti = paths["time"]
#dt = timeGrid[1]-timeGrid[0]
# Here we compare the price of an option on a ZCB from Monte Carlo the Market
P_tMC = np.zeros([NoOfSteps+1])
for i in range(0,NoOfSteps+1):
P_tMC[i] = np.mean(1.0/M[:,i])
plt.figure(1)
plt.grid()
plt.xlabel('T')
plt.ylabel('P(0,T)')
plt.plot(ti,P0T(ti))
plt.plot(ti,P_tMC,'--r')
plt.legend(['P(0,t) market','P(0,t) Monte Carlo'])
plt.title('ZCBs from Hull-White Model')
mainCalculation()
================================================
FILE: Lecture 04-Yield Curve Dynamics under Short Rate/Materials/Hull-White-CompRateSim.py
================================================
#%%
"""
Created on July 12 2021
Yield Curve shapes and the Hull-White Model 1 Factor
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
#from scipy.interpolate import interp1d
from scipy import interpolate
def f0T(t,P0T):
# time-step needed for differentiation
dt = 0.01
expr = - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
return expr
def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta):
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.01,P0T)
theta = lambda t: 1.0/lambd * (f0T(t+dt,P0T)-f0T(t-dt,P0T))/(2.0*dt) + f0T(t,P0T) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#theta = lambda t: 0.1 +t -t
#print("changed theta")
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
R[:,0]=r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta(time[i]) - R[:,i]) * dt + eta* (W[:,i+1]-W[:,i])
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R}
return paths
def HW_theta(lambd,eta,P0T):
dt = 0.01
theta = lambda t: 1.0/lambd * (f0T(t+dt,P0T)-f0T(t-dt,P0T))/(2.0*dt) + f0T(t,P0T) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#print("CHANGED THETA")
return theta#lambda t: 0.1+t-t
def HW_A(lambd,eta,P0T,T1,T2):
tau = T2-T1
zGrid = np.linspace(0.0,tau,250)
B_r = lambda tau: 1.0/lambd * (np.exp(-lambd *tau)-1.0)
theta = HW_theta(lambd,eta,P0T)
temp1 = lambd * integrate.trapz(theta(T2-zGrid)*B_r(zGrid),zGrid)
temp2 = eta*eta/(4.0*np.power(lambd,3.0)) * (np.exp(-2.0*lambd*tau)*(4*np.exp(lambd*tau)-1.0) -3.0) + eta*eta*tau/(2.0*lambd*lambd)
return temp1 + temp2
def HW_B(lambd,eta,T1,T2):
return 1.0/lambd *(np.exp(-lambd*(T2-T1))-1.0)
def HW2F_ZCB(lambd1,lambd2,eta1,eta2,rho,P0T,T1,T2,xT1,yT1):
V = lambda t,T: (eta1**2.0)/(lambd1**2.0)*((T2-T1)+2.0/lambd1*np.exp(-lambd1*(T2-T1))-1.0/(2.0*lambd1)*np.exp(-2.0*lambd1*(T2-T1))-3.0/(2.0*lambd1))+\
+(eta2**2.0)/(lambd2**2.0)*((T2-T1)+2.0/lambd2*np.exp(-lambd2*(T2-T1))-1.0/(2.0*lambd2)*np.exp(-2.0*lambd2*(T2-T1))-3.0/(2.0*lambd2))+\
+ 2.0*rho*eta1*eta2/(lambd1*lambd2)*(T2-T1 + 1.0/lambd1*(np.exp(-lambd1*(T2-T1))-1.0)+ 1.0/lambd2*(np.exp(-lambd2*(T2-T1))-1.0) - 1.0/(lambd1+lambd2)*(np.exp(-(lambd1+lambd2)*(T2-T1))-1.0))
intPhi = - np.log(P0T(T2)/P0T(T1)*np.exp(-0.5*(V(0,T2)-V(0,T1))))
A = 1.0/lambd1 * (1.0-np.exp(-lambd1 * (T2-T1)))
B = 1.0/lambd2 * (1.0-np.exp(-lambd2 * (T2-T1)))
return np.exp(- intPhi -A * xT1 - B * yT1 + 0.5 * V(T1,T2))
def HW_ZCB(lambd,eta,P0T,T1,T2,rT1):
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
return np.exp(A_r + B_r *rT1)
def HW_r_0(P0T,lambd,eta):
r0 = f0T(0.001,P0T)
return r0
def mainCalculation():
NoOfPaths = 20000
NoOfSteps = 100
#HW1F
lambd = 0.01
eta = 0.002
# We define a ZCB curve (obtained from the market)
ti = [0.0,0.00273972600000000,0.0876712330000000,0.172602740000000,0.257534247000000,0.342465753000000,0.427397260000000,0.512328767000000,0.597260274000000,0.682191781000000,0.767123288000000,0.852054795000000,0.936986301000000,1.02191780800000,1.10684931500000,1.19178082200000,1.27671232900000,1.36164383600000,1.44657534200000,1.53150684900000,1.61643835600000,1.70136986300000,1.78630137000000,1.87123287700000,1.95616438400000,2.04109589000000,2.12602739700000,2.21095890400000,2.29589041100000,2.38082191800000,2.46575342500000,2.55068493200000,2.63561643800000,2.72054794500000,2.80547945200000,2.89041095900000,2.97534246600000,3.06027397300000,3.14520547900000,3.23013698600000,3.31506849300000,3.40000000000000,3.48493150700000,3.56986301400000,3.65479452100000,3.73972602700000,3.82465753400000,3.90958904100000,3.99452054800000,4.07945205500000,4.16438356200000,4.24931506800000,4.33424657500000,4.41917808200000,4.50410958900000,4.58904109600000,4.67397260300000,4.75890411000000,4.84383561600000,4.92876712300000,5.01369863000000,5.09863013700000,5.18356164400000,5.26849315100000,5.35342465800000,5.43835616400000,5.52328767100000,5.60821917800000,5.69315068500000,5.77808219200000,5.86301369900000,5.94794520500000,6.03287671200000,6.11780821900000,6.20273972600000,6.28767123300000,6.37260274000000,6.45753424700000,6.54246575300000,6.62739726000000,6.71232876700000,6.79726027400000,6.88219178100000,6.96712328800000,7.05205479500000,7.13698630100000,7.22191780800000,7.30684931500000,7.39178082200000,7.47671232900000,7.56164383600000,7.64657534200000,7.73150684900000,7.81643835600000,7.90136986300000,7.98630137000000,8.07123287700000,8.15616438400000,8.24109589000000,8.32602739700000,8.41095890400000,8.49589041100000,8.58082191800000,8.66575342500000,8.75068493200000,8.83561643800000,8.92054794500000,9.00547945200000,9.09041095900000,9.17534246600000,9.26027397300000,9.34520547900000,9.43013698600000,9.51506849300000,9.60000000000000,9.68493150700000,9.76986301400000,9.85479452100000,9.93972602700000,10.0246575300000,10.1095890400000,10.1945205500000,10.2794520500000,10.3643835600000,10.4493150700000,10.5342465800000,10.6191780800000,10.7041095900000,10.7890411000000,10.8739726000000,10.9589041100000,11.0438356200000,11.1287671200000,11.2136986300000,11.2986301400000,11.3835616400000,11.4684931500000,11.5534246600000,11.6383561600000,11.7232876700000,11.8082191800000,11.8931506800000,11.9780821900000,12.0630137000000,12.1479452100000,12.2328767100000,12.3178082200000,12.4027397300000,12.4876712300000,12.5726027400000,12.6575342500000,12.7424657500000,12.8273972600000,12.9123287700000,12.9972602700000,13.0821917800000,13.1671232900000,13.2520547900000,13.3369863000000,13.4219178100000,13.5068493200000,13.5917808200000,13.6767123300000,13.7616438400000,13.8465753400000,13.9315068500000,14.0164383600000,14.1013698600000,14.1863013700000,14.2712328800000,14.3561643800000,14.4410958900000,14.5260274000000,14.6109589000000,14.6958904100000,14.7808219200000,14.8657534200000,14.9506849300000,15.0356164400000,15.1205479500000,15.2054794500000,15.2904109600000,15.3753424700000,15.4602739700000,15.5452054800000,15.6301369900000,15.7150684900000,15.8000000000000,15.8849315100000,15.9698630100000,16.0547945200000,16.1397260300000,16.2246575300000,16.3095890400000,16.3945205500000,16.4794520500000,16.5643835600000,16.6493150700000,16.7342465800000,16.8191780800000,16.9041095900000,16.9890411000000,17.0739726000000,17.1589041100000,17.2438356200000,17.3287671200000,17.4136986300000,17.4986301400000,17.5835616400000,17.6684931500000,17.7534246600000,17.8383561600000,17.9232876700000,18.0082191800000,18.0931506800000,18.1780821900000,18.2630137000000,18.3479452100000,18.4328767100000,18.5178082200000,18.6027397300000,18.6876712300000,18.7726027400000,18.8575342500000,18.9424657500000,19.0273972600000,19.1123287700000,19.1972602700000,19.2821917800000,19.3671232900000,19.4520547900000,19.5369863000000,19.6219178100000,19.7068493200000,19.7917808200000,19.8767123300000,19.9616438400000,20.0465753400000,20.1315068500000,20.2164383600000,20.3013698600000,20.3863013700000,20.4712328800000,20.5561643800000,20.6410958900000,20.7260274000000,20.8109589000000,20.8958904100000,20.9808219200000,21.0657534200000,21.1506849300000,21.2356164400000,21.3205479500000,21.4054794500000,21.4904109600000,21.5753424700000,21.6602739700000,21.7452054800000,21.8301369900000,21.9150684900000,22,22.0849315100000,22.1698630100000,22.2547945200000,22.3397260300000,22.4246575300000,22.5095890400000,22.5945205500000,22.6794520500000,22.7643835600000,22.8493150700000,22.9342465800000,23.0191780800000,23.1041095900000,23.1890411000000,23.2739726000000,23.3589041100000,23.4438356200000,23.5287671200000,23.6136986300000,23.6986301400000,23.7835616400000,23.8684931500000,23.9534246600000,24.0383561600000,24.1232876700000,24.2082191800000,24.2931506800000,24.3780821900000,24.4630137000000,24.5479452100000,24.6328767100000,24.7178082200000,24.8027397300000,24.8876712300000,24.9726027400000,25.0575342500000,25.1424657500000,25.2273972600000,25.3123287700000,25.3972602700000,25.4821917800000,25.5671232900000,25.6520547900000,25.7369863000000,25.8219178100000,25.9068493200000,25.9917808200000,26.0767123300000,26.1616438400000,26.2465753400000,26.3315068500000,26.4164383600000,26.5013698600000,26.5863013700000,26.6712328800000,26.7561643800000,26.8410958900000,26.9260274000000,27.0109589000000,27.0958904100000,27.1808219200000,27.2657534200000,27.3506849300000,27.4356164400000,27.5205479500000,27.6054794500000,27.6904109600000,27.7753424700000,27.8602739700000,27.9452054800000,28.0301369900000,28.1150684900000,28.2000000000000,28.2849315100000,28.3698630100000,28.4547945200000,28.5397260300000,28.6246575300000,28.7095890400000,28.7945205500000,28.8794520500000,28.9643835600000,29.0493150700000,29.1342465800000,29.2191780800000,29.3041095900000,29.3890411000000,29.4739726000000,29.5589041100000,29.6438356200000,29.7287671200000,29.8136986300000,29.8986301400000,29.9835616400000,30.0684931500000,30.1534246600000,30.2383561600000,30.3232876700000,30.4082191800000,30.4931506800000,30.5780821900000,30.6630137000000,30.7479452100000,30.8328767100000,30.9178082200000,31.0027397300000,31.0876712300000,31.1726027400000,31.2575342500000,31.3424657500000,31.4273972600000,31.5123287700000,31.5972602700000,31.6821917800000,31.7671232900000,31.8520547900000,31.9369863000000,32.0219178100000,32.1068493200000,32.1917808200000,32.2767123300000,32.3616438400000,32.4465753400000,32.5315068500000,32.6164383600000,32.7013698600000,32.7863013700000,32.8712328800000,32.9561643800000,33.0410958900000,33.1260274000000,33.2109589000000,33.2958904100000,33.3808219200000,33.4657534200000,33.5506849300000,33.6356164400000,33.7205479500000,33.8054794500000,33.8904109600000,33.9753424700000,34.0602739700000,34.1452054800000,34.2301369900000,34.3150684900000,34.4000000000000,34.4849315100000,34.5698630100000,34.6547945200000,34.7397260300000,34.8246575300000,34.9095890400000,34.9945205500000,35.0794520500000,35.1643835600000,35.2493150700000,35.3342465800000,35.4191780800000,35.5041095900000,35.5890411000000,35.6739726000000,35.7589041100000,35.8438356200000,35.9287671200000,36.0136986300000,36.0986301400000,36.1835616400000,36.2684931500000,36.3534246600000,36.4383561600000,36.5232876700000,36.6082191800000,36.6931506800000,36.7780821900000,36.8630137000000,36.9479452100000,37.0328767100000,37.1178082200000,37.2027397300000,37.2876712300000,37.3726027400000,37.4575342500000,37.5424657500000,37.6273972600000,37.7123287700000,37.7972602700000,37.8821917800000,37.9671232900000,38.0520547900000,38.1369863000000,38.2219178100000,38.3068493200000,38.3917808200000,38.4767123300000,38.5616438400000,38.6465753400000,38.7315068500000,38.8164383600000,38.9013698600000,38.9863013700000,39.0712328800000,39.1561643800000,39.2410958900000,39.3260274000000,39.4109589000000,39.4958904100000,39.5808219200000,39.6657534200000,39.7506849300000,39.8356164400000,39.9205479500000,40.0054794500000]
Pi = [1.0,0.999966573000000,0.998930882000000,0.997824062000000,0.996511145000000,0.995199956000000,0.993821602000000,0.992277014000000,0.990734827000000,0.989164324000000,0.987428762000000,0.985704346000000,0.983946708000000,0.982068207000000,0.980193293000000,0.978281187000000,0.976255832000000,0.974234670000000,0.972174514000000,0.970028236000000,0.967886697000000,0.965693800000000,0.963440984000000,0.961193424000000,0.958903753000000,0.956575247000000,0.954252397000000,0.951842433000000,0.949228406000000,0.946482248000000,0.943632525000000,0.940707509000000,0.937735160000000,0.934743101000000,0.931758611000000,0.928808623000000,0.925919731000000,0.923110403000000,0.920338655000000,0.917589739000000,0.914858924000000,0.912141532000000,0.909432941000000,0.906728583000000,0.904023944000000,0.901314567000000,0.898596044000000,0.895864025000000,0.893114214000000,0.890346348000000,0.887569841000000,0.884786140000000,0.881996090000000,0.879200528000000,0.876400278000000,0.873596157000000,0.870788975000000,0.867979528000000,0.865168605000000,0.862356987000000,0.859545442000000,0.856732322000000,0.853915005000000,0.851094371000000,0.848271318000000,0.845446732000000,0.842621487000000,0.839796449000000,0.836972470000000,0.834150393000000,0.831331050000000,0.828515261000000,0.825703790000000,0.822893949000000,0.820084325000000,0.817275636000000,0.814468590000000,0.811663887000000,0.808862219000000,0.806064266000000,0.803270701000000,0.800482187000000,0.797699379000000,0.794922921000000,0.792153338000000,0.789389717000000,0.786631547000000,0.783878633000000,0.781130782000000,0.778387801000000,0.775649505000000,0.772915708000000,0.770186227000000,0.767460882000000,0.764739497000000,0.762021898000000,0.759308200000000,0.756599781000000,0.753896558000000,0.751198194000000,0.748504356000000,0.745814715000000,0.743128950000000,0.740446741000000,0.737767776000000,0.735091745000000,0.732418345000000,0.729747277000000,0.727078670000000,0.724413443000000,0.721751645000000,0.719093279000000,0.716438346000000,0.713786850000000,0.711138792000000,0.708494177000000,0.705853006000000,0.703215284000000,0.700581015000000,0.697950202000000,0.695320544000000,0.692690217000000,0.690059929000000,0.687430378000000,0.684802253000000,0.682176235000000,0.679552992000000,0.676933185000000,0.674317462000000,0.671706465000000,0.669100824000000,0.666501159000000,0.663908082000000,0.661322194000000,0.658744089000000,0.656174348000000,0.653613545000000,0.651062245000000,0.648521002000000,0.645990363000000,0.643470865000000,0.640963036000000,0.638467395000000,0.635983411000000,0.633503959000000,0.631028021000000,0.628556105000000,0.626088714000000,0.623626343000000,0.621169478000000,0.618718599000000,0.616274178000000,0.613836679000000,0.611406558000000,0.608984265000000,0.606570242000000,0.604164924000000,0.601768738000000,0.599382105000000,0.597005438000000,0.594639144000000,0.592283621000000,0.589939262000000,0.587606453000000,0.585285574000000,0.582976996000000,0.580681085000000,0.578398202000000,0.576128698000000,0.573872922000000,0.571631214000000,0.569403908000000,0.567191334000000,0.564993814000000,0.562811666000000,0.560645202000000,0.558494727000000,0.556360543000000,0.554242913000000,0.552138115000000,0.550043470000000,0.547959012000000,0.545884776000000,0.543820795000000,0.541767101000000,0.539723726000000,0.537690699000000,0.535668051000000,0.533655809000000,0.531654001000000,0.529662652000000,0.527681790000000,0.525711438000000,0.523751620000000,0.521802360000000,0.519863678000000,0.517935598000000,0.516018137000000,0.514111318000000,0.512215157000000,0.510329674000000,0.508454885000000,0.506590807000000,0.504737456000000,0.502894847000000,0.501062994000000,0.499241910000000,0.497431609000000,0.495632103000000,0.493843403000000,0.492065521000000,0.490298466000000,0.488542249000000,0.486796879000000,0.485062364000000,0.483338711000000,0.481625929000000,0.479924024000000,0.478233003000000,0.476552870000000,0.474883632000000,0.473225293000000,0.471577858000000,0.469941330000000,0.468315712000000,0.466701007000000,0.465097218000000,0.463504347000000,0.461922395000000,0.460351364000000,0.458791253000000,0.457242065000000,0.455703798000000,0.454176452000000,0.452660027000000,0.451154522000000,0.449659935000000,0.448176257000000,0.446703083000000,0.445240105000000,0.443787209000000,0.442344286000000,0.440911226000000,0.439487920000000,0.438074261000000,0.436670143000000,0.435275460000000,0.433890109000000,0.432513986000000,0.431146989000000,0.429789017000000,0.428439971000000,0.427099751000000,0.425768260000000,0.424445399000000,0.423131074000000,0.421825189000000,0.420527650000000,0.419238363000000,0.417957237000000,0.416684180000000,0.415419101000000,0.414161910000000,0.412912520000000,0.411670842000000,0.410436789000000,0.409210275000000,0.407991213000000,0.406779521000000,0.405575114000000,0.404377909000000,0.403187824000000,0.402004777000000,0.400828688000000,0.399659478000000,0.398497066000000,0.397341375000000,0.396192326000000,0.395049844000000,0.393913852000000,0.392784274000000,0.391661036000000,0.390544064000000,0.389433283000000,0.388328623000000,0.387230010000000,0.386137373000000,0.385050641000000,0.383969745000000,0.382894615000000,0.381825182000000,0.380761378000000,0.379703135000000,0.378650387000000,0.377603067000000,0.376561110000000,0.375524516000000,0.374495509000000,0.373475055000000,0.372462955000000,0.371459016000000,0.370463043000000,0.369474847000000,0.368494238000000,0.367521029000000,0.366555034000000,0.365596072000000,0.364643959000000,0.363698516000000,0.362759566000000,0.361826932000000,0.360900441000000,0.359979918000000,0.359065194000000,0.358156099000000,0.357252465000000,0.356354126000000,0.355460918000000,0.354572678000000,0.353689243000000,0.352810455000000,0.351936156000000,0.351066187000000,0.350200393000000,0.349338622000000,0.348480719000000,0.347626535000000,0.346775919000000,0.345928722000000,0.345084799000000,0.344244003000000,0.343406190000000,0.342571217000000,0.341738943000000,0.340909228000000,0.340081932000000,0.339256917000000,0.338434048000000,0.337613190000000,0.336794207000000,0.335976969000000,0.335161343000000,0.334347200000000,0.333534411000000,0.332722847000000,0.331912384000000,0.331102895000000,0.330294257000000,0.329486347000000,0.328679044000000,0.327872227000000,0.327065778000000,0.326259578000000,0.325453511000000,0.324647462000000,0.323841447000000,0.323037558000000,0.322236538000000,0.321438361000000,0.320642997000000,0.319850421000000,0.319060604000000,0.318273519000000,0.317489140000000,0.316707441000000,0.315928394000000,0.315151974000000,0.314378155000000,0.313606911000000,0.312838216000000,0.312072045000000,0.311308373000000,0.310547175000000,0.309788426000000,0.309032102000000,0.308278178000000,0.307526629000000,0.306777432000000,0.306030564000000,0.305285999000000,0.304543716000000,0.303803690000000,0.303065898000000,0.302330318000000,0.301596926000000,0.300865701000000,0.300136619000000,0.299409659000000,0.298684798000000,0.297962014000000,0.297241286000000,0.296522593000000,0.295805912000000,0.295091222000000,0.294378503000000,0.293667734000000,0.292958894000000,0.292251962000000,0.291546917000000,0.290843741000000,0.290142412000000,0.289442910000000,0.288745217000000,0.288049311000000,0.287355175000000,0.286662788000000,0.285972131000000,0.285283185000000,0.284595932000000,0.283910353000000,0.283226429000000,0.282544142000000,0.281863473000000,0.281184405000000,0.280506920000000,0.279830999000000,0.279156625000000,0.278483781000000,0.277812449000000,0.277142612000000,0.276474253000000,0.275807354000000,0.275141900000000,0.274477873000000,0.273815257000000,0.273154036000000,0.272494193000000,0.271835712000000,0.271178577000000,0.270522772000000,0.269868283000000,0.269215092000000,0.268563184000000,0.267912545000000,0.267263159000000,0.266615011000000,0.265968086000000,0.265322370000000,0.264677847000000,0.264034503000000,0.263392324000000,0.262751295000000,0.262111403000000,0.261472633000000,0.260834972000000,0.260198405000000,0.259562919000000,0.258928501000000,0.258295136000000,0.257662813000000,0.257031517000000,0.256401236000000,0.255771957000000,0.255143667000000,0.254516353000000,0.253890002000000,0.253264603000000,0.252640144000000,0.252016611000000,0.251393992000000,0.250772277000000,0.250151453000000,0.249531508000000,0.248912431000000,0.248294210000000,0.247676834000000,0.247060291000000,0.246444571000000,0.245829663000000,0.245215555000000,0.244602237000000,0.243989698000000,0.243377928000000]
interpolator = interpolate.splrep(ti, np.log(Pi), s=0.00001)
P0T = lambda T: np.exp(interpolate.splev(T, interpolator, der=0))
r0 = HW_r_0(P0T,lambd,eta)
# In this experiment we compare ZCB from the Market and Analytical expression
N = 20
T_end0 = 49.0
Tgrid= np.linspace(0.1,T_end0,N)
Exact = np.zeros([N,1])
Proxy_1FHW= np.zeros ([N,1])
Yield = np.zeros ([N,1])
for i,Ti in enumerate(Tgrid):
Proxy_1FHW[i] = HW_ZCB(lambd,eta,P0T,0.0,Ti,r0)
Exact[i] = P0T(Ti)
Yield[i] = -np.log(Proxy_1FHW[i])/Ti
plt.figure(1)
plt.grid()
plt.xlabel('T')
plt.xlabel('ZCB, P(0,t)')
plt.plot(Tgrid,Exact,'-k')
plt.plot(Tgrid,Proxy_1FHW,'--r')
plt.legend(["Analytcal ZCB","ZCB- 1F Model","ZCB- 2F Model"])
plt.title('P(0,T) from Monte Carlo vs. Analytical expression')
# Yield
plt.figure(2)
plt.grid()
plt.plot(Tgrid,Yield,'-k')
plt.xlabel('T')
plt.ylabel('yield, r(0,T)')
"Monte Carlo part"
T_end = 10.0
paths= GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T_end ,P0T, lambd, eta)
r = paths["R"]
timeGrid = paths["time"]
# Yield Curves on the last point
plt.figure(3)
plt.xlabel('time')
plt.ylabel('r(t)')
plt.title('MC Paths + Yield Curve (Hull-White)')
plt.grid()
T_end2 = T_end + 40.0
Tgrid2= np.linspace(T_end+0.001,T_end2-0.01,N)
ZCB = np.zeros([N,1])
r_T = r[:,-1]
for i in range(0,20):
for j,Tj in enumerate(Tgrid2):
ZCB[j] = HW_ZCB(lambd,eta,P0T,T_end,Tj,r_T[i])
Yield[j] = -np.log(ZCB[j])/(Tj-T_end)
plt.plot(Tgrid2, Yield)
plt.plot(timeGrid,r[i,:])
mainCalculation()
================================================
FILE: Lecture 04-Yield Curve Dynamics under Short Rate/Materials/Hull-White-ZCBs2.py
================================================
#%%
"""
Created on July 12 2021
Hull-White Model, Simulation of the Model + Computation of ZCBs, P(0,t)
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
def f0T(t,P0T):
# time-step needed for differentiation
dt = 0.01
expr = - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
return expr
def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta):
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.01,P0T)
theta = lambda t: 1.0/lambd * (f0T(t+dt,P0T)-f0T(t-dt,P0T))/(2.0*dt) + f0T(t,P0T) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#theta = lambda t: 0.1 +t -t
#print("changed theta")
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
M = np.zeros([NoOfPaths, NoOfSteps+1])
M[:,0]= 1.0
R[:,0]= r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta(time[i]) - R[:,i]) * dt + eta* (W[:,i+1]-W[:,i])
M[:,i+1] = M[:,i] * np.exp((R[:,i+1]+R[:,i])*0.5*dt)
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R,"M":M}
return paths
def HW_theta(lambd,eta,P0T):
dt = 0.01
theta = lambda t: 1.0/lambd * (f0T(t+dt,P0T)-f0T(t-dt,P0T))/(2.0*dt) + f0T(t,P0T) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#print("CHANGED THETA")
return theta#lambda t: 0.1+t-t
def HW_A(lambd,eta,P0T,T1,T2):
tau = T2-T1
zGrid = np.linspace(0.0,tau,250)
B_r = lambda tau: 1.0/lambd * (np.exp(-lambd *tau)-1.0)
theta = HW_theta(lambd,eta,P0T)
temp1 = lambd * integrate.trapz(theta(T2-zGrid)*B_r(zGrid),zGrid)
temp2 = eta*eta/(4.0*np.power(lambd,3.0)) * (np.exp(-2.0*lambd*tau)*(4*np.exp(lambd*tau)-1.0) -3.0) + eta*eta*tau/(2.0*lambd*lambd)
return temp1 + temp2
def HW_B(lambd,eta,T1,T2):
return 1.0/lambd *(np.exp(-lambd*(T2-T1))-1.0)
def HW_ZCB(lambd,eta,P0T,T1,T2,rT1):
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
return np.exp(A_r + B_r *rT1)
def mainCalculation():
NoOfPaths = 20000
NoOfSteps = 25
lambd = 0.04
eta = 0.01
# We define a ZCB curve (obtained from the market)
P0T = lambda T: np.exp(-0.1*T)
# Initial r_0
r0 = f0T(0.01,P0T)
# In this experiment we compare ZCB from the Market and Monte Carlo
"Monte Carlo part"
T = 40
paths= GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta)
M = paths["M"]
ti = paths["time"]
#dt = timeGrid[1]-timeGrid[0]
# Here we compare the price of an option on a ZCB from Monte Carlo the Market
P_tMC = np.zeros([NoOfSteps+1])
for i in range(0,NoOfSteps+1):
P_tMC[i] = np.mean(1.0/M[:,i])
# Analytical expression for ZCB using the Affine properties of the HW model
P_tHW = np.zeros([NoOfSteps+1])
for i in range(0,NoOfSteps+1):
P_tHW[i] = HW_ZCB(lambd,eta,P0T,0.0,ti[i],r0)
plt.figure(1)
plt.grid()
plt.xlabel('T')
plt.ylabel('P(0,T)')
plt.plot(ti,P0T(ti))
plt.plot(ti,P_tMC,'--r')
plt.plot(ti,P_tHW,'.k')
plt.legend(['P(0,t) market','P(0,t) Monte Carlo','P(0,t) from Affine form'])
plt.title('ZCBs from Hull-White Model')
mainCalculation()
================================================
FILE: Lecture 04-Yield Curve Dynamics under Short Rate/Materials/Hull_White_1F_2F_Comparison.py
================================================
#%%
"""
Created on July 12 2021
Yield Curve shapes and the Hull-White Model (1 and 2 factor cases)
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
from scipy import interpolate
def GeneratePathsHW2FEuler(NoOfPaths,NoOfSteps,T,P0T, lambd1,lambd2, eta1,eta2,rho):
# time-step needed for differentiation
phi = lambda t: f0T(t,P0T) + (eta1**2.0)/(2.0*lambd1**2.0)*(1.0-np.exp(-lambd1*t))*(1.0-np.exp(-lambd1*t))+\
+(eta2**2.0)/(2.0*lambd2**2.0)*(1.0-np.exp(-lambd2*t))*(1.0-np.exp(-lambd2*t))+\
+ rho*eta1*eta2/(lambd1*lambd2)*(1.0-np.exp(-lambd1*t))*(1.0-np.exp(-lambd2*t))
Z1 = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
Z2 = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W1 = np.zeros([NoOfPaths, NoOfSteps+1])
W2 = np.zeros([NoOfPaths, NoOfSteps+1])
X = np.zeros([NoOfPaths, NoOfSteps+1])
Y = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
R[:,0] = phi(0)
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z1[:,i] = (Z1[:,i] - np.mean(Z1[:,i])) / np.std(Z1[:,i])
Z2[:,i] = (Z2[:,i] - np.mean(Z2[:,i])) / np.std(Z2[:,i])
Z2[:,i] = rho * Z1[:,i] + np.sqrt(1.0-rho**2)*Z2[:,i]
W1[:,i+1] = W1[:,i] + np.power(dt, 0.5)*Z1[:,i]
W2[:,i+1] = W2[:,i] + np.power(dt, 0.5)*Z2[:,i]
X[:,i+1] = X[:,i] - lambd1*X[:,i] * dt + eta1* (W1[:,i+1]-W1[:,i])
Y[:,i+1] = Y[:,i] - lambd2*Y[:,i] * dt + eta2* (W2[:,i+1]-W2[:,i])
time[i+1] = time[i] +dt
R[:,i+1] = X[:,i+1] + Y[:,i+1] + phi(time[i+1])
# Outputs
paths = {"time":time,"R":R,"X":X,"Y":Y}
return paths
def f0T(t,P0T):
# time-step needed for differentiation
dt = 0.01
expr = - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
return expr
def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta):
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.01,P0T)
theta = lambda t: 1.0/lambd * (f0T(t+dt,P0T)-f0T(t-dt,P0T))/(2.0*dt) + f0T(t,P0T) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#theta = lambda t: 0.1 +t -t
#print("changed theta")
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
R[:,0]=r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta(time[i]) - R[:,i]) * dt + eta* (W[:,i+1]-W[:,i])
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R}
return paths
def HW_theta(lambd,eta,P0T):
dt = 0.01
theta = lambda t: 1.0/lambd * (f0T(t+dt,P0T)-f0T(t-dt,P0T))/(2.0*dt) + f0T(t,P0T) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#print("CHANGED THETA")
return theta#lambda t: 0.1+t-t
def HW_A(lambd,eta,P0T,T1,T2):
tau = T2-T1
zGrid = np.linspace(0.0,tau,250)
B_r = lambda tau: 1.0/lambd * (np.exp(-lambd *tau)-1.0)
theta = HW_theta(lambd,eta,P0T)
temp1 = lambd * integrate.trapz(theta(T2-zGrid)*B_r(zGrid),zGrid)
temp2 = eta*eta/(4.0*np.power(lambd,3.0)) * (np.exp(-2.0*lambd*tau)*(4*np.exp(lambd*tau)-1.0) -3.0) + eta*eta*tau/(2.0*lambd*lambd)
return temp1 + temp2
def HW_B(lambd,eta,T1,T2):
return 1.0/lambd *(np.exp(-lambd*(T2-T1))-1.0)
def HW2F_ZCB(lambd1,lambd2,eta1,eta2,rho,P0T,T1,T2,xT1,yT1):
V = lambda t,T: (eta1**2.0)/(lambd1**2.0)*((T-t)+2.0/lambd1*np.exp(-lambd1*(T-t))-1.0/(2.0*lambd1)*np.exp(-2.0*lambd1*(T-t))-3.0/(2.0*lambd1))+\
+(eta2**2.0)/(lambd2**2.0)*((T-t)+2.0/lambd2*np.exp(-lambd2*(T-t))-1.0/(2.0*lambd2)*np.exp(-2.0*lambd2*(T-t))-3.0/(2.0*lambd2))+\
+ 2.0*rho*eta1*eta2/(lambd1*lambd2)*(T-t + 1.0/lambd1*(np.exp(-lambd1*(T-t))-1.0)+ 1.0/lambd2*(np.exp(-lambd2*(T-t))-1.0) - 1.0/(lambd1+lambd2)*(np.exp(-(lambd1+lambd2)*(T-t))-1.0))
intPhi = - np.log(P0T(T2)/P0T(T1)*np.exp(-0.5*(V(0,T2)-V(0,T1))))
A = 1.0/lambd1 * (1.0-np.exp(-lambd1 * (T2-T1)))
B = 1.0/lambd2 * (1.0-np.exp(-lambd2 * (T2-T1)))
return np.exp(- intPhi -A * xT1 - B * yT1 + 0.5 * V(T1,T2))
def HW_ZCB(lambd,eta,P0T,T1,T2,rT1):
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
return np.exp(A_r + B_r *rT1)
def HW_r_0(P0T,lambd,eta):
r0 = f0T(0.001,P0T)
return r0
def mainCalculation():
NoOfPaths = 2000
NoOfSteps = 100
#HW1F
lambd = 0.01
eta = 0.002
#HW2F
lambd2 = 0.1
eta2 = 0.002
rho = -0.2
# We define a ZCB curve (obtained from the market), 1-D points with a B-spline curve.
ti = [0.0,0.00273972600000000,0.0876712330000000,0.172602740000000,0.257534247000000,0.342465753000000,0.427397260000000,0.512328767000000,0.597260274000000,0.682191781000000,0.767123288000000,0.852054795000000,0.936986301000000,1.02191780800000,1.10684931500000,1.19178082200000,1.27671232900000,1.36164383600000,1.44657534200000,1.53150684900000,1.61643835600000,1.70136986300000,1.78630137000000,1.87123287700000,1.95616438400000,2.04109589000000,2.12602739700000,2.21095890400000,2.29589041100000,2.38082191800000,2.46575342500000,2.55068493200000,2.63561643800000,2.72054794500000,2.80547945200000,2.89041095900000,2.97534246600000,3.06027397300000,3.14520547900000,3.23013698600000,3.31506849300000,3.40000000000000,3.48493150700000,3.56986301400000,3.65479452100000,3.73972602700000,3.82465753400000,3.90958904100000,3.99452054800000,4.07945205500000,4.16438356200000,4.24931506800000,4.33424657500000,4.41917808200000,4.50410958900000,4.58904109600000,4.67397260300000,4.75890411000000,4.84383561600000,4.92876712300000,5.01369863000000,5.09863013700000,5.18356164400000,5.26849315100000,5.35342465800000,5.43835616400000,5.52328767100000,5.60821917800000,5.69315068500000,5.77808219200000,5.86301369900000,5.94794520500000,6.03287671200000,6.11780821900000,6.20273972600000,6.28767123300000,6.37260274000000,6.45753424700000,6.54246575300000,6.62739726000000,6.71232876700000,6.79726027400000,6.88219178100000,6.96712328800000,7.05205479500000,7.13698630100000,7.22191780800000,7.30684931500000,7.39178082200000,7.47671232900000,7.56164383600000,7.64657534200000,7.73150684900000,7.81643835600000,7.90136986300000,7.98630137000000,8.07123287700000,8.15616438400000,8.24109589000000,8.32602739700000,8.41095890400000,8.49589041100000,8.58082191800000,8.66575342500000,8.75068493200000,8.83561643800000,8.92054794500000,9.00547945200000,9.09041095900000,9.17534246600000,9.26027397300000,9.34520547900000,9.43013698600000,9.51506849300000,9.60000000000000,9.68493150700000,9.76986301400000,9.85479452100000,9.93972602700000,10.0246575300000,10.1095890400000,10.1945205500000,10.2794520500000,10.3643835600000,10.4493150700000,10.5342465800000,10.6191780800000,10.7041095900000,10.7890411000000,10.8739726000000,10.9589041100000,11.0438356200000,11.1287671200000,11.2136986300000,11.2986301400000,11.3835616400000,11.4684931500000,11.5534246600000,11.6383561600000,11.7232876700000,11.8082191800000,11.8931506800000,11.9780821900000,12.0630137000000,12.1479452100000,12.2328767100000,12.3178082200000,12.4027397300000,12.4876712300000,12.5726027400000,12.6575342500000,12.7424657500000,12.8273972600000,12.9123287700000,12.9972602700000,13.0821917800000,13.1671232900000,13.2520547900000,13.3369863000000,13.4219178100000,13.5068493200000,13.5917808200000,13.6767123300000,13.7616438400000,13.8465753400000,13.9315068500000,14.0164383600000,14.1013698600000,14.1863013700000,14.2712328800000,14.3561643800000,14.4410958900000,14.5260274000000,14.6109589000000,14.6958904100000,14.7808219200000,14.8657534200000,14.9506849300000,15.0356164400000,15.1205479500000,15.2054794500000,15.2904109600000,15.3753424700000,15.4602739700000,15.5452054800000,15.6301369900000,15.7150684900000,15.8000000000000,15.8849315100000,15.9698630100000,16.0547945200000,16.1397260300000,16.2246575300000,16.3095890400000,16.3945205500000,16.4794520500000,16.5643835600000,16.6493150700000,16.7342465800000,16.8191780800000,16.9041095900000,16.9890411000000,17.0739726000000,17.1589041100000,17.2438356200000,17.3287671200000,17.4136986300000,17.4986301400000,17.5835616400000,17.6684931500000,17.7534246600000,17.8383561600000,17.9232876700000,18.0082191800000,18.0931506800000,18.1780821900000,18.2630137000000,18.3479452100000,18.4328767100000,18.5178082200000,18.6027397300000,18.6876712300000,18.7726027400000,18.8575342500000,18.9424657500000,19.0273972600000,19.1123287700000,19.1972602700000,19.2821917800000,19.3671232900000,19.4520547900000,19.5369863000000,19.6219178100000,19.7068493200000,19.7917808200000,19.8767123300000,19.9616438400000,20.0465753400000,20.1315068500000,20.2164383600000,20.3013698600000,20.3863013700000,20.4712328800000,20.5561643800000,20.6410958900000,20.7260274000000,20.8109589000000,20.8958904100000,20.9808219200000,21.0657534200000,21.1506849300000,21.2356164400000,21.3205479500000,21.4054794500000,21.4904109600000,21.5753424700000,21.6602739700000,21.7452054800000,21.8301369900000,21.9150684900000,22,22.0849315100000,22.1698630100000,22.2547945200000,22.3397260300000,22.4246575300000,22.5095890400000,22.5945205500000,22.6794520500000,22.7643835600000,22.8493150700000,22.9342465800000,23.0191780800000,23.1041095900000,23.1890411000000,23.2739726000000,23.3589041100000,23.4438356200000,23.5287671200000,23.6136986300000,23.6986301400000,23.7835616400000,23.8684931500000,23.9534246600000,24.0383561600000,24.1232876700000,24.2082191800000,24.2931506800000,24.3780821900000,24.4630137000000,24.5479452100000,24.6328767100000,24.7178082200000,24.8027397300000,24.8876712300000,24.9726027400000,25.0575342500000,25.1424657500000,25.2273972600000,25.3123287700000,25.3972602700000,25.4821917800000,25.5671232900000,25.6520547900000,25.7369863000000,25.8219178100000,25.9068493200000,25.9917808200000,26.0767123300000,26.1616438400000,26.2465753400000,26.3315068500000,26.4164383600000,26.5013698600000,26.5863013700000,26.6712328800000,26.7561643800000,26.8410958900000,26.9260274000000,27.0109589000000,27.0958904100000,27.1808219200000,27.2657534200000,27.3506849300000,27.4356164400000,27.5205479500000,27.6054794500000,27.6904109600000,27.7753424700000,27.8602739700000,27.9452054800000,28.0301369900000,28.1150684900000,28.2000000000000,28.2849315100000,28.3698630100000,28.4547945200000,28.5397260300000,28.6246575300000,28.7095890400000,28.7945205500000,28.8794520500000,28.9643835600000,29.0493150700000,29.1342465800000,29.2191780800000,29.3041095900000,29.3890411000000,29.4739726000000,29.5589041100000,29.6438356200000,29.7287671200000,29.8136986300000,29.8986301400000,29.9835616400000,30.0684931500000,30.1534246600000,30.2383561600000,30.3232876700000,30.4082191800000,30.4931506800000,30.5780821900000,30.6630137000000,30.7479452100000,30.8328767100000,30.9178082200000,31.0027397300000,31.0876712300000,31.1726027400000,31.2575342500000,31.3424657500000,31.4273972600000,31.5123287700000,31.5972602700000,31.6821917800000,31.7671232900000,31.8520547900000,31.9369863000000,32.0219178100000,32.1068493200000,32.1917808200000,32.2767123300000,32.3616438400000,32.4465753400000,32.5315068500000,32.6164383600000,32.7013698600000,32.7863013700000,32.8712328800000,32.9561643800000,33.0410958900000,33.1260274000000,33.2109589000000,33.2958904100000,33.3808219200000,33.4657534200000,33.5506849300000,33.6356164400000,33.7205479500000,33.8054794500000,33.8904109600000,33.9753424700000,34.0602739700000,34.1452054800000,34.2301369900000,34.3150684900000,34.4000000000000,34.4849315100000,34.5698630100000,34.6547945200000,34.7397260300000,34.8246575300000,34.9095890400000,34.9945205500000,35.0794520500000,35.1643835600000,35.2493150700000,35.3342465800000,35.4191780800000,35.5041095900000,35.5890411000000,35.6739726000000,35.7589041100000,35.8438356200000,35.9287671200000,36.0136986300000,36.0986301400000,36.1835616400000,36.2684931500000,36.3534246600000,36.4383561600000,36.5232876700000,36.6082191800000,36.6931506800000,36.7780821900000,36.8630137000000,36.9479452100000,37.0328767100000,37.1178082200000,37.2027397300000,37.2876712300000,37.3726027400000,37.4575342500000,37.5424657500000,37.6273972600000,37.7123287700000,37.7972602700000,37.8821917800000,37.9671232900000,38.0520547900000,38.1369863000000,38.2219178100000,38.3068493200000,38.3917808200000,38.4767123300000,38.5616438400000,38.6465753400000,38.7315068500000,38.8164383600000,38.9013698600000,38.9863013700000,39.0712328800000,39.1561643800000,39.2410958900000,39.3260274000000,39.4109589000000,39.4958904100000,39.5808219200000,39.6657534200000,39.7506849300000,39.8356164400000,39.9205479500000,40.0054794500000]
Pi = [1.0,0.999966573000000,0.998930882000000,0.997824062000000,0.996511145000000,0.995199956000000,0.993821602000000,0.992277014000000,0.990734827000000,0.989164324000000,0.987428762000000,0.985704346000000,0.983946708000000,0.982068207000000,0.980193293000000,0.978281187000000,0.976255832000000,0.974234670000000,0.972174514000000,0.970028236000000,0.967886697000000,0.965693800000000,0.963440984000000,0.961193424000000,0.958903753000000,0.956575247000000,0.954252397000000,0.951842433000000,0.949228406000000,0.946482248000000,0.943632525000000,0.940707509000000,0.937735160000000,0.934743101000000,0.931758611000000,0.928808623000000,0.925919731000000,0.923110403000000,0.920338655000000,0.917589739000000,0.914858924000000,0.912141532000000,0.909432941000000,0.906728583000000,0.904023944000000,0.901314567000000,0.898596044000000,0.895864025000000,0.893114214000000,0.890346348000000,0.887569841000000,0.884786140000000,0.881996090000000,0.879200528000000,0.876400278000000,0.873596157000000,0.870788975000000,0.867979528000000,0.865168605000000,0.862356987000000,0.859545442000000,0.856732322000000,0.853915005000000,0.851094371000000,0.848271318000000,0.845446732000000,0.842621487000000,0.839796449000000,0.836972470000000,0.834150393000000,0.831331050000000,0.828515261000000,0.825703790000000,0.822893949000000,0.820084325000000,0.817275636000000,0.814468590000000,0.811663887000000,0.808862219000000,0.806064266000000,0.803270701000000,0.800482187000000,0.797699379000000,0.794922921000000,0.792153338000000,0.789389717000000,0.786631547000000,0.783878633000000,0.781130782000000,0.778387801000000,0.775649505000000,0.772915708000000,0.770186227000000,0.767460882000000,0.764739497000000,0.762021898000000,0.759308200000000,0.756599781000000,0.753896558000000,0.751198194000000,0.748504356000000,0.745814715000000,0.743128950000000,0.740446741000000,0.737767776000000,0.735091745000000,0.732418345000000,0.729747277000000,0.727078670000000,0.724413443000000,0.721751645000000,0.719093279000000,0.716438346000000,0.713786850000000,0.711138792000000,0.708494177000000,0.705853006000000,0.703215284000000,0.700581015000000,0.697950202000000,0.695320544000000,0.692690217000000,0.690059929000000,0.687430378000000,0.684802253000000,0.682176235000000,0.679552992000000,0.676933185000000,0.674317462000000,0.671706465000000,0.669100824000000,0.666501159000000,0.663908082000000,0.661322194000000,0.658744089000000,0.656174348000000,0.653613545000000,0.651062245000000,0.648521002000000,0.645990363000000,0.643470865000000,0.640963036000000,0.638467395000000,0.635983411000000,0.633503959000000,0.631028021000000,0.628556105000000,0.626088714000000,0.623626343000000,0.621169478000000,0.618718599000000,0.616274178000000,0.613836679000000,0.611406558000000,0.608984265000000,0.606570242000000,0.604164924000000,0.601768738000000,0.599382105000000,0.597005438000000,0.594639144000000,0.592283621000000,0.589939262000000,0.587606453000000,0.585285574000000,0.582976996000000,0.580681085000000,0.578398202000000,0.576128698000000,0.573872922000000,0.571631214000000,0.569403908000000,0.567191334000000,0.564993814000000,0.562811666000000,0.560645202000000,0.558494727000000,0.556360543000000,0.554242913000000,0.552138115000000,0.550043470000000,0.547959012000000,0.545884776000000,0.543820795000000,0.541767101000000,0.539723726000000,0.537690699000000,0.535668051000000,0.533655809000000,0.531654001000000,0.529662652000000,0.527681790000000,0.525711438000000,0.523751620000000,0.521802360000000,0.519863678000000,0.517935598000000,0.516018137000000,0.514111318000000,0.512215157000000,0.510329674000000,0.508454885000000,0.506590807000000,0.504737456000000,0.502894847000000,0.501062994000000,0.499241910000000,0.497431609000000,0.495632103000000,0.493843403000000,0.492065521000000,0.490298466000000,0.488542249000000,0.486796879000000,0.485062364000000,0.483338711000000,0.481625929000000,0.479924024000000,0.478233003000000,0.476552870000000,0.474883632000000,0.473225293000000,0.471577858000000,0.469941330000000,0.468315712000000,0.466701007000000,0.465097218000000,0.463504347000000,0.461922395000000,0.460351364000000,0.458791253000000,0.457242065000000,0.455703798000000,0.454176452000000,0.452660027000000,0.451154522000000,0.449659935000000,0.448176257000000,0.446703083000000,0.445240105000000,0.443787209000000,0.442344286000000,0.440911226000000,0.439487920000000,0.438074261000000,0.436670143000000,0.435275460000000,0.433890109000000,0.432513986000000,0.431146989000000,0.429789017000000,0.428439971000000,0.427099751000000,0.425768260000000,0.424445399000000,0.423131074000000,0.421825189000000,0.420527650000000,0.419238363000000,0.417957237000000,0.416684180000000,0.415419101000000,0.414161910000000,0.412912520000000,0.411670842000000,0.410436789000000,0.409210275000000,0.407991213000000,0.406779521000000,0.405575114000000,0.404377909000000,0.403187824000000,0.402004777000000,0.400828688000000,0.399659478000000,0.398497066000000,0.397341375000000,0.396192326000000,0.395049844000000,0.393913852000000,0.392784274000000,0.391661036000000,0.390544064000000,0.389433283000000,0.388328623000000,0.387230010000000,0.386137373000000,0.385050641000000,0.383969745000000,0.382894615000000,0.381825182000000,0.380761378000000,0.379703135000000,0.378650387000000,0.377603067000000,0.376561110000000,0.375524516000000,0.374495509000000,0.373475055000000,0.372462955000000,0.371459016000000,0.370463043000000,0.369474847000000,0.368494238000000,0.367521029000000,0.366555034000000,0.365596072000000,0.364643959000000,0.363698516000000,0.362759566000000,0.361826932000000,0.360900441000000,0.359979918000000,0.359065194000000,0.358156099000000,0.357252465000000,0.356354126000000,0.355460918000000,0.354572678000000,0.353689243000000,0.352810455000000,0.351936156000000,0.351066187000000,0.350200393000000,0.349338622000000,0.348480719000000,0.347626535000000,0.346775919000000,0.345928722000000,0.345084799000000,0.344244003000000,0.343406190000000,0.342571217000000,0.341738943000000,0.340909228000000,0.340081932000000,0.339256917000000,0.338434048000000,0.337613190000000,0.336794207000000,0.335976969000000,0.335161343000000,0.334347200000000,0.333534411000000,0.332722847000000,0.331912384000000,0.331102895000000,0.330294257000000,0.329486347000000,0.328679044000000,0.327872227000000,0.327065778000000,0.326259578000000,0.325453511000000,0.324647462000000,0.323841447000000,0.323037558000000,0.322236538000000,0.321438361000000,0.320642997000000,0.319850421000000,0.319060604000000,0.318273519000000,0.317489140000000,0.316707441000000,0.315928394000000,0.315151974000000,0.314378155000000,0.313606911000000,0.312838216000000,0.312072045000000,0.311308373000000,0.310547175000000,0.309788426000000,0.309032102000000,0.308278178000000,0.307526629000000,0.306777432000000,0.306030564000000,0.305285999000000,0.304543716000000,0.303803690000000,0.303065898000000,0.302330318000000,0.301596926000000,0.300865701000000,0.300136619000000,0.299409659000000,0.298684798000000,0.297962014000000,0.297241286000000,0.296522593000000,0.295805912000000,0.295091222000000,0.294378503000000,0.293667734000000,0.292958894000000,0.292251962000000,0.291546917000000,0.290843741000000,0.290142412000000,0.289442910000000,0.288745217000000,0.288049311000000,0.287355175000000,0.286662788000000,0.285972131000000,0.285283185000000,0.284595932000000,0.283910353000000,0.283226429000000,0.282544142000000,0.281863473000000,0.281184405000000,0.280506920000000,0.279830999000000,0.279156625000000,0.278483781000000,0.277812449000000,0.277142612000000,0.276474253000000,0.275807354000000,0.275141900000000,0.274477873000000,0.273815257000000,0.273154036000000,0.272494193000000,0.271835712000000,0.271178577000000,0.270522772000000,0.269868283000000,0.269215092000000,0.268563184000000,0.267912545000000,0.267263159000000,0.266615011000000,0.265968086000000,0.265322370000000,0.264677847000000,0.264034503000000,0.263392324000000,0.262751295000000,0.262111403000000,0.261472633000000,0.260834972000000,0.260198405000000,0.259562919000000,0.258928501000000,0.258295136000000,0.257662813000000,0.257031517000000,0.256401236000000,0.255771957000000,0.255143667000000,0.254516353000000,0.253890002000000,0.253264603000000,0.252640144000000,0.252016611000000,0.251393992000000,0.250772277000000,0.250151453000000,0.249531508000000,0.248912431000000,0.248294210000000,0.247676834000000,0.247060291000000,0.246444571000000,0.245829663000000,0.245215555000000,0.244602237000000,0.243989698000000,0.243377928000000]
interpolator = interpolate.splrep(ti, Pi, s=0.0001)
P0T = lambda T: interpolate.splev(T, interpolator, der=0)
r0 = HW_r_0(P0T,lambd,eta)
# In this experiment we compare ZCB from the Market and Analytical expression
N = 20
T_end0 = 39.0
Tgrid= np.linspace(0.1,T_end0,N)
Exact = np.zeros([N,1])
Proxy_1FHW= np.zeros ([N,1])
Proxy_2FHW= np.zeros ([N,1])
Yield = np.zeros ([N,1])
for i,Ti in enumerate(Tgrid):
Proxy_1FHW[i] = HW_ZCB(lambd,eta,P0T,0.0,Ti,r0)
Proxy_2FHW[i] = HW2F_ZCB(lambd,lambd2,eta,eta2,rho,P0T,0.0,Ti,0.0,0.0)
Exact[i] = P0T(Ti)
Yield[i] = -np.log(Proxy_1FHW[i])/Ti
plt.figure(1)
plt.grid()
plt.plot(Tgrid,Exact,'-k')
plt.plot(Tgrid,Proxy_1FHW,'--r')
plt.plot(Tgrid,Proxy_2FHW,'.k')
plt.legend(["Analytcal ZCB","ZCB- 1F Model","ZCB- 2F Model"])
plt.title('P(0,T) from Monte Carlo vs. Analytical expression')
"Monte Carlo part"
T_end = 10.0
paths= GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T_end ,P0T, lambd, eta)
r = paths["R"]
timeGrid = paths["time"]
# Yield
#r = -np.log(Proxy)/T
plt.figure(2)
plt.grid()
plt.plot(Tgrid,Yield,'-k')
# Yield Curves on the last point
plt.figure(3)
plt.xlabel('time')
plt.ylabel('r(t)')
plt.title('MC Paths + Yield Curve (Hull-White)')
plt.grid()
T_end2 = T_end + 40.0
Tgrid2= np.linspace(T_end+0.001,T_end2-0.01,N)
ZCB = np.zeros([N,1])
r_T = r[:,-1]
for i in range(0,20):
for j,Tj in enumerate(Tgrid2):
ZCB[j] = HW_ZCB(lambd,eta,P0T,T_end,Tj,r_T[i])
Yield[j] = -np.log(ZCB[j])/(Tj-T_end)
plt.plot(Tgrid2, Yield)
plt.plot(timeGrid,r[i,:])
# Analysis for the 2F model
paths= GeneratePathsHW2FEuler(NoOfPaths,NoOfSteps,T_end,P0T, lambd,lambd2, eta,eta2,rho)
x = paths["X"]
y = paths["Y"]
r = paths["R"]
timeGrid = paths["time"]
# Yield Curves on the last point
plt.figure(4)
plt.xlabel('time')
plt.ylabel('r(t)')
plt.title('MC Paths + Yield Curve (Hull-White 2F)')
plt.grid()
x_T = x[:,-1]
y_T = y[:,-1]
r_T = r[:,-1]
Tgrid2= np.linspace(T_end+0.001,T_end2,N)
ZCB = np.zeros([N,1])
for i in range(0,20):
for j,Tj in enumerate(Tgrid2):
ZCB[j] = HW2F_ZCB(lambd,lambd2,eta,eta2,rho,P0T,T_end,Tj,x_T[i],y_T[i])
#HW_ZCB(lambd,eta,P0T,T_end,Tj,r_T[i])
Yield[j] = -np.log(ZCB[j])/(Tj-T_end)
plt.plot(timeGrid,r[i,:])
plt.plot(Tgrid2, Yield)
mainCalculation()
================================================
FILE: Lecture 05-Interest Rate Products/Materials/HW_Caplets.py
================================================
#%%
"""
Created on July 12 2021
Caplets under the Hull-White Model
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import enum
import matplotlib.pyplot as plt
import scipy.stats as st
import scipy.integrate as integrate
# This class defines puts and calls
class OptionType(enum.Enum):
CALL = 1.0
PUT = -1.0
def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#theta = lambda t: 0.1 +t -t
#print("changed theta")
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
R[:,0]=r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta(time[i]) - R[:,i]) * dt + eta* (W[:,i+1]-W[:,i])
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R}
return paths
def HW_theta(lambd,eta,P0T):
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#print("CHANGED THETA")
return theta#lambda t: 0.1+t-t
def HW_A(lambd,eta,P0T,T1,T2):
tau = T2-T1
zGrid = np.linspace(0.0,tau,250)
B_r = lambda tau: 1.0/lambd * (np.exp(-lambd *tau)-1.0)
theta = HW_theta(lambd,eta,P0T)
temp1 = lambd * integrate.trapz(theta(T2-zGrid)*B_r(zGrid),zGrid)
temp2 = eta*eta/(4.0*np.power(lambd,3.0)) * (np.exp(-2.0*lambd*tau)*(4*np.exp(lambd*tau)-1.0) -3.0) + eta*eta*tau/(2.0*lambd*lambd)
return temp1 + temp2
def HW_B(lambd,eta,T1,T2):
return 1.0/lambd *(np.exp(-lambd*(T2-T1))-1.0)
def HW_ZCB(lambd,eta,P0T,T1,T2,rT1):
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
return np.exp(A_r + B_r *rT1)
def HWMean_r(P0T,lambd,eta,T):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2.0*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = HW_theta(lambd,eta,P0T)
zGrid = np.linspace(0.0,T,2500)
temp =lambda z: theta(z) * np.exp(-lambd*(T-z))
r_mean = r0*np.exp(-lambd*T) + lambd * integrate.trapz(temp(zGrid),zGrid)
return r_mean
def HW_r_0(P0T,lambd,eta):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
return r0
def HW_Mu_FrwdMeasure(P0T,lambd,eta,T):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = HW_theta(lambd,eta,P0T)
zGrid = np.linspace(0.0,T,500)
theta_hat =lambda t,T: theta(t) + eta*eta / lambd *1.0/lambd * (np.exp(-lambd*(T-t))-1.0)
temp =lambda z: theta_hat(z,T) * np.exp(-lambd*(T-z))
r_mean = r0*np.exp(-lambd*T) + lambd * integrate.trapz(temp(zGrid),zGrid)
return r_mean
def HWVar_r(lambd,eta,T):
return eta*eta/(2.0*lambd) *( 1.0-np.exp(-2.0*lambd *T))
def HWDensity(P0T,lambd,eta,T):
r_mean = HWMean_r(P0T,lambd,eta,T)
r_var = HWVar_r(lambd,eta,T)
return lambda x: st.norm.pdf(x,r_mean,np.sqrt(r_var))
def HW_CapletFloorletPrice(CP,N,K,lambd,eta,P0T,T1,T2):
if CP == OptionType.CALL:
N_new = N * (1.0+(T2-T1)*K)
K_new = 1.0 + (T2-T1)*K
caplet = N_new*HW_ZCB_CallPutPrice(OptionType.PUT,1.0/K_new,lambd,eta,P0T,T1,T2)
value= caplet
elif CP==OptionType.PUT:
value = 0.0 # In the homework assignmnet you need to fill this part in
return value
def HW_ZCB_CallPutPrice(CP,K,lambd,eta,P0T,T1,T2):
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
mu_r = HW_Mu_FrwdMeasure(P0T,lambd,eta,T1)
v_r = np.sqrt(HWVar_r(lambd,eta,T1))
K_hat = K * np.exp(-A_r)
a = (np.log(K_hat) - B_r*mu_r)/(B_r*v_r)
d1 = a - B_r*v_r
d2 = d1 + B_r*v_r
term1 = np.exp(0.5* B_r*B_r*v_r*v_r + B_r*mu_r)*st.norm.cdf(d1) - K_hat * st.norm.cdf(d2)
value =P0T(T1) * np.exp(A_r) * term1
if CP == OptionType.CALL:
return value
elif CP==OptionType.PUT:
return value - P0T(T2) + K*P0T(T1)
def mainCalculation():
CP= OptionType.CALL
NoOfPaths = 20000
NoOfSteps = 1000
lambd = 0.02
eta = 0.02
# We define a ZCB curve (obtained from the market)
P0T = lambda T: np.exp(-0.1*T)#np.exp(-0.03*T*T-0.1*T)
r0 = HW_r_0(P0T,lambd,eta)
# In this experiment we compare ZCB from the Market and Analytical expression
N = 25
T_end = 50
Tgrid= np.linspace(0,T_end,N)
Exact = np.zeros([N,1])
Proxy= np.zeros ([N,1])
for i,Ti in enumerate(Tgrid):
Proxy[i] = HW_ZCB(lambd,eta,P0T,0.0,Ti,r0)
Exact[i] = P0T(Ti)
plt.figure(1)
plt.grid()
plt.plot(Tgrid,Exact,'-k')
plt.plot(Tgrid,Proxy,'--r')
plt.legend(["Analytcal ZCB","Monte Carlo ZCB"])
plt.title('P(0,T) from Monte Carlo vs. Analytical expression')
# In this experiment we compare Monte Carlo results for
T1 = 4.0
T2 = 8.0
paths= GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T1 ,P0T, lambd, eta)
r = paths["R"]
timeGrid = paths["time"]
dt = timeGrid[1]-timeGrid[0]
# Here we compare the price of an option on a ZCB from Monte Carlo and Analytical expression
M_t = np.zeros([NoOfPaths,NoOfSteps])
for i in range(0,NoOfPaths):
M_t[i,:] = np.exp(np.cumsum(r[i,:-1])*dt)
KVec = np.linspace(0.01,1.7,50)
Price_MC_V = np.zeros([len(KVec),1])
Price_Th_V =np.zeros([len(KVec),1])
P_T1_T2 = HW_ZCB(lambd,eta,P0T,T1,T2,r[:,-1])
for i,K in enumerate(KVec):
if CP==OptionType.CALL:
Price_MC_V[i] =np.mean( 1.0/M_t[:,-1] * np.maximum(P_T1_T2-K,0.0))
elif CP==OptionType.PUT:
Price_MC_V[i] =np.mean( 1.0/M_t[:,-1] * np.maximum(K-P_T1_T2,0.0))
Price_Th_V[i] =HW_ZCB_CallPutPrice(CP,K,lambd,eta,P0T,T1,T2)#HW_ZCB_CallPrice(K,lambd,eta,P0T,T1,T2)
plt.figure(2)
plt.grid()
plt.plot(KVec,Price_MC_V)
plt.plot(KVec,Price_Th_V,'--r')
plt.legend(['Monte Carlo','Theoretical'])
plt.title('Option on ZCB')
# Effect of the HW model parameters on Implied Volatilities
# define a forward rate between T1 and T2
frwd = 1.0/(T2-T1) *(P0T(T1)/P0T(T2)-1.0)
K = np.linspace(frwd/2.0,3.0*frwd,25)
Notional = 1.0
capletPrice = np.zeros(len(K))
for idx in range(0,len(K)):
capletPrice[idx] = HW_CapletFloorletPrice(CP,Notional,K[idx],lambd,eta,P0T,T1,T2)
plt.figure(3)
plt.title('Caplet Price')
plt.plot(K,capletPrice)
plt.xlabel('strike')
plt.ylabel('Caplet Price')
plt.grid()
mainCalculation()
================================================
FILE: Lecture 05-Interest Rate Products/Materials/HW_OptionsOnZCBs.py
================================================
#%%
"""
Created on July 12 2021
Options on ZCBs under the Hull-White model
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import enum
import matplotlib.pyplot as plt
import scipy.stats as st
import scipy.integrate as integrate
from scipy import interpolate
# This class defines puts and calls
class OptionType(enum.Enum):
CALL = 1.0
PUT = -1.0
def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#theta = lambda t: 0.1 +t -t
#print("changed theta")
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
R[:,0]=r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta(time[i]) - R[:,i]) * dt + eta* (W[:,i+1]-W[:,i])
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R}
return paths
def HW_theta(lambd,eta,P0T):
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#print("CHANGED THETA")
return theta#lambda t: 0.1+t-t
def HW_A(lambd,eta,P0T,T1,T2):
tau = T2-T1
zGrid = np.linspace(0.0,tau,250)
B_r = lambda tau: 1.0/lambd * (np.exp(-lambd *tau)-1.0)
theta = HW_theta(lambd,eta,P0T)
temp1 = lambd * integrate.trapz(theta(T2-zGrid)*B_r(zGrid),zGrid)
temp2 = eta*eta/(4.0*np.power(lambd,3.0)) * (np.exp(-2.0*lambd*tau)*(4*np.exp(lambd*tau)-1.0) -3.0) + eta*eta*tau/(2.0*lambd*lambd)
return temp1 + temp2
def HW_B(lambd,eta,T1,T2):
return 1.0/lambd *(np.exp(-lambd*(T2-T1))-1.0)
def HW_ZCB(lambd,eta,P0T,T1,T2,rT1):
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
return np.exp(A_r + B_r *rT1)
def HWMean_r(P0T,lambd,eta,T):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2.0*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = HW_theta(lambd,eta,P0T)
zGrid = np.linspace(0.0,T,2500)
temp =lambda z: theta(z) * np.exp(-lambd*(T-z))
r_mean = r0*np.exp(-lambd*T) + lambd * integrate.trapz(temp(zGrid),zGrid)
return r_mean
def HW_r_0(P0T,lambd,eta):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
return r0
def HW_Mu_FrwdMeasure(P0T,lambd,eta,T):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = HW_theta(lambd,eta,P0T)
zGrid = np.linspace(0.0,T,500)
theta_hat =lambda t,T: theta(t) + eta*eta / lambd *1.0/lambd * (np.exp(-lambd*(T-t))-1.0)
temp =lambda z: theta_hat(z,T) * np.exp(-lambd*(T-z))
r_mean = r0*np.exp(-lambd*T) + lambd * integrate.trapz(temp(zGrid),zGrid)
return r_mean
def HWVar_r(lambd,eta,T):
return eta*eta/(2.0*lambd) *( 1.0-np.exp(-2.0*lambd *T))
def HWDensity(P0T,lambd,eta,T):
r_mean = HWMean_r(P0T,lambd,eta,T)
r_var = HWVar_r(lambd,eta,T)
return lambda x: st.norm.pdf(x,r_mean,np.sqrt(r_var))
def HW_ZCB_CallPutPrice(CP,K,lambd,eta,P0T,T1,T2):
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
mu_r = HW_Mu_FrwdMeasure(P0T,lambd,eta,T1)
v_r = np.sqrt(HWVar_r(lambd,eta,T1))
K_hat = K * np.exp(-A_r)
a = (np.log(K_hat) - B_r*mu_r)/(B_r*v_r)
d1 = a - B_r*v_r
d2 = d1 + B_r*v_r
term1 = np.exp(0.5* B_r*B_r*v_r*v_r + B_r*mu_r)*st.norm.cdf(d1) - K_hat * st.norm.cdf(d2)
value =P0T(T1) * np.exp(A_r) * term1
if CP == OptionType.CALL:
return value
elif CP==OptionType.PUT:
return value - P0T(T2) + K*P0T(T1)
def mainCalculation():
CP= OptionType.CALL
NoOfPaths = 20000
NoOfSteps = 1000
lambd = 0.02
eta = 0.02
# We define a ZCB curve (obtained from the market)
# We define a ZCB curve (obtained from the market), 1-D points with a B-spline curve.
ti = [0.0,0.00273972600000000,0.0876712330000000,0.172602740000000,0.257534247000000,0.342465753000000,0.427397260000000,0.512328767000000,0.597260274000000,0.682191781000000,0.767123288000000,0.852054795000000,0.936986301000000,1.02191780800000,1.10684931500000,1.19178082200000,1.27671232900000,1.36164383600000,1.44657534200000,1.53150684900000,1.61643835600000,1.70136986300000,1.78630137000000,1.87123287700000,1.95616438400000,2.04109589000000,2.12602739700000,2.21095890400000,2.29589041100000,2.38082191800000,2.46575342500000,2.55068493200000,2.63561643800000,2.72054794500000,2.80547945200000,2.89041095900000,2.97534246600000,3.06027397300000,3.14520547900000,3.23013698600000,3.31506849300000,3.40000000000000,3.48493150700000,3.56986301400000,3.65479452100000,3.73972602700000,3.82465753400000,3.90958904100000,3.99452054800000,4.07945205500000,4.16438356200000,4.24931506800000,4.33424657500000,4.41917808200000,4.50410958900000,4.58904109600000,4.67397260300000,4.75890411000000,4.84383561600000,4.92876712300000,5.01369863000000,5.09863013700000,5.18356164400000,5.26849315100000,5.35342465800000,5.43835616400000,5.52328767100000,5.60821917800000,5.69315068500000,5.77808219200000,5.86301369900000,5.94794520500000,6.03287671200000,6.11780821900000,6.20273972600000,6.28767123300000,6.37260274000000,6.45753424700000,6.54246575300000,6.62739726000000,6.71232876700000,6.79726027400000,6.88219178100000,6.96712328800000,7.05205479500000,7.13698630100000,7.22191780800000,7.30684931500000,7.39178082200000,7.47671232900000,7.56164383600000,7.64657534200000,7.73150684900000,7.81643835600000,7.90136986300000,7.98630137000000,8.07123287700000,8.15616438400000,8.24109589000000,8.32602739700000,8.41095890400000,8.49589041100000,8.58082191800000,8.66575342500000,8.75068493200000,8.83561643800000,8.92054794500000,9.00547945200000,9.09041095900000,9.17534246600000,9.26027397300000,9.34520547900000,9.43013698600000,9.51506849300000,9.60000000000000,9.68493150700000,9.76986301400000,9.85479452100000,9.93972602700000,10.0246575300000,10.1095890400000,10.1945205500000,10.2794520500000,10.3643835600000,10.4493150700000,10.5342465800000,10.6191780800000,10.7041095900000,10.7890411000000,10.8739726000000,10.9589041100000,11.0438356200000,11.1287671200000,11.2136986300000,11.2986301400000,11.3835616400000,11.4684931500000,11.5534246600000,11.6383561600000,11.7232876700000,11.8082191800000,11.8931506800000,11.9780821900000,12.0630137000000,12.1479452100000,12.2328767100000,12.3178082200000,12.4027397300000,12.4876712300000,12.5726027400000,12.6575342500000,12.7424657500000,12.8273972600000,12.9123287700000,12.9972602700000,13.0821917800000,13.1671232900000,13.2520547900000,13.3369863000000,13.4219178100000,13.5068493200000,13.5917808200000,13.6767123300000,13.7616438400000,13.8465753400000,13.9315068500000,14.0164383600000,14.1013698600000,14.1863013700000,14.2712328800000,14.3561643800000,14.4410958900000,14.5260274000000,14.6109589000000,14.6958904100000,14.7808219200000,14.8657534200000,14.9506849300000,15.0356164400000,15.1205479500000,15.2054794500000,15.2904109600000,15.3753424700000,15.4602739700000,15.5452054800000,15.6301369900000,15.7150684900000,15.8000000000000,15.8849315100000,15.9698630100000,16.0547945200000,16.1397260300000,16.2246575300000,16.3095890400000,16.3945205500000,16.4794520500000,16.5643835600000,16.6493150700000,16.7342465800000,16.8191780800000,16.9041095900000,16.9890411000000,17.0739726000000,17.1589041100000,17.2438356200000,17.3287671200000,17.4136986300000,17.4986301400000,17.5835616400000,17.6684931500000,17.7534246600000,17.8383561600000,17.9232876700000,18.0082191800000,18.0931506800000,18.1780821900000,18.2630137000000,18.3479452100000,18.4328767100000,18.5178082200000,18.6027397300000,18.6876712300000,18.7726027400000,18.8575342500000,18.9424657500000,19.0273972600000,19.1123287700000,19.1972602700000,19.2821917800000,19.3671232900000,19.4520547900000,19.5369863000000,19.6219178100000,19.7068493200000,19.7917808200000,19.8767123300000,19.9616438400000,20.0465753400000,20.1315068500000,20.2164383600000,20.3013698600000,20.3863013700000,20.4712328800000,20.5561643800000,20.6410958900000,20.7260274000000,20.8109589000000,20.8958904100000,20.9808219200000,21.0657534200000,21.1506849300000,21.2356164400000,21.3205479500000,21.4054794500000,21.4904109600000,21.5753424700000,21.6602739700000,21.7452054800000,21.8301369900000,21.9150684900000,22,22.0849315100000,22.1698630100000,22.2547945200000,22.3397260300000,22.4246575300000,22.5095890400000,22.5945205500000,22.6794520500000,22.7643835600000,22.8493150700000,22.9342465800000,23.0191780800000,23.1041095900000,23.1890411000000,23.2739726000000,23.3589041100000,23.4438356200000,23.5287671200000,23.6136986300000,23.6986301400000,23.7835616400000,23.8684931500000,23.9534246600000,24.0383561600000,24.1232876700000,24.2082191800000,24.2931506800000,24.3780821900000,24.4630137000000,24.5479452100000,24.6328767100000,24.7178082200000,24.8027397300000,24.8876712300000,24.9726027400000,25.0575342500000,25.1424657500000,25.2273972600000,25.3123287700000,25.3972602700000,25.4821917800000,25.5671232900000,25.6520547900000,25.7369863000000,25.8219178100000,25.9068493200000,25.9917808200000,26.0767123300000,26.1616438400000,26.2465753400000,26.3315068500000,26.4164383600000,26.5013698600000,26.5863013700000,26.6712328800000,26.7561643800000,26.8410958900000,26.9260274000000,27.0109589000000,27.0958904100000,27.1808219200000,27.2657534200000,27.3506849300000,27.4356164400000,27.5205479500000,27.6054794500000,27.6904109600000,27.7753424700000,27.8602739700000,27.9452054800000,28.0301369900000,28.1150684900000,28.2000000000000,28.2849315100000,28.3698630100000,28.4547945200000,28.5397260300000,28.6246575300000,28.7095890400000,28.7945205500000,28.8794520500000,28.9643835600000,29.0493150700000,29.1342465800000,29.2191780800000,29.3041095900000,29.3890411000000,29.4739726000000,29.5589041100000,29.6438356200000,29.7287671200000,29.8136986300000,29.8986301400000,29.9835616400000,30.0684931500000,30.1534246600000,30.2383561600000,30.3232876700000,30.4082191800000,30.4931506800000,30.5780821900000,30.6630137000000,30.7479452100000,30.8328767100000,30.9178082200000,31.0027397300000,31.0876712300000,31.1726027400000,31.2575342500000,31.3424657500000,31.4273972600000,31.5123287700000,31.5972602700000,31.6821917800000,31.7671232900000,31.8520547900000,31.9369863000000,32.0219178100000,32.1068493200000,32.1917808200000,32.2767123300000,32.3616438400000,32.4465753400000,32.5315068500000,32.6164383600000,32.7013698600000,32.7863013700000,32.8712328800000,32.9561643800000,33.0410958900000,33.1260274000000,33.2109589000000,33.2958904100000,33.3808219200000,33.4657534200000,33.5506849300000,33.6356164400000,33.7205479500000,33.8054794500000,33.8904109600000,33.9753424700000,34.0602739700000,34.1452054800000,34.2301369900000,34.3150684900000,34.4000000000000,34.4849315100000,34.5698630100000,34.6547945200000,34.7397260300000,34.8246575300000,34.9095890400000,34.9945205500000,35.0794520500000,35.1643835600000,35.2493150700000,35.3342465800000,35.4191780800000,35.5041095900000,35.5890411000000,35.6739726000000,35.7589041100000,35.8438356200000,35.9287671200000,36.0136986300000,36.0986301400000,36.1835616400000,36.2684931500000,36.3534246600000,36.4383561600000,36.5232876700000,36.6082191800000,36.6931506800000,36.7780821900000,36.8630137000000,36.9479452100000,37.0328767100000,37.1178082200000,37.2027397300000,37.2876712300000,37.3726027400000,37.4575342500000,37.5424657500000,37.6273972600000,37.7123287700000,37.7972602700000,37.8821917800000,37.9671232900000,38.0520547900000,38.1369863000000,38.2219178100000,38.3068493200000,38.3917808200000,38.4767123300000,38.5616438400000,38.6465753400000,38.7315068500000,38.8164383600000,38.9013698600000,38.9863013700000,39.0712328800000,39.1561643800000,39.2410958900000,39.3260274000000,39.4109589000000,39.4958904100000,39.5808219200000,39.6657534200000,39.7506849300000,39.8356164400000,39.9205479500000,40.0054794500000]
Pi = [1.0,0.999966573000000,0.998930882000000,0.997824062000000,0.996511145000000,0.995199956000000,0.993821602000000,0.992277014000000,0.990734827000000,0.989164324000000,0.987428762000000,0.985704346000000,0.983946708000000,0.982068207000000,0.980193293000000,0.978281187000000,0.976255832000000,0.974234670000000,0.972174514000000,0.970028236000000,0.967886697000000,0.965693800000000,0.963440984000000,0.961193424000000,0.958903753000000,0.956575247000000,0.954252397000000,0.951842433000000,0.949228406000000,0.946482248000000,0.943632525000000,0.940707509000000,0.937735160000000,0.934743101000000,0.931758611000000,0.928808623000000,0.925919731000000,0.923110403000000,0.920338655000000,0.917589739000000,0.914858924000000,0.912141532000000,0.909432941000000,0.906728583000000,0.904023944000000,0.901314567000000,0.898596044000000,0.895864025000000,0.893114214000000,0.890346348000000,0.887569841000000,0.884786140000000,0.881996090000000,0.879200528000000,0.876400278000000,0.873596157000000,0.870788975000000,0.867979528000000,0.865168605000000,0.862356987000000,0.859545442000000,0.856732322000000,0.853915005000000,0.851094371000000,0.848271318000000,0.845446732000000,0.842621487000000,0.839796449000000,0.836972470000000,0.834150393000000,0.831331050000000,0.828515261000000,0.825703790000000,0.822893949000000,0.820084325000000,0.817275636000000,0.814468590000000,0.811663887000000,0.808862219000000,0.806064266000000,0.803270701000000,0.800482187000000,0.797699379000000,0.794922921000000,0.792153338000000,0.789389717000000,0.786631547000000,0.783878633000000,0.781130782000000,0.778387801000000,0.775649505000000,0.772915708000000,0.770186227000000,0.767460882000000,0.764739497000000,0.762021898000000,0.759308200000000,0.756599781000000,0.753896558000000,0.751198194000000,0.748504356000000,0.745814715000000,0.743128950000000,0.740446741000000,0.737767776000000,0.735091745000000,0.732418345000000,0.729747277000000,0.727078670000000,0.724413443000000,0.721751645000000,0.719093279000000,0.716438346000000,0.713786850000000,0.711138792000000,0.708494177000000,0.705853006000000,0.703215284000000,0.700581015000000,0.697950202000000,0.695320544000000,0.692690217000000,0.690059929000000,0.687430378000000,0.684802253000000,0.682176235000000,0.679552992000000,0.676933185000000,0.674317462000000,0.671706465000000,0.669100824000000,0.666501159000000,0.663908082000000,0.661322194000000,0.658744089000000,0.656174348000000,0.653613545000000,0.651062245000000,0.648521002000000,0.645990363000000,0.643470865000000,0.640963036000000,0.638467395000000,0.635983411000000,0.633503959000000,0.631028021000000,0.628556105000000,0.626088714000000,0.623626343000000,0.621169478000000,0.618718599000000,0.616274178000000,0.613836679000000,0.611406558000000,0.608984265000000,0.606570242000000,0.604164924000000,0.601768738000000,0.599382105000000,0.597005438000000,0.594639144000000,0.592283621000000,0.589939262000000,0.587606453000000,0.585285574000000,0.582976996000000,0.580681085000000,0.578398202000000,0.576128698000000,0.573872922000000,0.571631214000000,0.569403908000000,0.567191334000000,0.564993814000000,0.562811666000000,0.560645202000000,0.558494727000000,0.556360543000000,0.554242913000000,0.552138115000000,0.550043470000000,0.547959012000000,0.545884776000000,0.543820795000000,0.541767101000000,0.539723726000000,0.537690699000000,0.535668051000000,0.533655809000000,0.531654001000000,0.529662652000000,0.527681790000000,0.525711438000000,0.523751620000000,0.521802360000000,0.519863678000000,0.517935598000000,0.516018137000000,0.514111318000000,0.512215157000000,0.510329674000000,0.508454885000000,0.506590807000000,0.504737456000000,0.502894847000000,0.501062994000000,0.499241910000000,0.497431609000000,0.495632103000000,0.493843403000000,0.492065521000000,0.490298466000000,0.488542249000000,0.486796879000000,0.485062364000000,0.483338711000000,0.481625929000000,0.479924024000000,0.478233003000000,0.476552870000000,0.474883632000000,0.473225293000000,0.471577858000000,0.469941330000000,0.468315712000000,0.466701007000000,0.465097218000000,0.463504347000000,0.461922395000000,0.460351364000000,0.458791253000000,0.457242065000000,0.455703798000000,0.454176452000000,0.452660027000000,0.451154522000000,0.449659935000000,0.448176257000000,0.446703083000000,0.445240105000000,0.443787209000000,0.442344286000000,0.440911226000000,0.439487920000000,0.438074261000000,0.436670143000000,0.435275460000000,0.433890109000000,0.432513986000000,0.431146989000000,0.429789017000000,0.428439971000000,0.427099751000000,0.425768260000000,0.424445399000000,0.423131074000000,0.421825189000000,0.420527650000000,0.419238363000000,0.417957237000000,0.416684180000000,0.415419101000000,0.414161910000000,0.412912520000000,0.411670842000000,0.410436789000000,0.409210275000000,0.407991213000000,0.406779521000000,0.405575114000000,0.404377909000000,0.403187824000000,0.402004777000000,0.400828688000000,0.399659478000000,0.398497066000000,0.397341375000000,0.396192326000000,0.395049844000000,0.393913852000000,0.392784274000000,0.391661036000000,0.390544064000000,0.389433283000000,0.388328623000000,0.387230010000000,0.386137373000000,0.385050641000000,0.383969745000000,0.382894615000000,0.381825182000000,0.380761378000000,0.379703135000000,0.378650387000000,0.377603067000000,0.376561110000000,0.375524516000000,0.374495509000000,0.373475055000000,0.372462955000000,0.371459016000000,0.370463043000000,0.369474847000000,0.368494238000000,0.367521029000000,0.366555034000000,0.365596072000000,0.364643959000000,0.363698516000000,0.362759566000000,0.361826932000000,0.360900441000000,0.359979918000000,0.359065194000000,0.358156099000000,0.357252465000000,0.356354126000000,0.355460918000000,0.354572678000000,0.353689243000000,0.352810455000000,0.351936156000000,0.351066187000000,0.350200393000000,0.349338622000000,0.348480719000000,0.347626535000000,0.346775919000000,0.345928722000000,0.345084799000000,0.344244003000000,0.343406190000000,0.342571217000000,0.341738943000000,0.340909228000000,0.340081932000000,0.339256917000000,0.338434048000000,0.337613190000000,0.336794207000000,0.335976969000000,0.335161343000000,0.334347200000000,0.333534411000000,0.332722847000000,0.331912384000000,0.331102895000000,0.330294257000000,0.329486347000000,0.328679044000000,0.327872227000000,0.327065778000000,0.326259578000000,0.325453511000000,0.324647462000000,0.323841447000000,0.323037558000000,0.322236538000000,0.321438361000000,0.320642997000000,0.319850421000000,0.319060604000000,0.318273519000000,0.317489140000000,0.316707441000000,0.315928394000000,0.315151974000000,0.314378155000000,0.313606911000000,0.312838216000000,0.312072045000000,0.311308373000000,0.310547175000000,0.309788426000000,0.309032102000000,0.308278178000000,0.307526629000000,0.306777432000000,0.306030564000000,0.305285999000000,0.304543716000000,0.303803690000000,0.303065898000000,0.302330318000000,0.301596926000000,0.300865701000000,0.300136619000000,0.299409659000000,0.298684798000000,0.297962014000000,0.297241286000000,0.296522593000000,0.295805912000000,0.295091222000000,0.294378503000000,0.293667734000000,0.292958894000000,0.292251962000000,0.291546917000000,0.290843741000000,0.290142412000000,0.289442910000000,0.288745217000000,0.288049311000000,0.287355175000000,0.286662788000000,0.285972131000000,0.285283185000000,0.284595932000000,0.283910353000000,0.283226429000000,0.282544142000000,0.281863473000000,0.281184405000000,0.280506920000000,0.279830999000000,0.279156625000000,0.278483781000000,0.277812449000000,0.277142612000000,0.276474253000000,0.275807354000000,0.275141900000000,0.274477873000000,0.273815257000000,0.273154036000000,0.272494193000000,0.271835712000000,0.271178577000000,0.270522772000000,0.269868283000000,0.269215092000000,0.268563184000000,0.267912545000000,0.267263159000000,0.266615011000000,0.265968086000000,0.265322370000000,0.264677847000000,0.264034503000000,0.263392324000000,0.262751295000000,0.262111403000000,0.261472633000000,0.260834972000000,0.260198405000000,0.259562919000000,0.258928501000000,0.258295136000000,0.257662813000000,0.257031517000000,0.256401236000000,0.255771957000000,0.255143667000000,0.254516353000000,0.253890002000000,0.253264603000000,0.252640144000000,0.252016611000000,0.251393992000000,0.250772277000000,0.250151453000000,0.249531508000000,0.248912431000000,0.248294210000000,0.247676834000000,0.247060291000000,0.246444571000000,0.245829663000000,0.245215555000000,0.244602237000000,0.243989698000000,0.243377928000000]
interpolator = interpolate.splrep(ti, Pi, s=0.0001)
P0T = lambda T: interpolate.splev(T, interpolator, der=0)
r0 = HW_r_0(P0T,lambd,eta)
# In this experiment we compare ZCB from the Market and Analytical expression
N = 25
T_end = 50
Tgrid= np.linspace(0,T_end,N)
Exact = np.zeros([N,1])
Proxy= np.zeros ([N,1])
for i,Ti in enumerate(Tgrid):
Proxy[i] = HW_ZCB(lambd,eta,P0T,0.0,Ti,r0)
Exact[i] = P0T(Ti)
plt.figure(1)
plt.grid()
plt.plot(Tgrid,Exact,'-k')
plt.plot(Tgrid,Proxy,'--r')
plt.legend(["Analytcal ZCB","Monte Carlo ZCB"])
plt.title('P(0,T) from Monte Carlo vs. Analytical expression')
# In this experiment we compare Monte Carlo results for
T1 = 4.0
T2 = 8.0
paths= GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T1 ,P0T, lambd, eta)
r = paths["R"]
timeGrid = paths["time"]
dt = timeGrid[1]-timeGrid[0]
# Here we compare the price of an option on a ZCB from Monte Carlo and Analytical expression
M_t = np.zeros([NoOfPaths,NoOfSteps])
for i in range(0,NoOfPaths):
M_t[i,:] = np.exp(np.cumsum(r[i,:-1])*dt)
KVec = np.linspace(0.01,1.7,50)
Price_MC_V = np.zeros([len(KVec),1])
Price_Th_V =np.zeros([len(KVec),1])
P_T1_T2 = HW_ZCB(lambd,eta,P0T,T1,T2,r[:,-1])
for i,K in enumerate(KVec):
if CP==OptionType.CALL:
Price_MC_V[i] =np.mean( 1.0/M_t[:,-1] * np.maximum(P_T1_T2-K,0.0))
elif CP==OptionType.PUT:
Price_MC_V[i] =np.mean( 1.0/M_t[:,-1] * np.maximum(K-P_T1_T2,0.0))
Price_Th_V[i] =HW_ZCB_CallPutPrice(CP,K,lambd,eta,P0T,T1,T2)
plt.figure(2)
plt.grid()
plt.plot(KVec,Price_MC_V)
plt.plot(KVec,Price_Th_V,'--r')
plt.legend(['Monte Carlo','Theoretical'])
plt.title('Call Option on ZCB')
plt.xlabel('Strike')
plt.ylabel('Option value')
mainCalculation()
================================================
FILE: Lecture 05-Interest Rate Products/Materials/Swaps_HW.py
================================================
#%%
"""
Created on July 12 2021
Swaps computed from a yield curve and from the Hull-White Model
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import enum
import matplotlib.pyplot as plt
import scipy.integrate as integrate
from scipy import interpolate
from scipy.optimize import newton
# This class defines puts and calls
class OptionTypeSwap(enum.Enum):
RECEIVER = 1.0
PAYER = -1.0
def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#theta = lambda t: 0.1 +t -t
#print("changed theta")
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
R[:,0]=r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta(time[i]) - R[:,i]) * dt + eta* (W[:,i+1]-W[:,i])
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R}
return paths
def HW_theta(lambd,eta,P0T):
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#print("CHANGED THETA")
return theta#lambda t: 0.1+t-t
def HW_A(lambd,eta,P0T,T1,T2):
tau = T2-T1
zGrid = np.linspace(0.0,tau,250)
B_r = lambda tau: 1.0/lambd * (np.exp(-lambd *tau)-1.0)
theta = HW_theta(lambd,eta,P0T)
temp1 = lambd * integrate.trapz(theta(T2-zGrid)*B_r(zGrid),zGrid)
temp2 = eta*eta/(4.0*np.power(lambd,3.0)) * (np.exp(-2.0*lambd*tau)*(4*np.exp(lambd*tau)-1.0) -3.0) + eta*eta*tau/(2.0*lambd*lambd)
return temp1 + temp2
def HW_B(lambd,eta,T1,T2):
return 1.0/lambd *(np.exp(-lambd*(T2-T1))-1.0)
def HW_ZCB(lambd,eta,P0T,T1,T2,rT1):
n = np.size(rT1)
if T1<T2:
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
return np.exp(A_r + B_r *rT1)
else:
return np.ones([n])
def HW_r_0(P0T,lambd,eta):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
return r0
def SwapPrice(CP,notional,K,t,Ti,Tm,n,P0T):
# CP- payer of receiver
# n- notional
# K- strike
# t- today's date
# Ti- beginning of the swap
# Tm- end of Swap
# n- number of dates payments between Ti and Tm
# r_t -interest rate at time t
if n == 1:
ti_grid =np.array([Ti,Tm])
else:
ti_grid = np.linspace(Ti,Tm,n)
tau = ti_grid[1]- ti_grid[0]
# overwrite Ti if t>Ti
prevTi = ti_grid[np.where(ti_grid<t)]
if np.size(prevTi) > 0: #prevTi != []:
Ti = prevTi[-1]
# Now we need to handle the case when some payments are already done
ti_grid = ti_grid[np.where(ti_grid>t)]
temp= 0.0
for (idx,ti) in enumerate(ti_grid):
if ti>Ti:
temp = temp + tau * P0T(ti)
if CP==OptionTypeSwap.PAYER:
swap = (P0T(Ti) - P0T(Tm)) - K * temp
elif CP==OptionTypeSwap.RECEIVER:
swap = K * temp- (P0T(Ti) - P0T(Tm))
return swap*notional
def HW_SwapPrice(CP,notional,K,t,Ti,Tm,n,r_t,P0T,lambd,eta):
# CP- payer of receiver
# n- notional
# K- strike
# t- today's date
# Ti- beginning of the swap
# Tm- end of Swap
# n- number of dates payments between Ti and Tm
# r_t -interest rate at time t
if n == 1:
ti_grid =np.array([Ti,Tm])
else:
ti_grid = np.linspace(Ti,Tm,n)
tau = ti_grid[1]- ti_grid[0]
# overwrite Ti if t>Ti
prevTi = ti_grid[np.where(ti_grid<t)]
if np.size(prevTi) > 0: #prevTi != []:
Ti = prevTi[-1]
# Now we need to handle the case when some payments are already done
ti_grid = ti_grid[np.where(ti_grid>t)]
temp= np.zeros(np.size(r_t));
P_t_TiLambda = lambda Ti : HW_ZCB(lambd,eta,P0T,t,Ti,r_t)
for (idx,ti) in enumerate(ti_grid):
if ti>Ti:
temp = temp + tau * P_t_TiLambda(ti)
P_t_Ti = P_t_TiLambda(Ti)
P_t_Tm = P_t_TiLambda(Tm)
if CP==OptionTypeSwap.PAYER:
swap = (P_t_Ti - P_t_Tm) - K * temp
elif CP==OptionTypeSwap.RECEIVER:
swap = K * temp- (P_t_Ti - P_t_Tm)
return swap*notional
def mainCalculation():
NoOfPaths = 2000
NoOfSteps = 1000
CP = OptionTypeSwap.PAYER
lambd = 0.5
eta = 0.03
notional = 10000.0
# We define a ZCB curve (obtained from the market), 1-D points with a B-spline curve.
ti = [0.0,0.00273972600000000,0.0876712330000000,0.172602740000000,0.257534247000000,0.342465753000000,0.427397260000000,0.512328767000000,0.597260274000000,0.682191781000000,0.767123288000000,0.852054795000000,0.936986301000000,1.02191780800000,1.10684931500000,1.19178082200000,1.27671232900000,1.36164383600000,1.44657534200000,1.53150684900000,1.61643835600000,1.70136986300000,1.78630137000000,1.87123287700000,1.95616438400000,2.04109589000000,2.12602739700000,2.21095890400000,2.29589041100000,2.38082191800000,2.46575342500000,2.55068493200000,2.63561643800000,2.72054794500000,2.80547945200000,2.89041095900000,2.97534246600000,3.06027397300000,3.14520547900000,3.23013698600000,3.31506849300000,3.40000000000000,3.48493150700000,3.56986301400000,3.65479452100000,3.73972602700000,3.82465753400000,3.90958904100000,3.99452054800000,4.07945205500000,4.16438356200000,4.24931506800000,4.33424657500000,4.41917808200000,4.50410958900000,4.58904109600000,4.67397260300000,4.75890411000000,4.84383561600000,4.92876712300000,5.01369863000000,5.09863013700000,5.18356164400000,5.26849315100000,5.35342465800000,5.43835616400000,5.52328767100000,5.60821917800000,5.69315068500000,5.77808219200000,5.86301369900000,5.94794520500000,6.03287671200000,6.11780821900000,6.20273972600000,6.28767123300000,6.37260274000000,6.45753424700000,6.54246575300000,6.62739726000000,6.71232876700000,6.79726027400000,6.88219178100000,6.96712328800000,7.05205479500000,7.13698630100000,7.22191780800000,7.30684931500000,7.39178082200000,7.47671232900000,7.56164383600000,7.64657534200000,7.73150684900000,7.81643835600000,7.90136986300000,7.98630137000000,8.07123287700000,8.15616438400000,8.24109589000000,8.32602739700000,8.41095890400000,8.49589041100000,8.58082191800000,8.66575342500000,8.75068493200000,8.83561643800000,8.92054794500000,9.00547945200000,9.09041095900000,9.17534246600000,9.26027397300000,9.34520547900000,9.43013698600000,9.51506849300000,9.60000000000000,9.68493150700000,9.76986301400000,9.85479452100000,9.93972602700000,10.0246575300000,10.1095890400000,10.1945205500000,10.2794520500000,10.3643835600000,10.4493150700000,10.5342465800000,10.6191780800000,10.7041095900000,10.7890411000000,10.8739726000000,10.9589041100000,11.0438356200000,11.1287671200000,11.2136986300000,11.2986301400000,11.3835616400000,11.4684931500000,11.5534246600000,11.6383561600000,11.7232876700000,11.8082191800000,11.8931506800000,11.9780821900000,12.0630137000000,12.1479452100000,12.2328767100000,12.3178082200000,12.4027397300000,12.4876712300000,12.5726027400000,12.6575342500000,12.7424657500000,12.8273972600000,12.9123287700000,12.9972602700000,13.0821917800000,13.1671232900000,13.2520547900000,13.3369863000000,13.4219178100000,13.5068493200000,13.5917808200000,13.6767123300000,13.7616438400000,13.8465753400000,13.9315068500000,14.0164383600000,14.1013698600000,14.1863013700000,14.2712328800000,14.3561643800000,14.4410958900000,14.5260274000000,14.6109589000000,14.6958904100000,14.7808219200000,14.8657534200000,14.9506849300000,15.0356164400000,15.1205479500000,15.2054794500000,15.2904109600000,15.3753424700000,15.4602739700000,15.5452054800000,15.6301369900000,15.7150684900000,15.8000000000000,15.8849315100000,15.9698630100000,16.0547945200000,16.1397260300000,16.2246575300000,16.3095890400000,16.3945205500000,16.4794520500000,16.5643835600000,16.6493150700000,16.7342465800000,16.8191780800000,16.9041095900000,16.9890411000000,17.0739726000000,17.1589041100000,17.2438356200000,17.3287671200000,17.4136986300000,17.4986301400000,17.5835616400000,17.6684931500000,17.7534246600000,17.8383561600000,17.9232876700000,18.0082191800000,18.0931506800000,18.1780821900000,18.2630137000000,18.3479452100000,18.4328767100000,18.5178082200000,18.6027397300000,18.6876712300000,18.7726027400000,18.8575342500000,18.9424657500000,19.0273972600000,19.1123287700000,19.1972602700000,19.2821917800000,19.3671232900000,19.4520547900000,19.5369863000000,19.6219178100000,19.7068493200000,19.7917808200000,19.8767123300000,19.9616438400000,20.0465753400000,20.1315068500000,20.2164383600000,20.3013698600000,20.3863013700000,20.4712328800000,20.5561643800000,20.6410958900000,20.7260274000000,20.8109589000000,20.8958904100000,20.9808219200000,21.0657534200000,21.1506849300000,21.2356164400000,21.3205479500000,21.4054794500000,21.4904109600000,21.5753424700000,21.6602739700000,21.7452054800000,21.8301369900000,21.9150684900000,22,22.0849315100000,22.1698630100000,22.2547945200000,22.3397260300000,22.4246575300000,22.5095890400000,22.5945205500000,22.6794520500000,22.7643835600000,22.8493150700000,22.9342465800000,23.0191780800000,23.1041095900000,23.1890411000000,23.2739726000000,23.3589041100000,23.4438356200000,23.5287671200000,23.6136986300000,23.6986301400000,23.7835616400000,23.8684931500000,23.9534246600000,24.0383561600000,24.1232876700000,24.2082191800000,24.2931506800000,24.3780821900000,24.4630137000000,24.5479452100000,24.6328767100000,24.7178082200000,24.8027397300000,24.8876712300000,24.9726027400000,25.0575342500000,25.1424657500000,25.2273972600000,25.3123287700000,25.3972602700000,25.4821917800000,25.5671232900000,25.6520547900000,25.7369863000000,25.8219178100000,25.9068493200000,25.9917808200000,26.0767123300000,26.1616438400000,26.2465753400000,26.3315068500000,26.4164383600000,26.5013698600000,26.5863013700000,26.6712328800000,26.7561643800000,26.8410958900000,26.9260274000000,27.0109589000000,27.0958904100000,27.1808219200000,27.2657534200000,27.3506849300000,27.4356164400000,27.5205479500000,27.6054794500000,27.6904109600000,27.7753424700000,27.8602739700000,27.9452054800000,28.0301369900000,28.1150684900000,28.2000000000000,28.2849315100000,28.3698630100000,28.4547945200000,28.5397260300000,28.6246575300000,28.7095890400000,28.7945205500000,28.8794520500000,28.9643835600000,29.0493150700000,29.1342465800000,29.2191780800000,29.3041095900000,29.3890411000000,29.4739726000000,29.5589041100000,29.6438356200000,29.7287671200000,29.8136986300000,29.8986301400000,29.9835616400000,30.0684931500000,30.1534246600000,30.2383561600000,30.3232876700000,30.4082191800000,30.4931506800000,30.5780821900000,30.6630137000000,30.7479452100000,30.8328767100000,30.9178082200000,31.0027397300000,31.0876712300000,31.1726027400000,31.2575342500000,31.3424657500000,31.4273972600000,31.5123287700000,31.5972602700000,31.6821917800000,31.7671232900000,31.8520547900000,31.9369863000000,32.0219178100000,32.1068493200000,32.1917808200000,32.2767123300000,32.3616438400000,32.4465753400000,32.5315068500000,32.6164383600000,32.7013698600000,32.7863013700000,32.8712328800000,32.9561643800000,33.0410958900000,33.1260274000000,33.2109589000000,33.2958904100000,33.3808219200000,33.4657534200000,33.5506849300000,33.6356164400000,33.7205479500000,33.8054794500000,33.8904109600000,33.9753424700000,34.0602739700000,34.1452054800000,34.2301369900000,34.3150684900000,34.4000000000000,34.4849315100000,34.5698630100000,34.6547945200000,34.7397260300000,34.8246575300000,34.9095890400000,34.9945205500000,35.0794520500000,35.1643835600000,35.2493150700000,35.3342465800000,35.4191780800000,35.5041095900000,35.5890411000000,35.6739726000000,35.7589041100000,35.8438356200000,35.9287671200000,36.0136986300000,36.0986301400000,36.1835616400000,36.2684931500000,36.3534246600000,36.4383561600000,36.5232876700000,36.6082191800000,36.6931506800000,36.7780821900000,36.8630137000000,36.9479452100000,37.0328767100000,37.1178082200000,37.2027397300000,37.2876712300000,37.3726027400000,37.4575342500000,37.5424657500000,37.6273972600000,37.7123287700000,37.7972602700000,37.8821917800000,37.9671232900000,38.0520547900000,38.1369863000000,38.2219178100000,38.3068493200000,38.3917808200000,38.4767123300000,38.5616438400000,38.6465753400000,38.7315068500000,38.8164383600000,38.9013698600000,38.9863013700000,39.0712328800000,39.1561643800000,39.2410958900000,39.3260274000000,39.4109589000000,39.4958904100000,39.5808219200000,39.6657534200000,39.7506849300000,39.8356164400000,39.9205479500000,40.0054794500000]
Pi = [1.0,0.999966573000000,0.998930882000000,0.997824062000000,0.996511145000000,0.995199956000000,0.993821602000000,0.992277014000000,0.990734827000000,0.989164324000000,0.987428762000000,0.985704346000000,0.983946708000000,0.982068207000000,0.980193293000000,0.978281187000000,0.976255832000000,0.974234670000000,0.972174514000000,0.970028236000000,0.967886697000000,0.965693800000000,0.963440984000000,0.961193424000000,0.958903753000000,0.956575247000000,0.954252397000000,0.951842433000000,0.949228406000000,0.946482248000000,0.943632525000000,0.940707509000000,0.937735160000000,0.934743101000000,0.931758611000000,0.928808623000000,0.925919731000000,0.923110403000000,0.920338655000000,0.917589739000000,0.914858924000000,0.912141532000000,0.909432941000000,0.906728583000000,0.904023944000000,0.901314567000000,0.898596044000000,0.895864025000000,0.893114214000000,0.890346348000000,0.887569841000000,0.884786140000000,0.881996090000000,0.879200528000000,0.876400278000000,0.873596157000000,0.870788975000000,0.867979528000000,0.865168605000000,0.862356987000000,0.859545442000000,0.856732322000000,0.853915005000000,0.851094371000000,0.848271318000000,0.845446732000000,0.842621487000000,0.839796449000000,0.836972470000000,0.834150393000000,0.831331050000000,0.828515261000000,0.825703790000000,0.822893949000000,0.820084325000000,0.817275636000000,0.814468590000000,0.811663887000000,0.808862219000000,0.806064266000000,0.803270701000000,0.800482187000000,0.797699379000000,0.794922921000000,0.792153338000000,0.789389717000000,0.786631547000000,0.783878633000000,0.781130782000000,0.778387801000000,0.775649505000000,0.772915708000000,0.770186227000000,0.767460882000000,0.764739497000000,0.762021898000000,0.759308200000000,0.756599781000000,0.753896558000000,0.751198194000000,0.748504356000000,0.745814715000000,0.743128950000000,0.740446741000000,0.737767776000000,0.735091745000000,0.732418345000000,0.729747277000000,0.727078670000000,0.724413443000000,0.721751645000000,0.719093279000000,0.716438346000000,0.713786850000000,0.711138792000000,0.708494177000000,0.705853006000000,0.703215284000000,0.700581015000000,0.697950202000000,0.695320544000000,0.692690217000000,0.690059929000000,0.687430378000000,0.684802253000000,0.682176235000000,0.679552992000000,0.676933185000000,0.674317462000000,0.671706465000000,0.669100824000000,0.666501159000000,0.663908082000000,0.661322194000000,0.658744089000000,0.656174348000000,0.653613545000000,0.651062245000000,0.648521002000000,0.645990363000000,0.643470865000000,0.640963036000000,0.638467395000000,0.635983411000000,0.633503959000000,0.631028021000000,0.628556105000000,0.626088714000000,0.623626343000000,0.621169478000000,0.618718599000000,0.616274178000000,0.613836679000000,0.611406558000000,0.608984265000000,0.606570242000000,0.604164924000000,0.601768738000000,0.599382105000000,0.597005438000000,0.594639144000000,0.592283621000000,0.589939262000000,0.587606453000000,0.585285574000000,0.582976996000000,0.580681085000000,0.578398202000000,0.576128698000000,0.573872922000000,0.571631214000000,0.569403908000000,0.567191334000000,0.564993814000000,0.562811666000000,0.560645202000000,0.558494727000000,0.556360543000000,0.554242913000000,0.552138115000000,0.550043470000000,0.547959012000000,0.545884776000000,0.543820795000000,0.541767101000000,0.539723726000000,0.537690699000000,0.535668051000000,0.533655809000000,0.531654001000000,0.529662652000000,0.527681790000000,0.525711438000000,0.523751620000000,0.521802360000000,0.519863678000000,0.517935598000000,0.516018137000000,0.514111318000000,0.512215157000000,0.510329674000000,0.508454885000000,0.506590807000000,0.504737456000000,0.502894847000000,0.501062994000000,0.499241910000000,0.497431609000000,0.495632103000000,0.493843403000000,0.492065521000000,0.490298466000000,0.488542249000000,0.486796879000000,0.485062364000000,0.483338711000000,0.481625929000000,0.479924024000000,0.478233003000000,0.476552870000000,0.474883632000000,0.473225293000000,0.471577858000000,0.469941330000000,0.468315712000000,0.466701007000000,0.465097218000000,0.463504347000000,0.461922395000000,0.460351364000000,0.458791253000000,0.457242065000000,0.455703798000000,0.454176452000000,0.452660027000000,0.451154522000000,0.449659935000000,0.448176257000000,0.446703083000000,0.445240105000000,0.443787209000000,0.442344286000000,0.440911226000000,0.439487920000000,0.438074261000000,0.436670143000000,0.435275460000000,0.433890109000000,0.432513986000000,0.431146989000000,0.429789017000000,0.428439971000000,0.427099751000000,0.425768260000000,0.424445399000000,0.423131074000000,0.421825189000000,0.420527650000000,0.419238363000000,0.417957237000000,0.416684180000000,0.415419101000000,0.414161910000000,0.412912520000000,0.411670842000000,0.410436789000000,0.409210275000000,0.407991213000000,0.406779521000000,0.405575114000000,0.404377909000000,0.403187824000000,0.402004777000000,0.400828688000000,0.399659478000000,0.398497066000000,0.397341375000000,0.396192326000000,0.395049844000000,0.393913852000000,0.392784274000000,0.391661036000000,0.390544064000000,0.389433283000000,0.388328623000000,0.387230010000000,0.386137373000000,0.385050641000000,0.383969745000000,0.382894615000000,0.381825182000000,0.380761378000000,0.379703135000000,0.378650387000000,0.377603067000000,0.376561110000000,0.375524516000000,0.374495509000000,0.373475055000000,0.372462955000000,0.371459016000000,0.370463043000000,0.369474847000000,0.368494238000000,0.367521029000000,0.366555034000000,0.365596072000000,0.364643959000000,0.363698516000000,0.362759566000000,0.361826932000000,0.360900441000000,0.359979918000000,0.359065194000000,0.358156099000000,0.357252465000000,0.356354126000000,0.355460918000000,0.354572678000000,0.353689243000000,0.352810455000000,0.351936156000000,0.351066187000000,0.350200393000000,0.349338622000000,0.348480719000000,0.347626535000000,0.346775919000000,0.345928722000000,0.345084799000000,0.344244003000000,0.343406190000000,0.342571217000000,0.341738943000000,0.340909228000000,0.340081932000000,0.339256917000000,0.338434048000000,0.337613190000000,0.336794207000000,0.335976969000000,0.335161343000000,0.334347200000000,0.333534411000000,0.332722847000000,0.331912384000000,0.331102895000000,0.330294257000000,0.329486347000000,0.328679044000000,0.327872227000000,0.327065778000000,0.326259578000000,0.325453511000000,0.324647462000000,0.323841447000000,0.323037558000000,0.322236538000000,0.321438361000000,0.320642997000000,0.319850421000000,0.319060604000000,0.318273519000000,0.317489140000000,0.316707441000000,0.315928394000000,0.315151974000000,0.314378155000000,0.313606911000000,0.312838216000000,0.312072045000000,0.311308373000000,0.310547175000000,0.309788426000000,0.309032102000000,0.308278178000000,0.307526629000000,0.306777432000000,0.306030564000000,0.305285999000000,0.304543716000000,0.303803690000000,0.303065898000000,0.302330318000000,0.301596926000000,0.300865701000000,0.300136619000000,0.299409659000000,0.298684798000000,0.297962014000000,0.297241286000000,0.296522593000000,0.295805912000000,0.295091222000000,0.294378503000000,0.293667734000000,0.292958894000000,0.292251962000000,0.291546917000000,0.290843741000000,0.290142412000000,0.289442910000000,0.288745217000000,0.288049311000000,0.287355175000000,0.286662788000000,0.285972131000000,0.285283185000000,0.284595932000000,0.283910353000000,0.283226429000000,0.282544142000000,0.281863473000000,0.281184405000000,0.280506920000000,0.279830999000000,0.279156625000000,0.278483781000000,0.277812449000000,0.277142612000000,0.276474253000000,0.275807354000000,0.275141900000000,0.274477873000000,0.273815257000000,0.273154036000000,0.272494193000000,0.271835712000000,0.271178577000000,0.270522772000000,0.269868283000000,0.269215092000000,0.268563184000000,0.267912545000000,0.267263159000000,0.266615011000000,0.265968086000000,0.265322370000000,0.264677847000000,0.264034503000000,0.263392324000000,0.262751295000000,0.262111403000000,0.261472633000000,0.260834972000000,0.260198405000000,0.259562919000000,0.258928501000000,0.258295136000000,0.257662813000000,0.257031517000000,0.256401236000000,0.255771957000000,0.255143667000000,0.254516353000000,0.253890002000000,0.253264603000000,0.252640144000000,0.252016611000000,0.251393992000000,0.250772277000000,0.250151453000000,0.249531508000000,0.248912431000000,0.248294210000000,0.247676834000000,0.247060291000000,0.246444571000000,0.245829663000000,0.245215555000000,0.244602237000000,0.243989698000000,0.243377928000000]
interpolator = interpolate.splrep(ti, Pi, s=0.0001)
P0T = lambda T: interpolate.splev(T, interpolator, der=0)
r0 = HW_r_0(P0T,lambd,eta)
# In this experiment we compare ZCB from the Market and Analytical expression
N = 25
T_end = 50
Tgrid= np.linspace(0,T_end,N)
Exact = np.zeros([N,1])
Proxy= np.zeros ([N,1])
for i,Ti in enumerate(Tgrid):
Proxy[i] = HW_ZCB(lambd,eta,P0T,0.0,Ti,r0)
Exact[i] = P0T(Ti)
plt.figure(1)
plt.grid()
plt.plot(Tgrid,Exact,'-k')
plt.plot(Tgrid,Proxy,'--r')
plt.legend(["Analytcal ZCB","Monte Carlo ZCB"])
plt.title('P(0,T) from Monte Carlo vs. Analytical expression')
# Here we simulate the exposure profiles for a swap, using the HW model
# Swap settings
K = np.linspace(-0.1,0.1,25) # strike
Ti = 1.0 # begining of the swap
Tm = 10.0 # end date of the swap
n = 10 # number of payments between Ti and Tm
paths= GeneratePathsHWEuler(NoOfPaths, NoOfSteps,Tm + 1.0, P0T, lambd, eta)
r = paths["R"]
timeGrid = paths["time"]
dt = timeGrid[1]-timeGrid[0]
# Here we compare the price of an option on a ZCB from Monte Carlo and Analytical expression
M_t = np.zeros([NoOfPaths,NoOfSteps])
for i in range(0,NoOfPaths):
M_t[i,:] = np.exp(np.cumsum(r[i,0:-1])*dt)
# Portfolio without netting
VSwapHW = np.zeros(len(K))
VSwap = np.zeros(len(K))
t0= 0
for (idx,Ki) in enumerate(K):
VHW = HW_SwapPrice(CP,notional,Ki,t0,Ti,Tm,n,r0,P0T,lambd,eta)
V = SwapPrice(CP,notional,Ki,t0,Ti,Tm,n,P0T)
VSwap[idx] = V
VSwapHW[idx] = VHW[0]
plt.figure(2)
plt.plot(K,VSwap)
plt.plot(K,VSwapHW,'--r')
plt.grid()
plt.xlabel('strike,K')
plt.ylabel('Swap value')
plt.title('Swap pricing')
# Computation for Swap Par
K=0.0
print('Swap price for K = 0 is {0}'.format(SwapPrice(CP,notional,K,t0,Ti,Tm,n,P0T)))
# Determine a part swap
func = lambda k: SwapPrice(CP,notional,k,t0,Ti,Tm,n,P0T)
K_par = newton(func, 0.0)
print('Swap price for K_par = {0} is {1}'.format(K_par,SwapPrice(CP,notional,K_par,t0,Ti,Tm,n,P0T)))
mainCalculation()
================================================
FILE: Lecture 06-Construction of Yield Curve and Multi-Curve/Materials/MultiCurveBuild.py
================================================
#%%
"""
Created on June 27 2021
Construction of a multi- curve for a given set of swap instruments
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import enum
from copy import deepcopy
import matplotlib.pyplot as plt
from scipy.interpolate import splrep, splev, interp1d
# This class defines puts and calls
class OptionTypeSwap(enum.Enum):
RECEIVER = 1.0
PAYER = -1.0
def IRSwap(CP,notional,K,t,Ti,Tm,n,P0T):
# CP- payer of receiver
# n- notional
# K- strike
# t- today's date
# Ti- beginning of the swap
# Tm- end of Swap
# n- number of dates payments between Ti and Tm
# r_t -interest rate at time t
ti_grid = np.linspace(Ti,Tm,int(n))
tau = ti_grid[1]- ti_grid[0]
temp= 0.0
for (idx,ti) in enumerate(ti_grid):
if idx>0:
temp = temp + tau * P0T(ti)
P_t_Ti = P0T(Ti)
P_t_Tm = P0T(Tm)
if CP==OptionTypeSwap.PAYER:
swap = (P_t_Ti - P_t_Tm) - K * temp
elif CP==OptionTypeSwap.RECEIVER:
swap = K * temp - (P_t_Ti - P_t_Tm)
return swap * notional
def IRSwapMultiCurve(CP,notional,K,t,Ti,Tm,n,P0T,P0TFrd):
# CP- payer of receiver
# n- notional
# K- strike
# t- today's date
# Ti- beginning of the swap
# Tm- end of Swap
# n- number of dates payments between Ti and Tm
# r_t -interest rate at time t
ti_grid = np.linspace(Ti,Tm,int(n))
tau = ti_grid[1]- ti_grid[0]
swap = 0.0
for (idx,ti) in enumerate(ti_grid):
#L(t_0,t_{k-1},t_{k}) from the forward curve
if idx>0:
L_frwd = 1.0/tau * (P0TFrd(ti_grid[idx-1])-P0TFrd(ti_grid[idx])) / P0TFrd(ti_grid[idx])
swap = swap + tau * P0T(ti_grid[idx]) * (L_frwd - K)
return swap * notional
def P0TModel(t,ti,ri,method):
rInterp = method(ti,ri)
r = rInterp(t)
return np.exp(-r*t)
def YieldCurve(instruments, maturities, r0, method, tol):
r0 = deepcopy(r0)
ri = MultivariateNewtonRaphson(r0, maturities, instruments, method, tol=tol)
return ri
def MultivariateNewtonRaphson(ri, ti, instruments, method, tol):
err = 10e10
idx = 0
while err > tol:
idx = idx +1
values = EvaluateInstruments(ti,ri,instruments,method)
J = Jacobian(ti,ri, instruments, method)
J_inv = np.linalg.inv(J)
err = - np.dot(J_inv, values)
ri[0:] = ri[0:] + err
err = np.linalg.norm(err)
print('index in the loop is',idx,' Error is ', err)
return ri
def Jacobian(ti, ri, instruments, method):
eps = 1e-05
swap_num = len(ti)
J = np.zeros([swap_num, swap_num])
val = EvaluateInstruments(ti,ri,instruments,method)
ri_up = deepcopy(ri)
for j in range(0, len(ri)):
ri_up[j] = ri[j] + eps
val_up = EvaluateInstruments(ti,ri_up,instruments,method)
ri_up[j] = ri[j]
dv = (val_up - val) / eps
J[:, j] = dv[:]
return J
def EvaluateInstruments(ti,ri,instruments,method):
P0Ttemp = lambda t: P0TModel(t,ti,ri,method)
val = np.zeros(len(instruments))
for i in range(0,len(instruments)):
val[i] = instruments[i](P0Ttemp)
return val
def linear_interpolation(ti,ri):
interpolator = lambda t: np.interp(t, ti, ri)
return interpolator
def spline_interpolate(ti,ri):
interpolator = splrep(ti, ri, s=0.01)
interp = lambda t: splev(t,interpolator)
return interp
def scipy_1d_interpolate(ti, ri):
interpolator = lambda t: interp1d(ti, ri, kind='quadratic')(t)
return interpolator
#def ComputeDelta(instrument,)
def mainCode():
# Convergence tolerance
tol = 1.0e-15
# Initial guess for the spine points
r0 = np.array([0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01])
# Interpolation method
method = linear_interpolation
# Construct swaps that are used for building of the yield curve
K = np.array([0.04/100.0, 0.16/100.0, 0.31/100.0, 0.81/100.0, 1.28/100.0, 1.62/100.0, 2.22/100.0, 2.30/100.0])
mat = np.array([1.0,2.0,3.0,5.0,7.0,10.0,20.0,30.0])
# IRSwap(CP, notional,K, t, Ti,Tm, n,P0T)
swap1 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[0],0.0,0.0,mat[0],4*mat[0],P0T)
swap2 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[1],0.0,0.0,mat[1],5*mat[1],P0T)
swap3 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[2],0.0,0.0,mat[2],6*mat[2],P0T)
swap4 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[3],0.0,0.0,mat[3],7*mat[3],P0T)
swap5 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[4],0.0,0.0,mat[4],8*mat[4],P0T)
swap6 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[5],0.0,0.0,mat[5],9*mat[5],P0T)
swap7 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[6],0.0,0.0,mat[6],10*mat[6],P0T)
swap8 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[7],0.0,0.0,mat[7],11*mat[7],P0T)
instruments = [swap1,swap2,swap3,swap4,swap5,swap6,swap7,swap8]
# determine optimal spine points
ri = YieldCurve(instruments, mat, r0, method, tol)
print('\n Spine points are',ri,'\n')
# Build a ZCB-curve/yield curve from the spine points
P0T_Initial = lambda t: P0TModel(t,mat,r0,method)
P0T = lambda t: P0TModel(t,mat,ri,method)
# price back the swaps
swapsModel = np.zeros(len(instruments))
swapsInitial = np.zeros(len(instruments))
for i in range(0,len(instruments)):
swapsModel[i] = instruments[i](P0T)
swapsInitial[i] = instruments[i](P0T_Initial)
print('Prices for Pas Swaps (initial) = ',swapsInitial,'\n')
print('Prices for Par Swaps = ',swapsModel,'\n')
# Multi Curve extension- simple sanity check
P0TFrd = deepcopy(P0T)
Ktest = 0.2
swap1 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,Ktest,0.0,0.0,mat[0],4*mat[0],P0T)
swap1MC = lambda P0T: IRSwapMultiCurve(OptionTypeSwap.PAYER,1,Ktest,0.0,0.0,mat[0],4*mat[0],P0T,P0TFrd)
print('Sanity check: swap1 = {0}, swap2 = {1}'.format(swap1(P0T),swap1MC(P0T)))
# Forward curve settings
r0Frwd = np.array([0.01, 0.01, 0.01, 0.01])
KFrwd = np.array([0.09/100.0, 0.26/100.0, 0.37/100.0, 1.91/100.0])
matFrwd = np.array([1.0, 2.0, 3.0, 5.0])
# At this point we already know P(0,T) for the discount curve
P0TDiscount = lambda t: P0TModel(t,mat,ri,method)
swap1Frwd = lambda P0TFrwd: IRSwapMultiCurve(OptionTypeSwap.PAYER,1,KFrwd[0],0.0,0.0,matFrwd[0],4*matFrwd[0],P0TDiscount,P0TFrwd)
swap2Frwd = lambda P0TFrwd: IRSwapMultiCurve(OptionTypeSwap.PAYER,1,KFrwd[1],0.0,0.0,matFrwd[1],5*matFrwd[1],P0TDiscount,P0TFrwd)
swap3Frwd = lambda P0TFrwd: IRSwapMultiCurve(OptionTypeSwap.PAYER,1,KFrwd[2],0.0,0.0,matFrwd[2],6*matFrwd[2],P0TDiscount,P0TFrwd)
swap4Frwd = lambda P0TFrwd: IRSwapMultiCurve(OptionTypeSwap.PAYER,1,KFrwd[3],0.0,0.0,matFrwd[3],7*matFrwd[3],P0TDiscount,P0TFrwd)
instrumentsFrwd = [swap1Frwd,swap2Frwd,swap3Frwd,swap4Frwd]
# determine optimal spine points for the forward curve
riFrwd = YieldCurve(instrumentsFrwd, matFrwd, r0Frwd, method, tol)
print('\n Frwd Spine points are',riFrwd,'\n')
# Build a ZCB-curve/yield curve from the spine points
P0TFrwd_Initial = lambda t: P0TModel(t,matFrwd,r0Frwd,method)
P0TFrwd = lambda t: P0TModel(t,matFrwd,riFrwd,method)
# price back the swaps
swapsModelFrwd = np.zeros(len(instrumentsFrwd))
swapsInitialFrwd = np.zeros(len(instrumentsFrwd))
for i in range(0,len(instrumentsFrwd)):
swapsModelFrwd[i] = instrumentsFrwd[i](P0TFrwd)
swapsInitialFrwd[i] = instrumentsFrwd[i](P0TFrwd_Initial)
print('Prices for Pas Swaps (initial) = ',swapsInitialFrwd,'\n')
print('Prices for Par Swaps = ',swapsModelFrwd,'\n')
print(swap1Frwd(P0TFrwd))
t = np.linspace(0,10,100)
plt.figure()
plt.plot(t,P0TDiscount(t),'--r')
plt.plot(t,P0TFrwd(t),'-b')
plt.legend(['discount','forecast'])
return 0.0
mainCode()
================================================
FILE: Lecture 06-Construction of Yield Curve and Multi-Curve/Materials/YieldCurveBuildGreeks.py
================================================
#%%
"""
Created on June 27 2021
Construction of a yield curve for a given set of swap instruments
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import enum
from copy import deepcopy
from scipy.interpolate import splrep, splev, interp1d
# This class defines puts and calls
class OptionTypeSwap(enum.Enum):
RECEIVER = 1.0
PAYER = -1.0
def IRSwap(CP,notional,K,t,Ti,Tm,n,P0T):
# CP- payer of receiver
# n- notional
# K- strike
# t- today's date
# Ti- beginning of the swap
# Tm- end of Swap
# n- number of dates payments between Ti and Tm
# r_t -interest rate at time t
ti_grid = np.linspace(Ti,Tm,int(n))
tau = ti_grid[1]- ti_grid[0]
# overwrite Ti if t>Ti
prevTi = ti_grid[np.where(ti_grid<t)]
if np.size(prevTi) > 0:
Ti = prevTi[-1]
# Now we need to handle the case when some payments are already done
ti_grid = ti_grid[np.where(ti_grid>t)]
temp= 0.0
for (idx,ti) in enumerate(ti_grid):
if ti>Ti:
temp = temp + tau * P0T(ti)
P_t_Ti = P0T(Ti)
P_t_Tm = P0T(Tm)
if CP==OptionTypeSwap.PAYER:
swap = (P_t_Ti - P_t_Tm) - K * temp
elif CP==OptionTypeSwap.RECEIVER:
swap = K * temp - (P_t_Ti - P_t_Tm)
return swap * notional
def P0TModel(t,ti,ri,method):
rInterp = method(ti,ri)
if t>=ti[-1]:
r = ri[-1]
if t<=ti[0]:
r= ri[0]
if t>ti[0] and t<ti[-1]:
r = rInterp(t)
return np.exp(-r*t)
def YieldCurve(instruments, maturities, r0, method, tol):
r0 = deepcopy(r0)
ri = MultivariateNewtonRaphson(r0, maturities, instruments, method, tol=tol)
return ri
def MultivariateNewtonRaphson(ri, ti, instruments, method, tol):
err = 10e10
idx = 0
while err > tol:
idx = idx +1
values = EvaluateInstruments(ti,ri,instruments,method)
J = Jacobian(ti,ri, instruments, method)
J_inv = np.linalg.inv(J)
err = - np.dot(J_inv, values)
ri[0:] = ri[0:] + err
err = np.linalg.norm(err)
print('index in the loop is',idx,' Error is ', err)
return ri
def Jacobian(ti, ri, instruments, method):
eps = 1e-05
swap_num = len(ti)
J = np.zeros([swap_num, swap_num])
val = EvaluateInstruments(ti,ri,instruments,method)
ri_up = deepcopy(ri)
for j in range(0, len(ri)):
ri_up[j] = ri[j] + eps
val_up = EvaluateInstruments(ti,ri_up,instruments,method)
ri_up[j] = ri[j]
dv = (val_up - val) / eps
J[:, j] = dv[:]
return J
def EvaluateInstruments(ti,ri,instruments,method):
P0Ttemp = lambda t: P0TModel(t,ti,ri,method)
val = np.zeros(len(instruments))
for i in range(0,len(instruments)):
val[i] = instruments[i](P0Ttemp)
return val
def linear_interpolation(ti,ri):
interpolator = lambda t: np.interp(t, ti, ri)
return interpolator
def quadratic_interpolation(ti, ri):
interpolator = interp1d(ti, ri, kind='quadratic')
return interpolator
def cubic_interpolation(ti, ri):
interpolator = interp1d(ti, ri, kind='cubic')
return interpolator
#def ComputeDelta(instrument,)
def BuildInstruments(K,mat):
swap1 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[0],0.0,0.0,mat[0],4*mat[0],P0T)
swap2 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[1],0.0,0.0,mat[1],4*mat[1],P0T)
swap3 = lambda P0T: IRSwap(OptionTypeSwap.RECEIVER,1,K[2],0.0,0.0,mat[2],4*mat[2],P0T)
swap4 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[3],0.0,0.0,mat[3],4*mat[3],P0T)
swap5 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[4],0.0,0.0,mat[4],4*mat[4],P0T)
swap6 = lambda P0T: IRSwap(OptionTypeSwap.RECEIVER,1,K[5],0.0,0.0,mat[5],4*mat[5],P0T)
swap7 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[6],0.0,0.0,mat[6],4*mat[6],P0T)
swap8 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[7],0.0,0.0,mat[7],4*mat[7],P0T)
instruments = [swap1,swap2,swap3,swap4,swap5,swap6,swap7,swap8]
return instruments
def mainCode():
# Convergence tolerance
tol = 1.0e-8
# Initial guess for the spine points
r0 = np.array([0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01])
# Interpolation method
method = cubic_interpolation
# Construct swaps that are used for building of the yield curve
K = np.array([0.02, 0.03, 0.04, 0.05, 0.06, 0.07,0.08,0.09])
mat = np.array([1.0,2.0,3.0,5.0,7.0,10.0,20.0,30.0])
instruments = BuildInstruments(K,mat)
ri = YieldCurve(instruments, mat, r0, method, tol)
P0T = lambda t: P0TModel(t,mat,ri,method)
# Define off market Swap
SwapLambda = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,0.03,0.0,0.0,4,6*mat[0],P0T)
swap= SwapLambda(P0T)
print('Swap price = ',swap)
dK = 0.0001
delta = np.zeros(len(K))
K_new = K
for i in range(0,len(K)):
K_new[i] = K_new[i] + dK
instruments = BuildInstruments(K_new,mat)
ri = YieldCurve(instruments, mat, r0, method, tol)
P0T_new = lambda t: P0TModel(t,mat,ri,method)
swap_shock = SwapLambda(P0T_new)
delta[i] = (swap_shock-swap)/dK
K_new[i] = K_new[i] - dK
print(delta)
return 0.0
mainCode()
================================================
FILE: Lecture 06-Construction of Yield Curve and Multi-Curve/Materials/YieldCurveBuild_Treasury.py
================================================
#%%
"""
Created on June 27 2021
Construction of a yield curve for a given set of swap instruments
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import enum
from copy import deepcopy
from scipy.interpolate import splrep, splev, interp1d
# This class defines puts and calls
class OptionTypeSwap(enum.Enum):
RECEIVER = 1.0
PAYER = -1.0
def IRSwap(CP,notional,K,t,Ti,Tm,n,P0T):
# CP- payer of receiver
# n- notional
# K- strike
# t- today's date
# Ti- beginning of the swap
# Tm- end of Swap
# n- number of dates payments between Ti and Tm
# r_t -interest rate at time t
ti_grid = np.linspace(Ti,Tm,int(n))
tau = ti_grid[1]- ti_grid[0]
# overwrite Ti if t>Ti
prevTi = ti_grid[np.where(ti_grid<t)]
if np.size(prevTi) > 0: #prevTi != []:
Ti = prevTi[-1]
# Now we need to handle the case when some payments are already done
ti_grid = ti_grid[np.where(ti_grid>t)]
temp= 0.0
for (idx,ti) in enumerate(ti_grid):
if ti>Ti:
temp = temp + tau * P0T(ti)
P_t_Ti = P0T(Ti)
P_t_Tm = P0T(Tm)
if CP==OptionTypeSwap.PAYER:
swap = (P_t_Ti - P_t_Tm) - K * temp
elif CP==OptionTypeSwap.RECEIVER:
swap = K * temp - (P_t_Ti - P_t_Tm)
return swap * notional
def P0TModel(t,ti,ri,method):
rInterp = method(ti,ri)
r = rInterp(t)
return np.exp(-r*t)
def YieldCurve(instruments, maturities, r0, method, tol):
r0 = deepcopy(r0)
ri = MultivariateNewtonRaphson(r0, maturities, instruments, method, tol=tol)
return ri
def MultivariateNewtonRaphson(ri, ti, instruments, method, tol):
err = 10e10
idx = 0
while err > tol:
idx = idx +1
values = EvaluateInstruments(ti,ri,instruments,method)
J = Jacobian(ti,ri, instruments, method)
J_inv = np.linalg.inv(J)
err = - np.dot(J_inv, values)
ri[0:] = ri[0:] + err
err = np.linalg.norm(err)
print('index in the loop is',idx,' Error is ', err)
return ri
def Jacobian(ti, ri, instruments, method):
eps = 1e-05
swap_num = len(ti)
J = np.zeros([swap_num, swap_num])
val = EvaluateInstruments(ti,ri,instruments,method)
ri_up = deepcopy(ri)
for j in range(0, len(ri)):
ri_up[j] = ri[j] + eps
val_up = EvaluateInstruments(ti,ri_up,instruments,method)
ri_up[j] = ri[j]
dv = (val_up - val) / eps
J[:, j] = dv[:]
return J
def EvaluateInstruments(ti,ri,instruments,method):
P0Ttemp = lambda t: P0TModel(t,ti,ri,method)
val = np.zeros(len(instruments))
for i in range(0,len(instruments)):
val[i] = instruments[i](P0Ttemp)
return val
def linear_interpolation(ti,ri):
interpolator = lambda t: np.interp(t, ti, ri)
return interpolator
def mainCode():
# Convergence tolerance
tol = 1.0e-15
# Initial guess for the spine points
r0 = np.array([0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01])
# Interpolation method
method = linear_interpolation
# Construct swaps that are used for building of the yield curve
K = np.array([0.04/100.0, 0.16/100.0, 0.31/100.0, 0.81/100.0, 1.28/100.0, 1.62/100.0, 2.22/100.0, 2.30/100.0])
mat = np.array([1.0,2.0,3.0,5.0,7.0,10.0,20.0,30.0])
swap1 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[0],0.0,0.0,mat[0],4*mat[0],P0T)
swap2 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[1],0.0,0.0,mat[1],4*mat[1],P0T)
swap3 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[2],0.0,0.0,mat[2],4*mat[2],P0T)
swap4 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[3],0.0,0.0,mat[3],4*mat[3],P0T)
swap5 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[4],0.0,0.0,mat[4],4*mat[4],P0T)
swap6 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[5],0.0,0.0,mat[5],4*mat[5],P0T)
swap7 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[6],0.0,0.0,mat[6],4*mat[6],P0T)
swap8 = lambda P0T: IRSwap(OptionTypeSwap.PAYER,1,K[7],0.0,0.0,mat[7],4*mat[7],P0T)
instruments = [swap1,swap2,swap3,swap4,swap5,swap6,swap7,swap8]
# determine optimal spine points
ri = YieldCurve(instruments, mat, r0, method, tol)
print('\n Spine points are',ri,'\n')
# Build a ZCB-curve/yield curve from the spine points
P0T_Initial = lambda t: P0TModel(t,mat,r0,method)
P0T = lambda t: P0TModel(t,mat,ri,method)
# price back the swaps
swapsModel = np.zeros(len(instruments))
swapsInitial = np.zeros(len(instruments))
for i in range(0,len(instruments)):
swapsInitial[i] = instruments[i](P0T_Initial)
swapsModel[i] = instruments[i](P0T)
print('Prices for Pas Swaps (initial) = ',swapsInitial,'\n')
print('Prices for Par Swaps = ',swapsModel,'\n')
return 0.0
mainCode()
================================================
FILE: Lecture 07-Pricing of Swaptions and Negative Interest Rates/Materials/HW_CapletsAndFloorlets.py
================================================
#%%
"""
Created on July 12 2021
Caplets and floorlets under the Hull-White Model
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import enum
import matplotlib.pyplot as plt
import scipy.stats as st
import scipy.integrate as integrate
import scipy.optimize as optimize
# This class defines puts and calls
class OptionType(enum.Enum):
CALL = 1.0
PUT = -1.0
def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#theta = lambda t: 0.1 +t -t
#print("changed theta")
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
R[:,0]=r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta(time[i]) - R[:,i]) * dt + eta* (W[:,i+1]-W[:,i])
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R}
return paths
def HW_theta(lambd,eta,P0T):
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#print("CHANGED THETA")
return theta#lambda t: 0.1+t-t
def HW_A(lambd,eta,P0T,T1,T2):
tau = T2-T1
zGrid = np.linspace(0.0,tau,250)
B_r = lambda tau: 1.0/lambd * (np.exp(-lambd *tau)-1.0)
theta = HW_theta(lambd,eta,P0T)
temp1 = lambd * integrate.trapz(theta(T2-zGrid)*B_r(zGrid),zGrid)
temp2 = eta*eta/(4.0*np.power(lambd,3.0)) * (np.exp(-2.0*lambd*tau)*(4*np.exp(lambd*tau)-1.0) -3.0) + eta*eta*tau/(2.0*lambd*lambd)
return temp1 + temp2
def HW_B(lambd,eta,T1,T2):
return 1.0/lambd *(np.exp(-lambd*(T2-T1))-1.0)
def HW_ZCB(lambd,eta,P0T,T1,T2,rT1):
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
return np.exp(A_r + B_r *rT1)
def HWMean_r(P0T,lambd,eta,T):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2.0*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = HW_theta(lambd,eta,P0T)
zGrid = np.linspace(0.0,T,2500)
temp =lambda z: theta(z) * np.exp(-lambd*(T-z))
r_mean = r0*np.exp(-lambd*T) + lambd * integrate.trapz(temp(zGrid),zGrid)
return r_mean
def HW_r_0(P0T,lambd,eta):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
return r0
def HW_Mu_FrwdMeasure(P0T,lambd,eta,T):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = HW_theta(lambd,eta,P0T)
zGrid = np.linspace(0.0,T,500)
theta_hat =lambda t,T: theta(t) + eta*eta / lambd *1.0/lambd * (np.exp(-lambd*(T-t))-1.0)
temp =lambda z: theta_hat(z,T) * np.exp(-lambd*(T-z))
r_mean = r0*np.exp(-lambd*T) + lambd * integrate.trapz(temp(zGrid),zGrid)
return r_mean
def HWVar_r(lambd,eta,T):
return eta*eta/(2.0*lambd) *( 1.0-np.exp(-2.0*lambd *T))
def HWDensity(P0T,lambd,eta,T):
r_mean = HWMean_r(P0T,lambd,eta,T)
r_var = HWVar_r(lambd,eta,T)
return lambda x: st.norm.pdf(x,r_mean,np.sqrt(r_var))
def HW_CapletFloorletPrice(CP,N,K,lambd,eta,P0T,T1,T2):
if CP == OptionType.CALL:
N_new = N * (1.0+(T2-T1)*K)
K_new = 1.0 + (T2-T1)*K
caplet = N_new*HW_ZCB_CallPutPrice(OptionType.PUT,1.0/K_new,lambd,eta,P0T,T1,T2)
value= caplet
elif CP==OptionType.PUT:
N_new = N * (1.0+ (T2-T1)*K)
K_new = 1.0 + (T2-T1)*K
floorlet = N_new*HW_ZCB_CallPutPrice(OptionType.CALL,1.0/K_new,lambd,eta,P0T,T1,T2)
value = floorlet
return value
def HW_ZCB_CallPutPrice(CP,K,lambd,eta,P0T,T1,T2):
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
mu_r = HW_Mu_FrwdMeasure(P0T,lambd,eta,T1)
v_r = np.sqrt(HWVar_r(lambd,eta,T1))
K_hat = K * np.exp(-A_r)
a = (np.log(K_hat) - B_r*mu_r)/(B_r*v_r)
d1 = a - B_r*v_r
d2 = d1 + B_r*v_r
term1 = np.exp(0.5* B_r*B_r*v_r*v_r + B_r*mu_r)*st.norm.cdf(d1) - K_hat * st.norm.cdf(d2)
value =P0T(T1) * np.exp(A_r) * term1
if CP == OptionType.CALL:
return value
elif CP==OptionType.PUT:
return value - P0T(T2) + K*P0T(T1)
# Black-Scholes Call option price
def BS_Call_Put_Option_Price(CP,S_0,K,sigma,tau,r):
if K is list:
K = np.array(K).reshape([len(K),1])
d1 = (np.log(S_0 / K) + (r + 0.5 * np.power(sigma,2.0)) * tau) / (sigma * np.sqrt(tau))
d2 = d1 - sigma * np.sqrt(tau)
if CP == OptionType.CALL:
value = st.norm.cdf(d1) * S_0 - st.norm.cdf(d2) * K * np.exp(-r * tau)
elif CP == OptionType.PUT:
value = st.norm.cdf(-d2) * K * np.exp(-r * tau) - st.norm.cdf(-d1)*S_0
return value
# Implied volatility method
def ImpliedVolatilityBlack76(CP,marketPrice,K,T,S_0):
# To determine initial volatility we interpolate define a grid for sigma
# and interpolate on the inverse
sigmaGrid = np.linspace(0.0,5.0,5000)
optPriceGrid = BS_Call_Put_Option_Price(CP,S_0,K,sigmaGrid,T,0.0)
sigmaInitial = np.interp(marketPrice,optPriceGrid,sigmaGrid)
print("Strike = {0}".format(K))
print("Initial volatility = {0}".format(sigmaInitial))
# Use determined input for the local-search (final tuning)
func = lambda sigma: np.power(BS_Call_Put_Option_Price(CP,S_0,K,sigma,T,0.0) - marketPrice, 1.0)
impliedVol = optimize.newton(func, sigmaInitial, tol=1e-15)
print("Final volatility = {0}".format(impliedVol))
if impliedVol > 2.0:
impliedVol = 0.0
return impliedVol
def mainCalculation():
CP= OptionType.CALL
NoOfPaths = 20000
NoOfSteps = 1000
lambd = 0.02
eta = 0.02
# We define a ZCB curve (obtained from the market)
P0T = lambda T: np.exp(-0.1*T)#np.exp(-0.03*T*T-0.1*T)
r0 = HW_r_0(P0T,lambd,eta)
# In this experiment we compare ZCB from the Market and Analytical expression
N = 25
T_end = 50
Tgrid= np.linspace(0,T_end,N)
Exact = np.zeros([N,1])
Proxy= np.zeros ([N,1])
for i,Ti in enumerate(Tgrid):
Proxy[i] = HW_ZCB(lambd,eta,P0T,0.0,Ti,r0)
Exact[i] = P0T(Ti)
plt.figure(1)
plt.grid()
plt.plot(Tgrid,Exact,'-k')
plt.plot(Tgrid,Proxy,'--r')
plt.legend(["Analytcal ZCB","Monte Carlo ZCB"])
plt.title('P(0,T) from Monte Carlo vs. Analytical expression')
# In this experiment we compare Monte Carlo results for
T1 = 4.0
T2 = 8.0
paths= GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T1 ,P0T, lambd, eta)
r = paths["R"]
timeGrid = paths["time"]
dt = timeGrid[1]-timeGrid[0]
# Here we compare the price of an option on a ZCB from Monte Carlo and Analytical expression
M_t = np.zeros([NoOfPaths,NoOfSteps])
for i in range(0,NoOfPaths):
M_t[i,:] = np.exp(np.cumsum(r[i,:-1])*dt)
KVec = np.linspace(0.01,1.7,50)
Price_MC_V = np.zeros([len(KVec),1])
Price_Th_V =np.zeros([len(KVec),1])
P_T1_T2 = HW_ZCB(lambd,eta,P0T,T1,T2,r[:,-1])
for i,K in enumerate(KVec):
if CP==OptionType.CALL:
Price_MC_V[i] =np.mean( 1.0/M_t[:,-1] * np.maximum(P_T1_T2-K,0.0))
elif CP==OptionType.PUT:
Price_MC_V[i] =np.mean( 1.0/M_t[:,-1] * np.maximum(K-P_T1_T2,0.0))
Price_Th_V[i] =HW_ZCB_CallPutPrice(CP,K,lambd,eta,P0T,T1,T2)#HW_ZCB_CallPrice(K,lambd,eta,P0T,T1,T2)
plt.figure(2)
plt.grid()
plt.plot(KVec,Price_MC_V)
plt.plot(KVec,Price_Th_V,'--r')
plt.legend(['Monte Carlo','Theoretical'])
plt.title('Option on ZCB')
# Effect of the HW model parameters on Implied Volatilities
# define a forward rate between T1 and T2
frwd = 1.0/(T2-T1) *(P0T(T1)/P0T(T2)-1.0)
K = np.linspace(frwd/2.0,3.0*frwd,25)
# Effect of eta
plt.figure(3)
plt.grid()
plt.xlabel('strike, K')
plt.ylabel('implied volatility')
etaV =[0.01, 0.02, 0.03, 0.04]
legend = []
Notional = 1.0
for etaTemp in etaV:
optPrice = HW_CapletFloorletPrice(CP,Notional,K,lambd,etaTemp,P0T,T1,T2)
# Implied volatilities
IV =np.zeros([len(K),1])
for idx in range(0,len(K)):
valFrwd = optPrice[idx]/P0T(T2)/(T2-T1)
IV[idx] = ImpliedVolatilityBlack76(CP,valFrwd,K[idx],T2,frwd)
plt.plot(K,IV*100.0)
#plt.plot(K,optPrice)
legend.append('eta={0}'.format(etaTemp))
plt.legend(legend)
# Effect of lambda
plt.figure(4)
plt.grid()
plt.xlabel('strike, K')
plt.ylabel('implied volatility')
lambdaV = [0.01, 0.03, 0.05, 0.09]
legend = []
Notional = 1.0
for lambdTemp in lambdaV:
optPrice = HW_CapletFloorletPrice(CP,Notional,K,lambdTemp,eta,P0T,T1,T2)
# Implied volatilities
IV =np.zeros([len(K),1])
for idx in range(0,len(K)):
valFrwd = optPrice[idx]/P0T(T2)/(T2-T1)
IV[idx] = ImpliedVolatilityBlack76(CP,valFrwd,K[idx],T2,frwd)
#plt.plot(K,optPrice)
plt.plot(K,IV*100.0)
legend.append('lambda={0}'.format(lambdTemp))
plt.legend(legend)
print('frwd={0}'.format(frwd*P0T(T2)))
mainCalculation()
================================================
FILE: Lecture 07-Pricing of Swaptions and Negative Interest Rates/Materials/JamshidianTrick.py
================================================
#%%
"""
Created on July 18 2021
Jamshidian's trick for handling E max sum -> sum E max
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize as optimize
def PsiSum(psi,N,x):
temp = 0
for i in range(0,N):
temp = temp + psi(i,x)
return temp
def JamshidianTrick(psi,N,K):
A = lambda x: PsiSum(psi,N,x) - K
result = optimize.newton(A,0.1)
return result
def Main():
NoOfSamples = 1000
X = np.random.normal(0.0,1.0,[NoOfSamples,1])
psi_i = lambda i,X: np.exp(-i*np.abs(X))
# Number of terms
N = 15
A = 0
for i in range(0,N):
A = A + psi_i(i,X)
K = np.linspace(2,10,10)
resultMC = np.zeros(len(K))
for (i,Ki) in enumerate(K):
resultMC[i] = np.mean(np.maximum(A-Ki,0))
# Jamshidians trick
resultJams = np.zeros(len(K))
for i,Ki in enumerate(K):
# Compute optimal K*
optX = JamshidianTrick(psi_i,N,Ki)
A = 0
for j in range(0,N):
A = A + np.mean(np.maximum(psi_i(j,X)-psi_i(j,optX),0))
resultJams[i] = A
plt.figure()
plt.plot(K,resultMC)
plt.plot(K,resultJams,'--r')
plt.grid()
plt.xlabel('K')
plt.ylabel('expectation')
plt.legend(['Monte Carlo','Jamshidians trick'])
Main()
================================================
FILE: Lecture 07-Pricing of Swaptions and Negative Interest Rates/Materials/ShiftedLognormal.py
================================================
#%%
"""
Created on July 12 2021
Shifted GBM and pricing of caplets/floorlets
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as st
import enum
import scipy.optimize as optimize
# This class defines puts and calls
class OptionType(enum.Enum):
CALL = 1.0
PUT = -1.0
def GeneratePathsGBMShifted(NoOfPaths,NoOfSteps,T,r,sigma,S_0,shift):
S_0shift = S_0 + shift
if (S_0shift < 0.0):
raise('Shift is too small!')
paths =GeneratePathsGBM(NoOfPaths,NoOfSteps,T,r,sigma,S_0shift)
Sshifted = paths["S"] - shift
time = paths["time"]
return {"time":time,"S":Sshifted}
def GeneratePathsGBM(NoOfPaths,NoOfSteps,T,r,sigma,S_0):
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
X = np.zeros([NoOfPaths, NoOfSteps+1])
W = np.zeros([NoOfPaths, NoOfSteps+1])
time = np.zeros([NoOfSteps+1])
X[:,0] = np.log(S_0)
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
X[:,i+1] = X[:,i] + (r - 0.5 * sigma * sigma) * dt + sigma * (W[:,i+1]-W[:,i])
time[i+1] = time[i] +dt
#Compute exponent of ABM
S = np.exp(X)
paths = {"time":time,"S":S}
return paths
# Shifted Black-Scholes Call option price
def BS_Call_Put_Option_Price_Shifted(CP,S_0,K,sigma,tau,r,shift):
K_new = K + shift
S_0_new = S_0 + shift
return BS_Call_Put_Option_Price(CP,S_0_new,K_new,sigma,tau,r)
# Black-Scholes Call option price
def BS_Call_Put_Option_Price(CP,S_0,K,sigma,tau,r):
if K is list:
K = np.array(K).reshape([len(K),1])
d1 = (np.log(S_0 / K) + (r + 0.5 * np.power(sigma,2.0)) * tau) / (sigma * np.sqrt(tau))
d2 = d1 - sigma * np.sqrt(tau)
if CP == OptionType.CALL:
value = st.norm.cdf(d1) * S_0 - st.norm.cdf(d2) * K * np.exp(-r * tau)
elif CP == OptionType.PUT:
value = st.norm.cdf(-d2) * K * np.exp(-r * tau) - st.norm.cdf(-d1)*S_0
return value
# Implied volatility method
def ImpliedVolatilityBlack76(CP,marketPrice,K,T,S_0):
# To determine initial volatility we interpolate define a grid for sigma
# and interpolate on the inverse
sigmaGrid = np.linspace(0.0,2.0,5000)
optPriceGrid = BS_Call_Put_Option_Price(CP,S_0,K,sigmaGrid,T,0.0)
sigmaInitial = np.interp(marketPrice,optPriceGrid,sigmaGrid)
print("Initial volatility = {0}".format(sigmaInitial))
# Use determined input for the local-search (final tuning)
func = lambda sigma: np.power(BS_Call_Put_Option_Price(CP,S_0,K,sigma,T,0.0) - marketPrice, 1.0)
impliedVol = optimize.newton(func, sigmaInitial, tol=1e-15)
print("Final volatility = {0}".format(impliedVol))
return impliedVol
# Implied volatility method
def ImpliedVolatilityBlack76Shifted(CP,marketPrice,K,T,S_0,shift):
# To determine initial volatility we interpolate define a grid for sigma
# and interpolate on the inverse
sigmaGrid = np.linspace(0.0,2.0,5000)
optPriceGrid = BS_Call_Put_Option_Price_Shifted(CP,S_0,K,sigmaGrid,T,0.0,shift)
sigmaInitial = np.interp(marketPrice,optPriceGrid,sigmaGrid)
print("Initial volatility = {0}".format(sigmaInitial))
# Use determined input for the local-search (final tuning)
func = lambda sigma: np.power(BS_Call_Put_Option_Price_Shifted(CP,S_0,K,sigma,T,0.0,shift) - marketPrice, 1.0)
impliedVol = optimize.newton(func, sigmaInitial, tol=1e-15)
print("Final volatility = {0}".format(impliedVol))
return impliedVol
def mainCalculation():
NoOfPaths = 10000
NoOfSteps = 500
T = 3.0
sigma = 0.2
L0 = -0.05
shift = 0.1
K = [0.95]
CP = OptionType.CALL
P0T =lambda T: np.exp(-0.1*T)
np.random.seed(4)
Paths = GeneratePathsGBMShifted(NoOfPaths,NoOfSteps,T,0.0,sigma,L0,shift)
time = Paths["time"]
L = Paths["S"]
print(np.mean(L[:,-1]))
# Plot first few paths
plt.figure(1)
plt.plot(time,np.transpose(L[0:20,:]))
plt.grid()
# Shifted lognormal for different shift parameters
plt.figure(2)
shiftV = [1.0,2.0,3.0,4.0,5.0]
legend = []
for shiftTemp in shiftV:
x = np.linspace(-shiftTemp,10,1000)
lognnormPDF = lambda x,t : st.lognorm.pdf(x+shiftTemp, scale = np.exp(np.log(L0+shiftTemp) + (- 0.5 * sigma * sigma)*t), s= np.sqrt(t) * sigma)
pdf_x= lognnormPDF(x,T)
plt.plot(x,pdf_x)
legend.append('shift={0}'.format(shiftTemp))
plt.legend(legend)
plt.xlabel('x')
plt.ylabel('pdf')
plt.title('shifted lognormal density')
plt.grid()
# Call/Put option prices, MC vs. Analytical
plt.figure(3)
K = np.linspace(-shift,np.abs(L0)*3,25)
optPriceMCV=np.zeros([len(K),1])
for idx in range(0,len(K)):
optPriceMCV[idx] =0.0
if CP == OptionType.CALL:
optPriceMCV[idx] = P0T(T)*np.mean(np.maximum(L[:,-1]-K[idx],0.0))
elif CP == OptionType.PUT:
optPriceMCV[idx] = P0T(T)*np.mean(np.maximum(K[idx]-L[:,-1],0.0))
optPriceExact = P0T(T)*BS_Call_Put_Option_Price_Shifted(CP,L0,K,sigma,T,0.0,shift)
plt.plot(K,optPriceMCV)
plt.plot(K,optPriceExact,'--r')
plt.grid()
plt.xlabel('strike,K')
plt.ylabel('option price')
plt.legend(['Monte Carlo','Exact'])
# Shift Effect on Option prices
plt.figure(4)
legend = []
for shiftTemp in [0.2,0.3,0.4,0.5]:
K = np.linspace(-shiftTemp,np.abs(L0)*6.0,25)
optPriceExact = P0T(T)*BS_Call_Put_Option_Price_Shifted(CP,L0,K,sigma,T,0.0,shiftTemp)
plt.plot(K,optPriceExact)
legend.append('shift={0}'.format(shiftTemp))
plt.grid()
plt.xlabel('strike,K')
plt.ylabel('option price')
plt.legend(legend)
mainCalculation()
================================================
FILE: Lecture 08-Mortgages and Prepayments/Materials/AnnuityMortgage.py
================================================
#%%
"""
Created on July 05 2021
Annuity mortgage- payment profile
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak and Emanuele Casamassima
"""
import numpy as np
import matplotlib.pyplot as plt
def Annuity(rate,notional,periods,CPR):
# it returns a matrix M such that
# M = [t notional(t) prepayment(t) notional_quote(t) interest_quote(t) installment(t)]
# WARNING! here "rate" and "periods" are quite general, the choice of getting year/month/day.. steps, depends on the rate
# that the function receives. So, it is necessary to pass the correct rate to the function
M = np.zeros((periods + 1,6))
M[:,0] = np.arange(periods + 1) # we define the times
M[0,1] = notional
for t in range(1,periods + 1):
# we are computing the installment at time t knowing the oustanding at time (t-1)
remaining_periods = periods - (t - 1)
# Installment, C(t_i)
M[t,5] = rate * M[t-1,1]/(1 - 1/(1 + rate)**remaining_periods)
# Interest rate payment, I(t_i) = K * N(t_{i})
M[t,4] = rate * M[t-1,1]
# Notional payment, Q(t_i) = C(t_i) - I(t_i)
M[t,3] = M[t,5] - M[t,4]
# Prepayment, P(t_i)= Lambda * (N(t_i) -Q(t_i))
M[t,2] = CPR * (M[t-1,1] - M[t,3])
# notional, N(t_{i+1}) = N(t_{i}) - lambda * (Q(t_{i} + P(t_i)))
M[t,1] = M[t-1,1] - M[t,3] - M[t,2]
return M
def mainCode():
# Initial notional
N0 = 1000000
# Interest rates from a bank
r = 0.05
# Prepayment rate, 0.1 = 10%
Lambda = 0.1
# For simplicity we assume 1 as unit (yearly payments of mortgage)
T_end = 30
M = Annuity(r,N0,T_end,Lambda)
for i in range(0,T_end+1):
print("Ti={0}, Notional={1:.0f}, Prepayment={2:.0f}, Notional Repayment={3:.0f}, Interest Rate={4:.0f}, Installment={5:.0f} ".format(M[i,0],M[i,1],M[i,2],M[i,3],M[i,4],M[i,5]))
plt.figure(1)
plt.plot(M[:,0],M[:,1],'.r')
plt.grid()
plt.xlabel('time')
plt.ylabel('notional')
return 0.0
mainCode()
================================================
FILE: Lecture 08-Mortgages and Prepayments/Materials/BulletMortgage.py
================================================
#%%
"""
Created on July 05 2021
Bullet mortgage- payment profile
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak and Emanuele Casamassima
"""
import numpy as np
import matplotlib.pyplot as plt
def Bullet(rate,notional,periods,CPR):
# it returns a matrix M such that
# M = [t notional(t) prepayment(t) notional_quote(t) interest_(t) installment(t)]
# WARNING! here "rate" and "periods" are quite general, the choice of getting year/month/day.. steps, depends on the rate
# that the function receives. So, it is necessary to pass the correct rate to the function
M = np.zeros((periods + 1,6))
M[:,0] = np.arange(periods + 1) # we define the times
M[0,1] = notional
for t in range(1,periods):
M[t,4] = rate*M[t-1,1] # interest quote
M[t,3] = 0 # repayment, 0 for bullet mortgage
scheduled_oustanding = M[t-1,1] - M[t,3]
M[t,2] = scheduled_oustanding * CPR # prepayment
M[t,1] = scheduled_oustanding - M[t,2] # notional(t) = notional(t-1) - (repayment + prepayment)
M[t,5] = M[t,4] + M[t,2] + M[t,3]
M[periods,4] = rate*M[periods-1,1] # interest quote
M[periods,3] = M[periods-1,1] # notional quote
M[periods,5] = M[periods,4] + M[periods,2] + M[periods,3]
return M
def mainCode():
# Initial notional
N0 = 1000000
# Interest rates from a bank
r = 0.05
# Prepayment rate, 0.1 = 10%
Lambda = 0.01
# For simplicity we assume 1 as unit (yearly payments of mortgage)
T_end = 30
M = Bullet(r,N0,T_end,Lambda)
for i in range(0,T_end+1):
print("Ti={0}, Notional={1:.0f}, Prepayment={2:.0f}, Notional Repayment={3:.0f}, Interest Rate={4:.0f}, Installment={5:.0f} ".format(M[i,0],M[i,1],M[i,2],M[i,3],M[i,4],M[i,5]))
plt.figure(1)
plt.plot(M[:,0],M[:,1],'-r')
plt.grid()
plt.xlabel('time')
plt.ylabel('notional')
return 0.0
mainCode()
================================================
FILE: Lecture 08-Mortgages and Prepayments/Materials/Incentives.py
================================================
#%%
"""
Created on July 05 2021
Incentive function as a function of a swap rate or the differential w.r.t. "old" mortgage rate
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak and Emanuele Cassamassima
"""
import numpy as np
import matplotlib.pyplot as plt
def Annuity(rate,notional,periods,CPR):
# it returns a matrix M such that
# M = [t notional(t) prepayment(t) notional_quote(t) interest_quote(t) installment(t)]
# WARNING! here "rate" and "periods" are quite general, the choice of getting year/month/day.. steps, depends on the rate
# that the function receives. So, it is necessary to pass the correct rate to the function
M = np.zeros((periods + 1,6))
M[:,0] = np.arange(periods + 1) # we define the times
M[0,1] = notional
for t in range(1,periods + 1):
# we are computing the installment at time t knowing the oustanding at time (t-1)
remaining_periods = periods - (t - 1)
# Installment, C(t_i)
M[t,5] = rate * M[t-1,1]/(1 - 1/(1 + rate)**remaining_periods)
# Interest rate payment, I(t_i) = r * N(t_{i})
M[t,4] = rate * M[t-1,1]
# Notional payment, Q(t_i) = C(t_i) - I(t_i)
M[t,3] = M[t,5] - M[t,4]
# Prepayment, P(t_i)= Lambda * (N(t_i) -Q(t_i))
M[t,2] = CPR * (M[t-1,1] - M[t,3])
# notional, N(t_{i+1}) = N(t_{i}) - lambda * (Q(t_{i} + P(t_i)))
M[t,1] = M[t-1,1] - M[t,3] - M[t,2]
return M
def mainCode():
IncentiveFunction = lambda x : 0.04 + 0.1/(1 + np.exp(115 * (0.02-x)))
oldRate = 0.05
newRate = np.linspace(-0.05,0.15,25)
epsilon = oldRate-newRate
incentive = IncentiveFunction(epsilon)
plt.figure(1)
plt.plot(newRate,incentive)
plt.xlabel('S(t)')
plt.ylabel('Incentive')
plt.grid()
plt.figure(2)
plt.plot(epsilon,incentive)
plt.xlabel('epsilon= K - S(t)')
plt.ylabel('Incentive')
plt.grid()
return 0.0
mainCode()
================================================
FILE: Lecture 08-Mortgages and Prepayments/Materials/StochasticAmortizingSwap.py
================================================
#%%
"""
Created on July 05 2021
Stochastic amortization given the incentive function and irrational/rational behavior profile
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak and Emanuele Cassamassima
"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize as optimize
import scipy.integrate as integrate
def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta):
# time-step needed for differentiation
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
#theta = lambda t: 0.1 +t -t
#print("changed theta")
Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
W = np.zeros([NoOfPaths, NoOfSteps+1])
R = np.zeros([NoOfPaths, NoOfSteps+1])
R[:,0]=r0
time = np.zeros([NoOfSteps+1])
dt = T / float(NoOfSteps)
for i in range(0,NoOfSteps):
# making sure that samples from normal have mean 0 and variance 1
if NoOfPaths > 1:
Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
R[:,i+1] = R[:,i] + lambd*(theta(time[i]) - R[:,i]) * dt + eta* (W[:,i+1]-W[:,i])
time[i+1] = time[i] +dt
# Outputs
paths = {"time":time,"R":R}
return paths
def HW_theta(lambd,eta,P0T):
dt = 0.0001
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
return theta
def HW_A(lambd,eta,P0T,T1,T2):
tau = T2-T1
zGrid = np.linspace(0.0,tau,250)
B_r = lambda tau: 1.0/lambd * (np.exp(-lambd *tau)-1.0)
theta = HW_theta(lambd,eta,P0T)
temp1 = lambd * integrate.trapz(theta(T2-zGrid)*B_r(zGrid),zGrid)
temp2 = eta*eta/(4.0*np.power(lambd,3.0)) * (np.exp(-2.0*lambd*tau)*(4*np.exp(lambd*tau)-1.0) -3.0) + eta*eta*tau/(2.0*lambd*lambd)
return temp1 + temp2
def HW_B(lambd,eta,T1,T2):
return 1.0/lambd *(np.exp(-lambd*(T2-T1))-1.0)
def HW_ZCB(lambd,eta,P0T,T1,T2,rT1):
n = np.size(rT1)
if T1<T2:
B_r = HW_B(lambd,eta,T1,T2)
A_r = HW_A(lambd,eta,P0T,T1,T2)
return np.exp(A_r + B_r *rT1)
else:
return np.ones([n])
def SwapRateHW(t,Ti,Tm,n,r_t,P0T,lambd,eta):
# CP- payer of receiver
# n- notional
# K- strike
# t- today's date
# Ti- beginning of the swap
# Tm- end of Swap
# n- number of dates payments between Ti and Tm
# r_t -interest rate at time t
if n == 1:
ti_grid =np.array([Ti,Tm])
else:
ti_grid = np.linspace(Ti,Tm,n)
tau = ti_grid[1]- ti_grid[0]
# overwrite Ti if t>Ti
prevTi = ti_grid[np.where(ti_grid<t)]
if np.size(prevTi) > 0: #prevTi != []:
Ti = prevTi[-1]
# Now we need to handle the case when some payments are already done
ti_grid = ti_grid[np.where(ti_grid>t)]
temp= np.zeros(np.size(r_t));
P_t_TiLambda = lambda Ti : HW_ZCB(lambd,eta,P0T,t,Ti,r_t)
for (idx,ti) in enumerate(ti_grid):
if ti>Ti:
temp = temp + tau * P_t_TiLambda(ti)
P_t_Ti = P_t_TiLambda(Ti)
P_t_Tm = P_t_TiLambda(Tm)
swapRate = (P_t_Ti - P_t_Tm) / temp
return swapRate
def Bullet(rate,notional,periods,CPR):
# it returns a matrix M such that
# M = [t notional(t) prepayment(t) notional_quote(t) interest_(t) installment(t)]
# WARNING! here "rate" and "periods" are quite general, the choice of getting year/month/day.. steps, depends on the rate
# that the function receives. So, it is necessary to pass the correct rate to the function
M = np.zeros((periods + 1,6))
M[:,0] = np.arange(periods + 1) # we define the times
M[0,1] = notional
for t in range(1,periods):
M[t,4] = rate*M[t-1,1] # interest quote
M[t,3] = 0 # notional quote, 0 for bullet mortgage
scheduled_oustanding = M[t-1,1] - M[t,3]
M[t,2] = scheduled_oustanding * CPR[t] # prepayment
M[t,1] = scheduled_oustanding - M[t,2] # notional(t) = notional(t-1) - (notional quote + prepayment)
M[t,5] = M[t,4] + M[t,2] + M[t,3]
M[periods,4] = rate*M[periods-1,1] # interest quote
M[periods,3] = M[periods-1,1] # notional quote
M[periods,5] = M[periods,4] + M[periods,2] + M[periods,3]
return M
def Annuity(rate,notional,periods,CPR):
# it returns a matrix M such that
# M = [t notional(t) prepayment(t) notional_quote(t) interest_quote(t) installment(t)]
# WARNING! here "rate" and "periods" are quite general, the choice of getting year/month/day.. steps, depends on the rate
# that the function receives. So, it is necessary to pass the correct rate to the function
M = np.zeros((periods + 1,6))
M[:,0] = np.arange(periods + 1) # we define the times
M[0,1] = notional
for t in range(1,periods + 1):
# we are computing the installment at time t knowing the oustanding at time (t-1)
remaining_periods = periods - (t - 1)
# Installment, C(t_i)
M[t,5] = rate * M[t-1,1]/(1 - 1/(1 + rate)**remaining_periods)
# Interest rate payment, I(t_i) = r * N(t_{i})
M[t,4] = rate * M[t-1,1]
# Notional payment, Q(t_i) = C(t_i) - I(t_i)
M[t,3] = M[t,5] - M[t,4]
# Prepayment, P(t_i)= Lambda * (N(t_i) -Q(t_i))
M[t,2] = CPR[t] * (M[t-1,1] - M[t,3])
# notional, N(t_{i+1}) = N(t_{i}) - lambda * (Q(t_{i} + P(t_i)))
M[t,1] = M[t-1,1] - M[t,3] - M[t,2]
return M
def mainCode():
Irrational = lambda x : 0.04 + 0.1/(1 + np.exp(200 * (-x)))
Rational = lambda x : 0.04*(x>0.0)
IncentiveFunction = Irrational
K = 0.05
newRate = np.linspace(-0.1,0.1,150)
epsilon = K - newRate
incentive = IncentiveFunction(epsilon)
plt.figure(1)
plt.plot(newRate,incentive)
plt.xlabel('S(t)')
plt.ylabel('Incentive')
plt.grid()
plt.figure(2)
plt.plot(epsilon,incentive)
plt.xlabel('epsilon= K - S(t)')
plt.ylabel('Incentive')
plt.grid()
# Stochastic interest rates
NoOfPaths = 2000
NoOfSteps = 30
lambd = 0.05
eta = 0.01
# End date of the underlying swap / mortgage
Tend = 30
# Market ZCB
P0T = lambda T: np.exp(-0.05*T)
paths = GeneratePathsHWEuler(NoOfPaths, NoOfSteps,Tend, P0T, lambd, eta)
R = paths["R"]
tiGrid = paths["time"]
# Compute swap rates, at this point we assume that the incentive is driven by the CMS rate
S = np.zeros([NoOfPaths,NoOfSteps+1])
for (i,ti) in enumerate(tiGrid):
S[:,i] = SwapRateHW(ti,ti,Tend+ti,30,R[:,i],P0T,lambd,eta)
# Incentive for the new swap rate
epsilon = K - S[:,-1]
incentive = IncentiveFunction(epsilon)
plt.figure(3)
plt.plot(epsilon,incentive,'.r')
plt.xlabel('epsilon= K - S(t)')
plt.ylabel('Incentive')
plt.grid()
plt.title('Incentive for prepayment given stochastis S(t)')
plt.figure(4)
plt.hist(S[:,-1],bins=50)
plt.grid()
plt.title('Swap distribution at Tend')
# Building up of stochastic notional N(ti) for every ti
MortgageProfile = Annuity
notional = 1000000
N = np.zeros([NoOfPaths,NoOfSteps+1])
for i in range(0,NoOfPaths):
epsilon = K - S[i,:]
Lambda = IncentiveFunction(epsilon)
NotionalProfile = MortgageProfile(K,notional,NoOfSteps,Lambda)
N[i,:] = NotionalProfile[:,1]
plt.figure(6)
plt.grid()
plt.xlabel('time')
plt.ylabel('notional')
n = 100
for k in range(0,n):
plt.plot(tiGrid,N[k,:],'-b')
AnnuityProfile_NoPrepayment = MortgageProfile(K,notional,NoOfSteps,np.zeros(NoOfSteps+1))
plt.plot(tiGrid,AnnuityProfile_NoPrepayment[:,1],'--r')
plt.title('Notional profile')
return 0.0
mainCode()
================================================
FILE: Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/BSHW_Comparison.py
================================================
#%%
"""
Created on Thu Jan 04 2019
The BSHW model and implied volatilities obtained with the COS method and comparisons
against a factorized 1D case where the implied volatilities are known analytically
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
import scipy.stats as st
import enum
import scipy.optimize as optimize
# set i= imaginary number
i = np.complex(0.0,1.0)
# time-step needed for differentiation
dt = 0.0001
# This class defines puts and calls
class OptionType(enum.Enum):
CALL = 1.0
PUT = -1.0
def CallPutOptionPriceCOSMthd_StochIR(cf,CP,S0,tau,K,N,L,P0T):
# cf - characteristic function as a functon, in the book denoted as \varphi
# CP - C for call and P for put
# S0 - Initial stock price
# tau - time to maturity
# K - list of strikes
# N - Number of expansion terms
# L - size of truncation domain (typ.:L=8 or L=10)
# P0T - zero-coupon bond for maturity T.
# reshape K to a column vector
if K is not np.array:
K = np.array(K).reshape([len(K),1])
#assigning i=sqrt(-1)
i = np.complex(0.0,1.0)
x0 = np.log(S0 / K)
# truncation domain
a = 0.0 - L * np.sqrt(tau)
b = 0.0 + L * np.sqrt(tau)
# sumation from k = 0 to k=N-1
k = np.linspace(0,N-1,N).reshape([N,1])
u = k * np.pi / (b - a);
# Determine coefficients for Put Prices
H_k = CallPutCoefficients(OptionType.PUT,a,b,k)
mat = np.exp(i * np.outer((x0 - a) , u))
temp = cf(u) * H_k
temp[0] = 0.5 * temp[0]
value = K * np.real(mat.dot(temp))
# we use call-put parity for call options
if CP == OptionType.CALL:
value = value + S0 - K * P0T
return value
# Determine coefficients for Put Prices
def CallPutCoefficients(CP,a,b,k):
if CP==OptionType.CALL:
c = 0.0
d = b
coef = Chi_Psi(a,b,c,d,k)
Chi_k = coef["chi"]
Psi_k = coef["psi"]
if a < b and b < 0.0:
H_k = np.zeros([len(k),1])
else:
H_k = 2.0 / (b - a) * (Chi_k - Psi_k)
elif CP==OptionType.PUT:
c = a
d = 0.0
coef = Chi_Psi(a,b,c,d,k)
Chi_k = coef["chi"]
Psi_k = coef["psi"]
H_k = 2.0 / (b - a) * (- Chi_k + Psi_k)
return H_k
def Chi_Psi(a,b,c,d,k):
psi = np.sin(k * np.pi * (d - a) / (b - a)) - np.sin(k * np.pi * (c - a)/(b - a))
psi[1:] = psi[1:] * (b - a) / (k[1:] * np.pi)
psi[0] = d - c
chi = 1.0 / (1.0 + np.power((k * np.pi / (b - a)) , 2.0))
expr1 = np.cos(k * np.pi * (d - a)/(b - a)) * np.exp(d) - np.cos(k * np.pi
* (c - a) / (b - a)) * np.exp(c)
expr2 = k * np.pi / (b - a) * np.sin(k * np.pi *
(d - a) / (b - a)) - k * np.pi / (b - a) * np.sin(k
* np.pi * (c - a) / (b - a)) * np.exp(c)
chi = chi * (expr1 + expr2)
value = {"chi":chi,"psi":psi }
return value
# Black-Scholes Call option price
def BS_Call_Option_Price(CP,S_0,K,sigma,tau,r):
if K is list:
K = np.array(K).reshape([len(K),1])
d1 = (np.log(S_0 / K) + (r + 0.5 * np.power(sigma,2.0))
* tau) / float(sigma * np.sqrt(tau))
d2 = d1 - sigma * np.sqrt(tau)
if CP == OptionType.CALL:
value = st.norm.cdf(d1) * S_0 - st.norm.cdf(d2) * K * np.exp(-r * tau)
elif CP == OptionType.PUT:
value = st.norm.cdf(-d2) * K * np.exp(-r * tau) - st.norm.cdf(-d1)*S_0
return value
# Implied volatility method
def ImpliedVolatilityBlack76(CP,marketPrice,K,T,S_0):
func = lambda sigma: np.power(BS_Call_Option_Price(CP,S_0,K,sigma,T,0.0) - marketPrice, 1.0)
impliedVol = optimize.newton(func, 0.2, tol=1e-9)
#impliedVol = optimize.brent(func, brack= (0.05, 2))
return impliedVol
def ChFBSHW(u, T, P0T, lambd, eta, rho, sigma):
i = np.complex(0.0,1.0)
f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
# Initial interest rate is a forward rate at time t->0
r0 = f0T(0.00001)
theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) \
+ eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))
C = lambda u,tau: 1.0/lambd*(i*u-1.0)*(1.0-np.exp(-lambd*tau))
# define a grid for the numerical integration of function theta
zGrid = np.linspace(0.0,T,2500)
term1 = lambda u: 0.5*sigma*sigma *i*u*(i*u-1.0)*T
term2 = lambda u: i*u*rho*sigma*eta/lambd*(i*u-1.0)*(T+1.0/lambd \
*(np.exp(-lambd*T)-1.0))
term3 = lambda u: eta*eta/(4.0*np.power(lambd,3.0))*np.power(i+u,2.0)*\
(3.0+np.exp(-2.0*lambd*T)-4.0*np.exp(-lambd*T)-2.0*lambd*T)
term4 = lambda u: lambd*integrate.trapz(theta(T-zGrid)*C(u,zGrid), zGrid)
A= lambda u: term1(u) + term2(u) + term3(u) + term4(u)
# Note that we don't include B(u)*x0 term as it is included in the COS method
cf = lambda u : np.exp(A(u) + C(u,T)*r0 )
# Iterate over all u and collect the ChF, iteration is necessary due to the integration over in term4
cfV = []
for ui in u:
cfV.append(cf(ui))
return cfV
def BSHWVolatility(T,eta,sigma,rho,lambd):
Br= lambda t,T: 1/lambd * (np.exp(-lambd*(T-t))-1.0)
sigmaF = lambda t: np.sqrt(sigma * sigma + eta * eta * Br(t,T) * Br(t,T)\
- 2.0 * rho * sigma * eta * Br(t,T))
zGrid = np.linspace(0.0,T,2500)
sigmaC = np.sqrt(1/T*integrate.trapz(sigmaF(zGrid)*sigmaF(zGrid), zGrid))
return sigmaC
def BSHWOptionPrice(CP,S0,K,P0T,T,eta,sigma,rho,lambd):
frwdS0 = S0 / P0T
vol = BSHWVolatility(T,eta,sigma,rho,lambd)
# As we deal with the forward prices we evaluate Black's 76 prices
r = 0.0
BlackPrice = BS_Call_Option_Price(CP,frwdS0,K,vol,T,r)
return P0T * BlackPrice
def mainCalculation():
CP = OptionType.CALL
K = np.linspace(40.0,220.0,100)
K = np.array(K).reshape([len(K),1])
# HW model settings
lambd = 0.1
eta = 0.05
sigma = 0.2
rho = 0.3
S0 = 100
T = 5.0
# We define a ZCB curve (obtained from the market)
P0T = lambda T: np.exp(-0.05*T)
N = 500
L = 8
# Characteristic function of the BSHW model + the COS method
cf = lambda u: ChFBSHW(u, T, P0T, lambd, eta, rho, sigma)
valCOS = CallPutOptionPriceCOSMthd_StochIR(cf, CP, S0, T, K, N, L,P0T(T))
exactBSHW = BSHWOptionPrice(CP,S0,K,P0T(T),T,eta,sigma,rho,lambd)
IV = np.zeros([len(K),1])
for idx in range(0,len(K)):
frwdStock = S0 / P0T(T)
valCOSFrwd = valCOS[idx] / P0T(T)
IV[idx] = ImpliedVolatilityBlack76(CP,valCOSFrwd, K[idx], T, frwdStock)
IVExact = BSHWVolatility(T,eta,sigma,rho,lambd)
print(IVExact)
# Plot option prices
plt.figure(1)
plt.plot(K,valCOS)
plt.plot(K,exactBSHW,'--r')
plt.grid()
plt.xlabel("strike")
plt.ylabel("option price")
plt.legend(["BSHW, COS method","BSHW, exact solution"])
# Plot implied volatilities
plt.figure(2)
plt.plot(K,IV*100.0)
plt.plot(K,np.ones([len(K),1])*IVExact*100.0,'--r')
plt.grid()
plt.xlabel("strike")
plt.ylabel("Implied Volatility [%]")
plt.legend(["BSHW, COS method","BSHW, exact solution"])
plt.axis([np.min(K),np.max(K),0,100])
mainCalculation()
================================================
FILE: Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/BSHW_ImpliedVolatility.py
================================================
#%%
"""
Created on July 05 2021
The BSHW model and implied volatilities term structure computerion
This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
import scipy.stats as st
import enum
import scipy.optimize as optimize
# set i= imaginary number
i = np.complex(0.0,1.0)
# time-step needed for differentiation
dt = 0.0001
# This class defines puts and calls
class OptionType(enum.Enum):
CALL = 1.0
PUT = -1.0
# Black-Scholes Call option price
def BS_Call_Option_Price(CP,S_0,K,sigma,tau,r):
if K is list:
K = np.array(K).reshape([len(K),1])
d1 = (np.log(S_0 / K) + (r + 0.5 * np.power(sigma,2.0))
* tau) / float(sigma * np.sqrt(tau))
d2 = d1 - sigma * np.sqrt(tau)
if CP == OptionType.CALL:
value = st.norm.cdf(d1) * S_0 - st.norm.cdf(d2) * K * np.exp(-r * tau)
elif CP == OptionType.PUT:
value = st.norm.cdf(-d2) * K * np.exp(-r * tau) - st.norm.cdf(-d1)*S_0
return value
# Implied volatility method
def ImpliedVolatilityBlack76(CP,frwdMarketPrice,K,T,frwdStock):
func = lambda sigma: np.power(BS_Call_Option_Price(CP,frwdStock,K,sigma,T,0.0) - frwdMarketPrice, 1.0)
impliedVol = optimize.newton(func, 0.2, tol=1e-9)
#impliedVol = optimize.brent(func, brack= (0.05, 2))
return impliedVol
def BSHWVolatility(T,eta,sigma,rho,lambd):
Br= lambda t,T: 1/lambd * (np.exp(-lambd*(T-t))-1.0)
sigmaF = lambda t: np.sqrt(sigma * sigma + eta * eta * Br(t,T) * Br(t,T)\
- 2.0 * rho * sigma * eta * Br(t,T))
zGrid = np.linspace(0.0,T,2500)
sigmaC = np.sqrt(1/T*integrate.trapz(sigmaF(zGrid)*sigmaF(zGrid), zGrid))
return sigmaC
def BSHWOptionPrice(CP,S0,K,P0T,T,eta,sigma,rho,lambd):
frwdS0 = S0 / P0T
vol = BSHWVolatility(T,eta,sigma,rho,lambd)
# As we deal with the forward prices we evaluate Black's 76 prices
r = 0.0
BlackPrice = BS_Call_Option_Price(CP,frwdS0,K,vol,T,r)
return P0T * BlackPrice
def mainCalculation():
CP = OptionType.CALL
# HW model settings
lambd = 0.1
eta = 0.01
sigma = 0.2
rho = 0.3
S0 = 100
# Strike equals stock value, thus ATM
K = [100]
K = np.array(K).reshape([len(K),1])
# We define a ZCB curve (obtained from the market)
P0T = lambda T: np.exp(-0.05*T)
# Maturitires at which we compute implied volatility
TMat = np.linspace(0.1,5.0,20)
# Effect of lambda
plt.figure(2)
plt.grid()
plt.xlabel('maturity, T')
plt.ylabel('implied volatility')
lambdV = [0.001,0.1,0.5,1.5]
legend = []
for lambdaTemp in lambdV:
IV =np.zeros([len(TMat),1])
for idx in range(0,len(TMat)):
T = TMat[idx]
val = BSHWOptionPrice(CP,S0,K,P0T(T),T,eta,sigma,rho,lambdaTemp)
frwdStock = S0 / P0T(T)
valFrwd = val / P0T(T)
IV[idx] = ImpliedVolatilityBlack76(CP,valFrwd,K,T,frwdStock)
plt.plot(TMat,IV*100.0)
legend.append('lambda={0}'.format(lambdaTemp))
plt.legend(legend)
# Effect of eta
plt.figure(3)
plt.grid()
plt.xlabel('maturity, T')
plt.ylabel('implied volatility')
etaV = [0.001,0.05,0.1,0.15]
legend = []
for etaTemp in etaV:
IV =np.zeros([len(TMat),1])
for idx in range(0,len(TMat)):
T = TMat[idx]
val = BSHWOptionPrice(CP,S0,K,P0T(T),T,etaTemp,sigma,rho,lambd)
frwdStock = S0 / P0T(T)
valFrwd = val/P0T(T)
IV[idx] = ImpliedVolatilityBlack76(CP,valFrwd,K,T,frwdStock)
plt.plot(TMat,IV*100.0)
legend.append('eta={0}'.format(etaTemp))
plt.legend(legend)
# Effect of sigma
plt.figure(4)
plt.grid()
plt.xlabel('maturity, T')
plt.ylabel('implied volatility')
sigmaV = [0.1,0.2,0.3,0.4]
legend = []
for sigmaTemp in sigmaV:
IV =np.zeros([len(TMat),1])
for idx in range(0,len(TMat)):
T = TMat[idx]
val = BSHWOptionPrice(CP,S0,K,P0T(T),T,eta,sigmaTemp,rho,lambd)
frwdStock = S0 / P0T(T)
valFrwd = val / P0T(T)
IV[idx] = ImpliedVolatilityBlack76(CP,valFrwd,K,T,frwdStock)
plt.plot(TMat,IV*100.0)
legend.append('sigma={0}'.format(sigmaTemp))
plt.legend(legend)
# Effect of rho
plt.figure(5)
plt.grid()
plt.xlabel('maturity, T')
plt.ylabel('implied volatility')
rhoV = [-0.7, -0.3, 0.3, 0.7]
legend = []
for rhoTemp in rhoV:
IV =np.zeros([len(TMat),1])
for idx in range(0,len(TMat)):
T = TMat[idx]
val = BSHWOptionPrice(CP,S0,K,P0T(T),T,eta,sigma,rhoTemp,lambd)
frwdStock = S0 / P0T(T)
gitextract__8ev3d3k/ ├── LICENSE ├── Lecture 02-Understanding of Filtrations and Measures/ │ └── Materials/ │ ├── Black_Scholes_Jumps.py │ └── Martingale.py ├── Lecture 03-The HJM Framework/ │ └── Materials/ │ ├── CIR_IR_paths.py │ ├── Ho-Lee-ZCBs.py │ ├── Hull-White-Paths.py │ └── Hull-White-ZCBs.py ├── Lecture 04-Yield Curve Dynamics under Short Rate/ │ └── Materials/ │ ├── Hull-White-CompRateSim.py │ ├── Hull-White-ZCBs2.py │ └── Hull_White_1F_2F_Comparison.py ├── Lecture 05-Interest Rate Products/ │ └── Materials/ │ ├── HW_Caplets.py │ ├── HW_OptionsOnZCBs.py │ └── Swaps_HW.py ├── Lecture 06-Construction of Yield Curve and Multi-Curve/ │ └── Materials/ │ ├── MultiCurveBuild.py │ ├── YieldCurveBuildGreeks.py │ └── YieldCurveBuild_Treasury.py ├── Lecture 07-Pricing of Swaptions and Negative Interest Rates/ │ └── Materials/ │ ├── HW_CapletsAndFloorlets.py │ ├── JamshidianTrick.py │ └── ShiftedLognormal.py ├── Lecture 08-Mortgages and Prepayments/ │ └── Materials/ │ ├── AnnuityMortgage.py │ ├── BulletMortgage.py │ ├── Incentives.py │ └── StochasticAmortizingSwap.py ├── Lecture 09-Hybrid Models and Stochastic Interest Rates/ │ └── Materials/ │ ├── BSHW_Comparison.py │ ├── BSHW_ImpliedVolatility.py │ ├── H1_HW_COS_vs_MC.py │ ├── SZHW_ImpliedVolatilities.py │ └── SZHW_MonteCarlo_DiversificationProduct.py ├── Lecture 10-Foreign Exchange (FX) and Inflation/ │ └── Materials/ │ └── H1_HW_COS_vs_MC_FX.py ├── Lecture 11-Market Model and Convexity Adjustments/ │ └── Materials/ │ ├── ConvexityCorrection.py │ └── DD_ImpliedVolatility.py ├── Lecture 12-Valuation Adjustments- xVA (CVA,BCVA and FVA)/ │ └── Materials/ │ └── Exposures_HW_Netting.py ├── Lecture 13-Value-at-Risk and Expected Shortfall/ │ └── Materials/ │ ├── HistoricalVaR_Calculation.py │ ├── MonteCarloVaR.py │ └── MrktData.xlsx └── README.md
SYMBOL INDEX (275 symbols across 33 files) FILE: Lecture 02-Understanding of Filtrations and Measures/Materials/Black_Scholes_Jumps.py class OptionType (line 18) | class OptionType(enum.Enum): function GeneratePaths (line 22) | def GeneratePaths(NoOfPaths,NoOfSteps,S0,T,muJ,sigmaJ,r): function EUOptionPriceFromMCPaths (line 46) | def EUOptionPriceFromMCPaths(CP,S,K,T,r): function BS_Call_Put_Option_Price (line 53) | def BS_Call_Put_Option_Price(CP,S_0,K,sigma,t,T,r): function CallOption_CondExpectation (line 64) | def CallOption_CondExpectation(NoOfPaths,T,S0,K,J,r): function mainCalculation (line 77) | def mainCalculation(): FILE: Lecture 02-Understanding of Filtrations and Measures/Materials/Martingale.py function martingaleA (line 20) | def martingaleA(): function martingaleB (line 26) | def martingaleB(): FILE: Lecture 03-The HJM Framework/Materials/CIR_IR_paths.py function GeneratePathsCIREuler (line 15) | def GeneratePathsCIREuler(NoOfPaths,NoOfSteps,T,lambd,r0,theta,gamma): function mainCalculation (line 37) | def mainCalculation(): FILE: Lecture 03-The HJM Framework/Materials/Ho-Lee-ZCBs.py function f0T (line 16) | def f0T(t,P0T): function GeneratePathsHoLeeEuler (line 22) | def GeneratePathsHoLeeEuler(NoOfPaths,NoOfSteps,T,P0T,sigma): function mainCalculation (line 50) | def mainCalculation(): FILE: Lecture 03-The HJM Framework/Materials/Hull-White-Paths.py function GeneratePathsHWEuler (line 15) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function mainCalculation (line 44) | def mainCalculation(): FILE: Lecture 03-The HJM Framework/Materials/Hull-White-ZCBs.py function f0T (line 16) | def f0T(t,P0T): function GeneratePathsHWEuler (line 22) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 53) | def HW_theta(lambd,eta,P0T): function mainCalculation (line 60) | def mainCalculation(): FILE: Lecture 04-Yield Curve Dynamics under Short Rate/Materials/Hull-White-CompRateSim.py function f0T (line 19) | def f0T(t,P0T): function GeneratePathsHWEuler (line 25) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 53) | def HW_theta(lambd,eta,P0T): function HW_A (line 59) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 70) | def HW_B(lambd,eta,T1,T2): function HW2F_ZCB (line 73) | def HW2F_ZCB(lambd1,lambd2,eta1,eta2,rho,P0T,T1,T2,xT1,yT1): function HW_ZCB (line 85) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function HW_r_0 (line 90) | def HW_r_0(P0T,lambd,eta): function mainCalculation (line 95) | def mainCalculation(): FILE: Lecture 04-Yield Curve Dynamics under Short Rate/Materials/Hull-White-ZCBs2.py function f0T (line 17) | def f0T(t,P0T): function GeneratePathsHWEuler (line 23) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 54) | def HW_theta(lambd,eta,P0T): function HW_A (line 60) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 69) | def HW_B(lambd,eta,T1,T2): function HW_ZCB (line 72) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function mainCalculation (line 77) | def mainCalculation(): FILE: Lecture 04-Yield Curve Dynamics under Short Rate/Materials/Hull_White_1F_2F_Comparison.py function GeneratePathsHW2FEuler (line 18) | def GeneratePathsHW2FEuler(NoOfPaths,NoOfSteps,T,P0T, lambd1,lambd2, eta... function f0T (line 55) | def f0T(t,P0T): function GeneratePathsHWEuler (line 61) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 89) | def HW_theta(lambd,eta,P0T): function HW_A (line 95) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 106) | def HW_B(lambd,eta,T1,T2): function HW2F_ZCB (line 109) | def HW2F_ZCB(lambd1,lambd2,eta1,eta2,rho,P0T,T1,T2,xT1,yT1): function HW_ZCB (line 121) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function HW_r_0 (line 126) | def HW_r_0(P0T,lambd,eta): function mainCalculation (line 131) | def mainCalculation(): FILE: Lecture 05-Interest Rate Products/Materials/HW_Caplets.py class OptionType (line 19) | class OptionType(enum.Enum): function GeneratePathsHWEuler (line 23) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 54) | def HW_theta(lambd,eta,P0T): function HW_A (line 61) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 72) | def HW_B(lambd,eta,T1,T2): function HW_ZCB (line 75) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function HWMean_r (line 80) | def HWMean_r(P0T,lambd,eta,T): function HW_r_0 (line 92) | def HW_r_0(P0T,lambd,eta): function HW_Mu_FrwdMeasure (line 100) | def HW_Mu_FrwdMeasure(P0T,lambd,eta,T): function HWVar_r (line 117) | def HWVar_r(lambd,eta,T): function HWDensity (line 120) | def HWDensity(P0T,lambd,eta,T): function HW_CapletFloorletPrice (line 125) | def HW_CapletFloorletPrice(CP,N,K,lambd,eta,P0T,T1,T2): function HW_ZCB_CallPutPrice (line 135) | def HW_ZCB_CallPutPrice(CP,K,lambd,eta,P0T,T1,T2): function mainCalculation (line 158) | def mainCalculation(): FILE: Lecture 05-Interest Rate Products/Materials/HW_OptionsOnZCBs.py class OptionType (line 20) | class OptionType(enum.Enum): function GeneratePathsHWEuler (line 24) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 55) | def HW_theta(lambd,eta,P0T): function HW_A (line 62) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 73) | def HW_B(lambd,eta,T1,T2): function HW_ZCB (line 76) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function HWMean_r (line 81) | def HWMean_r(P0T,lambd,eta,T): function HW_r_0 (line 93) | def HW_r_0(P0T,lambd,eta): function HW_Mu_FrwdMeasure (line 101) | def HW_Mu_FrwdMeasure(P0T,lambd,eta,T): function HWVar_r (line 118) | def HWVar_r(lambd,eta,T): function HWDensity (line 121) | def HWDensity(P0T,lambd,eta,T): function HW_ZCB_CallPutPrice (line 126) | def HW_ZCB_CallPutPrice(CP,K,lambd,eta,P0T,T1,T2): function mainCalculation (line 148) | def mainCalculation(): FILE: Lecture 05-Interest Rate Products/Materials/Swaps_HW.py class OptionTypeSwap (line 21) | class OptionTypeSwap(enum.Enum): function GeneratePathsHWEuler (line 25) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 56) | def HW_theta(lambd,eta,P0T): function HW_A (line 63) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 74) | def HW_B(lambd,eta,T1,T2): function HW_ZCB (line 77) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function HW_r_0 (line 87) | def HW_r_0(P0T,lambd,eta): function SwapPrice (line 95) | def SwapPrice(CP,notional,K,t,Ti,Tm,n,P0T): function HW_SwapPrice (line 132) | def HW_SwapPrice(CP,notional,K,t,Ti,Tm,n,r_t,P0T,lambd,eta): function mainCalculation (line 174) | def mainCalculation(): FILE: Lecture 06-Construction of Yield Curve and Multi-Curve/Materials/MultiCurveBuild.py class OptionTypeSwap (line 19) | class OptionTypeSwap(enum.Enum): function IRSwap (line 23) | def IRSwap(CP,notional,K,t,Ti,Tm,n,P0T): function IRSwapMultiCurve (line 52) | def IRSwapMultiCurve(CP,notional,K,t,Ti,Tm,n,P0T,P0TFrd): function P0TModel (line 75) | def P0TModel(t,ti,ri,method): function YieldCurve (line 80) | def YieldCurve(instruments, maturities, r0, method, tol): function MultivariateNewtonRaphson (line 85) | def MultivariateNewtonRaphson(ri, ti, instruments, method, tol): function Jacobian (line 99) | def Jacobian(ti, ri, instruments, method): function EvaluateInstruments (line 114) | def EvaluateInstruments(ti,ri,instruments,method): function linear_interpolation (line 121) | def linear_interpolation(ti,ri): function spline_interpolate (line 125) | def spline_interpolate(ti,ri): function scipy_1d_interpolate (line 130) | def scipy_1d_interpolate(ti, ri): function mainCode (line 136) | def mainCode(): FILE: Lecture 06-Construction of Yield Curve and Multi-Curve/Materials/YieldCurveBuildGreeks.py class OptionTypeSwap (line 18) | class OptionTypeSwap(enum.Enum): function IRSwap (line 22) | def IRSwap(CP,notional,K,t,Ti,Tm,n,P0T): function P0TModel (line 58) | def P0TModel(t,ti,ri,method): function YieldCurve (line 69) | def YieldCurve(instruments, maturities, r0, method, tol): function MultivariateNewtonRaphson (line 74) | def MultivariateNewtonRaphson(ri, ti, instruments, method, tol): function Jacobian (line 88) | def Jacobian(ti, ri, instruments, method): function EvaluateInstruments (line 103) | def EvaluateInstruments(ti,ri,instruments,method): function linear_interpolation (line 110) | def linear_interpolation(ti,ri): function quadratic_interpolation (line 114) | def quadratic_interpolation(ti, ri): function cubic_interpolation (line 118) | def cubic_interpolation(ti, ri): function BuildInstruments (line 124) | def BuildInstruments(K,mat): function mainCode (line 136) | def mainCode(): FILE: Lecture 06-Construction of Yield Curve and Multi-Curve/Materials/YieldCurveBuild_Treasury.py class OptionTypeSwap (line 19) | class OptionTypeSwap(enum.Enum): function IRSwap (line 23) | def IRSwap(CP,notional,K,t,Ti,Tm,n,P0T): function P0TModel (line 59) | def P0TModel(t,ti,ri,method): function YieldCurve (line 64) | def YieldCurve(instruments, maturities, r0, method, tol): function MultivariateNewtonRaphson (line 69) | def MultivariateNewtonRaphson(ri, ti, instruments, method, tol): function Jacobian (line 83) | def Jacobian(ti, ri, instruments, method): function EvaluateInstruments (line 98) | def EvaluateInstruments(ti,ri,instruments,method): function linear_interpolation (line 105) | def linear_interpolation(ti,ri): function mainCode (line 109) | def mainCode(): FILE: Lecture 07-Pricing of Swaptions and Negative Interest Rates/Materials/HW_CapletsAndFloorlets.py class OptionType (line 20) | class OptionType(enum.Enum): function GeneratePathsHWEuler (line 24) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 55) | def HW_theta(lambd,eta,P0T): function HW_A (line 62) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 73) | def HW_B(lambd,eta,T1,T2): function HW_ZCB (line 76) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function HWMean_r (line 81) | def HWMean_r(P0T,lambd,eta,T): function HW_r_0 (line 93) | def HW_r_0(P0T,lambd,eta): function HW_Mu_FrwdMeasure (line 101) | def HW_Mu_FrwdMeasure(P0T,lambd,eta,T): function HWVar_r (line 118) | def HWVar_r(lambd,eta,T): function HWDensity (line 121) | def HWDensity(P0T,lambd,eta,T): function HW_CapletFloorletPrice (line 126) | def HW_CapletFloorletPrice(CP,N,K,lambd,eta,P0T,T1,T2): function HW_ZCB_CallPutPrice (line 139) | def HW_ZCB_CallPutPrice(CP,K,lambd,eta,P0T,T1,T2): function BS_Call_Put_Option_Price (line 162) | def BS_Call_Put_Option_Price(CP,S_0,K,sigma,tau,r): function ImpliedVolatilityBlack76 (line 174) | def ImpliedVolatilityBlack76(CP,marketPrice,K,T,S_0): function mainCalculation (line 191) | def mainCalculation(): FILE: Lecture 07-Pricing of Swaptions and Negative Interest Rates/Materials/JamshidianTrick.py function PsiSum (line 16) | def PsiSum(psi,N,x): function JamshidianTrick (line 22) | def JamshidianTrick(psi,N,K): function Main (line 27) | def Main(): FILE: Lecture 07-Pricing of Swaptions and Negative Interest Rates/Materials/ShiftedLognormal.py class OptionType (line 20) | class OptionType(enum.Enum): function GeneratePathsGBMShifted (line 24) | def GeneratePathsGBMShifted(NoOfPaths,NoOfSteps,T,r,sigma,S_0,shift): function GeneratePathsGBM (line 34) | def GeneratePathsGBM(NoOfPaths,NoOfSteps,T,r,sigma,S_0): function BS_Call_Put_Option_Price_Shifted (line 57) | def BS_Call_Put_Option_Price_Shifted(CP,S_0,K,sigma,tau,r,shift): function BS_Call_Put_Option_Price (line 63) | def BS_Call_Put_Option_Price(CP,S_0,K,sigma,tau,r): function ImpliedVolatilityBlack76 (line 75) | def ImpliedVolatilityBlack76(CP,marketPrice,K,T,S_0): function ImpliedVolatilityBlack76Shifted (line 90) | def ImpliedVolatilityBlack76Shifted(CP,marketPrice,K,T,S_0,shift): function mainCalculation (line 104) | def mainCalculation(): FILE: Lecture 08-Mortgages and Prepayments/Materials/AnnuityMortgage.py function Annuity (line 15) | def Annuity(rate,notional,periods,CPR): function mainCode (line 43) | def mainCode(): FILE: Lecture 08-Mortgages and Prepayments/Materials/BulletMortgage.py function Bullet (line 15) | def Bullet(rate,notional,periods,CPR): function mainCode (line 36) | def mainCode(): FILE: Lecture 08-Mortgages and Prepayments/Materials/Incentives.py function Annuity (line 15) | def Annuity(rate,notional,periods,CPR): function mainCode (line 43) | def mainCode(): FILE: Lecture 08-Mortgages and Prepayments/Materials/StochasticAmortizingSwap.py function GeneratePathsHWEuler (line 17) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 48) | def HW_theta(lambd,eta,P0T): function HW_A (line 54) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 65) | def HW_B(lambd,eta,T1,T2): function HW_ZCB (line 68) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function SwapRateHW (line 78) | def SwapRateHW(t,Ti,Tm,n,r_t,P0T,lambd,eta): function Bullet (line 117) | def Bullet(rate,notional,periods,CPR): function Annuity (line 138) | def Annuity(rate,notional,periods,CPR): function mainCode (line 166) | def mainCode(): FILE: Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/BSHW_Comparison.py class OptionType (line 27) | class OptionType(enum.Enum): function CallPutOptionPriceCOSMthd_StochIR (line 31) | def CallPutOptionPriceCOSMthd_StochIR(cf,CP,S0,tau,K,N,L,P0T): function CallPutCoefficients (line 71) | def CallPutCoefficients(CP,a,b,k): function Chi_Psi (line 92) | def Chi_Psi(a,b,c,d,k): function BS_Call_Option_Price (line 109) | def BS_Call_Option_Price(CP,S_0,K,sigma,tau,r): function ImpliedVolatilityBlack76 (line 122) | def ImpliedVolatilityBlack76(CP,marketPrice,K,T,S_0): function ChFBSHW (line 128) | def ChFBSHW(u, T, P0T, lambd, eta, rho, sigma): function BSHWVolatility (line 159) | def BSHWVolatility(T,eta,sigma,rho,lambd): function BSHWOptionPrice (line 167) | def BSHWOptionPrice(CP,S0,K,P0T,T,eta,sigma,rho,lambd): function mainCalculation (line 176) | def mainCalculation(): FILE: Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/BSHW_ImpliedVolatility.py class OptionType (line 27) | class OptionType(enum.Enum): function BS_Call_Option_Price (line 32) | def BS_Call_Option_Price(CP,S_0,K,sigma,tau,r): function ImpliedVolatilityBlack76 (line 45) | def ImpliedVolatilityBlack76(CP,frwdMarketPrice,K,T,frwdStock): function BSHWVolatility (line 52) | def BSHWVolatility(T,eta,sigma,rho,lambd): function BSHWOptionPrice (line 60) | def BSHWOptionPrice(CP,S0,K,P0T,T,eta,sigma,rho,lambd): function mainCalculation (line 69) | def mainCalculation(): FILE: Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/H1_HW_COS_vs_MC.py class OptionType (line 20) | class OptionType(enum.Enum): function CallPutOptionPriceCOSMthd_StochIR (line 24) | def CallPutOptionPriceCOSMthd_StochIR(cf,CP,S0,tau,K,N,L,P0T): function CallPutCoefficients (line 64) | def CallPutCoefficients(CP,a,b,k): function Chi_Psi (line 85) | def Chi_Psi(a,b,c,d,k): function EUOptionPriceFromMCPathsGeneralizedStochIR (line 101) | def EUOptionPriceFromMCPathsGeneralizedStochIR(CP,S,K,T,M): function CIR_Sample (line 113) | def CIR_Sample(NoOfPaths,kappa,gamma,vbar,s,t,v_s): function GeneratePathsHestonHW_AES (line 120) | def GeneratePathsHestonHW_AES(NoOfPaths,NoOfSteps,P0T,T,S_0,kappa,gamma,... function GeneratePathsHestonHWEuler (line 181) | def GeneratePathsHestonHWEuler(NoOfPaths,NoOfSteps,P0T,T,S_0,kappa,gamma... function meanSqrtV_3 (line 240) | def meanSqrtV_3(kappa,v0,vbar,gamma): function C_H1HW (line 247) | def C_H1HW(u,tau,lambd): function D_H1HW (line 252) | def D_H1HW(u,tau,kappa,gamma,rhoxv): function A_H1HW (line 261) | def A_H1HW(u,tau,P0T,lambd,eta,kappa,gamma,vbar,v0,rhoxv,rhoxr): function ChFH1HWModel (line 290) | def ChFH1HWModel(P0T,lambd,eta,tau,kappa,gamma,vbar,v0,rhoxv, rhoxr): function mainCalculation (line 301) | def mainCalculation(): FILE: Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/SZHW_ImpliedVolatilities.py class OptionType (line 27) | class OptionType(enum.Enum): function CallPutOptionPriceCOSMthd_StochIR (line 32) | def CallPutOptionPriceCOSMthd_StochIR(cf,CP,S0,tau,K,N,L,P0T): function CallPutCoefficients (line 72) | def CallPutCoefficients(CP,a,b,k): function Chi_Psi (line 93) | def Chi_Psi(a,b,c,d,k): function BS_Call_Put_Option_Price (line 110) | def BS_Call_Put_Option_Price(CP,S_0,K,sigma,tau,r): function ImpliedVolatilityBlack76 (line 123) | def ImpliedVolatilityBlack76(CP,marketPrice,K,T,S_0): function C (line 140) | def C(u,tau,lambd): function D (line 144) | def D(u,tau,kappa,Rxsigma,gamma): function E (line 153) | def E(u,tau,lambd,gamma,Rxsigma,Rrsigma,Rxr,eta,kappa,sigmabar): function A (line 173) | def A(u,tau,eta,lambd,Rxsigma,Rrsigma,Rxr,gamma,kappa,sigmabar): function ChFSZHW (line 198) | def ChFSZHW(u,P0T,sigma0,tau,lambd,gamma, Rxsigma,Rrsigma,Rxr,eta,kap... function ChFBSHW (line 212) | def ChFBSHW(u, T, P0T, lambd, eta, rho, sigma): function mainCalculation (line 240) | def mainCalculation(): FILE: Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/SZHW_MonteCarlo_DiversificationProduct.py class OptionType (line 25) | class OptionType(enum.Enum): function HW_theta (line 29) | def HW_theta(lambd,eta,P0T): function HW_A (line 35) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 46) | def HW_B(lambd,eta,T1,T2): function HW_ZCB (line 49) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function EUOptionPriceFromMCPathsGeneralizedStochIR (line 54) | def EUOptionPriceFromMCPathsGeneralizedStochIR(CP,S,K,T,M): function GeneratePathsSZHWEuler (line 65) | def GeneratePathsSZHWEuler(NoOfPaths,NoOfSteps,P0T,T,S0,sigma0,sigmabar,... function DiversifcationPayoff (line 117) | def DiversifcationPayoff(P0T,S_T,S0,r_T,M_T,T,T1,lambd,eta,omegaV): function mainCalculation (line 127) | def mainCalculation(): FILE: Lecture 10-Foreign Exchange (FX) and Inflation/Materials/H1_HW_COS_vs_MC_FX.py class OptionType (line 22) | class OptionType(enum.Enum): function BS_Call_Put_Option_Price (line 27) | def BS_Call_Put_Option_Price(CP,S_0,K,sigma,tau,r): function ImpliedVolatilityBlack76 (line 40) | def ImpliedVolatilityBlack76(CP,marketPrice,K,T,S_0): function CallPutOptionPriceCOSMthd_StochIR (line 57) | def CallPutOptionPriceCOSMthd_StochIR(cf,CP,S0,tau,K,N,L,P0T): function CallPutCoefficients (line 97) | def CallPutCoefficients(CP,a,b,k): function Chi_Psi (line 118) | def Chi_Psi(a,b,c,d,k): function EUOptionPriceFromMCPathsGeneralizedFXFrwd (line 134) | def EUOptionPriceFromMCPathsGeneralizedFXFrwd(CP,S,K): function GeneratePathsHHWFXHWEuler (line 145) | def GeneratePathsHHWFXHWEuler(NoOfPaths,NoOfSteps,T,frwdFX,v0,vbar,kappa... function meanSqrtV_3 (line 194) | def meanSqrtV_3(kappa,v0,vbar,gamma): function C_H1HW_FX (line 201) | def C_H1HW_FX(u,tau,kappa,gamma,rhoxv): function ChFH1HW_FX (line 210) | def ChFH1HW_FX(u,tau,gamma,Rxv,Rxrd,Rxrf,Rrdrf,Rvrd,Rvrf,lambdd,etad,lam... function GenerateStrikes (line 250) | def GenerateStrikes(frwd,Ti): function mainCalculation (line 254) | def mainCalculation(): FILE: Lecture 11-Market Model and Convexity Adjustments/Materials/ConvexityCorrection.py class OptionType (line 18) | class OptionType(enum.Enum): function GeneratePathsHWEuler (line 22) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 53) | def HW_theta(lambd,eta,P0T): function HW_A (line 60) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 71) | def HW_B(lambd,eta,T1,T2): function HW_ZCB (line 74) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function HWMean_r (line 79) | def HWMean_r(P0T,lambd,eta,T): function HW_r_0 (line 91) | def HW_r_0(P0T,lambd,eta): function mainCalculation (line 99) | def mainCalculation(): FILE: Lecture 11-Market Model and Convexity Adjustments/Materials/DD_ImpliedVolatility.py class OptionType (line 25) | class OptionType(enum.Enum): function BS_Call_Option_Price (line 30) | def BS_Call_Option_Price(CP,S_0,K,sigma,tau,r): function ImpliedVolatilityBlack76_xxx (line 42) | def ImpliedVolatilityBlack76_xxx(CP,marketPrice,K,T,S_0): function ImpliedVolatilityBlack76 (line 49) | def ImpliedVolatilityBlack76(CP,marketPrice,K,T,S_0): function DisplacedDiffusionModel_CallPrice (line 63) | def DisplacedDiffusionModel_CallPrice(K,P0T,beta,sigma,frwd,T): function mainCalculation (line 68) | def mainCalculation(): FILE: Lecture 12-Valuation Adjustments- xVA (CVA,BCVA and FVA)/Materials/Exposures_HW_Netting.py class OptionTypeSwap (line 20) | class OptionTypeSwap(enum.Enum): function GeneratePathsHWEuler (line 24) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 55) | def HW_theta(lambd,eta,P0T): function HW_A (line 62) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 73) | def HW_B(lambd,eta,T1,T2): function HW_ZCB (line 76) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function HWMean_r (line 87) | def HWMean_r(P0T,lambd,eta,T): function HW_r_0 (line 99) | def HW_r_0(P0T,lambd,eta): function HW_Mu_FrwdMeasure (line 107) | def HW_Mu_FrwdMeasure(P0T,lambd,eta,T): function HWVar_r (line 124) | def HWVar_r(lambd,eta,T): function HWDensity (line 127) | def HWDensity(P0T,lambd,eta,T): function HW_SwapPrice (line 132) | def HW_SwapPrice(CP,notional,K,t,Ti,Tm,n,r_t,P0T,lambd,eta): function mainCalculation (line 174) | def mainCalculation(): FILE: Lecture 13-Value-at-Risk and Expected Shortfall/Materials/HistoricalVaR_Calculation.py class OptionTypeSwap (line 21) | class OptionTypeSwap(enum.Enum): function IRSwap (line 25) | def IRSwap(CP,notional,K,t,Ti,Tm,n,P0T): function P0TModel (line 61) | def P0TModel(t,ti,ri,method): function YieldCurve (line 66) | def YieldCurve(instruments, maturities, r0, method, tol): function MultivariateNewtonRaphson (line 71) | def MultivariateNewtonRaphson(ri, ti, instruments, method, tol): function Jacobian (line 85) | def Jacobian(ti, ri, instruments, method): function EvaluateInstruments (line 100) | def EvaluateInstruments(ti,ri,instruments,method): function linear_interpolation (line 107) | def linear_interpolation(ti,ri): function BuildYieldCurve (line 111) | def BuildYieldCurve(K,mat): function Portfolio (line 137) | def Portfolio(P0T): function mainCode (line 152) | def mainCode(): FILE: Lecture 13-Value-at-Risk and Expected Shortfall/Materials/MonteCarloVaR.py class OptionTypeSwap (line 21) | class OptionTypeSwap(enum.Enum): function GeneratePathsHWEuler (line 25) | def GeneratePathsHWEuler(NoOfPaths,NoOfSteps,T,P0T, lambd, eta): function HW_theta (line 56) | def HW_theta(lambd,eta,P0T): function HW_A (line 63) | def HW_A(lambd,eta,P0T,T1,T2): function HW_B (line 74) | def HW_B(lambd,eta,T1,T2): function HW_ZCB (line 77) | def HW_ZCB(lambd,eta,P0T,T1,T2,rT1): function HWMean_r (line 88) | def HWMean_r(P0T,lambd,eta,T): function HW_r_0 (line 100) | def HW_r_0(P0T,lambd,eta): function HW_Mu_FrwdMeasure (line 108) | def HW_Mu_FrwdMeasure(P0T,lambd,eta,T): function HWVar_r (line 125) | def HWVar_r(lambd,eta,T): function HWDensity (line 128) | def HWDensity(P0T,lambd,eta,T): function HW_SwapPrice (line 133) | def HW_SwapPrice(CP,notional,K,t,Ti,Tm,n,r_t,P0T,lambd,eta): function Portfolio (line 175) | def Portfolio(P0T,r_t,lambd,eta): function mainCalculation (line 191) | def mainCalculation():
Condensed preview — 36 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (299K chars).
[
{
"path": "LICENSE",
"chars": 1493,
"preview": "BSD 3-Clause License\n\nCopyright (c) 2024, leszek\n\nRedistribution and use in source and binary forms, with or without\nmod"
},
{
"path": "Lecture 02-Understanding of Filtrations and Measures/Materials/Black_Scholes_Jumps.py",
"chars": 4181,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nImpact of conditional expectation pricing (Black-Scholes with Jump volatility)\r\n\r\nTh"
},
{
"path": "Lecture 02-Understanding of Filtrations and Measures/Materials/Martingale.py",
"chars": 2686,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nSimulation of, E(W(t)|F(s)) = W(s) using nested Monte Carlo\r\n\r\nThis code is purely e"
},
{
"path": "Lecture 03-The HJM Framework/Materials/CIR_IR_paths.py",
"chars": 2572,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 11 2021\r\nMonte Carlo Paths for the CIR Process\r\n\r\nThis code is purely educational and comes fr"
},
{
"path": "Lecture 03-The HJM Framework/Materials/Ho-Lee-ZCBs.py",
"chars": 2652,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nHo-Lee Model, Simulation of the Model + Computation of ZCBs, P(0,t)\r\n\r\nThis code is p"
},
{
"path": "Lecture 03-The HJM Framework/Materials/Hull-White-Paths.py",
"chars": 2870,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nMonte Carlo paths for the Hull-White model\r\n\r\nThis code is purely educational and com"
},
{
"path": "Lecture 03-The HJM Framework/Materials/Hull-White-ZCBs.py",
"chars": 3061,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nHull-White Model, Simulation of the Model\r\n\r\nThis code is purely educational and come"
},
{
"path": "Lecture 04-Yield Curve Dynamics under Short Rate/Materials/Hull-White-CompRateSim.py",
"chars": 22230,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nYield Curve shapes and the Hull-White Model 1 Factor\r\n\r\nThis code is purely education"
},
{
"path": "Lecture 04-Yield Curve Dynamics under Short Rate/Materials/Hull-White-ZCBs2.py",
"chars": 4101,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nHull-White Model, Simulation of the Model + Computation of ZCBs, P(0,t)\r\n\r\nThis code "
},
{
"path": "Lecture 04-Yield Curve Dynamics under Short Rate/Materials/Hull_White_1F_2F_Comparison.py",
"chars": 24949,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nYield Curve shapes and the Hull-White Model (1 and 2 factor cases)\r\n\r\nThis code is pu"
},
{
"path": "Lecture 05-Interest Rate Products/Materials/HW_Caplets.py",
"chars": 8182,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nCaplets under the Hull-White Model\r\n\r\nThis code is purely educational and comes from "
},
{
"path": "Lecture 05-Interest Rate Products/Materials/HW_OptionsOnZCBs.py",
"chars": 23971,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nOptions on ZCBs under the Hull-White model\r\n\r\nThis code is purely educational and com"
},
{
"path": "Lecture 05-Interest Rate Products/Materials/Swaps_HW.py",
"chars": 24692,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nSwaps computed from a yield curve and from the Hull-White Model\r\n\r\nThis code is purel"
},
{
"path": "Lecture 06-Construction of Yield Curve and Multi-Curve/Materials/MultiCurveBuild.py",
"chars": 8550,
"preview": "#%%\r\n\"\"\"\r\nCreated on June 27 2021\r\nConstruction of a multi- curve for a given set of swap instruments\r\n\r\nThis code is pu"
},
{
"path": "Lecture 06-Construction of Yield Curve and Multi-Curve/Materials/YieldCurveBuildGreeks.py",
"chars": 5742,
"preview": "#%%\r\n\"\"\"\r\nCreated on June 27 2021\r\nConstruction of a yield curve for a given set of swap instruments\r\n\r\nThis code is pur"
},
{
"path": "Lecture 06-Construction of Yield Curve and Multi-Curve/Materials/YieldCurveBuild_Treasury.py",
"chars": 5299,
"preview": "#%%\r\n\"\"\"\r\nCreated on June 27 2021\r\nConstruction of a yield curve for a given set of swap instruments\r\n\r\nThis code is pur"
},
{
"path": "Lecture 07-Pricing of Swaptions and Negative Interest Rates/Materials/HW_CapletsAndFloorlets.py",
"chars": 10763,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nCaplets and floorlets under the Hull-White Model\r\n\r\nThis code is purely educational a"
},
{
"path": "Lecture 07-Pricing of Swaptions and Negative Interest Rates/Materials/JamshidianTrick.py",
"chars": 1706,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 18 2021\r\nJamshidian's trick for handling E max sum -> sum E max \r\n\r\nThis code is purely educat"
},
{
"path": "Lecture 07-Pricing of Swaptions and Negative Interest Rates/Materials/ShiftedLognormal.py",
"chars": 6530,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 12 2021\r\nShifted GBM and pricing of caplets/floorlets\r\n\r\nThis code is purely educational and c"
},
{
"path": "Lecture 08-Mortgages and Prepayments/Materials/AnnuityMortgage.py",
"chars": 2461,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nAnnuity mortgage- payment profile\r\n\r\nThis code is purely educational and comes from "
},
{
"path": "Lecture 08-Mortgages and Prepayments/Materials/BulletMortgage.py",
"chars": 2331,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nBullet mortgage- payment profile\r\n\r\nThis code is purely educational and comes from \""
},
{
"path": "Lecture 08-Mortgages and Prepayments/Materials/Incentives.py",
"chars": 2385,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nIncentive function as a function of a swap rate or the differential w.r.t. \"old\" mor"
},
{
"path": "Lecture 08-Mortgages and Prepayments/Materials/StochasticAmortizingSwap.py",
"chars": 8790,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nStochastic amortization given the incentive function and irrational/rational behavio"
},
{
"path": "Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/BSHW_Comparison.py",
"chars": 8042,
"preview": "#%%\r\n\"\"\"\r\nCreated on Thu Jan 04 2019\r\nThe BSHW model and implied volatilities obtained with the COS method and compariso"
},
{
"path": "Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/BSHW_ImpliedVolatility.py",
"chars": 5529,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nThe BSHW model and implied volatilities term structure computerion\r\n\r\nThis code is p"
},
{
"path": "Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/H1_HW_COS_vs_MC.py",
"chars": 13837,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nThe Heston-Hull-White model and pricing European Options with Monte Carlo and the CO"
},
{
"path": "Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/SZHW_ImpliedVolatilities.py",
"chars": 13072,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nThe SZHW model and implied volatilities\r\n\r\nThis code is purely educational and comes"
},
{
"path": "Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/SZHW_MonteCarlo_DiversificationProduct.py",
"chars": 7034,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nThe SZHW model and pricing of diversification product\r\n\r\nThis code is purely educati"
},
{
"path": "Lecture 10-Foreign Exchange (FX) and Inflation/Materials/H1_HW_COS_vs_MC_FX.py",
"chars": 13175,
"preview": "#%%\r\n\"\"\"\r\nCreated on August 25 2021\r\nThe Heston Hull-White model used for pricing of European type of FX options using \r"
},
{
"path": "Lecture 11-Market Model and Convexity Adjustments/Materials/ConvexityCorrection.py",
"chars": 5943,
"preview": "#%%\r\n\"\"\"\r\nCreated on August 25 2021\r\nConvexity correction exercise\r\n\r\nThis code is purely educational and comes from \"Fi"
},
{
"path": "Lecture 11-Market Model and Convexity Adjustments/Materials/DD_ImpliedVolatility.py",
"chars": 4424,
"preview": "#%%\r\n\"\"\"\r\nCreated on August 25 2021\r\nDisplaced Diffusion and implied volatilities\r\n\r\nThis code is purely educational and"
},
{
"path": "Lecture 12-Valuation Adjustments- xVA (CVA,BCVA and FVA)/Materials/Exposures_HW_Netting.py",
"chars": 10201,
"preview": "#%%\r\n\"\"\"\r\nCreated on July 05 2021\r\nExposures for an IR swaps, under the Hull-White model - case of netting\r\n\r\nThis code"
},
{
"path": "Lecture 13-Value-at-Risk and Expected Shortfall/Materials/HistoricalVaR_Calculation.py",
"chars": 8066,
"preview": "#%%\r\n\"\"\"\r\nCreated on August 27 2021\r\n\r\nHistorical Value-at-Risk Calculation based on real market data \r\nhttps://www.trea"
},
{
"path": "Lecture 13-Value-at-Risk and Expected Shortfall/Materials/MonteCarloVaR.py",
"chars": 9434,
"preview": "#%%\r\n\"\"\"\r\nCreated on August 25 2021\r\nValue-At-Risk computation based on Monte Carlo simulation of the Hull-White model\r"
},
{
"path": "README.md",
"chars": 1268,
"preview": "# Financial Engineering Course\nHere you will find materials for the course of \"Financial Engineering: Interest rates & x"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the LechGrzelak/FinancialEngineering_IR_xVA GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 36 files (275.6 KB), approximately 107.2k tokens, and a symbol index with 275 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.