Benchmark Bond Portfolio Returns using R code
[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 calculates the holding period returns of four benchmark zero coupon bonds portfolios such as bullet, barbell, ladder and buy-and-hold using R code Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Benchmark Bond Portfolio Strategies
Bond portfolio strategies are too many to enumerate. But there are benchmark bond strategies which are frequently introduced to textbook. Among them are bullet, barbell, ladder and buy-and-hold portfolios. For these bond portfolios, we are going to calculate monthly and cumulative returns.
We assume the case of zero coupon bond portfolio for empirical exercises. Without target duration constraints, differences between zero coupon bond and coupon bearing bond are not large. It is well known that the coupon bond is the portfolio of zero coupon bonds.
At first, we need to calculate monthly returns of each bond. After getting these monthly returns, we can construct monthly returns of 4 portfolios.
The price of a pure discount (zero coupon) bond with maturity \(\tau\) at time t is the discounted value of 1 receivable \(\tau\) periods later. \[\begin{align} P_t^{[\tau]} =exp(-\tau \times s_t^{[\tau]}) \end{align}\] Here, \(s_t^{[\tau]}\) is the spot rate.
Using the log-return expression, the monthly holding period return is as follows.
\[\begin{align} r_t^{[\tau]}& = \log {P_t^{\left[\tau-\frac{1}{12}\right]}} – \log {P_{t-1}^{\left[\tau \right]}} \\ &= \left(\tau-\frac{1}{12}\right) \times s_t^{\left[\tau-\frac{1}{12}\right]} – \tau \times s_{t-1}^{\left[\tau\right]} \end{align}\]
However, yield curve is reported for some major relevant maturities such as 1-year or 3-year, and so on. In most cases, \(s_t^{\left[\tau-\frac{1}{12}\right]}\) are not observed. Therefore, we need to interpolate these unobserved spot rates using observed spot rates. If \(s_t^{\left[\tau-\frac{1}{12}\right]}\) are interpolated using spline or linear interpolation or Nelson-Siegel model, we can apply the above log-return expression to these interpolated spot rates. For our study, we use the spline interpolation using splinefun R function.
Now it is ready to use the time \(t-1\) spot rates with maturities \(1\), \(3\), \(5\), \(7\), and \(10\) and the time \(t\) spot rates with maturities \(1-\frac{1}{12}\), \(3-\frac{1}{12}\), \(5-\frac{1}{12}\), \(7-\frac{1}{12}\), and \(10-\frac{1}{12}\) to calculate monthly returns for each selected maturity.
Given time series of monthly spot rate for each maturity, monthly returns of each bullet portfolio is the same as the previous monthly returns of each column respectively. It is only to read each column which corresponds each maturity respectively.
It is also easy to calculate monthly returns for barbell, and ladder. For barbell, 1- and 10-year returns are averaged. For ladder, returns of all 5 maturities are averaged. But for Buy-and-Hold, some caution is needed.
Buy-and-Hold (BH) strategy 1) buy a bond with issuance maturity and 2) hold it until maturity. Therefore, as soon as redemption is made, new BH strategy starts with a full issuance maturity. This means that remaining maturity shows periodic behavior. For example, time variation of remaining maturity of 3-year BH portfolio is as follows.
\[\begin{align} 2 &\rightarrow 1.92 \rightarrow 1.83 \rightarrow… \\ &\rightarrow 0.17 \rightarrow 0.08 \rightarrow 2 \rightarrow 1.92 \rightarrow … \end{align}\]
The following figure shows the pattern of remaining maturity of BH portfolio with selected maturities.
Hence, monthly return series for BH portfolios for each maturity are defined in in full interpolation monthly return grid. As time passes, position of current spot rates are moved to the left by \(\frac{1}{12}\) in full interpolated spot rate grid until its maturity approaches zero. Of course, after redemption take places, position of current spot rate goes back to the original position which corresponds to the issuance maturity.
The following R code demonstrates the calculation of monthly and cumulative returns of 4 benchmark bond portfolio using Diebold, Rudebusch, and Aruoba (2006) data,
The following figure shows the monthly returns of 5-maturity bond which is, indeed, that of bullet portfolio. We can find that the longer the maturity of a bond, the more sensitive is its return to a change in interest rates. Therefore, it is not easy to forecast future return of a long-term bond.
To investigate the performance of bond portfolio strategy, it is useful to calculate the cumulative returns as follows. We can find a stylized fact that long term bond shows the higher returns and higher volatility.
Using mean and standard deviation, we can calculate the Sharpe ratio which is the risk-adjusted return as follows. (In fact, it is typical to use the excess return when calculating the Sharpe ratio and therefore this is the same as we assume 0% risk-free rate)
From this post, we have constructed some benchmark bond portfolio and calculate its monthly returns and cumulative performances.
- Bullet : maintain one bond with its maturity fixed
- Barbell : maintain two bonds with each maturities fixed and equal weights(1/2)
- Ladder : maintain all relevant bonds with each maturity fixed and equal weights(1/n)
- Buy and Hold : buy and hold one bond until its maturity and maintain this trade periodically
Return Calculation
At first, we need to calculate monthly returns of each bond. After getting these monthly returns, we can construct monthly returns of 4 portfolios.
The price of a pure discount (zero coupon) bond with maturity \(\tau\) at time t is the discounted value of 1 receivable \(\tau\) periods later. \[\begin{align} P_t^{[\tau]} =exp(-\tau \times s_t^{[\tau]}) \end{align}\] Here, \(s_t^{[\tau]}\) is the spot rate.
Using the log-return expression, the monthly holding period return is as follows.
\[\begin{align} r_t^{[\tau]}& = \log {P_t^{\left[\tau-\frac{1}{12}\right]}} – \log {P_{t-1}^{\left[\tau \right]}} \\ &= \left(\tau-\frac{1}{12}\right) \times s_t^{\left[\tau-\frac{1}{12}\right]} – \tau \times s_{t-1}^{\left[\tau\right]} \end{align}\]
However, yield curve is reported for some major relevant maturities such as 1-year or 3-year, and so on. In most cases, \(s_t^{\left[\tau-\frac{1}{12}\right]}\) are not observed. Therefore, we need to interpolate these unobserved spot rates using observed spot rates. If \(s_t^{\left[\tau-\frac{1}{12}\right]}\) are interpolated using spline or linear interpolation or Nelson-Siegel model, we can apply the above log-return expression to these interpolated spot rates. For our study, we use the spline interpolation using splinefun R function.
Now it is ready to use the time \(t-1\) spot rates with maturities \(1\), \(3\), \(5\), \(7\), and \(10\) and the time \(t\) spot rates with maturities \(1-\frac{1}{12}\), \(3-\frac{1}{12}\), \(5-\frac{1}{12}\), \(7-\frac{1}{12}\), and \(10-\frac{1}{12}\) to calculate monthly returns for each selected maturity.
Monthly Returns of Portfolio
Given time series of monthly spot rate for each maturity, monthly returns of each bullet portfolio is the same as the previous monthly returns of each column respectively. It is only to read each column which corresponds each maturity respectively.
It is also easy to calculate monthly returns for barbell, and ladder. For barbell, 1- and 10-year returns are averaged. For ladder, returns of all 5 maturities are averaged. But for Buy-and-Hold, some caution is needed.
Buy-and-Hold (BH) strategy 1) buy a bond with issuance maturity and 2) hold it until maturity. Therefore, as soon as redemption is made, new BH strategy starts with a full issuance maturity. This means that remaining maturity shows periodic behavior. For example, time variation of remaining maturity of 3-year BH portfolio is as follows.
\[\begin{align} 2 &\rightarrow 1.92 \rightarrow 1.83 \rightarrow… \\ &\rightarrow 0.17 \rightarrow 0.08 \rightarrow 2 \rightarrow 1.92 \rightarrow … \end{align}\]
The following figure shows the pattern of remaining maturity of BH portfolio with selected maturities.
Hence, monthly return series for BH portfolios for each maturity are defined in in full interpolation monthly return grid. As time passes, position of current spot rates are moved to the left by \(\frac{1}{12}\) in full interpolated spot rate grid until its maturity approaches zero. Of course, after redemption take places, position of current spot rate goes back to the original position which corresponds to the issuance maturity.
R code for Bond Portfolio
The following R code demonstrates the calculation of monthly and cumulative returns of 4 benchmark bond portfolio using Diebold, Rudebusch, and Aruoba (2006) data,
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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | #=========================================================================# # Financial Econometrics & Derivatives, ML/DL using R, Python, Tensorflow # by Sang-Heon Lee # # https://kiandlee.blogspot.com #————————————————————————-# # Benchmark Bond Portfolio Return Calculation #=========================================================================# library(readxl) library(xlsx) library(RColorBrewer) graphics.off() # clear all graphs rm(list = ls()) # remove all files from your workspace setwd(“D:/SHLEE/a_blog_ki_and_Lee/bond_portfolio”) #—————————————————— # 0) Read DRA (2006) spot yield curve data #—————————————————— fname <– “dra_spot_cc_data.xlsx” # maturity, date, spot rate vmatm <– read_excel(fname, “spotcc”,“B1:R1”, col_names = FALSE) df.ym <– read_excel(fname, “spotcc”,“A2:A480”, col_names = FALSE) df.spot <– read_excel(fname, “spotcc”,“B2:R480”, col_names = FALSE) # maturity as decimal, spot as y vmatm <– as.numeric(vmatm); vmaty <– vmatm/12 y <– as.matrix(df.spot/100); colnames(y) <– vmatm # counting nmat <– length(vmaty); ny <– nrow(y); nr <– ny – 1; # number of yields and returns #—————————————————— # 1) Interpolate monthly spot rates using cubic spline #—————————————————— # .ip : interpolated # use 0 for 1-month return temporarily matm.ip <– (0:max(vmatm)) max.matm.ip <– max(matm.ip) # use apply() for row-wise interpolation # output => collection of column vector => so transpose y.ip <– t(apply(y, 1, function (x) { # make interpolation function fs<–splinefun(vmatm,x); # apply fs function to each row fs(matm.ip) } )) #—————————————————— # 2) Calculate 1-month returns #—————————————————— # interpolated spot rate r.ip <– matrix(0, ny–1, length(matm.ip)–1) # calculate monthly returns for (t in 2:ny) { # spot rates and maturities at t and (t-1) spot.t <– y.ip[t,1:max.matm.ip] spot.t_1 <– y.ip[t–1,2:(max.matm.ip+1)] mat.t <– matm.ip[1:max.matm.ip]/12 mat.t_1 <– matm.ip[2:(max.matm.ip+1)]/12 # log-return expression r.ip[t–1,] = log(exp(–spot.t*mat.t)/exp(–spot.t_1*mat.t_1)) } # draw a graph for monthly returns x11(width=6, height=4); par(mar = c(5, 4, 4, 6) + 0.1) matplot(r.ip[,c(120, 60, 36, 12, 3)], type = “l”, lty = 1, col = c(brewer.pal(4, “Paired”),“black”), ylab = “return”, xlab = “date”, lwd = 2, main = “Monthly Zero Coupon Bond Returns”) legend(“right”, inset = c(–0.3,0), legend = paste(c(120, 60, 36, 12, 3),“M”), xpd = TRUE, horiz = FALSE, col = c(brewer.pal(4, “Paired”),“black”), lty = 1, bty = “n”, lwd = 2) #—————————————————— # 3) Construct benchmark bond portfolios # – barbell, bullet, ladder, Buy-and-Hold #—————————————————— # selected maturity maty_bm <– c(1,3,5,7,10) matm_bm <– maty_bm*12 nc <– length(maty_bm) # number of selected maturities # 1. Barbell (1, 10 year) maty1 <– c(1,10) col1.ret <– rowMeans(r.ip[1:nr, maty1*12]) col1.dur <– matrix(1/2, nr,2)%*%maty1 # 2. Bullet col2.ret <– r.ip[1:nr, matm_bm] colnames(col2.ret) <– paste0(“Bullet(“, maty_bm, “)”) col2.dur <– matrix(1,nr,nc)%*%diag(maty_bm); colnames(col2.dur) <– colnames(col2.ret) # 3. Ladder (equal weights for all maturities) col3.ret <– rowMeans(r.ip[1:nr, matm_bm]) col3.dur <– matrix(1/nc, nr, nc)%*%maty_bm # 4. Buy-and-Hold bah.ret <– bah.maty <– bah.matm <– matrix(0,nr, nc) for (t in 1:nr) { for(j in 1:nc) { # As t move forward, remaining maturity decreases. # When remaining maturity is zero, # a new bond is purchased # the remaining maturity is set to the issuance maturity. matm <– matm_bm[j]–(t–1)%%matm_bm[j] bah.matm[t,j] <– matm bah.maty[t,j] <– matm/12 bah.ret[t,j] <– r.ip[t, matm] }} col4.ret <– bah.ret; col4.dur <– bah.maty; colnames(col4.ret) <– paste0(“BH(“, maty_bm,“)”) colnames(col4.dur) <– colnames(col4.ret) # collect portfolio returns port.ret <– cbind(col1.ret,col2.ret,col3.ret,col4.ret) colnames(port.ret)[1] <– “Barbell(1,10)” colnames(port.ret)[7] <– “Ladder” #—————————————————— # 4) Cumulative Returns #—————————————————— port.cum.ret <– port.ret*0; # cumulative return for(i in 1:ncol(port.ret)) { port.cum.ret[,i] <– cumprod(1+port.ret[,i])–1 } #—————————————————— # 5) Performance Statistics #—————————————————— # Use data.frame not matrix when using sapply out.port <– sapply(as.data.frame(port.ret), function(x) { # average, stdev, Sharpe col1 <– mean(x)*100*12 col2 <– sd(x)*100*sqrt(12) col3 <– col1/col2 return(c(avg=col1, stdev = col2, Sharpe = col3))}) # print out round(out.port[,1:3],2) round(out.port[,4:6],2) round(out.port[,7:12],2) | cs |
The following figure shows the monthly returns of 5-maturity bond which is, indeed, that of bullet portfolio. We can find that the longer the maturity of a bond, the more sensitive is its return to a change in interest rates. Therefore, it is not easy to forecast future return of a long-term bond.
To investigate the performance of bond portfolio strategy, it is useful to calculate the cumulative returns as follows. We can find a stylized fact that long term bond shows the higher returns and higher volatility.
Using mean and standard deviation, we can calculate the Sharpe ratio which is the risk-adjusted return as follows. (In fact, it is typical to use the excess return when calculating the Sharpe ratio and therefore this is the same as we assume 0% risk-free rate)
From this post, we have constructed some benchmark bond portfolio and calculate its monthly returns and cumulative performances.
This analysis can be also applied to coupon bond portfolio. For more information, refer to Deguest, Fabozzi, Martellini, and Milhau (2018).
Deguest, R., F. Fabozzi, L. Martellini, and V Milhau (2018), “Bond Portfolio Optimization in the Presence of Duration Constraints,” The Journal of Fixed Income 28, 6-26
Diebold, F. X., G. D. Rudebusch, and S. B. Aruoba (2006), “The Macroeconomy and the Yield Curve: A Dynamic Latent Factor Approach,” Journal of Econometrics 131, 309-338. \(\blacksquare\)
Reference
Deguest, R., F. Fabozzi, L. Martellini, and V Milhau (2018), “Bond Portfolio Optimization in the Presence of Duration Constraints,” The Journal of Fixed Income 28, 6-26
Diebold, F. X., G. D. Rudebusch, and S. B. Aruoba (2006), “The Macroeconomy and the Yield Curve: A Dynamic Latent Factor Approach,” Journal of Econometrics 131, 309-338. \(\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.