Principal Component Analysis on Interest Rate Swaps

In partial satisfaction of the project requirement for the class Statistical Methods in Finance STAT W4290 Fall 2015.

Abstract

This project applies Principal Component Analysis (PCA) to interest rate swaps and shows that the first 3 principal components correspond to yields, slope, and curvature respectively. I first start with vanilla interest rate swaps, and explain how an analysis based purely on single trades are unsatisfactory. I then shift our analysis to curve trades done using pairs of interest rate swaps and show how that is more useful in modeling different parts of the yield curve. I then demonstrate how the principal components from the analysis corresponding to yield, slope and curvature.

Introduction

What are Interest Rate Swaps

A swap is an over-the-counter agreement between two companies to exchange cash flows in the future. The agreement defines the dates when the cash flows are to be paid and the way in which they are to be calculated. Usually, the calculation of the cash flows involve the future value of an interest rate, an exchange rate, or other market variable. … The most common type of swap is a “plain vanilla” interest rate swap. In this swap a company agrees to pay cash flows equal to interest at a predetermined fixed rate on a notional principal for a predetermined number of years. In return, it receives interest at a floating rate on the same notional principal for the same period of time. - Hull, Chapter 7

Mechanics of Interest Rate Swaps

In other words, an interest rate swap (IRS) is an exchange of

  • Floating Leg: a series of coupons paid out at predetermined intervals based on the prevailing interest rate at the beginning of those intervals
  • Fixed Leg: a series of fixed amount coupons paid out at predetermined intervals

Hence, it is a swap of a fixed payment and a floating payment flow. The floating leg and the fixed leg coupons are paid as a percent of a notional amount.

Consider a 5 year interest rate swap initiated on Jan 1 2012 between Google and Baidu. Let’s say that Google prefers a fixed payment. Then, Google will pay 5% on a notional of $1M every 6 months for 5 years. In return, Baidu pays Google the 6-month LIBOR rate on the same principal taken at the start of every 6 months for that 6 months. Hence, on Jan 1 2012, if the 6-month LIBOR rate is 2%, Baidu will pay Google $2000 / 2 = $1000 (since interest rate is annual. We assume away compounding complications for now) for that 6 months. This payment occurs at Jul 1 2012. On Jul 1 2012, if the 6-month LIBOR rate goes to 7%, Baidu will pay Google $7000 / 2 = $3500 on Jan 1 2013. For those 2 periods, Google always pays Baidu $2500 per period.

In real markets, companies rarely negotiate such contracts on their own. Instead, they negotiate with a financial intermediary who charges a spread between the parties. The average of the fixed rates charged to either party is known as the swap rate.

LIBOR and USD Interest Rate

The LIBOR is used as the reference rate for the floating legs in interest rate swaps. LIBOR is the rate that AA rated banks borrow from each other. Over different periods (ranging from say spot to 12 months) the USD LIBOR forward curve is usually above the Treasury Yield curve with mostly the same shape. Hence, the LIBOR forward curve is often used by speculators to speculate on the underlying treasury yield curve (or the ECB rate curve if EUR denominated IRS are used instead).

Interest Rate Swap Yield Rates and Speculation

How IRS can be used for interest rate speculation is straightforward. Say that I am “paying fixed” (hence receiving the floating leg) for a 5 year IRS, colloquially termed a “5y”, that exchanges payments every 6 months. Then, I am essentially betting on the spot 6 month rate, forward 6 month rate in 6 months, forward 6 month rate in 1 year … and the forward 6 month rate in 4.5 years for each of the floating leg payments. If each of those rates go up, I am profitting since I am paying a lower amount (via the fixed rate I locked in). My counterparty would be losing since he’s paying a larger amount than he originally though.

Then, the IRS swap rate can be viewered as the “yield to maturity” of the fixed leg that makes the present value of the entire swap 0, given the forward rates estimated for the floating leg. Or, in other words, the swap rate is the discount rate that makes the present value of the fixed leg equal to the present value of the floating leg when the flaoting leg is discounted by each of the forward rates.

Hence, an IRS is an instrument to bet on the entire forward curve from spot rate to the year of maturity. A “5y” allows us to bet on the entire curve up to the 5 year point.

In terms of directions:

  • If I pay fixed (receive float), when the yield curve goes up, I profit (since I’m paying less than I would have).
  • If I receive fixed (pay float), when the yield curve goes up, I lose (since I’m paying more).

