Python : Nelson-Siegel or Svensson model with fixed lambda

This post implements the period-by-period OLS estimation of the Nelson-Siegel and Svensson yield curve model with fixed lambda.




Nelson-Siegel or Svensson model with fixed lambda



Nelson-Siegel (NS) model is of the following yield function with positive dacay parameter (\(\lambda > 0 \)).
\[\begin{align} y(\tau) = \beta_1 + \beta_2 \left( \frac{1-e^{- \tau \lambda}}{\tau \lambda}\right) + \beta_3 \left(\frac{1-e^{- \tau \lambda}}{\tau \lambda}-e^{- \tau \lambda}\right) \end{align}\] Svensson (SV) model has the following yield function with positive dacay parameters (\( \lambda_1 > \lambda_2 > 0 \)).
\[\begin{align} y(\tau) &= \beta_1 + \beta_2 \left( \frac{1-e^{- \tau \lambda_1 }}{\tau \lambda_1 }\right) \\ &+ \beta_3 \left(\frac{1-e^{- \tau \lambda_1 }}{\tau \lambda_1 }-e^{- \tau \lambda_1 }\right) + \beta_4 \left(\frac{1-e^{- \tau \lambda_2 }}{\tau \lambda_2 }-e^{- \tau \lambda_2 }\right) \end{align}\]
Here, \(\tau\) is a maturity and \(y(\tau)\) is a continuously compounded spot rate with \(\tau\) maturity. \(\beta_1, \beta_2, \beta_3\) or \(\beta_4\) are coefficient parameters.

Given decay parameter(s), NS or SV model can be estimated easily by using OLS.


Library and user-defined functions


I implemented two functions: f_get_ns_factor_fixed_lambda() for the period-by-period OLS and f_draw_ns_factor() for drawing yield factors.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
%matplotlib inline
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
 
# function for estimate NS or SV yield factors
# by using period by period OLS
def f_get_ns_factor_fixed_lambda(vm, mrate, vla, type = 'ns'):
    nrow, ncol = mrate.shape
    out_fac = list()
    
    if type == 'ns':
        X = np.vstack([np.ones(len(vm)),
             (1-np.exp(-vla[0]*vm))/(vla[0]*vm), 
             (1-np.exp(-vla[0]*vm))/(vla[0]*vm) - np.exp(-vla[0]*vm)])
    elif type == 'sv':
        X = np.vstack([np.ones(len(vm)),
             (1-np.exp(-vla[0]*vm))/(vla[0]*vm), 
             (1-np.exp(-vla[0]*vm))/(vla[0]*vm) - np.exp(-vla[0]*vm),
             (1-np.exp(-vla[1]*vm))/(vla[1]*vm) - np.exp(-vla[1]*vm)])
            
    for t in range(nrow):
        model = LinearRegression(fit_intercept=False)
        model.fit(np.transpose(X), mrate[t])
        out_fac.append(model.coef_)
        
    return np.array(out_fac)
 
# function for drawing a graph of yield factors
def f_draw_ns_factor(y, ylegend, x_ticks_no, x_ticks_label):
    fig, ax = plt.subplots(1,1,figsize=(83)) 
    # fig.set_figwidth(8)
    ax.plot(y)
    ax.set_xlabel('Year')
    ax.set_ylabel('Yield (%)')
    ax.legend(ylegend)
    # Set number of ticks for x-axis
    ax.set_xticks(x_ticks_no)
    # Set ticks labels for x-axis
    ax.set_xticklabels(x_ticks_label, rotation='horizontal'
                       fontsize=11)
    plt.show()
 
cs



Estimated yield factors and their movements


Using the above functions, we can easily extract NS yield factors over time.

# maturities in months
vm =  np.array([3,6,9,12,15,18,21,24,30,36,48,60,72,84,96,108,120])
nmat = len(vm)
 
# US Yield Curve 1972 - 2000
url = 'https://www.dropbox.com/s/inpnlugzkddp42q/bonds.csv?dl=1' 
df = pd.read_csv(url,sep=';',index_col=0); df
 
# Nelson-Siegel model with a fixed lambda  
vla = [0.0609]
out_ns_fac = f_get_ns_factor_fixed_lambda(vm, rates, vla)
pd.DataFrame(out_ns_fac)
 
# Svensson model with two fixed lambda
vla = [0.070.02]
out_sv_fac = f_get_ns_factor_fixed_lambda(vm, rates, vla, type='sv')
pd.DataFrame(out_sv_fac)
 
# Graph
x_ticks_no = np.arange(36,nrow,36)
x_ticks_label = np.array([x[:4for x in df.index])[x_ticks_no]
 
f_draw_ns_factor(out_ns_fac, ['L''S''C'], 
                 x_ticks_no, x_ticks_label)
f_draw_ns_factor(out_sv_fac, ['L''S''C1''C2'], 
                 x_ticks_no, x_ticks_label)
 
cs


For example, NS 3 factors are estimated as follows.

           0            1           2
0    6.532632    -3.450285     0.500544
1    6.715889    -3.435264    -0.410948
2    6.160223    -2.541603     2.346223
3    6.468915    -3.048362     0.783350
4    6.553063    -2.800586    -0.252667
...    ...    ...    ...
343    5.548216     0.709016     0.665006
344    5.737054     0.568882    -0.243924
345    5.698509     0.774611    -0.634403
346    5.426825     0.919011    -0.970996
347    5.294994     0.720964    -1.854887
 
cs

Using the drawing function, 3 or 4 yield factors are depicted in the following two graphs sequentially.
Python : Nelson-Siegel or Svensson model with fixed lambda

As can be seen in the above figures, in general, 4 factors are more noisy than 3 factors since the period-by-period OLS does not impose a temporal restriction on their dynamics. NS models with a higher number of yield factors tend to generate higher volatility of estimated yield factors with jumps and lumpy behaviors.


Concluding Remarks


This post implemented Nelson-Siegel or Svensson model with fixed lambda since it's never around when we need it. \(\blacksquare\)


No comments:

Post a Comment