Nelson-Siegel-Svensson model
Svensson (1995) suggests an extended NS model which is typically called NSS model. Many central banks use the NSS model and Gürkaynak et al. (2007) also use it to fit U.S. Treasury yield curve. The reason why NSS model is widely used is evident. As can be seen the following dynamic figure, It fits yields at long-term maturities well.
Nelson-Siegel-Svensson model
Nelson-Siegel-Svensson model is a non-linear least square problem with 6 parameters with some inequality constraints.
\[\begin{align} y(\tau) &= \beta_1 + \beta_2 \left( \frac{1-e^{- \tau \lambda_1 }}{\tau \lambda_1 }\right) \\ &+ \beta_3 \left(\frac{1-e^{- \tau \lambda_1 }}{\tau \lambda_1 }-e^{- \tau \lambda_1 }\right) \\ &+ \beta_4 \left(\frac{1-e^{- \tau \lambda_2 }}{\tau \lambda_2 }-e^{- \tau \lambda_2 }\right) \end{align}\] \[\begin{align} \beta_1 > 0, \beta_1 + \beta_2 >0 \\ \lambda_1 > \lambda_2 > 0 \end{align}\]
Here, \(\tau\) is a maturity and \(y(\tau)\) is a continuously compounded spot rate with \(\tau\) maturity. \(\beta_1, \beta_2, \beta_3\, \beta_4\) are coefficient parameters. \(\lambda_1\) and \(\lambda_2 \) are the decay parameters.
\(\lambda_1\) and \(\lambda_2\) determine the shapes of one slope and two curvature factor loadings as follows.
In the above figure, I use \(\lambda_1 = 0.0609\) and \(\lambda_2 = 0.01 \), which represent the maximum of curvature loadings are attained at nearly 30-month and 180-month respectively. Smaller \(\lambda_2\) fits the yield curve at longer maturities well but it lowers the interpretability of the level factor. For this reason, too small \(\lambda_2\) is not appropriate and is needed to be constrained by some reasonable upper and lower bounds. Of course, too high \(\lambda_1\) is not good since it tends to result in too excessive curvature estimate.
Restrictions on \(\lambda_1\) and \(\lambda_2\)
The most difficult part is how to choose \(\lambda_1\) and \(\lambda_2\). Without suitable restrictions on these two parameters, estimates of these parameters exhibit too excessive or erratic. It is typical to determine \(\lambda_1\) from ranges of medium-term maturities and \(\lambda_2\) from ranges of long-term maturities. I set two ranges of maturities as 1 ~ 5 years and 6 ~ 20 year for example.
However, reasonable constrains on two decay parameters are dependent on which data is used.
R code
The following R code estimates parameters of NSS model with yield curves at four dates and compares these results with that of NS model.
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 | #========================================================# # Quantitative Financial Econometrics & Derivatives # ML/DL using R, Python, Tensorflow by Sang-Heon Lee # # https://shleeai.blogspot.com #--------------------------------------------------------# # Estimation of Nelson-Siegel-Svensson model #========================================================# graphics.off(); rm(list = ls()) library(alabama) #----------------------------------------------- # Nelson-Siegel-Svensson function #----------------------------------------------- # NSS factor loading nss_loading <- function(la,m) { la1 <- la[1]; la2 <- la[2] C <- cbind( rep(1,length(m)), (1-exp(-la1*m))/(la1*m), (1-exp(-la1*m))/(la1*m)-exp(-la1*m), (1-exp(-la2*m))/(la2*m)-exp(-la2*m)) return(C) } # fitting function nss_fit <- function(para, m) { beta <- para[1:4] return(nss_loading(para[5:6],m)%*%beta) } # objective function nss_objf <- function(para, y, m) { return(sqrt(mean((y - nss_fit(para, m))^2))) } # constrOptim.nl constraint function ( > ) nss_constf <- function(para, y, m) { beta <- para[1:4] la1 <- para[5]; la2 <- para[6] h <- rep(NA, 6) # b1 > 0, b1+b2 > 0 h[1] <- beta[1] h[2] <- beta[1]+beta[2] # 0.15 > la1 > 0.03 h[3] <- 0.15-la1 h[4] <- la1-0.03 # 0.025 > la2 > 0.0075 h[5] <- 0.025-la2 h[6] <- la2-0.0075 return(h) } #=========================================================== # 1. Read data #=========================================================== # Estimated parameters of Nelson-Siegel (Benchmark) str.ns_est<- " beta1 beta2 beta3 lambda rmse 7.949446 0.2933681 -0.08749462 0.11555381 0.07665367 7.052864 -1.5778886 -0.42253575 0.01823114 0.06432032 5.586903 -0.5600670 -1.43976821 0.03357762 0.06423196 6.011739 0.3470154 -1.00018107 0.04565435 0.09314726" m.ns_est <- read.table(text = str.ns_est, header=TRUE) str.zero <- " mat 19890630 19950929 19980831 20000929 3 8.171964 5.413123 4.931036 6.180960 6 8.143305 5.509881 4.949141 6.207158 9 8.123782 5.592753 4.962873 6.204367 12 8.111922 5.654558 4.962513 6.176521 24 7.974091 5.782513 4.824109 5.932045 36 7.943757 5.841313 4.868401 5.862433 48 7.959505 5.920732 4.888494 5.825789 60 7.942511 5.967207 4.900205 5.799035 72 8.000704 6.013260 4.980854 5.826354 84 8.010956 6.055806 4.977891 5.847383 96 8.020059 6.151399 5.007556 5.859937 108 8.027901 6.224049 5.054961 5.875451 120 8.037343 6.265079 5.100250 5.912506 144 8.063573 6.364383 5.200412 6.002328 180 8.052892 6.512078 5.347487 6.093959 240 7.971860 6.718515 5.482335 6.091155 300 7.861711 6.789321 5.422557 5.972525 360 7.721083 6.599990 5.246325 5.697709" df <- read.table(text = str.zero, header=TRUE) m <- df$mat; nmat <- length(m) #=========================================================== # 2. Parameter estimation #=========================================================== ctrl.optim <- list(maxit=50000, trace=0, reltol = 1e-10, abstol = 1e-10) ctrl.outer <- list(eps = 1e-10, itmax = 50000, method = "Nelder-Mead", NMinit = TRUE) # output container m.nss_est <- matrix(NA, 4, 7) m.nss_fit <- matrix(NA, length(m), 4) # Estimation of Nelson-Siegel-Svensson for(i in 1:4) { y <- df[,1+i] x_init <- c(y[nmat], y[1]-y[nmat], 2*y[6] -y[1]-y[nmat], 2*y[15]-y[1]-y[nmat], 0.0609, 0.01) opt <- constrOptim.nl(y = y, m = m, par=x_init, fn=nss_objf, hin=nss_constf, control.optim =ctrl.optim, control.outer=ctrl.outer) m.nss_est[i,] <- c(opt$par, opt$value) m.nss_fit[,i] <- nss_fit(opt$par, m) colnames(m.nss_est) <- c("beta1", "beta2", "beta3", "beta4", "lambda1", "lambda2","rmse") } colnames(m.nss_fit) <- paste0("fitting-",1:4) #======================================================= # 3. Estimation results #======================================================= # NSS factor loading mi <- 1:360 x11(width = 16/2.5, height = 5); matplot(mi, nss_loading(c(0.0609, 0.01),mi), type = "l", lwd=6, xlab = "Maturity (month)", ylab = "Factor loading") legend("right", legend=c("Level", "Slope", "Curvature1", "Curvature2"), pch = c(15,16), border="white", box.lty=0, cex=1, col = c("black","#DF536B", "#61D04F", "#2297E6")) # Parameter estimates m.ns_est m.nss_est # data and fitted yields round(cbind(df[,-1], m.nss_fit),3) | cs |
The following estimation results show that NSS model provides good in-sample fit than NS model except the second date. This is related to the restrictions on the decay parameters. When these constraints are relaxed, the results will changed. I expect that.
Four selected dates, fitting results of NSS model show relative good fitting results. Instead, we can find that level factor (\(\beta_1\)) is affected by the introduction of the second curvature factor (\(\beta_4\)). In other words, there is some distinct difference in level factors between NS model and NSS model.
Using the same data, it seems that NS model has some difficulties in fitting yields at longer-term maturities. Unlike NSS model, the level factor of NS model represent current long-term rate well. However, in most cases, NS model delivers sufficiently good fitting results. In other words, it is likely that the second curvature factor can be a superfluous factor in many normal days, which is considered an overfitting.
In particular, when the second curvature factor is not identified by data, so called compounding effect can take place. This is the case when two curvature estimates are too large in magnitude with the opposite signs. This compounding effect can be occurred between the slope and the curvature factors.
Concluding Remarks
This post estimates Nelson-Siegel-Svensson yield curve model which aims to fit longer term maturities well. It is worth noting that to get reasonable parameter estimates, proper restrictions on two decay parameters are needed. I think that research on estimation of NSS model is under way.
Reference
Gürkaynak, R. S., B. Sack, and J. H. Wright (2007), "The U.S. Treasury Yield Curve: 1961 to the Present," Journal of Monetary Economics 54-8, 2291–2304.
Svensson, L.E. (1995), "Estimating Forward Interest Rates with the Extended Nelson and Siegel Method," Sveriges Riksbank Quarterly Review 3, 13–26.
vegabook
ReplyDelete2023. 5. 28.
Where is the input data? Is it already in zero curve format? The main use case for Nelson Siegel et al is for BONDS. Swaps are already very well arbitraged. But bonds curve fit needs to be done by minimizing DCF cashflow minimization to market price, which is quite a lot more complex on the data preparation side, and you'll need to weight by the inverse of duration so that long bonds don't dominate the optimization.
Sang-Heon Lee
2023. 5. 29.
The input data is in the code after the line: str.zero <- ".
I use the data from Gürkaynak et al. (2007), which is the US Treasury zero curve.
I agree that using the market price of the bond as the input data and the inverse of the duration as the weight is a good choice.
In particular, Andreasen et al. (2019) use the market price of the bond as the input instead of the zero rate and show the feasibility and desirability of the one-step approach you suggest.
Thank you for good suggestion.
Andreasen, M. M., J. H. E. Christensen, and G. D. Rudebusch (2019). Term Structure Analysis with Big Data: One-Step Estimation Using Bond Prices, Journal of Econometrics 212(1): 26-46.