Curve Trades

Then, one can imagine a trade where:

  • I pay fixed (receive float) on one 2 year IRS: I profit from the yield curve going up at the short end
  • I pay float (receive fixed) on one 10 year IRS: I profit from the yield curve going down at the long end

What am I doing here? The fixed payments from now to year 2 cancel each other out, just as the floating payments. Hence, I am betting on the curve from year 2 to year 10. In other words, I am betting on a specific section of the curve, not just from today till the end of the curve. In particular, I am betting that the section of the curve flattens (going up at the short end and going down at the long end). There’s an important caveat to this: I do not hold this trade to maturity. Otherwise, this would cease to be a curve trade. The 2 year would expire and I would be left with one outstanding swap, making this a normal directional bet.

Butterfly Trades

Butterfly trades benefit from differing movements in 3 instruments. Imagine this trade:

  • I pay fixed (receive float) on 2 year IRS: I profit from yield curve going up at the short end
  • I receive float (pay fixed) on 10 year IRS: I profit from the yield curve going down at the middle end
  • I pay fixed (receive float) on 30 year IRS: I profit from the yield curve going up at the long end

I essentially betting on the curvature of the curve. An imaginative trader gave this trade the name, presumably because of the symmetric direction.

PCA on Vanilla IRS

We begin by performing PCA on single IRS rates. Here, I try to find the relationships between different IRS maturities. By doing this, I am essentially finding out similar components in movements of the yield curve between

  • Spot to 1 year
  • Spot to 2 year
  • Spot to 3 year
  • Spot to 4 year
  • Spot to 5 year
  • Spot to 7 year
  • Spot to 10 year
  • Spot to 30 year

An astute reader would realize that we are including the short end of the curve in all the time series, which is a problem we can solve by using curve trade yields instead in a later section.

Data Collection

We collect data for the various time series from the St. Louis FRED using the quantmod package.

1
2
3
4
5
6
7
library(quantmod)
library(downloader)

terms = c(1, 2, 3, 4, 5, 7, 10, 30)
for (term in terms) {
getSymbols(paste('DSWP', term, sep=''), src='FRED')
}

Then I select the data for year to date which leaves me with 250 yields over the past year.

1
2
3
4
5
6
7
8
9
10
11
DSWP1 = DSWP1[!is.na(DSWP1)]
DSWP2 = DSWP2[!is.na(DSWP2)]
DSWP3 = DSWP3[!is.na(DSWP3)]
DSWP4 = DSWP4[!is.na(DSWP4)]
DSWP5 = DSWP5[!is.na(DSWP5)]
DSWP7 = DSWP7[!is.na(DSWP7)]
DSWP10 = DSWP10[!is.na(DSWP10)]
DSWP30 = DSWP30[!is.na(DSWP30)]

rates = cbind(DSWP1, DSWP2, DSWP3, DSWP4, DSWP5, DSWP7, DSWP10, DSWP30)
rates = last(rates, 250)

I can plot the yields for the different IRS maturities. Due to the upward sloping nature of the yield curve, yields of IRS with longer maturities are always higher

1
2
library(ggplot2)
library(reshape)
1
2
3
4
5
dataframe = data.frame(index(rates), rates)
colnames(dataframe) = c('date', 'y1', 'y2', 'y3', 'y4', 'y5', 'y7', 'y10', 'y30')
melted = melt(dataframe, id.vars='date')
plot = ggplot(data=melted, aes(x=date, y=value, color=variable)) + geom_line() + xlab('Date') + ylab('IRS Yield Rate')
plot
svg

svg

Results

I perform PCA on the time series using the covariance matrix of the various time series.

1
2
3
4
pcadata = rates
colnames(pcadata) = c('y1', 'y2', 'y3', 'y4', 'y5', 'y7', 'y10', 'y30')
fit = princomp(pcadata, cor=FALSE, scores=TRUE)
summary(fit)
Importance of components:
                          Comp.1    Comp.2     Comp.3      Comp.4       Comp.5
Standard deviation     0.3437851 0.1209262 0.07618897 0.019044680 0.0051760656
Proportion of Variance 0.8500517 0.1051749 0.04174988 0.002608666 0.0001926951
Cumulative Proportion  0.8500517 0.9552266 0.99697646 0.999585128 0.9997778228
                             Comp.6       Comp.7       Comp.8
