The second in a series of derivatives that you can learn using Python. It's been a while, but I'm going to continue until option pricing. If you haven't seen the first one yet, please take a look.
First: Calculation of forward exchange rate- 2nd: Draw a yield curve (JPYLibor curve)
Write about how to draw a simple yield curve. Collateral, tenor spreads and basis spreads are not considered here. The world of single curves, not multi-curves. First of all, this time is a circular curve. Next time I will write dollars.
There are other ways to draw a curve besides the bootstrap method, Here, the bootstrap method is explained.
Actually, it is necessary to consider the number of days calculation method (whether one year is 360 days or 365 days, etc.) and holidays, but I don't care here. Only the cache rate and swap rate are used.
In the case of yen interest rate swaps, the floating side will be exchanged at 6 months Libor and the fixed side will be exchanged at 6 months fixed interest rate.
Current time t, Maturity T, Swap rate of time point t maturity T is S (t, T), When the discount factor of time point t maturity T is DF (t, T)
The present value P (t) on the fixed interest rate side is
P(t) = \frac{1}{2}S(t,T) \times DF(t, t_{0.5}))
+ \frac{1}{2}S(t,T) \times DF(t, t_{1.0}))
+ ...
+ \frac{1}{2}S(t,T) \times DF(t, T))
Will be.
The present value F (t) on the floating interest rate side is
F(t) = \frac{1}{2}f(t, t_0, T_{0.5}) \times DF(t, t_{0.5})
+ \frac{1}{2}f(t, t_{0.5}, T_{1.0}) \times DF(t, t_{1.0})
+ ...
+ \frac{1}{2}f(t, T-0.5, T) \times DF(t, T)
Will be.
Here, f (t, t1, t2) represents the yen forward rate of the time point t1 start at the time point t and the time point t2 maturity. The interest rate swap contract is concluded when the present value on the fixed side and the present value on the variable side are equal.
P(t) = F(t)
Is established.
Then, in the actual interest rate swap, the notional principal is equal and the principal is not exchanged, but for convenience, considering the notional principal 1, the formula is as follows.
\frac{1}{2}S(t,T) \times DF(t, t_{0.5}))
+ \frac{1}{2}S(t,T) \times DF(t, t_{1.0}))
+ ...
+ \frac{1}{2}S(t,T) \times DF(t, T))
+ 1 \times DF(t, T) \\
=
\frac{1}{2}f(t, t_0, T_{0.5}) \times DF(t, t_{0.5})
+ \frac{1}{2}f(t, t_{0.5}, T_{1.0}) \times DF(t, t_{1.0})
+ ...
+ \frac{1}{2}f(t, T-0.5, T) \times DF(t, T)
+ 1 \times DF(t, T)
The value on the right side is 1 because it only returns the forward rate to the present value.
(Supplement) Why 1? Consider a Libor floating rate note with a face value of 100. The value of the coupon is equivalent by 100 cash out at the time of purchase, 100 cash in at redemption (future) and Libor. In other words, if the notional principal is 1, the discounted Libor floating interest rate and principal 1 will be 1.
for that reason,
Basic formula
\frac{1}{2}S(t,T) \times DF(t, t_{0.5}))
+ \frac{1}{2}S(t,T) \times DF(t, t_{1.0}))
+ ...
+ \frac{1}{2}S(t,T) \times DF(t, T))
+ 1 \times DF(t, T)
= 1
Will be.
The bootstrap method uses this formula to sequentially find the DF from the one with the shortest maturity.
I wrote it for a long time, but the bootstrap method itself As the name suggests, use the previous result and take the subsequent results sequentially.
Now, let's actually calculate the DF sequentially.
It was said that the DF will be calculated sequentially, but within one year the DF will be calculated using the Libor interest rate, or the so-called cash rate, so the DF can be calculated directly from the interest rate.
6-month Libor is used to calculate DF at 0.5 years. In addition, the calculation is performed by simple interest calculation.
DF(t, t_{0.5}) = \frac{1}{(1 + r(t, t_{0.5}) \times \frac{1}{2})}
Here, r (t, T) represents the yen Libor interest rate of maturity T at time point t.
Use 12-month Libor to calculate DF at 1 year. (In some cases, 1Y swap rate is used)
DF(t, t_{1.0}) = \frac{1}{(1 + r(t, t_{1.0}))}
DF up to 1 year using Libor interest rate (cash rate) can be calculated by the following formula.
DF = \frac{1}{1 + r(t) * t / 100} \\
r(t):Libor interest rate at t(\%Convert from to real number) \\
t:Years\\
From here on, the story of sequentially seeking DF. After supplementing the Libor Swap interest rate acquired from the market and the so-called swap rate in 0.5-year increments, it is calculated using the above-mentioned bootstrap method formula.
The reason for every 0.5 years is that the assumed yen interest rate swap coupon is a half-year roll.
When considering the time of 1.5 years in the bootstrap method formula
\frac{1}{2}S(t, t_{1.5}) \times DF(t, t_{0.5}))
+ \frac{1}{2}S(t, t_{1.5}) \times DF(t, t_{1.0}))
+ \frac{1}{2}S(t, t_{1.5}) \times DF(t, t_{1.5}))
+ 1 \times DF(t, t_{1.5})
= 1
Will be. (The formula derived by the bootstrap method is stopped in 1.5 years)
The only unknown variable here is DF (t, t_ {1.5}). for that reason,
DF(t, t_{1.5}) = \frac{ 1 - ( DF(t, t_{0.5}) + DF(t, t_{1.0}) ) \times \frac{S(t, t_{1.5})}{2}}{ 1 + \frac{S(t, T_{1.5})}{2} }
Therefore, the DF for 1.5 years can be calculated.
Similarly for DF at maturity of 2 years
\frac{1}{2}S(t, t_{2.0}) \times DF(t, t_{0.5}))
+ \frac{1}{2}S(t, t_{2.0}) \times DF(t, t_{1.0}))
+ \frac{1}{2}S(t, t_{2.0}) \times DF(t, t_{1.5})) \\
+ \frac{1}{2}S(t, t_{2.0}) \times DF(t, t_{2.0}))
+ 1 \times DF(t, t_{2.0})
= 1
Because it can be done
DF(t, t_{2.0}) = \frac{ 1 - ( DF(t, t_{0.5}) + DF(t, t_{1.0}) + DF(t, t_{1.5}) ) \times \frac{S(t, t_{2.0})}{2}}{ 1 + \frac{S(t, T_{2.0})}{2} }
And can be calculated.
The DF for a period of more than one year using the Libor Swap interest rate (swap rate) can be calculated by the following formula when using the bootstrap method.
DF(t, T) = \frac{ 1 - \sum_{i=0.5}^{T-0.5}DF(t, t_i) \times \frac{S(t, T)}{2}}{1 + \frac{S(t,T)}{2}} \\
(T > 1)
It is possible to calculate the continuous interest rate base (zero rate) from the calculated DF.
y(t, T) = \frac{ \ln DF(t, T) }{ -(T - t) }
DF(t, T) = e^{-rτ}Deformation of
τ:period(T - t)
r:Spot rate interest rate from point t to maturity T(Zero rate, y(t,T)That)
y (t, T) represents the zero rate on a continuous interest rate basis at time point t maturity T. For zero rates other than the calculated maturity, DF is calculated by linear interpolation or spline interpolation.
If you plot the calculated zero rate, you can draw a curve.
The zero rate is the final yield of compound interest-based discount bonds (zero coupon bonds). Discount bonds are financial products that generate cash flows only on the maturity date. The zero rate is the interest rate that starts from the present time and is also called the spot rate to distinguish it from the forward rate. Zero rate is used when discounting future cash flow to present value because there is no interest payment in the middle and no problem of re-operation occurs.
Parrate is the final yield of compound interest-bearing bonds. Interest-bearing bonds are financial products in which a certain amount of interest is paid each year until maturity.
If you generalize with 6 months interest payment
DF_i = \frac{1 - r_i \sum_{k=1}^{i-1} DF_{i-1}}{ 1 + r_i} \\
i:Point in time\\
r_i:Interest rate at time i\\
DF_i:Discant factor at point i\\
It's been long, but finally I'll actually draw a curve in Python. It seems that I will rewrite various things when drawing other currencies or multi-curves, but it is good because it works once. (excuse?)
# -*- coding: utf-8 -*-
#Preparation of what is necessary for graphing
import matplotlib
import matplotlib.pyplot as plt
#Library required for handling data
import numpy as np
from scipy import interpolate
#Other
from enum import Enum
#The magic of matplotlib
plt.style.use('ggplot')
font = {'family': 'meiryo'}
matplotlib.rc('font', **font)
#Various data
# JPY Libor
cash_rate = {
"ON": 0.1, "1W": 0.10357, "1M": 0.12014, "2M": 0.13857, "3M": 0.15429, "6M": 0.16123, "12M": 0.23875
}
# JPY Libor Swap
swap_rate = {
"2Y": 0.26250, "3Y": 0.30250, "4Y": 0.36000, "5Y": 0.44813, "6Y": 0.55250,
"7Y": 0.66750, "8Y": 0.77500, "9Y": 0.88250, "10Y": 0.98500, "12Y": 1.17750, "15Y": 1.44750, "20Y": 1.75000,
"25Y": 1.89000, "30Y": 1.95813
}
input_market_data = cash_rate.copy()
input_market_data.update(swap_rate)
class RateType(Enum):
CASH_RATE = 1
SWAP_RATE = 2
class MarketData:
def __init__(self, grid, rate):
self.grid = grid
self.rate = rate / 100 #Of input%To a real number
self.set_term()
self.set_rate_type()
def set_term(self):
if self.grid == "ON":
self.term = 1 / 365
else:
num = float(self.grid.replace("M", "").replace("W", "").replace("Y", ""))
if "W" in self.grid:
self.term = num * 7 / 365
elif "M" in self.grid:
self.term = num * 1 / 12
elif "Y" in self.grid:
self.term = num
else:
self.term = 0.0
def set_rate_type(self):
if self.term <= 1:
self.rate_type = RateType.CASH_RATE
else:
self.rate_type = RateType.SWAP_RATE
def value(self):
print("Grid:{0}, RateType: {1}, Term:{2}, Rate: {3}".format(self.grid, self.rate_type, self.term, self.rate))
class YieldCurve:
def __init__(self, market_data_list):
self.market_data_list = market_data_list
self.sigma_df = 0.0 # TODO:Do better
self.df_list = []
self.zero_list = []
def get_grids(self):
return list(map(lambda x: x.term, self.market_data_list))
def interpolate_swap_rate(self):
i = 1.5 #After 1Y, JPY is a half-year roll, so 1.Start from 5
original_rates = list(map(lambda x: x.rate * 100, self.market_data_list))
f = interpolate.interp1d(self.get_grids(), original_rates)
while i <= 30:
r = list(filter(lambda x: x == i, self.get_grids()))
if not r:
m = MarketData(str(i) + "Y", f(i))
self.market_data_list.append(m)
i += 0.5 #JPY is for half a year roll+0.5
#Sort at the end
self.market_data_list.sort(key=lambda x: x.term)
def output_market_data_list(self):
for mkt in self.market_data_list:
mkt.value()
def generate_curve(self):
for mkt in self.market_data_list:
self.calc_df(mkt)
def calc_df(self, mkt):
if mkt.rate_type == RateType.CASH_RATE:
d = 1 / (1 + mkt.term * mkt.rate)
self.df_list.append(d)
elif mkt.rate_type == RateType.SWAP_RATE:
#Total of DF only for half year roll grid
d = (1 - self.sigma_df * mkt.rate / 2) / (1 + mkt.rate / 2)
self.df_list.append(d)
if mkt.term % 0.5 == 0:
self.sigma_df += d
self.calc_zero(mkt, d)
def get_df(self, term):
f = interpolate.interp1d(self.get_grids(), self.df_list, kind="cubic")
return f(term)
def calc_zero(self, mkt, d):
if mkt.rate_type == RateType.CASH_RATE:
self.zero_list.append(mkt.rate)
elif mkt.rate_type == RateType.SWAP_RATE:
zero = -1 * np.log(d) / mkt.term
self.zero_list.append(zero)
def output(self):
print("Grid: DF: ZeroRate:")
for i, v in enumerate(self.market_data_list):
print("{0}: {1}: {2}".format(v.grid, self.df_list[i], self.zero_list[i] * 100))
def plot(self):
fig = plt.figure(figsize=(10, 10))
ax_df = fig.add_subplot(2, 1, 1)
plt.subplots_adjust(hspace=0.3)
ax_df.set_ylim([0, 1.1])
ax_df.plot(self.get_grids(), self.df_list)
ax_df.set_title("Discount Factor")
ax_df.set_xlabel("Grid")
ax_df.set_ylabel("DF")
ax_zero = fig.add_subplot(2, 1, 2)
ax_zero.set_ylim([0, 3])
ax_zero.plot(self.get_grids(), list(map(lambda z: z * 100, self.zero_list)))
ax_zero.set_title("Zero Rate")
ax_zero.set_xlabel("Grid")
ax_zero.set_ylabel("Zero Rate")
plt.show()
if __name__ == '__main__':
# read market data
market_data_list = list(map(lambda x: MarketData(x[0], x[1]), input_market_data.items()))
# generate yield curve
curve = YieldCurve(market_data_list)
curve.interpolate_swap_rate()
curve.generate_curve()
curve.plot()
[All of the derivatives that can be understood by illustration](https://www.amazon.co.jp/ All of the derivatives that can be understood by illustration-with EXCEL sheet CD-ROM that can be used in practice-Tabuchi-Naoya / dp / 4534038186 / ref = sr_1_21? ie = UTF8 & qid = 1467724878 & sr = 8-21 & keywords = derivatives)
[LIBOR discount and OIS discount on EXCEL](https://www.amazon.co.jp/ LIBOR discount and OIS discount on EXCEL-with CD-ROM-Nakahara-Genta / dp / 432123848 / ref = sr_1_2? Ie = UTF8 & qid = 1467724851 & sr = 8-2 & keywords = LIBOR)
I'm not familiar with it, but in my work I'm drawing a curve with more consideration.
Recommended Posts