knitr::opts_chunk$set(collapse=TRUE, # hadley comment = "#>", # hadley error=TRUE, purl=FALSE, # to be able to see errors fig.width=7.25, fig.height=6) # nice-sized pictures
US Treasury bonds are the standard by which all other bonds in the world are measured:
On the Internet, there is a lot of static/stale US Treasury bond data and some of these sources show up in various R
packages. This package provides real-time daily interest-rate information for the US Treasury yield curve from 1962 to the present and real-time daily US Treasury bond price information for about 400 different bonds going back to 2010. More data downloads are planned.
In addition to data, this package is steadily adding to its collection of analyses that can be performed on the data:
The help facility for the package, ?ustreasuries
has a current list of all the facilities available and each function's documentation is current. Several vignettes are also listed.
I have a GitHub account and have made several formal releases of the package as the development process has moved forward: https://github.com/grfiv/ustreasuries The README file is kept current and a Wiki has been built.
The two books
are the canonical texts for derivatives and fixed-income analysis at many graduate courses in quantitative finance, such as my own alma mater MIT. I am in the process of building a series of vignettes that demonstrate how these books' examples were actually derived.
One of the things I found frustrating about these two books was that the data and processes were simply presented as a fait accomplis, probably created by doctoral students under duress; I am creating what I wish I had: code and data that clearly show how the algorithms work in a way that carries on beyond the $\LaTeX$ derivations and the use of toy datasets.
It is my intention to distribute a prime-time version on CRAN when I get to a suitable point and to make a submission to the R Journal.
The authors of this package are Pratik Biswas and George Fisher. All of the code was written by George Fisher.
In the following two files can be seen the work of Pratik who converted Black-Scholes-Merton functions George Fisher wrote in Python into R; I then aded the roxygen2 documentation and examples, the testthat unit tests and descriptive vignettes:
The following files are entirely George Fisher's work:
The CMTrates()
function downloads daily Constant Maturity Treasury (CMT) rates
into a data.frame with
library(ustreasuries) CMT_data <- CMTrates() # Note the most-recent date relative to the date of the download format(Sys.Date(), "%A %Y-%m-%d") knitr::kable(tail(CMT_data, n=3L)[2:7], caption="Sample of CMT Data (percent format: divide by 100 for math)", row.names=FALSE)
PrintYieldCurves(dplyr::filter(CMT_data, CMT_data$NEW_DATE>=as.Date("2006-01-01") & CMT_data$NEW_DATE<=as.Date("2009-12-31")), rows=c(1, 272, 272*2, 272*3), title="Yield Curves Before & During the Financial Crisis")
The US Treasury publishes a daily list of about 400 bonds with their coupons and prices. The FedInvestData()
function returns a data.frame containing the FedInvest data from 2010 to the most-recently completed business day.
SECURITY.TYPE
– MARKET BASED BILL
– MARKET BASED NOTE
– MARKET BASED BOND
– TIPS
– MARKET BASED FRN
RATE (coupon in percent format)
NA
fedinvest_data <- FedInvestData() knitr::kable(tail(fedinvest_data, n=3L), caption="Sample of FedInvest Data", row.names=FALSE)
The CMT rates are synthetic, created using splines where the knots are observed rates and the CMT rates are read at discrete points along the curve.
Real-life situations present us with a lot of dirty data from which we would like to derive rates and discount factors. There are two primary methods for doing this:
(1) bootstrap, which means equation-solving with linear algebra: define $Ax = b$ such that $b$ is a vector of observed prices and $A$ contains observed rates for several maturities; solve for $x$, the vector of $Z(0, T)$.
(2) modeling, for which there are two primary contenders: Nelson Seigel, a four-factor model; and Svensson, a six-factor extension to Nelson Seigel.
The Nelson Seigel (Nelson and Seigel, 1987) and Svensson (Svensson, 1994) models are described as follows (Veronesi, 2010)
The continuously-compounded interest rate is derived
Nelson Seigel
$$r ( 0, T ) = \beta_0 + ( \beta_1 + \beta_2 ) \frac{1-e^{-T/\lambda}}{T/\lambda}-\beta_2 e^{-T/\lambda}$$
Svensson
$$r ( 0, T ) = \beta_0 + ( \beta_1 + \beta_2 ) \frac{1-e^{-T/\lambda_1}}{T/\lambda_1} - \beta_2 e^{-T/\lambda_1} + \beta_3 \left(\frac{1-e^{-T/\lambda_2}}{T/\lambda_2} - e^{-T/\lambda_2} \right)$$
The modeled price, given a semi-annual coupon $c$ and $n$ payments per year is
$$P_{model} = 100 \times \left( \frac{c}{2} \sum_{j=1}^n Z(0, T_j) + Z(0, T_n) \right) $$
The squared difference between the modeled price and the observed price for a collection of bonds is minimized to estimate ${\beta_0, \beta_1, \beta_2, \lambda}$ for Nelson Seigel and ${\beta_0, \beta_1, \beta_2, \beta_3, \lambda_1, \lambda_2}$ for Svensson.
# pick a range of dates to analyze # ================================ start.date <- Sys.Date() - 25 end.date <- Sys.Date()
# ================================================================== # pull the FedInvest data for the range of dates # ... omitting TIPS, which are weird # ================================================================== library(magrittr) rate_table_list <- dplyr::filter(fedinvest_data, Date >= start.date & Date <= end.date & SECURITY.TYPE != "TIPS") %>% CoerceFedInvest_xts() rate_table_xts <- rate_table_list$rate_table_xts unique_maturities <- rate_table_list$unique_maturities # one row per day; each column the average YTM of a maturity knitr::kable(as.data.frame(rate_table_xts[1:3, 1:5]), caption="Sample of FedInvest Data after Coersion to xts Format", row.names=TRUE)
# ================================================================== # pull the CMT rates for a representative date in the selected range # to use as a check on how good the models are # ================================================================== # pick a week-day date in the middle of the range for (date in seq(start.date, end.date, by=1)) { CMT_date <- as.Date(date, origin="1970-01-01") if (weekdays(CMT_date) %in% c("Monday","Tuesday","Wednesday","Thursday","Friday") & which(CMT_date==seq(start.date, end.date, by=1)) >= length(seq(start.date, end.date, by=1))/2 & CMT_date%in% CMT_data$NEW_DATE) break } # pull out the yield-curve data for the selected date; convert to decimal CMT_y <- sapply(dplyr::filter(CMT_data, NEW_DATE==CMT_date)[3:13], function(x) x[[1]]/100) CMT_y[1:5]
# ============= # Nelson Siegel # ============= # derive the model parameters from the coerced FedInvest data NSParameters <- YieldCurve::Nelson.Siegel(rate= rate_table_xts, maturity=unique_maturities) NSParameters[1:3,] # model the rates from the middle of the range of dates mid_parm <- ceiling(nrow(NSParameters)/2) NS_y <- YieldCurve::NSrates(NSParameters, maturity=c(1/12, 3/12, 6/12, 1, 2, 3, 5, 7, 10, 20, 30))[mid_parm,] # ------------------------------------------------------------ plot(x=1:11, y=NS_y, ylim=c(min(NS_y,CMT_y),max(NS_y,CMT_y)),pch=19,type="b",lty=1, ylab="Yield",xlab=NA,xaxt="n", main=paste0("Term Structure on ",CMT_date, " Actual and Modeled by Nelson Siegel")) points(x=1:11, y=CMT_y, pch=10) lines(x=1:11, y=CMT_y, lty=2) legend("topleft",legend=c("Nelson Siegel Model based on FedInvest Data", "CMT Rates from the Treasury"), pch=c(19,10), lty=c(1,2)) axis(1, at = axTicks(1), labels = substr(names(CMT_y)[axTicks(1)],4,9)) grid()
# ======== # SVENSSON # ======== # derive the model parameters from the coerced FedInvest data SVParameters <- YieldCurve::Svensson(rate= rate_table_xts, maturity=unique_maturities) SVParameters[1:3,] # model the rates from the middle of the range of dates mid_parm <- ceiling(nrow(SVParameters)/2) SV_y <- YieldCurve::Srates(SVParameters, maturity=c(1/12, 3/12, 6/12, 1, 2, 3, 5, 7, 10, 20, 30), whichRate="Spot")[mid_parm,] # ------------------------------------------------------------ plot(x=1:11, y=SV_y, ylim=c(min(SV_y,CMT_y),max(SV_y,CMT_y)),pch=19,type="b",lty=1, ylab="Yield",xlab=NA,xaxt="n", main=paste0("Term Structure on ",CMT_date, " Actual and Modeled by Svensson")) points(x=1:11, y=CMT_y, pch=10) lines(x=1:11, y=CMT_y, lty=2) legend("topleft",legend=c("Svensson Model based on FedInvest Data", "CMT Rates from the Treasury"), pch=c(19,10), lty=c(1,2)) axis(1, at = axTicks(1), labels = substr(names(CMT_y)[axTicks(1)],4,9)) grid()
# derive Z(0, T) Z <- NSzeros(SV_y) # ---------- Plot Z(0, T) plot(x=1:11, y=Z,xlab=NA, ylab=NA,xaxt="n",pch=19,col="red", main="Discount Factors and Spot Rates, Derived from Svensson") axis(side = 2) text(x=1.25, y=Z[1], labels=round(Z[1],6), pos=1, col="red") text(x=11, y=Z[11], labels=round(Z[11],6), pos=3, col="red") # ---------- Plot rates par(new=T) plot(x=1:11, y=SV_y,axes=F,xlab=NA, ylab=NA,xaxt="n",pch=19,type="b",lty=1) axis(side = 4) text(x=1.25, y=as.numeric(SV_y)[1], labels=paste0(round(as.numeric(SV_y)[1]*100,4),"%"), pos=3) text(x=10.5, y=as.numeric(SV_y)[11], labels=paste0(round(as.numeric(SV_y)[11]*100,4),"%"), pos=1) legend("center",legend=c("Discount Factors", "Rates"), pch=c(19,19), lty=c(NA,1),col=c("red","black")) axis(1, at = axTicks(1), labels = substr(names(CMT_y)[axTicks(1)],4,9)) grid()
sessionInfo()
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.