Standard deviation     3.715011e-03 3.106083e-03 2.727945e-03
Proportion of Variance 9.926388e-05 6.939006e-05 5.352322e-05
Cumulative Proportion  9.998771e-01 9.999465e-01 1.000000e+00

First observation would be that the data has a high degree of covariance among the time series. This should not be surprising at all, since the short end of the curve was included in all these measurements.

1
2
3
covariance_matrix = cor(pcadata)
require(corrplot)
corrplot(covariance_matrix, method='shade', type='full', shade.col=NA, tl.col='black')
svg

svg

Correspondingly, I should expect the first few principal components to have a high proportion of explained variance.

1
2
library(ggbiplot)
ggbiplot(fit, obs.scale=1, var.scale=1)
svg

svg

1
ggscreeplot(fit)
svg

svg

Indeed, the first principal component accounts for 85.0% of variance, with the second principal component getting 10.5% and the third 4.1%. The first 3 principal components account for, cumulatively, 99.7% of all movements in the data. Hence, in terms of dimensionality reduction, the first 3 principal components are representative of the data.

We can plot the scores of the first 3 components across time.

1
2
3
4
5
6
7
8
scores = fit$scores
scores_dataframe = data.frame(index(rates), scores)
colnames(scores_dataframe) = c('date', 'pc1', 'pc2', 'pc3', 'pc4', 'pc5', 'pc6', 'pc7', 'pc8')
keeps = c('date', 'pc1', 'pc2', 'pc3')
scores_dataframe = scores_dataframe[keeps]
scores_melted = melt(scores_dataframe, id.vars='date')
plot = ggplot(data=scores_melted, aes(x=as.Date(date), y=value, color=variable)) + geom_line() + xlab('Date') + ylab('Principal Component Score')
plot
svg

svg

Interpretation of Results

1
2
loadings = with(fit, unclass(loadings))
loadings
<tr><th scope=row>y1</th><td>-0.05693512</td><td>-0.13640484</td><td> 0.81073244</td><td>-0.16509581</td><td>-0.52010662</td><td>-0.10535402</td><td>-0.10119507</td><td>-0.04192361</td></tr>
<tr><th scope=row>y2</th><td>-0.16669185</td><td>-0.34625166</td><td> 0.43078043</td><td> 0.03625115</td><td> 0.62509086</td><td> 0.43345259</td><td> 0.19830580</td><td> 0.21792835</td></tr>
<tr><th scope=row>y3</th><td>-0.25832243</td><td>-0.41911497</td><td> 0.02728088</td><td> 0.32238835</td><td> 0.26957189</td><td>-0.50912909</td><td>-0.23991017</td><td>-0.51331798</td></tr>
<tr><th scope=row>y4</th><td>-0.3264334</td><td>-0.3807082</td><td>-0.1918120</td><td> 0.3024259</td><td>-0.3000946</td><td>-0.2516847</td><td> 0.2005263</td><td> 0.6531742</td></tr>
<tr><th scope=row>y5</th><td>-0.37780952</td><td>-0.27733220</td><td>-0.24318642</td><td> 0.00362353</td><td>-0.38673513</td><td> 0.55717635</td><td> 0.22904952</td><td>-0.45685974</td></tr>
<tr><th scope=row>y7</th><td>-0.43649941</td><td>-0.03879438</td><td>-0.15120515</td><td>-0.42341285</td><td> 0.05459486</td><td> 0.13634667</td><td>-0.73385353</td><td> 0.21379852</td></tr>
<tr><th scope=row>y10</th><td>-0.4708117505</td><td> 0.2213930338</td><td> 0.0007092894</td><td>-0.5524750953</td><td> 0.1530175357</td><td>-0.3715511987</td><td> 0.5064115711</td><td>-0.0785801970</td></tr>
<tr><th scope=row>y30</th><td>-0.490660311</td><td> 0.643019711</td><td> 0.193912132</td><td> 0.539919671</td><td> 0.008108114</td><td> 0.106656257</td><td>-0.072175546</td><td> 0.003512140</td></tr>
Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 Comp.7 Comp.8

However, I am also interested in the interpretation of the principal components. I hypothesized that the first 3 principal components should correspond to:

  • Principal Component 1: Directional movements in the yield curve. These are movements that shift the entire yield curve up or down.
  • Principal Component 2: Slope movements in the yield curve. These are movements that steepen or flatten (change the first derivative wrt maturity) the entire yield curve.
  • Principal Component 3: Curvature movements in the yield curve. These are movements that change the curvature (or the second derivative wrt maturity) of the entire yield curve.

