Yield to Maturity and Reinvestment Risk

[This article was first published on K & L Fintech Modeling, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

This post shows how the reinvestment risk affect the holding period return of coupon bond using R code


Coupon Bond and Reinvestment Risk using R code


At first, we need to make a distinction between par yield and YTM (Yield to Maturity).


Par Yield


The par yield is the coupon rate of a par bond at an issuance. A par bond has a price such as 1 or 100 or 100000 which is the principal amount. Hence, the discount rate which make this bond price to a par is also par yield. It is important that the par yield is defined only at issuance.


YTM (Yield to Maturity)


YTM is the annualized rate of return (internal rate of return; IRR) that makes a bond price to the current market price. From this definition, YTM is interpreted as the expected yield when holding bond to maturity. Unlike a par yield, YTM is defined at any time.

But there are two assumptions regarding YTM as follows.
  1. Roll rate (interest rate applied to cash flows received) is equal to YTM.
  2. Timing of Interest payment is in arrears not in advance.

Hence, every time when coupon payment is made, this cash flow is required to be reinvested at the same rate of YTM.

Also, if interest payments are made in advance, YTM is higher than that of the standard coupon bond with in arrears interest payments because compounding period for reinvestment is one quarter longer than otherwise.

As we assume a par bond at issuance, let \(YTM_{0}\) denote a par yield and \(YTM_{t}\) denote YTM at time t (pricing date).


Reinvestment Risk


When we hold a coupon bond to maturity, can we always obtain \(YTM_{0}\) as the holding period return? No, it is not always. It is only attainable when all \(YTM_{t}\) is equal to the \(YTM_{0}\). Reinvestment risk happens when these two YTMs are different.


Discount Factor and Realized Return

\[\begin{align} DF(t) &= \frac{1}{(1+\frac{YTM}{freq})^{t \times freq}} \\ R^{a}(t) &= [(1+{R_t^{c}})^{\frac{1}{t \times freq}}-1] \times freq \end{align}\] \(DF\) : discount factor
\(R^{a}\) : realized annual return
\(R^{c}\) : cumulative return
\(freq\) : payment frequency
\(t\) : remaining maturity


Sample Cash Flow Schedule for Coupon Bond


Now let’s look at examples. As a sample trade, consider the following 3-maturity semi-annual coupon bond with coupon rate of 4% and notional amount of 100. The following table demonstrates a row-wise stream of coupon and principal payments and column-wise time passages.

From the above table, time = 0 is the date when you issue or buy a bond. When you make an issuance, par yield (\(YTM_{0}\)) is equal to coupon rate. In the following example, we consider the case of new issuance at time 0. But the logic behind this also holds true for the case of buying at time 0.

For illustration purpose, we use Excel calculation. In the following four cases, green lower triangluar part indicates the reinvestment process. After cash flow is received, this amount is reinvested at \(YTM_{t}\) which may be equal to or different with \(YTM_{0}\). It depends on market environments.

Quarterly returns and cumulative returns are calculated using both the present values of future cash flows and compounded values of past cash flows received.


1) No compounding : \(YTM_{t>0} = 0\)


Before discussing the reinvestment risk, let’s see the case of no compounding. This case will makes our understanding of reinvestment process more clear.

In this no componuding case, interest payments made at earlier times are not reinvested (roll rate is zero) and stay 2($) until maturity. Since any proceeds are not generated from cash flows received, realized annual return is 3.81% which is less than \(YTM_{0}\).


2) \(YTM_{t>0} = YTM_{0} \)


When reinvestment risk is abasent, in other words, roll rate is equal to \(YTM_{0}\), holding period return until maturity is \(YTM_{0}\) as follows.

In this case, interest payment made at earlier times are reinvested at 4% roll rate until maturity. Since additional return of reinvestment is generated, realized annual return is 4% which is the same value as the \(YTM_{0}\).


3) \(YTM_{t>0} > YTM_{0}\)


When roll rate is higher than \(YTM_{0}\), holding period return until maturity is also higher than \(YTM_{0}\) as follows.

In this case, interest payment made at earlier times are reinvested at 6% roll rate until maturity. Realized annual return is 4.1% which is higher than the \(YTM_{0}\). This means that when bond strategy is the buy-and-hold and market interest rate increases, the cash flows received are reinvested in higher yield.


4) \(YTM_{t>0} < YTM_{0} \)


When roll rate is lower than \(YTM_{0}\), holding period return until maturity is also lower than \(YTM_{0}\) as follows.

In this case, interest payment made at earlier times are reinvested at 2% roll rate until maturity. Realized annual return is 3.9% which is lower than the \(YTM_{0}\). This means that when bond strategy is the buy-and-hold and market interest rate decreases, the cash flows received are reinvested in lower yield.




