May cohort is now open: How to secure your spot:

How to do interest rate analysis with multi-factor models

Interest rates are the driving force behind the economy.

They influence everything from the cost of purchasing a home to a company’s decision on capital investments.

Quants model how interest rate changes impact portfolios using principal component analysis (PCA).

(They also do it for stock portfolios!)

In today’s newsletter, we’ll look at how interest rate movements can be broken down.

We’ll do it to understand the yield curve’s responsiveness to factors.

Let’s dive in!

How to do multi-factor interest rate analysis

Simulating changes in the yield curve is important for managing risk and optimizing portfolios.

Especially for interest rate-sensitive investments.

Quants use PCA and eigenvalue decomposition for this purpose.

The result is four factors that shape interest rate changes: wiggle, flex, twist, and shift.

These factors reflect the sensitivities across different maturities to a 1 basis point change.

For example, if we multiply the factor representing the shift of the yield curve by 1 basis point, we would expect the entire yield curve to shift.

By reading today’s newsletter, you’ll be able to use Python to calculate these sensitivities and simulate yield curve adjustments.

Imports and set up

We’ll import pandas and OpenBB for data and NumPy for the math.

import numpy as np
import pandas as pd
from openbb import obb

Use OpenBB to download US treasury rates across 10 maturities (1 month through 30 years). Since OpenBB returns a DataFrame, it’s easy to compute the covariance matrix.

rates = (
    obb
    .fixedincome
    .government
    .treasury_rates(
        start_date="2020-01-01"
    )
    .to_df()
    .dropna()
    .div(100)
)

C = rates.cov()

Decompose the eigenvectors

First, let’s use NumPy to get the eigenvalues of the covariance matrix so we can decompose it into the principal components.

eigenvalues, eigenvectors = np.linalg.eig(C)
lambda_sqrt = np.sqrt(eigenvalues)
eigv_decom = np.diag(lambda_sqrt)

This step calculates the eigenvalues and eigenvectors of the covariance matrix where eigenvalues represent the variance explained by each eigenvector. The square root of the eigenvalues is used to construct a diagonal matrix which represents the standard deviation (volatility) associated with each principal component.

Finally, we’ll grab the first four principal components that represent movements in the yield curve. These components are typically associated with the wiggle, flex, twist, and shift of the curve.

B = eigv_decom @ eigenvectors.T
B = pd.DataFrame(
    data=B[:4] * 100,
    index=["wiggle", "flex", "twist", "shift"],
    columns=rates.columns
)

This code creates a DataFrame with the first four components and scales each one by 100 to represent a basis point change.

How to do multi-factor interest rate analysis. Simulating changes in the yield curve is important for managing risk and optimizing portfolios.

Each row of this DataFrame describes the sensitivity to changes in the factor at each maturity.

Shock the rate curve

Now that we have the sensitivities we can simulate changes to the yield curve. We do this by multiplying a variable by a row in B.

For example, if we multiply the fourth row representing the shift of the yield curve by 1, we would anticipate the entire yield curve to shift by the magnitude of the bottom row. This means the 1 month yield would decrease by 10 basis points, the 3 month would increase by 4 basis points, and so on along the curve.

We can simulate changes in the yield curve by multiplying each row by a normally distributed variable.

random_shocks = np.random.normal(0, 1, size=(4,))
random_shocks @ B

The output of this code is a Pandas Series with basis point changes in each of the points on the yield curve.

Next steps

This analysis presents a framework to assess changes in the yield curve. As a next step, experiment with shocking only portions of the yield curve. Try running hundreds or thousands of simulations and plotting the results.