I find that this interpretation stands. By evaluating the loadings of the principal components, I observe that

  • Principal Component 1 (PC1): All IRS yields are weighted in the same direction (negative). Since the sign of the loadings are arbitrary, this means that PC1 reflects movements that causes IRS of all maturities to move in the same direction. This corresponds to directional movements in the yield curve – if the yield curve goes up, all yields go up be it the short end or the long end and vice versa.
  • Principal Component 2 (PC2): IRS yields on the short end of the curve (from y1 to y7) are weighted negatively and the ones reaching the long end (y10 and y30) are weighted positively. Since the signs are arbitrary, this means that PC2 reflects movements that cause the short end to go in one direction and the long end in the other. This is exactly what slope movements do – if the yield curve steepens, the short end goes down and the long end goes up and vice versa if the yield curve flattens.
  • Principal Component 3 (PC3): IRS yields on the short and long ends of the curve are weighted positively while the ones in the middle are weighted negatively. Since the signs are arbitrary, this means that PC3 reflects movements that cause the short and long end to go in one direction, and the middle to go in the other. This is exactly what curvature movements do – if the yield curve increases in curvature, the short and long end goes down while the middle goes up.

Hence PC1 can be interpreted as directional movements, PC2 as slope movements, and PC3 as curvature movements.

Shortcoming

I find this analysis lacking in one striking way: we are unable to isolate portions of the curve. For example, the time series for y30 includes movements from the spot all the way to year 30. In other words, it includes movements of the y1, y2, y3 etc. In a similar way, the y3 includes movements of y1. Hence, we are unable to isolate movements in the “long end only” and are instead forced to make conclusions about yield rates “from the short end to the long end” or on the “short end” only.

This could be done instead by measuring yields for curve trades. This is what I will do in the next section.

PCA on Curve Trade Rates

Review on Curve Trades and Butterfly Trades

Refer to the introduction for a complete breakdown of curve trades and butterfly trades. As a reminder,

  • Curve trades are bets on the slope of a specific section of the curve
  • Butterflies are bets on the curvature of two specific sections of the curve.

Data Collection

I employ most of the same data collection methods as in the previous section.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
library(quantmod)
library(downloader)

terms = c(1, 2, 3, 4, 5, 7, 10, 30)
for (term in terms) {
getSymbols(paste('DSWP', term, sep=''), src='FRED')
}

DSWP1 = DSWP1[!is.na(DSWP1)]
DSWP2 = DSWP2[!is.na(DSWP2)]
DSWP3 = DSWP3[!is.na(DSWP3)]
DSWP4 = DSWP4[!is.na(DSWP4)]
DSWP5 = DSWP5[!is.na(DSWP5)]
DSWP7 = DSWP7[!is.na(DSWP7)]
DSWP10 = DSWP10[!is.na(DSWP10)]
DSWP30 = DSWP30[!is.na(DSWP30)]

The yield of a curve trade is calculated as follow:

\[C = S\_{0, t\_2} - S\_{0, t\_1}\]

where \(C\) is the curve trade rate, \(S\_{0,t\_2}\) is the swap rate for an IRS of maturity \(t\_2\) and \(S\_{0, t\_2}\) is the swap rate for an IRS of maturity \(t\_1\). Intuitively, we can think of this the forward rate from \(t\_1\) to \(t\_2\) and indeed it is. We select the following “tenors” (sections of the yield curve) to represent the whole yield curve. They are quoted as “XsYs” where we pay fix for X years and receive fix for Y years of IRS.

  • 2s1s
  • 3s1s
  • 4s1s
  • 5s1s
  • 7s1s
  • 10s2s
  • 10s5s
  • 30s10s

And select only the rates for the year to date.

1
2
3
4
5
6
7
8
9
10
11
12
13
# 2-1, 3-1, 4-1, 5-1, 7-1, 10-2, 10-5, 30-10
curve2_1 = DSWP2 - DSWP1
curve3_1 = DSWP3 - DSWP1
curve4_1 = DSWP4 - DSWP1
curve5_1 = DSWP5 - DSWP1
curve7_1 = DSWP7 - DSWP1
curve10_2 = DSWP10 - DSWP2
curve10_5 = DSWP10 - DSWP5
curve30_10 = DSWP30 - DSWP10