R code for Reinvestment Risk


The following R code demonstrates the calculation of discounted cash flows from coupon bond at every quarterly pricing time t. Besides quarterly price of bonds, This code also shows the reinvestment process of cash flows received at every quarter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#=========================================================================#
# Financial Econometrics & Derivatives, ML/DL using R, Python, Tensorflow  
# by Sang-Heon Lee 
#
# https://kiandlee.blogspot.com
#————————————————————————-#
# Demonstrate Reinvestment Risk with YTM and varying roll rate
#=========================================================================#
 
graphics.off()      # clear all graphs
rm(list = ls())     # remove all files from your workspace
    
iss_maty = 3        # issuance maturity in year
freq     = 2        # semi-annual coupon payment
face_val = 100      # face value
cpn_rate = 0.04     # coupon rate
dt       = 0.25     # time interval
nall     = freq*iss_maty
 
# cash flow table
df.cf < data.frame(no   = integer(nall), 
                    date = double (nall), 
                    amt  = double (nall))
 
for(i in 1:nall) {
    
    df.cf$no  [i] < i
    df.cf$date[i] < i/freq
    df.cf$amt [i] < (cpn_rate/freq)*face_val
    if(i==freq*iss_maty) df.cf$amt[i] < df.cf$amt[i] + face_val
}
df.cf
 
# time line
v.time < seq(0,iss_maty,dt)
 
