Bayesian Estimation of Nelson-Siegel model using rjags R package

[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 to use rjags R package to estimate Nelson-Siegel yield curve model based using a Bayesian MCMC in a compact way.

Nelson-Siegel model and rjags R package

We installed and used rjags R package and performed a Bayesian estimation of multiple linear regression as an example in the previous post below.

In an interesting and practical example, let’s run a Bayesian estimation of the Nelson-Siegel yield curve model.

Nelson-Siegel model

The Nelson-Siegel model is widely used in practice for fitting the cross-sectional term structure of interest rates in a parsimonious way. It is so famous and lots of extended models have been emerged based on the this model. It assumes that an instantaneous forward rate is of the following form.

\[\begin{align} f(\tau) = \beta_0 + \beta_1e^{- \tau \lambda } + \beta_2 \tau \lambda e^{- \tau \lambda } \end{align}\]
Integration of \(f(\tau)\) results in a continuously compounded spot rate.

\[\begin{align} y(\tau) &= \frac{1}{\tau} \int_{0}^{\tau} f(u) du \\ &= \beta_0 + \beta_1 \left( \frac{1-e^{- \tau \lambda }}{\tau \lambda }\right) + \beta_2 \left(\frac{1-e^{- \tau \lambda }}{\tau \lambda }-e^{- \tau \lambda }\right) \end{align}\]
Here, \(\tau\) is a maturity and \(y(\tau)\) is a continuously compounded spot rate. \(\beta_0, \beta_1, \beta_2\) are coefficients and \(\lambda\) is the shape or time decay parameter.


The likelihood of the Nelson-Siegel model is constructed from the following model specification.

\[\begin{align} Y_t &\sim \text{Normal}(\mu, \psi) \\ \mu &= \beta_0 + \beta_1 \left( \frac{1-e^{- \tau \lambda }}{\tau \lambda }\right) + \beta_2 \left(\frac{1-e^{- \tau \lambda }}{\tau \lambda }-e^{- \tau \lambda }\right) \\ \sigma &= 1/\sqrt{\psi} \end{align}\]
Here, \(\psi\) in \(\text{Normal}(\mu, \psi)\) is not a variance but a precision which is the reciprocal of a variance (\(\psi = \frac{1}{\sigma^2}\)).


\(\beta_0, \beta_1, \beta_2\) are assumed to have normal priors but \(\psi\) is non-negative and is assumed to have a gamma prior. \(\lambda\) is also non-negative and is assumed to have a uniform prior.

\[\begin{align} \beta_0 &\sim \text{Normal}(y(\tau_{max}), 0.01) \\ \beta_1 &\sim \text{Normal}(y(\tau_{min})-y(\tau_{max}), 0.01) \\ \beta_2 &\sim \text{Normal}(2y(\tau_{mid})-y(\tau_{min})-y(\tau_{max}), 0.01) \\ \lambda &\sim \text{Unit}(0.02,0.15) \\ \psi &\sim \text{Gamma}(0.1,0.001) \end{align}\]

R code

The R code below performs Bayesian estimation of Nelson-Siegel model using a cross-section of yields with the help of rjags R package.

# Quantitative ALM, Financial Econometrics & Derivatives 
# ML/DL using R, Python, Tensorflow by Sang-Heon Lee 
# Nelson-Siegel fitted yield curve using rjags
#========================================================#  # clear all graphs
rm(list = ls()) # remove all files from your workspace
# Prepare data
mat < c(369122436486072,
yield < c(5.6387815.8577756.0623396.232739
vnm < c(“beta0”,“beta1”,“beta2”,“la”,“sigma”)
Data < list(x = mat, y = yield)
# for use inside model
Data$y_short < yield[1]
Data$y_long  < yield[16]
Data$y_mid   < yield[6]
# Model string
jags.script < 
  # priors
  beta0 ~ dnorm(y_long,0.01)
  beta1 ~ dnorm(y_short-y_long,0.01)
  beta2 ~ dnorm(2*y_mid-y_short-y_long,0.01)  
  la    ~ dunif(0.02,0.15)
  psi   ~ dgamma(0.1,0.001)
  # transform
  sigma <- 1 / sqrt(psi)
  # likelihood
  for( i in 1:length(x[])) {
    y[i] ~ dnorm(mu[i], psi)
    mu[i] <- beta0 + beta1*((1-exp(-la*x[i]))/(la*x[i]))+
mod1 < jags.model(textConnection(jags.script), data = Data, 
                   n.chains = 4, n.adapt = 1000)
update(mod1, 4000)
mod1.samples < coda.samples(model = mod1, 
                             variable.names = vnm, 
                             n.iter = 2000)
# plot trace and posterior density for each parameter
x11(); plot(mod1.samples[][,c(1,2,3)])
x11(); plot(mod1.samples[][,c(4,5)])
# descriptive statistics 
sm < summary(mod1.samples)  
# correlation
# means of posteriors of parameters
b0 < sm$statistics[vnm[1], “Mean”]
b1 < sm$statistics[vnm[2], “Mean”]
b2 < sm$statistics[vnm[3], “Mean”]
la < sm$statistics[vnm[4], “Mean”]
c(b0, b1, b2, la)
# in-sample fit
ypred < b0 +
         b1*((1exp(la*mat))/(la*mat)) +
         b2*((1exp(la*mat))/(la*mat)  exp(la*mat))
# graph
x11(width = 16/2, height = 9/2); 
plot(mat, yield, col = “red”, lwd=3, pch=16
     ylab = “Yield(%)”,xlab = “Maturity(month)”)
lines(mat, ypred, col = “blue”, lwd = 3)

Running this R code produces the following estimation results.

Nelson-Siegel model by using rjags R Package

Estimation results also contain of the following posterior distributions for each parameters.

Nelson-Siegel model by using rjags R Package
Nelson-Siegel model by using rjags R Package

We can generate a time series of fitted response variable with average coefficients. Fitted yield curve can be generated with average coefficients.

Nelson-Siegel model by using rjags R Package

Concluding Remarks

This post shows how to use rjags R package to perform Bayesian MCMC estimation of Nelson-Siegel model. Of course, as my setting for priors is not an answer but an alternative proposal, I think more elaborations on setting priors are needed. \(\blacksquare\)

To leave a comment for the author, please follow the link and comment on their blog: K & L Fintech Modeling. 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)