rates = cbind(curve2_1, curve3_1, curve4_1, curve5_1, curve7_1, curve10_2, curve10_5, curve30_10)
rates = last(rates, 250)
colnames(rates) = c('curve2y1y', 'curve3y1y', 'curve4y1y', 'curve5y1y', 'curve7y1y', 'curve10y2y', 'curve10y5y', 'curve30y10y')

As for the vanilla yields, I can plot a time series of the different curve rates. However, this chart isn’t very telling, since I am measuring different ends of the curve.

1
2
3
4
5
6
7
library(ggplot2)
library(reshape)
dataframe = data.frame(index(rates), rates)
colnames(dataframe) = c('date', 'curve2y1y', 'curve3y1y', 'curve4y1y', 'curve5y1y', 'curve7y1y', 'curve10y2y', 'curve10y5y', 'curve30y10y')
melted = melt(dataframe, id.vars='date')
plot = ggplot(data=melted, aes(x=as.Date(date), y=value, color=variable)) + geom_line()
plot
svg

svg

Results

I perform PCA on the series as well using the covariance matrix of the time series.

1
2
3
4
pcadata = rates
colnames(pcadata) = c('curve2y1y', 'curve3y1y', 'curve4y1y', 'curve5y1y', 'curve7y1y', 'curve10y2y', 'curve10y5y', 'curve30y10y')
fit = princomp(pcadata, cor=FALSE, scores=TRUE)
summary(fit)
Importance of components:
                          Comp.1    Comp.2      Comp.3      Comp.4       Comp.5
Standard deviation     0.2775099 0.1294640 0.028713618 0.011572150 0.0048336969
Proportion of Variance 0.8126072 0.1768567 0.008699608 0.001413032 0.0002465373
Cumulative Proportion  0.8126072 0.9894639 0.998163497 0.999576529 0.9998230661
                             Comp.6       Comp.7 Comp.8
Standard deviation     2.971792e-03 2.817212e-03      0
Proportion of Variance 9.318811e-05 8.374576e-05      0
Cumulative Proportion  9.999163e-01 1.000000e+00      1

Again, the series has a high degree of covariance among the time series. This again should not be surprising, since they are all yield rates after all (though of different tenors) hence should be driven by the same macroeconomic factors.

1
2
3
covariance_matrix = cor(pcadata)
require(corrplot)
corrplot(covariance_matrix, method='shade', type='full', shade.col=NA, tl.col='black')
svg

svg

However, what should be interesting to note is that some time series now have somewhat negative covariance with others. These happen to be between the far short end of the curve and the far long end (eg. 2s1s vs 30s10s). This makes sense, since the far end usually doesn’t move as much as the short end, and sometimes in the opposite direction (slope).

1
2
library(ggbiplot)
ggbiplot(fit, obs.scale=1, var.scale=1)
svg

svg

1
ggscreeplot(fit)
svg

svg

The first principal component accounts for 81.3% of variance, with the second principal component getting 17.7% and the third 0.87%. The first 3 principal components account for, cumulatively, 99.8% of all movements in the data. Hence, in terms of dimensionality reduction, the first 3 principal components are representative of the data.

We can plot the scores of the first 3 components across time.

1
2
3
4
5
6
7
8
scores = fit$scores
scores_dataframe = data.frame(index(rates), scores)
colnames(scores_dataframe) = c('date', 'pc1', 'pc2', 'pc3', 'pc4', 'pc5', 'pc6', 'pc7', 'pc8')
keeps = c('date', 'pc1', 'pc2', 'pc3')
scores_dataframe = scores_dataframe[keeps]
scores_melted = melt(scores_dataframe, id.vars='date')
plot = ggplot(data=scores_melted, aes(x=as.Date(date), y=value, color=variable)) + geom_line() + xlab('Date') + ylab('Principal Component Score')
plot
svg

svg

Interpretation of Results