#————————————————
# YTM pricing and Reinvestment Risk
#————————————————
 
    # YTM for t > 0
    ytm_after_0= 0.04  # 1) roll rate (4%) = 4%
    #ytm_after_0= 0.06  # 2) roll rate (6%) > 4%
    #ytm_after_0 = 0.02  # 3) roll rate (2%) < 4%
 
    # table for time line and cash flow schedule
    df.bond < data.frame()
 
    for(t in v.time) { # As time t elapsed
 
        # use df.cf temporarily
        df < df.cf
        
        # YTM (t>0) for discounting
        # t = 0 : par yield  (make price to a par)
        # t > 0 : yield to maturity (market yield) 
        rate = ifelse(t==0, cpn_rate, ytm_after_0)
 
        # discount factor
        df$DF = 1/(1+rate/freq)^((df$datet)*freq)
        
        # present value of cash flow
        df$PV = df$amt*df$DF
        
        # add rows to df.bond with sum(PV) = price
        df.bond < rbind(df.bond, c(df$PV, sum(df$PV)))
    }
 
    # append time to the left
    df.bond < cbind(v.time, df.bond)
    
    # set column names for convenience
    colnames(df.bond) < c(“time”, paste0(“cf”,1:nall), “price”)
    
    # quarter on quarter (qoq) returns 
    nr < nrow(df.bond)
    df.bond$qoq_rtn < c(0,df.bond$price[2:nr]/
                           df.bond$price[1:(nr1)]  1)
    
    # cumulative returns
    df.bond$cum_rtn < 0
    for(i in 2:nr) { 
        df.bond$cum_rtn[i] < (1+df.bond$cum_rtn[i1])*
                              (1+df.bond$qoq_rtn[i])1
    }
 
    # 3-year returns
    print(paste0(“3-year return = “
                 round(100*((1+df.bond$cum_rtn[nr])^(1/nall)1)*freq,4),
                 “% when roll rate is “100*ytm_after_0, 
                 “% and YTM at time 0 is “100*cpn_rate,“%”))
 
    # rounding for printing out
    df.bond[,2:8< round(df.bond[,2:8],2)
    df.bond$qoq_rtn < round(df.bond$qoq_rtn,5)
    df.bond$cum_rtn < round(df.bond$cum_rtn,3)
    df.bond
    
cs

By changing “ytm_after_0” variable in R code, three types of result are obtained.

Firstly, under the condition that roll rate is the same as \(YTM_{0}\), the following result can be obtained. This results is consistent with the above Excel counterpart.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[1“3-year return = 4% when roll rate is 4% and YTM at time 0 is 4%”
> 
   time  cf1  cf2  cf3  cf4  cf5    cf6  price qoq_rtn cum_rtn
1  0.00 1.96 1.92 1.88 1.85 1.81  90.57 100.00 0.00000   0.000
2  0.25 1.98 1.94 1.90 1.87 1.83  91.47 101.00 0.00995   0.010
3  0.50 2.00 1.96 1.92 1.88 1.85  92.38 102.00 0.00995   0.020
4  0.75 2.02 1.98 1.94 1.90 1.87  93.30 103.01 0.00995   0.030
5  1.00 2.04 2.00 1.96 1.92 1.88  94.23 104.04 0.00995   0.040
6  1.25 2.06 2.02 1.98 1.94 1.90  95.17 105.08 0.00995   0.051
7  1.50 2.08 2.04 2.00 1.96 1.92  96.12 106.12 0.00995   0.061
8  1.75 2.10 2.06 2.02 1.98 1.94  97.07 107.18 0.00995   0.072
9  2.00 2.12 2.08 2.04 2.00 1.96  98.04 108.24 0.00995   0.082
10 2.25 2.14 2.10 2.06 2.02 1.98  99.01 109.32 0.00995   0.093
11 2.50 2.16 2.12 2.08 2.04 2.00 100.00 110.41 0.00995   0.104
12 2.75 2.19 2.14 2.10 2.06 2.02 101.00 111.51 0.00995   0.115
13 3.00 2.21 2.16 2.12 2.08 2.04 102.00 112.62 0.00995   0.126
cs


Secondly, under the condition that roll rate is higher than \(YTM_{0}\), the following result can be generated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[1“3-year return = 4.0967% when roll rate is 6% and YTM at time 0 is 4%”
> 
   time  cf1  cf2  cf3  cf4  cf5    cf6  price  qoq_rtn cum_rtn
1  0.00 1.96 1.92 1.88 1.85 1.81  90.57 100.00  0.00000   0.000
2  0.25 1.97 1.91 1.86 1.80 1.75  86.70  95.99 0.04009  0.040
3  0.50 2.00 1.94 1.89 1.83 1.78  87.99  97.42  0.01489  0.026
4  0.75 2.03 1.97 1.91 1.86 1.80  89.30  98.87  0.01489  0.011
5  1.00 2.06 2.00 1.94 1.89 1.83  90.63 100.34  0.01489   0.003
6  1.25 2.09 2.03 1.97 1.91 1.86  91.98 101.84  0.01489   0.018
7  1.50 2.12 2.06 2.00 1.94 1.89  93.34 103.35  0.01489   0.034
8  1.75 2.15 2.09 2.03 1.97 1.91  94.73 104.89  0.01489   0.049
9  2.00 2.19 2.12 2.06 2.00 1.94  96.14 106.45  0.01489   0.065
10 2.25 2.22 2.15 2.09 2.03 1.97  97.58 108.04  0.01489   0.080
11 2.50 2.25 2.19 2.12 2.06 2.00  99.03 109.65  0.01489   0.096
12 2.75 2.28 2.22 2.15 2.09 2.03 100.50 111.28  0.01489   0.113
13 3.00 2.32 2.25 2.19 2.12 2.06 102.00 112.94  0.01489   0.129
cs


Thirdly, under the condition that roll rate is lower than \(YTM_{0}\), the following result can be returned.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[1“3-year return = 3.9056% when roll rate is 2% and YTM at time 0 is 4%”
> 
   time  cf1  cf2  cf3  cf4  cf5    cf6  price qoq_rtn cum_rtn
1  0.00 1.96 1.92 1.88 1.85 1.81  90.57 100.00 0.00000   0.000
2  0.25 1.99 1.97 1.95 1.93 1.91  96.57 106.32 0.06323   0.063
3  0.50 2.00 1.98 1.96 1.94 1.92  97.05 106.85 0.00499   0.069
4  0.75 2.01 1.99 1.97 1.95 1.93  97.53 107.39 0.00499   0.074
5  1.00 2.02 2.00 1.98 1.96 1.94  98.02 107.92 0.00499   0.079
6  1.25 2.03 2.01 1.99 1.97 1.95  98.51 108.46 0.00499   0.085
7  1.50 2.04 2.02 2.00 1.98 1.96  99.00 109.00 0.00499   0.090
8  1.75 2.05 2.03 2.01 1.99 1.97  99.49 109.54 0.00499   0.095
9  2.00 2.06 2.04 2.02 2.00 1.98  99.99 110.09 0.00499   0.101
10 2.25 2.07 2.05 2.03 2.01 1.99 100.49 110.64 0.00499   0.106
11 2.50 2.08 2.06 2.04 2.02 2.00 100.99 111.19 0.00499   0.112
12 2.75 2.09 2.07 2.05 2.03 2.01 101.49 111.75 0.00499   0.117
13 3.00 2.10 2.08 2.06 2.04 2.02 102.00 112.30 0.00499   0.123
cs

From this post, we can learn the reinvestment risk of coupon bond. It is worth noting that 1) YTM is attainable when roll rate is the same as YTM and this argument is only applied to standard coupon bond with in arrears interest payments schedule. Unlike standard coupon bond, coupon bond with in advance interest payment has a higher YTM than coupon rate at an issuance.

Next time, let’s calculate the benchmark bond portfolio stragegies such as bullet, ladder, buy-and-hold, barbell and so on. \(\blacksquare\)

To leave a comment for the author, please follow the link and comment on their blog: K & L Fintech Modeling.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)