Derivatives learned using Python-(2) Draw a yield curve (JPYLibor curve)-

Introduction

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.

Derivative series to learn using Python

First: Calculation of forward exchange rate- 2nd: Draw a yield curve (JPYLibor curve)

Contents of this time

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.

jpy_yield.png

How to draw a curve

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.

How to draw a curve with the bootstrap method

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.

DF calculation within 1 year

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.

Calculation of DF as of 0.5 years

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.

Calculation of DF as of 1 year

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}))}

Summary

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\\

DF calculation for over 1 year

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.

Calculation of DF as of 1.5 years

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.

Calculation of DF as of 2 years

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.

Summary

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)

Zero rate calculation

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.

Supplement

Zero rate and par rate

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.

DF calculation formula

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\\

Program to draw a yield curve

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?)

Process flow

program


# -*- 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()


Reference book

[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)

Finally

I'm not familiar with it, but in my work I'm drawing a curve with more consideration.

Recommended Posts

Derivatives learned using Python-(2) Draw a yield curve (JPYLibor curve)-
Drawing a silverstone curve using python
Draw a tree in Python 3 using graphviz
Derivatives Learned Using Python-(1) Calculation of Forward Exchange Rate-
Implementing a generator using Python> link> yield and next ()> yield
Flatten using Python yield from
Draw a heart in Python
I made a Line-bot using Python!
Try to draw a Bezier curve
Create a python GUI using tkinter
Draw a scatterplot matrix in python
Draw Koch curve with Python Turtle
Draw a CNN diagram in Python
How to draw a graph using Matplotlib
[Python] Create a Batch environment using AWS-CDK
Draw a heart in Python Part 2 (SymPy)
Scraping a website using JavaScript in Python
[Python] Scraping a table using Beautiful Soup
A program that plays rock-paper-scissors using Python
Draw a graph of a quadratic function in Python
Create a GIF file using Pillow in Python
[Python] Split a large Flask file using Blueprint
[Python] Draw a directed graph with Dash Cytoscape
[Python] Draw a Mickey Mouse with Turtle [Beginner]
Create a web map using Python and GDAL
View drug reviews using a list in Python
I tried reading a CSV file using Python
Sample to draw a simple clock using ebiten
Run a Python file from html using Django
[Python] How to draw a histogram in Matplotlib
Create a Mac app using py2app and Python3! !!
Create a MIDI file in Python using pretty_midi
Run a python script from excel (using xlwings)