1
2
loadings = with(fit, unclass(loadings))
loadings
<tr><th scope=row>curve2y1y</th><td>-0.171227734</td><td>-0.195448923</td><td> 0.004706401</td><td> 0.667761277</td><td> 0.255984666</td><td>-0.361642751</td><td>-0.200598804</td><td>-0.500000000</td></tr>
<tr><th scope=row>curve3y1y</th><td>-3.263336e-01</td><td>-2.874607e-01</td><td> 1.814160e-01</td><td> 2.487582e-01</td><td>-5.310257e-01</td><td> 5.745447e-01</td><td>-3.224754e-01</td><td> 3.785444e-13</td></tr>
<tr><th scope=row>curve4y1y</th><td>-4.295687e-01</td><td>-2.585274e-01</td><td> 2.043599e-01</td><td>-1.301226e-01</td><td>-4.286356e-01</td><td>-4.860891e-01</td><td> 5.195463e-01</td><td>-6.752862e-13</td></tr>
<tr><th scope=row>curve5y1y</th><td>-0.49071115</td><td>-0.14814235</td><td> 0.05408491</td><td>-0.21436777</td><td> 0.33797228</td><td>-0.25143465</td><td>-0.51081605</td><td> 0.50000000</td></tr>
<tr><th scope=row>curve7y1y</th><td>-5.385355e-01</td><td> 1.119178e-01</td><td>-1.587484e-01</td><td> 1.153416e-01</td><td> 4.549395e-01</td><td> 4.507466e-01</td><td> 4.988054e-01</td><td>-3.640491e-13</td></tr>
<tr><th scope=row>curve10y2y</th><td>-0.38081672</td><td> 0.58044994</td><td>-0.17867132</td><td>-0.33936332</td><td>-0.20605357</td><td>-0.06584042</td><td>-0.27234639</td><td>-0.50000000</td></tr>
<tr><th scope=row>curve10y5y</th><td>-0.06133330</td><td> 0.53314337</td><td>-0.22804983</td><td> 0.54276573</td><td>-0.28804119</td><td>-0.17604852</td><td> 0.03787086</td><td> 0.50000000</td></tr>
<tr><th scope=row>curve30y10y</th><td>7.557077e-03</td><td>3.958458e-01</td><td>9.018019e-01</td><td>7.913918e-02</td><td>1.487753e-01</td><td>3.332223e-02</td><td>2.224416e-02</td><td>5.464379e-16</td></tr>
Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 Comp.7 Comp.8

Earlier I confirmed my hypothesis of the interpretation of the principal components, namely that:

  • Principal Component 1: Directional movements in the yield curve. These are movements that shift the entire yield curve up or down.
  • Principal Component 2: Slope movements in the yield curve. These are movements that steepen or flatten (change the first derivative wrt maturity) the entire yield curve.
  • Principal Component 3: Curvature movements in the yield curve. These are movements that change the curvature (or the second derivative wrt maturity) of the entire yield curve.

Recall that the major shortcoming of that analysis was that we included the short end of the curve in all our time series and was unable to isolate the middle portion fo the curve individually. We are able to do that now. We see that the same interpretation of the principal components hold.

  • Principal Component 1: Almost all the loadings are negative (with a very small positive for the 30s10s which we can ignore since it is two orders of magnitude smaller than the other loadings and would have been suppressed by R’s output if not for the fact that I forced the output to be present). Since the signs of loadings are arbitrary in PCA, we can conclude that in PC1 type movement, all sections of the yield curve move in the same direction. This corresponds to directional movements in the yield curve where the entire curve shifts up or down.
  • Principal Component 2: The short end of the curve (2s1s, 3s1s, 4s1s, 5s1s) are negative while the middle (7s1s, 10s2s, 10s5s) are positive as far the far end (30s10s). Again, signs are arbitrary in PCA loadings. This means that in PC2 type movements, the far and middle end moves in opposite direction as the short end. This corresponds well with the slope interpretation.
  • Principal Component 3: The short end and the long end moves in the same direction while the middle end moves in the opposite direction. This again corresponds well with the interpretation of curvature movements.

This relationship is summarized in the plot below.

1
2
3
4
5
6
7
8
loadings = with(fit, unclass(loadings))
loadings_dataframe = data.frame(index(loadings), loadings)
colnames(loadings_dataframe) = c('tenor', 'pc1', 'pc2', 'pc3', 'pc4', 'pc5', 'pc6', 'pc7', 'pc8')
keeps = c('tenor', 'pc1', 'pc2', 'pc3')
loadings_dataframe = loadings_dataframe[keeps]
loadings_melted = melt(loadings_dataframe, id.vars='tenor')
plot = ggplot() + geom_line(data=loadings_melted, aes(x=tenor, y=value, color=variable)) + scale_x_discrete(labels=c('2y1y', '3y1y', '4y1y', '5y1y', '7y1y', '10y2y', '10y5y', '30y10y')) + xlab('Tenor') + ylab('Loading of First 3 PCs')
plot
svg

