The purpose of this project is demonstrate principal component analysis of interest rates using R. This includes accessing data from public sources and performing the necessary calculations.
Principal component analysis is frequently used to capture the variability in the movement of interest rates along the term structure. Understanding the varability allows for creation of stressed interest rate term structures that can be applied as part of a risk management program wherever the uncertainty of interest rates is a concern. Varying degrees of stress can be applied in the fashion of analytical Value-at-Risk (VaR) techniques. This exercise focuses on the 99.5th percentile as this degree is commonly the focus for many insurance companies and is aligned with benchmarks set by industry standards such as Solvency II Standard Formula. This technique is described in many places but this exercise will follow the process outlined in How to Estimate and Calibrate Analytical VaR for Interest Rate Risk. Please refer to this helpful document as efforts have been made to avoid repeating what has already been described there.
LIBOR interest rate data is obtained for the following terms: 1-month, 3-month, 6-month, and 1-year. The source of the data is the Federal Reserve Bank of St. Louis and is pulled via API’s the source makes available. To obtain the data a user must register through the website and request an API key. Data can then be queried and retrieved in either JSON or XML format. More information related to usage of the API can found on the sources website.
Swap rate data is obtained for the following terms: 1-year, 3-year, 5-year, 7-year, 10-year, and 30-year. The source of the data is the Federal Reserve. The data is available in the form of downloadable comma separated value (csv) files.
This project utilizes xts, jsonlite, ggplot2, and reshape2 packages. The xts package is useful for storing time series data while jsonlite allows for reading data stored in JSON format. The latter is especially helpful for reading LIBOR data from the source.The ggplot2 package is a well known plotting package used to generate the graphs. The reshape2 package is used to structure data to allow for easier computation or plotting.
require(xts)
require(jsonlite)
require(ggplot2)
require(reshape2)
Interest rate data is loaded into an xts time-series object. This object is useful for data where values correspond to dates.
json_file <- "https://api.stlouisfed.org/fred/series/observations?series_id=USD1MTD156N&frequency=m&aggregation_method=eop&file_type=json&api_key=29fb098284eda4e97e4678353c8f25f2"
json_data <- fromJSON(txt=json_file)
libor_1m <- xts(as.numeric(json_data$observations$value), as.yearmon(json_data$observations$date, format='%Y-%m-%d'))
json_file <- "https://api.stlouisfed.org/fred/series/observations?series_id=USD3MTD156N&frequency=m&aggregation_method=eop&file_type=json&api_key=29fb098284eda4e97e4678353c8f25f2"
json_data <- fromJSON(txt=json_file)
libor_3m <- xts(as.numeric(json_data$observations$value), as.yearmon(json_data$observations$date, format='%Y-%m-%d'))
libor <- merge(libor_1m, libor_3m, all=FALSE)
json_file <- "https://api.stlouisfed.org/fred/series/observations?series_id=USD6MTD156N&frequency=m&aggregation_method=eop&file_type=json&api_key=29fb098284eda4e97e4678353c8f25f2"
json_data <- fromJSON(txt=json_file)
libor_6m <- xts(as.numeric(json_data$observations$value), as.yearmon(json_data$observations$date, format='%Y-%m-%d'))
libor <- merge(libor, libor_6m, all=FALSE)
download.file("http://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=86f09fbc8cfcc804b71a3d827a5fddc8&lastObs=&from=&to=&filetype=csv&label=include&layout=seriescolumn", destfile="swap_1yr.csv")
data <- read.csv("swap_1yr.csv", skip=5, col.names=c("Date", "Rate"))
swap_1yr <- xts(data$Rate, as.yearmon(data$Date, format='%Y-%m'))
download.file("http://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=81223b01cb8c943e81dddfbd03cd2199&lastObs=&from=&to=&filetype=csv&label=include&layout=seriescolumn", destfile="swap_2yr.csv")
data <- read.csv("swap_2yr.csv", skip=5, col.names=c("Date", "Rate"))
swap_2yr <- xts(data$Rate, as.yearmon(data$Date, format='%Y-%m'))
swap <- merge(swap_1yr, swap_2yr)
download.file("http://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=23bf93b6527f59eff7f778462706d86d&lastObs=&from=&to=&filetype=csv&label=include&layout=seriescolumn", destfile="swap_3yr.csv")
data <- read.csv("swap_3yr.csv", skip=5, col.names=c("Date", "Rate"))
swap_3yr <- xts(data$Rate, as.yearmon(data$Date, format='%Y-%m'))
swap <- merge(swap, swap_3yr)
download.file("http://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=025cf7daa62487392306aa9fb6f3d830&lastObs=&from=&to=&filetype=csv&label=include&layout=seriescolumn", destfile="swap_4yr.csv")
data <- read.csv("swap_4yr.csv", skip=5, col.names=c("Date", "Rate"))
swap_4yr <- xts(data$Rate, as.yearmon(data$Date, format='%Y-%m'))
swap <- merge(swap, swap_4yr)
download.file("http://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=05629c455ea42adce05a1c4861e0ceaa&lastObs=&from=&to=&filetype=csv&label=include&layout=seriescolumn", destfile="swap_5yr.csv")
data <- read.csv("swap_5yr.csv", skip=5, col.names=c("Date", "Rate"))
swap_5yr <- xts(data$Rate, as.yearmon(data$Date, format='%Y-%m'))
swap <- merge(swap, swap_5yr)
download.file("http://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=1f1346dca868bbf046c10ad5b912eae1&lastObs=&from=&to=&filetype=csv&label=include&layout=seriescolumn", destfile="swap_7yr.csv")
data <- read.csv("swap_7yr.csv", skip=5, col.names=c("Date", "Rate"))
swap_7yr <- xts(data$Rate, as.yearmon(data$Date, format='%Y-%m'))
swap <- merge(swap, swap_7yr)
download.file("http://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=8f401407ccab6d90aeb1db13ffe746b5&lastObs=&from=&to=&filetype=csv&label=include&layout=seriescolumn", destfile="swap_10yr.csv")
data <- read.csv("swap_10yr.csv", skip=5, col.names=c("Date", "Rate"))
swap_10yr <- xts(data$Rate, as.yearmon(data$Date, format='%Y-%m'))
swap <- merge(swap, swap_10yr)
download.file("http://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=4f2a3bbeb30b5b4fbcb019a11a513fec&lastObs=&from=&to=&filetype=csv&label=include&layout=seriescolumn", destfile="swap_30yr.csv")
data <- read.csv("swap_30yr.csv", skip=5, col.names=c("Date", "Rate"))
swap_30yr <- xts(data$Rate, as.yearmon(data$Date, format='%Y-%m'))
swap <- merge(swap, swap_30yr)
The available interest rate data is now combined into a single xts object
rates <- merge(libor, swap, all=FALSE)
zoo.rates <- as.zoo(rates)
tsRainbow <- rainbow(ncol(zoo.rates))
plot(x=zoo.rates,col=tsRainbow, main="Historical Interest Rates", ylab="Rates", xlab="Time", plot.type="single", xaxt ="n", yaxt="n")
axis(1)
pts<- pretty(zoo.rates)
axis(2, at=pts, labels=paste(pts, "%", sep=""))
legend(x="topright", legend=colnames(rates), lty=1, col=tsRainbow, cex=.64,bty="n")
swap <- interpolateSwapsPeriods(1,30,.5, swap)
rates<- merge(libor, swap, all=FALSE)
zoo.rates <- as.zoo(rates)
tsRainbow <- rainbow(ncol(zoo.rates))
plot(x=zoo.rates,col=tsRainbow, main="Historical Interest Rates", ylab="Rates", xlab="Time", plot.type="single", xaxt ="n", yaxt="n")
axis(1)
pts<- pretty(zoo.rates)
axis(2, at=pts, labels=paste(pts, "%", sep=""))
legend(x="topright", legend=colnames(rates), lty=1, col=tsRainbow, cex=.64,bty="n")
#head(rates)
#tail(rates)
In this section spot rates are obtained via the commonly used bootstrapping technique.
spot <- bootstrap(.5, libor, swap)
libor <- merge(libor_1m/100, libor_3m/100)
spot <- merge(libor, spot)
spot <- na.omit(spot)
write.zoo(spot, file="spot.csv")
#tail(spot)
spotPctDiff <- spot/lag(spot,1) - 1
spotPctDiff <- unclass(spotPctDiff)[2:length(spotPctDiff[,1]),1:length(colnames(spotPctDiff))]
scaledSpotPctDiff <- scale(spotPctDiff)
pca <- eigen(cor(scaledSpotPctDiff))
write(pca$values, file="eigenvalues.csv", sep=",", ncolumns=1)
write(t(pca$vectors), file="eigenvectors.csv", sep=",", ncolumns=length(colnames(spot)))
vol.df <- data.frame(attr(scaledSpotPctDiff, "scaled:scale"))
colnames(vol.df)<-c("vol")
write(vol.df$vol, file="vol.csv", sep=",", ncolumns=1)
The plot shows that three principal components describe the bulk of the variation.
In this section the sign of the key principal components are changed to align with interpretations in the paper by Folpmers and Eweg referenced above. There the authors say:
Principal component analysis based shocks are developed by considering the product of the principal component vectors (eigenvectors) and their standard deviations (square root of the eigenvalues). The degree of the stress is obtained by applying the desired number of standard deviation. The normal distribution is assumed for this purpose in combination with the 99.5th percentile.
Shocked rates are obtaind by applying the principal component analysis based shocks described above to the current interest rate term structure. Finally, the square root of 12 is applied to convert the monthly volatility to an annual figure.
# Develop shocks using the principal component vectors and variances
# Need to multiply eigenvectors by eigenvalues. First must get eigevectors in form to multiply easily.
eigenGoodForm <- NULL
stdevRateGoodForm <- NULL
lastRateGoodForm <- NULL
curvePoints <- ncol(scaledSpotPctDiff)
for(i in 1:curvePoints)
{
eigenGoodForm <- c(eigenGoodForm, c(rep(pca$values[i], curvePoints)))
stdevRateGoodForm <- c(stdevRateGoodForm, c(attr(scaledSpotPctDiff, "scaled:scale")))
lastRateGoodForm <- c(lastRateGoodForm, c((unclass(last(spot))[1,])))
}
# Develop the shocked pc's and the rateShocks
pcaShockUp <- pca$vectors * eigenGoodForm^.5 * qnorm(.995)
rateShockUp <- (1+pcaShockUp*stdevRateGoodForm *sqrt(12))* lastRateGoodForm
pcaShockUp <- subset(data.frame(pcaShockUp), select=-c(4:length(colnames(data.frame(pcaShockUp)))))
colnames(pcaShockUp) <- c("PCA_1_Up", "PCA_2_Up", "PCA_3_Up")
rateShockUp <- subset(data.frame(rateShockUp), select=-c(4:length(colnames(data.frame(rateShockUp)))))
colnames(rateShockUp) <- c("rate_PCA_1_Up", "rate_PCA_2_Up", "rate_PCA_3_Up")
pcaShockDown <- pca$vectors * eigenGoodForm^.5 * -qnorm(.995)
rateShockDown <- (1+pcaShockDown*stdevRateGoodForm * sqrt(12))* lastRateGoodForm
pcaShockDown <- subset(data.frame(pcaShockDown), select=-c(4:length(colnames(data.frame(pcaShockDown)))))
colnames(pcaShockDown) <- c("PCA_1_Down", "PCA_2_Down", "PCA_3_Down")
rateShockDown <- subset(data.frame(rateShockDown), select=-c(4:length(colnames(data.frame(rateShockDown)))))
colnames(rateShockDown) <- c("rate_PCA_1_Down", "rate_PCA_2_Down", "rate_PCA_3_Down")
pcaShock <- cbind(pcaShockUp, pcaShockDown)
pcaShock$Term <- c(1/12, 3/12, seq(from=.5, t=30, by=.5))
col_id <- grep(c("Term"), names(pcaShock))
pcaShock <- pcaShock[,c(col_id, (1:ncol(pcaShock))[-col_id])]
rateShock <- cbind(rateShockUp, rateShockDown)
rateShock$Term <- c(1/12, 3/12, seq(from=.5, t=30, by=.5))
rateShock$Base <- unclass(last(spot))[1,]
col_id <- grep(c("Base"), names(rateShock))
rateShock <- rateShock[,c(col_id, (1:ncol(rateShock))[-col_id])]
col_id <- grep(c("Term"), names(rateShock))
rateShock <- rateShock[,c(col_id, (1:ncol(rateShock))[-col_id])]
| Term | PCA_1_Up | PCA_2_Up | PCA_3_Up | PCA_1_Down | PCA_2_Down | PCA_3_Down |
|---|---|---|---|---|---|---|
| 0.0833333 | 0.5226558 | -1.2805887 | 1.9347736 | -0.5226558 | 1.2805887 | -1.9347736 |
| 0.2500000 | 0.2131756 | -1.8095608 | 1.7256696 | -0.2131756 | 1.8095608 | -1.7256696 |
| 0.5000000 | 0.4654727 | -1.8177872 | 1.6238846 | -0.4654727 | 1.8177872 | -1.6238846 |
| 1.0000000 | 1.0797353 | -2.0917086 | 0.4787185 | -1.0797353 | 2.0917086 | -0.4787185 |
| 1.5000000 | 1.5978651 | -1.9186782 | -0.0421185 | -1.5978651 | 1.9186782 | 0.0421185 |
| 2.0000000 | 1.8307288 | -1.6672682 | -0.3634506 | -1.8307288 | 1.6672682 | 0.3634506 |
| 2.5000000 | 2.0188586 | -1.4550278 | -0.5149783 | -2.0188586 | 1.4550278 | 0.5149783 |
| 3.0000000 | 2.1235027 | -1.2663640 | -0.6178181 | -2.1235027 | 1.2663640 | 0.6178181 |
| 3.5000000 | 2.2120751 | -1.1228490 | -0.6177914 | -2.2120751 | 1.1228490 | 0.6177914 |
| 4.0000000 | 2.2723573 | -0.9927522 | -0.6182653 | -2.2723573 | 0.9927522 | 0.6182653 |
| 4.5000000 | 2.3343312 | -0.8829901 | -0.5737572 | -2.3343312 | 0.8829901 | 0.5737572 |
| 5.0000000 | 2.3813653 | -0.7768722 | -0.5358476 | -2.3813653 | 0.7768722 | 0.5358476 |
| 5.5000000 | 2.4215763 | -0.6905226 | -0.4866376 | -2.4215763 | 0.6905226 | 0.4866376 |
| 6.0000000 | 2.4543602 | -0.6043465 | -0.4412225 | -2.4543602 | 0.6043465 | 0.4412225 |
| 6.5000000 | 2.4807173 | -0.5188463 | -0.3992134 | -2.4807173 | 0.5188463 | 0.3992134 |
| 7.0000000 | 2.5014039 | -0.4343785 | -0.3602801 | -2.5014039 | 0.4343785 | 0.3602801 |
| 7.5000000 | 2.5168052 | -0.3778303 | -0.3255626 | -2.5168052 | 0.3778303 | 0.3255626 |
| 8.0000000 | 2.5298002 | -0.3205099 | -0.2917583 | -2.5298002 | 0.3205099 | 0.2917583 |
| 8.5000000 | 2.5404996 | -0.2625528 | -0.2588334 | -2.5404996 | 0.2625528 | 0.2588334 |
| 9.0000000 | 2.5489887 | -0.2040787 | -0.2267599 | -2.5489887 | 0.2040787 | 0.2267599 |
| 9.5000000 | 2.5553348 | -0.1451964 | -0.1955149 | -2.5553348 | 0.1451964 | 0.1955149 |
| 10.0000000 | 2.5595913 | -0.0860065 | -0.1650807 | -2.5595913 | 0.0860065 | 0.1650807 |
| 10.5000000 | 2.5617060 | -0.0697487 | -0.1513998 | -2.5617060 | 0.0697487 | 0.1513998 |
| 11.0000000 | 2.5636223 | -0.0530663 | -0.1376344 | -2.5636223 | 0.0530663 | 0.1376344 |
| 11.5000000 | 2.5653324 | -0.0359803 | -0.1237816 | -2.5653324 | 0.0359803 | 0.1237816 |
| 12.0000000 | 2.5668282 | -0.0185080 | -0.1098392 | -2.5668282 | 0.0185080 | 0.1098392 |
| 12.5000000 | 2.5681015 | -0.0006635 | -0.0958050 | -2.5681015 | 0.0006635 | 0.0958050 |
| 13.0000000 | 2.5691438 | 0.0175415 | -0.0816776 | -2.5691438 | -0.0175415 | 0.0816776 |
| 13.5000000 | 2.5699462 | 0.0360971 | -0.0674555 | -2.5699462 | -0.0360971 | 0.0674555 |
| 14.0000000 | 2.5704996 | 0.0549954 | -0.0531379 | -2.5704996 | -0.0549954 | 0.0531379 |
| 14.5000000 | 2.5707947 | 0.0742295 | -0.0387238 | -2.5707947 | -0.0742295 | 0.0387238 |
| 15.0000000 | 2.5708217 | 0.0937937 | -0.0242129 | -2.5708217 | -0.0937937 | 0.0242129 |
| 15.5000000 | 2.5705705 | 0.1136833 | -0.0096048 | -2.5705705 | -0.1136833 | 0.0096048 |
| 16.0000000 | 2.5700306 | 0.1338942 | 0.0051003 | -2.5700306 | -0.1338942 | -0.0051003 |
| 16.5000000 | 2.5691914 | 0.1544228 | 0.0199021 | -2.5691914 | -0.1544228 | -0.0199021 |
| 17.0000000 | 2.5680418 | 0.1752663 | 0.0348003 | -2.5680418 | -0.1752663 | -0.0348003 |
| 17.5000000 | 2.5665702 | 0.1964220 | 0.0497939 | -2.5665702 | -0.1964220 | -0.0497939 |
| 18.0000000 | 2.5647648 | 0.2178877 | 0.0648819 | -2.5647648 | -0.2178877 | -0.0648819 |
| 18.5000000 | 2.5626135 | 0.2396612 | 0.0800630 | -2.5626135 | -0.2396612 | -0.0800630 |
| 19.0000000 | 2.5601037 | 0.2617407 | 0.0953355 | -2.5601037 | -0.2617407 | -0.0953355 |
| 19.5000000 | 2.5572224 | 0.2841244 | 0.1106976 | -2.5572224 | -0.2841244 | -0.1106976 |
| 20.0000000 | 2.5539563 | 0.3068105 | 0.1261470 | -2.5539563 | -0.3068105 | -0.1261470 |
| 20.5000000 | 2.5502917 | 0.3297973 | 0.1416813 | -2.5502917 | -0.3297973 | -0.1416813 |
| 21.0000000 | 2.5462145 | 0.3530831 | 0.1572975 | -2.5462145 | -0.3530831 | -0.1572975 |
| 21.5000000 | 2.5417102 | 0.3766661 | 0.1729926 | -2.5417102 | -0.3766661 | -0.1729926 |
| 22.0000000 | 2.5367641 | 0.4005443 | 0.1887630 | -2.5367641 | -0.4005443 | -0.1887630 |
| 22.5000000 | 2.5313607 | 0.4247159 | 0.2046048 | -2.5313607 | -0.4247159 | -0.2046048 |
| 23.0000000 | 2.5254846 | 0.4491786 | 0.2205137 | -2.5254846 | -0.4491786 | -0.2205137 |
| 23.5000000 | 2.5191197 | 0.4739301 | 0.2364852 | -2.5191197 | -0.4739301 | -0.2364852 |
| 24.0000000 | 2.5122496 | 0.4989679 | 0.2525141 | -2.5122496 | -0.4989679 | -0.2525141 |
| 24.5000000 | 2.5048576 | 0.5242893 | 0.2685950 | -2.5048576 | -0.5242893 | -0.2685950 |
| 25.0000000 | 2.4969264 | 0.5498913 | 0.2847220 | -2.4969264 | -0.5498913 | -0.2847220 |
| 25.5000000 | 2.4884387 | 0.5757708 | 0.3008887 | -2.4884387 | -0.5757708 | -0.3008887 |
| 26.0000000 | 2.4793763 | 0.6019242 | 0.3170883 | -2.4793763 | -0.6019242 | -0.3170883 |
| 26.5000000 | 2.4697211 | 0.6283479 | 0.3333134 | -2.4697211 | -0.6283479 | -0.3333134 |
| 27.0000000 | 2.4594542 | 0.6550379 | 0.3495562 | -2.4594542 | -0.6550379 | -0.3495562 |
| 27.5000000 | 2.4485566 | 0.6819900 | 0.3658085 | -2.4485566 | -0.6819900 | -0.3658085 |
| 28.0000000 | 2.4370086 | 0.7091996 | 0.3820613 | -2.4370086 | -0.7091996 | -0.3820613 |
| 28.5000000 | 2.4247902 | 0.7366620 | 0.3983051 | -2.4247902 | -0.7366620 | -0.3983051 |
| 29.0000000 | 2.4118809 | 0.7643719 | 0.4145300 | -2.4118809 | -0.7643719 | -0.4145300 |
| 29.5000000 | 2.3982597 | 0.7923242 | 0.4307254 | -2.3982597 | -0.7923242 | -0.4307254 |
| 30.0000000 | 2.3839049 | 0.8205131 | 0.4468799 | -2.3839049 | -0.8205131 | -0.4468799 |
| Term | Base | rate_PCA_1_Up | rate_PCA_2_Up | rate_PCA_3_Up | rate_PCA_1_Down | rate_PCA_2_Down | rate_PCA_3_Down |
|---|---|---|---|---|---|---|---|
| 0.0833333 | 0.0018650 | 0.0022294 | 0.0009721 | 0.0032141 | 0.0015006 | 0.0027579 | 0.0005159 |
| 0.2500000 | 0.0028320 | 0.0030589 | 0.0009058 | 0.0046689 | 0.0026051 | 0.0047582 | 0.0009951 |
| 0.5000000 | 0.0044485 | 0.0051192 | 0.0018292 | 0.0067884 | 0.0037778 | 0.0070678 | 0.0021086 |
| 1.0000000 | 0.0053082 | 0.0072759 | 0.0014961 | 0.0061806 | 0.0033404 | 0.0091202 | 0.0044357 |
| 1.5000000 | 0.0073217 | 0.0112623 | 0.0025899 | 0.0072178 | 0.0033811 | 0.0120535 | 0.0074256 |
| 2.0000000 | 0.0093438 | 0.0154626 | 0.0037713 | 0.0081290 | 0.0032249 | 0.0149163 | 0.0105585 |
| 2.5000000 | 0.0111723 | 0.0192242 | 0.0053692 | 0.0091184 | 0.0031204 | 0.0169755 | 0.0132262 |
| 3.0000000 | 0.0130116 | 0.0230306 | 0.0070367 | 0.0100966 | 0.0029925 | 0.0189865 | 0.0159265 |
| 3.5000000 | 0.0145005 | 0.0257672 | 0.0087815 | 0.0113539 | 0.0032337 | 0.0202195 | 0.0176471 |
| 4.0000000 | 0.0159996 | 0.0285221 | 0.0105287 | 0.0125924 | 0.0034770 | 0.0214704 | 0.0194067 |
| 4.5000000 | 0.0171936 | 0.0303095 | 0.0122323 | 0.0139698 | 0.0040777 | 0.0221548 | 0.0204174 |
| 5.0000000 | 0.0183966 | 0.0321412 | 0.0139127 | 0.0153038 | 0.0046520 | 0.0228805 | 0.0214894 |
| 5.5000000 | 0.0192871 | 0.0332679 | 0.0153004 | 0.0164775 | 0.0053063 | 0.0232738 | 0.0220967 |
| 6.0000000 | 0.0201847 | 0.0344330 | 0.0166763 | 0.0176233 | 0.0059364 | 0.0236931 | 0.0227461 |
| 6.5000000 | 0.0210897 | 0.0356307 | 0.0180485 | 0.0187497 | 0.0065488 | 0.0241310 | 0.0234298 |
| 7.0000000 | 0.0220027 | 0.0368574 | 0.0194231 | 0.0198631 | 0.0071479 | 0.0245823 | 0.0241422 |
| 7.5000000 | 0.0225332 | 0.0374031 | 0.0203009 | 0.0206097 | 0.0076634 | 0.0247655 | 0.0244567 |
| 8.0000000 | 0.0230687 | 0.0379681 | 0.0211810 | 0.0213504 | 0.0081693 | 0.0249564 | 0.0247870 |
| 8.5000000 | 0.0236091 | 0.0385509 | 0.0220649 | 0.0220868 | 0.0086674 | 0.0251533 | 0.0251314 |
| 9.0000000 | 0.0241546 | 0.0391501 | 0.0229540 | 0.0228206 | 0.0091592 | 0.0253552 | 0.0254886 |
| 9.5000000 | 0.0247052 | 0.0397647 | 0.0238495 | 0.0235530 | 0.0096458 | 0.0255609 | 0.0258575 |
| 10.0000000 | 0.0252612 | 0.0403940 | 0.0247527 | 0.0242852 | 0.0101283 | 0.0257697 | 0.0262372 |
| 10.5000000 | 0.0253617 | 0.0404387 | 0.0249512 | 0.0244706 | 0.0102846 | 0.0257722 | 0.0262527 |
| 11.0000000 | 0.0254649 | 0.0404892 | 0.0251539 | 0.0246582 | 0.0104405 | 0.0257759 | 0.0262715 |
| 11.5000000 | 0.0255705 | 0.0405448 | 0.0253605 | 0.0248480 | 0.0105962 | 0.0257805 | 0.0262930 |
| 12.0000000 | 0.0256784 | 0.0406051 | 0.0255707 | 0.0250396 | 0.0107516 | 0.0257860 | 0.0263171 |
| 12.5000000 | 0.0257883 | 0.0406697 | 0.0257845 | 0.0252331 | 0.0109069 | 0.0257921 | 0.0263435 |
| 13.0000000 | 0.0259002 | 0.0407382 | 0.0260015 | 0.0254284 | 0.0110622 | 0.0257989 | 0.0263719 |
| 13.5000000 | 0.0260139 | 0.0408103 | 0.0262217 | 0.0256255 | 0.0112174 | 0.0258060 | 0.0264022 |
| 14.0000000 | 0.0261293 | 0.0408859 | 0.0264450 | 0.0258243 | 0.0113727 | 0.0258136 | 0.0264344 |
| 14.5000000 | 0.0262464 | 0.0409647 | 0.0266714 | 0.0260247 | 0.0115281 | 0.0258214 | 0.0264681 |
| 15.0000000 | 0.0263651 | 0.0410465 | 0.0269007 | 0.0262268 | 0.0116836 | 0.0258294 | 0.0265033 |
| 15.5000000 | 0.0264853 | 0.0411311 | 0.0271330 | 0.0264305 | 0.0118394 | 0.0258376 | 0.0265400 |
| 16.0000000 | 0.0266070 | 0.0412186 | 0.0273682 | 0.0266360 | 0.0119954 | 0.0258457 | 0.0265780 |
| 16.5000000 | 0.0267301 | 0.0413086 | 0.0276064 | 0.0268430 | 0.0121516 | 0.0258539 | 0.0266172 |
| 17.0000000 | 0.0268547 | 0.0414012 | 0.0278475 | 0.0270518 | 0.0123082 | 0.0258619 | 0.0266576 |
| 17.5000000 | 0.0269807 | 0.0414962 | 0.0280916 | 0.0272623 | 0.0124652 | 0.0258698 | 0.0266991 |
| 18.0000000 | 0.0271081 | 0.0415936 | 0.0283387 | 0.0274745 | 0.0126225 | 0.0258775 | 0.0267416 |
| 18.5000000 | 0.0272368 | 0.0416933 | 0.0285888 | 0.0276885 | 0.0127803 | 0.0258848 | 0.0267852 |
| 19.0000000 | 0.0273670 | 0.0417953 | 0.0288421 | 0.0279043 | 0.0129386 | 0.0258918 | 0.0268297 |
| 19.5000000 | 0.0274985 | 0.0418995 | 0.0290985 | 0.0281219 | 0.0130975 | 0.0258984 | 0.0268751 |
| 20.0000000 | 0.0276314 | 0.0420059 | 0.0293582 | 0.0283414 | 0.0132568 | 0.0259045 | 0.0269214 |
| 20.5000000 | 0.0277656 | 0.0421145 | 0.0296212 | 0.0285628 | 0.0134168 | 0.0259101 | 0.0269685 |
| 21.0000000 | 0.0279013 | 0.0422252 | 0.0298876 | 0.0287862 | 0.0135774 | 0.0259150 | 0.0270164 |
| 21.5000000 | 0.0280384 | 0.0423380 | 0.0301575 | 0.0290116 | 0.0137387 | 0.0259192 | 0.0270651 |
| 22.0000000 | 0.0281768 | 0.0424529 | 0.0304310 | 0.0292391 | 0.0139007 | 0.0259227 | 0.0271145 |
| 22.5000000 | 0.0283167 | 0.0425700 | 0.0307082 | 0.0294688 | 0.0140635 | 0.0259253 | 0.0271647 |
| 23.0000000 | 0.0284581 | 0.0426891 | 0.0309892 | 0.0297007 | 0.0142271 | 0.0259270 | 0.0272155 |
| 23.5000000 | 0.0286009 | 0.0428104 | 0.0312742 | 0.0299349 | 0.0143915 | 0.0259277 | 0.0272670 |
| 24.0000000 | 0.0287453 | 0.0429337 | 0.0315633 | 0.0301714 | 0.0145568 | 0.0259272 | 0.0273191 |
| 24.5000000 | 0.0288911 | 0.0430592 | 0.0318566 | 0.0304103 | 0.0147230 | 0.0259256 | 0.0273719 |
| 25.0000000 | 0.0290385 | 0.0431868 | 0.0321543 | 0.0306518 | 0.0148902 | 0.0259226 | 0.0274252 |
| 25.5000000 | 0.0291875 | 0.0433166 | 0.0324566 | 0.0308959 | 0.0150583 | 0.0259183 | 0.0274790 |
| 26.0000000 | 0.0293381 | 0.0434485 | 0.0327637 | 0.0311426 | 0.0152276 | 0.0259124 | 0.0275335 |
| 26.5000000 | 0.0294903 | 0.0435826 | 0.0330757 | 0.0313922 | 0.0153979 | 0.0259049 | 0.0275884 |
| 27.0000000 | 0.0296442 | 0.0437190 | 0.0333928 | 0.0316446 | 0.0155694 | 0.0258956 | 0.0276438 |
| 27.5000000 | 0.0297998 | 0.0438575 | 0.0337153 | 0.0319000 | 0.0157421 | 0.0258844 | 0.0276997 |
| 28.0000000 | 0.0299573 | 0.0439984 | 0.0340434 | 0.0321585 | 0.0159161 | 0.0258711 | 0.0277560 |
| 28.5000000 | 0.0301165 | 0.0441416 | 0.0343774 | 0.0324203 | 0.0160914 | 0.0258556 | 0.0278127 |
| 29.0000000 | 0.0302775 | 0.0442871 | 0.0347174 | 0.0326854 | 0.0162680 | 0.0258376 | 0.0278697 |
| 29.5000000 | 0.0304405 | 0.0444349 | 0.0350639 | 0.0329539 | 0.0164461 | 0.0258171 | 0.0279271 |
| 30.0000000 | 0.0306054 | 0.0445852 | 0.0354171 | 0.0332261 | 0.0166256 | 0.0257938 | 0.0279848 |
bootstrap<-function(freq,libor, swap){
at <- availTenors(colnames(swap))
max <- at$tenor[length(colnames(swap))]
result <- libor[,match("libor_6m", colnames(libor))]/100
colnames(result)[length(colnames(result))] <- "spot_.5yr"
df <- (1+result[,length(colnames(result))])^-.5
colnames(df)[length(colnames(df))] <- "df_.5yr"
for(i in seq(from=1, to=max, by=freq)){
txt <- paste0("swap_", i, "yr")
coupon <- swap[,match(txt, colnames(swap))]/100/2
result$newcol <- ((1+coupon)/(1-annuityDF(df)*coupon))^(1/i)-1
txt <- paste0("spot_", i, "yr")
colnames(result)[length(colnames(result))] <- txt
txt <- paste0("df_", i, "yr")
df$newcol <- (1+result[,length(colnames(result))])^-i
colnames(df)[length(colnames(df))] <- txt
}
result <- na.omit(result)
return(result)
}
annuityDF<-function(df)
{
max <- length(colnames(df))
for(i in seq(from=1, to=max,by=1)){
if(i==1){
result <- df[,1]
colnames(result)[length(colnames(result))] <- "sum"
}else{
result <- result + df[,i]
}
}
return(result)
}
# This function combines other functions to loop from the lowest desired swap
# rate to the highest and interpolates along the way. The intention of this function
# is to develop a swap rate for every half-year point from 1 - 30 years.
interpolateSwapsPeriods<-function(yrLow, yrHigh, freq, rates){
result <- rates
at <- availTenors(colnames(rates))
for(i in seq(from=yrLow, to=yrHigh, by=freq)){
if(i %in% at$tenor){
#do nothing
}
else{
txt <- paste0("swap_", i, "yr")
result$newcol <- interpolateSwaps(i, rates)
colnames(result)[length(colnames(result))] = txt
result <- sortSwapCols(result)
}
}
return(result)
}
# This function takes a year and a swap rate table and linearly interpolates across
# the whole time series
interpolateSwaps<-function(yr, rates){
hl <- highlow(yr, colnames(rates))
if(is.null(hl)){
return(NULL)
}
else if(hl[1]==hl[2]){
return(rates[,hl[3]])
}
else{
return((rates[,hl[3]]*(hl[2]-yr)+rates[,hl[4]]*(yr-hl[1]))/(hl[2]-hl[1]))
}
}
# Given a year and header of a swap rate table, this function returns a vector
# of length four where the first element is the known tenor just below the given
# year, the second elements is the know tenor just above the given year, and the
# third and fourth are the location of columns related to the values respectively.
highlow <- function(yr, availSwaps){
max <- length(availSwaps)
txtStart <- regexpr("_", availSwaps[1])
txtEnd <- regexpr("yr", availSwaps[1])
minVal <- as.numeric(substr(availSwaps[1], txtStart[1]+1, txtEnd[1]-1))
txtStart <- regexpr("_", availSwaps[max])
txtEnd <- regexpr("yr", availSwaps[max])
maxVal <- as.numeric(substr(availSwaps[max], txtStart[1]+1, txtEnd[1]-1))
if(yr<minVal || yr>maxVal){
return(NULL)
}
for(i in 1:max){
txtStart <- regexpr("_", availSwaps[i])
txtEnd <- regexpr("yr", availSwaps[i])
l <- as.numeric(substr(availSwaps[i], txtStart[1]+1, txtEnd[1]-1))
txtStart <- regexpr("_", availSwaps[i+1])
txtEnd <- regexpr("yr", availSwaps[i+1])
h <- as.numeric(substr(availSwaps[i+1], txtStart[1]+1, txtEnd[1]-1))
if(yr == l){
return(c(c(l,l), c(i,i)))
}
else if(yr == h ){
return(c(c(h,h), c(i+l, i+1)))
}
else if(yr > l && yr < h){
return(c(c(l,h),c(i,i+1)))
}
}
}
# This function sorts the columns of a swap rate table by tenor as identified in the
# text of the column header
sortSwapCols<- function(swap){
# Convert the time-series to data.frame (seems that sorting is easier)
df <- data.frame(date=index(swap), coredata(swap))
max <- length(colnames(swap))
sorter <- availTenors(colnames(swap))
sorter <- sorter[order(sorter$tenor),]
for(i in 1:max){
df <- movetolast(df, c(colnames(swap)[sorter$col[i]]))
}
result <- xts(subset(df, select=-date), order.by=df$date)
return(result)
}
# This function is used by the sorting algorithm to move a column to the end
# of a data.frame
movetolast <- function(data, move) {
data[c(setdiff(names(data), move), move)]
}
# This function reads the header of a swap rate table and returns an object
# with elements col and tenor both of which are vectors. They identify the
# known tenors and the column location of them in the swap rate table.
availTenors<- function(header){
max <- length(header)
tenors <- numeric(max)
col <- c(1:max)
for(i in 1:max)
{
txtStart <- regexpr("_", header[i])
txtEnd <- regexpr("yr", header[i])
tenors[i]<- as.numeric(substr(header[i], txtStart[1]+1, txtEnd[1]-1))
}
result <- data.frame(col=col)
result$tenor <- tenors
return(result)
}