svg

To confirm this further, I can plot each of the principal components with yield movements.

First Principal Component

I plot both the scores of the first principal component and the swap rate for a 10 year IRS and find that the correlation indeed holds up, and this shows that my interpretation of the principal component holds.

1
2
3
4
5
6
7
8
9
source('graphing_utility.R')
first_factor_10y = data.frame(rownames(scores), scores_dataframe$pc1, last(DSWP10, 250))
colnames(first_factor_10y) = c('date', 'pc1', 'y10')
first_factor_10y$pc1 = -first_factor_10y$pc1

p1 = ggplot() + geom_line(data=first_factor_10y, aes(x=as.Date(date), y=pc1), colour=gg_color_hue(1)) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) + xlab('Date') + ylab('left/red: PC1\nright/blue: 10y Yield')
p2 = ggplot() + geom_line(data=first_factor_10y, aes(x=as.Date(date), y=y10), colour=gg_color_hue(2)) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) + theme(panel.background = element_rect(fill = NA))
g = stack_plot(p1, p2)
grid.draw(g)
svg

svg

Second Principal Component

To see that the second principal component holds, I use a common curve trade: 10s2s. To see why this represents slope, one can consider the bets I made:

  • Receive fixed for 10 year IRS: if rates go up on the long end, I lose
  • Pay fixed for 2 year IRS: if rates go up in the short end, I gain

Hence, I’m making a bet on the slope of the curve. In fact, this bet is a steepener (I profit when the curve steepens). The yield rate for this trade is constructed as:

\[C\_{2, 10} = Y\_{0, 10} - Y\_{0, 2}\]

where \(C\) is the rate for curve trade, \(Y\) is the IRS yield.

I again find a high level of correlation in the movements.

1
2
3
4
5
6
7
second_factor_10y2y = data.frame(rownames(scores), scores_dataframe$pc2, last(curve10_2, 250))
colnames(second_factor_10y2y) = c('date', 'pc2', 'curve10y2y')

p1 = ggplot() + geom_line(data=second_factor_10y2y, aes(x=as.Date(date), y=pc2), colour=gg_color_hue(1)) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) + xlab('Date') + ylab('left/red: PC2\nright/blue: 10s2s Yield')
p2 = ggplot() + geom_line(data=second_factor_10y2y, aes(x=as.Date(date), y=curve10y2y), colour=gg_color_hue(2)) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) + theme(panel.background = element_rect(fill = NA))
g = stack_plot(p1, p2)
grid.draw(g)
svg

svg

Third Principal Component

To see that the third principal component holds, I use another common trade: a butterfly of 2s10s30s. This is essentially:

  • Receive fixed on 2 year IRS: I lose if rates go up on the short end
  • Pay fixed on two 10 year IRS: I gain if rates go up in the middle end
  • Receive fixed on 30 year IRS: I lose if rates go up in the far end

The yield can be constructed as

\[C\_{2,10,30} = \left(Y\_{0,10} - Y\_{0,2}\right) - \left(Y\_{0,30} - Y\_{0,10}\right)\]

Essentially, I am long two 10 year IRS, and short one 2 year IRS and one 30 year IRS. This can be seen as a bet on curvature. If the rates go up in the middle and go down in the two ends, I profit. Thus this is a curvature bet.

Again there is a high degree of correlation between this and the yields of a butterfly, confirming my interpretation of the third principal component.

1
2
3
4
5
6
7
8
9
butterfly2_10_30 = (DSWP10 - DSWP5) - (DSWP30 - DSWP10)
third_factor_butterfly = data.frame(rownames(scores), scores_dataframe$pc3, last(butterfly2_10_30, 250))
colnames(third_factor_butterfly) = c('date', 'pc3', 'butterfly2y10y30y')
third_factor_butterfly$pc3 = -third_factor_butterfly$pc3

p1 = ggplot() + geom_line(data=third_factor_butterfly, aes(x=as.Date(date), y=pc3), colour=gg_color_hue(1)) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) + xlab('Date') + ylab('left/red: PC3, right/blue: 2s10s30s Yield')
p2 = ggplot() + geom_line(data=third_factor_butterfly, aes(x=as.Date(date), y=butterfly2y10y30y), colour=gg_color_hue(2)) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) + theme(panel.background = element_rect(fill = NA))
g = stack_plot(p1, p2)
grid.draw(g)
svg

svg