Skip to content
All library documents

OLS Initialization and Data Issues in Nelson–Siegel–Svensson Fitting

Article Quant Q&A · Author: Jojo

Summary

The document describes an attempt to initialize nonlinear optimization for the Nelson–Siegel–Svensson yield curve model using ordinary least squares. The author derives spot rates from par bond coupons, constructs a coefficient matrix from maturities and decay parameters, and asks whether errors arise from the matrix setup or the input data. The shown implementation appears to build a separate coefficient matrix while later fitting with the original array, making the matrix construction a relevant point to inspect.

The included answer argues that yields, rather than coupon values, should be used for estimation, since the parametric curve is intended to represent yields. It also notes that the decay parameters should be constrained to avoid multicollinearity; equal decay parameters collapse the model’s two curvature components into the simpler Nelson–Siegel form. The exchange is brief and does not provide a corrected implementation or validate the spot-rate bootstrapping procedure.

Key ideas

  • OLS can provide starting parameters for nonlinear Nelson–Siegel–Svensson calibration.
  • The document questions both the coefficient matrix construction and the data used in the regression.
  • The included answer recommends fitting yields rather than treating bond coupons as yields.
  • Decay parameters should be constrained to limit multicollinearity.
  • Equal decay parameters make the two curvature terms coincide, reducing the model to Nelson–Siegel.

Tags

Full text
# Issue with OLS Regression for Nelson Siegel Svensson parameters


# Issue with OLS Regression for Nelson Siegel Svensson parameters












I have been working on getting input parameters to the Non-Linear Optimization which gives the Nelson Siegel Svensson model parameters and am carrying out the OLS regression as described in this answer. However, the input parameters obtained from the OLS are too far off the actual parameters, which I checked against some parameters I actually do have. I am using the equations shown in 'Figure 5' on Page 12 of this paper, and obtain the yield data, by choosing Par Bonds and using their coupons as Par Yields to bootstrap from to get the Spot Rates, which appears to be an okay method based on Page 3 of this paper. The code that I use is below, where I've just implemented the formula in the previous link and have carried out the regression in Python. My query is if there is an issue with the way I set `matrix_of_params` or if it could be to do with the data in `df` itself.

I run the function above for different values of `tau_1` and `tau_2`. I then have a function to get the `params` associated with the lowest `residuals`, which I am positive is correct.

```
#df is a Dataframe containing all the data about the Bonds
def obtainingparams(self, df, tau_1, tau_2, residuals):
    values = []
    face_values = df['FACE_VALUE'].values #Writing face values to an array
    yields = (df['coupon'].values) #COUPON = YTM for Par Bonds         
    spot_rate = np.zeros((yields.shape[0]))   

    #Calculating Spot Rates
    for x, value in np.ndenumerate(yields):
        index = x[0]
        if index == 0:
            spot_rate[index] = (yields[index]/face_values[index]) * 100

        else:    
            adding_negatives = 0
            if index < spot_rate.shape[0]:
                for i in range (0, index, 1):
                    adding_negatives = adding_negatives + (value*face_values[index]/200)/np.power((1+(spot_rate[i]/200)),i+1)
                    term_1 = face_values[index] - adding_negatives 
                    spot_rate[index] = (2 * ((np.power(((((face_values[index] + ((value*face_values[index]/200)))/term_1))),1/(index+1)))-1))*100

    matrix_of_params = np.empty(shape=[1, 4])
    months_to_maturity_matrix = df.months_to_maturity.values #Writing months to maturity to an array

    #Populating the Matrix of Parameter Coefficients
    count = 0
    for x, value in np.ndenumerate(months_to_maturity_matrix):
        if count < months_to_maturity_matrix.shape[0]:
            months_to_maturity = months_to_maturity_matrix[count]
            years_to_maturity = months_to_maturity/12.0  

            #Applying the equation in the link
            newrow = [1, ((1-np.exp(-years_to_maturity/tau_1))/(years_to_maturity/tau_1)), ((1-np.exp(-years_to_maturity/tau_1))/(years_to_maturity/tau_1))-(np.exp(-years_to_maturity/tau_1)), ((((1-np.exp(-years_to_maturity/tau_2))/(years_to_maturity/tau_2))))-(np.exp(-years_to_maturity/tau_2))] 
            count = count + 1

            #Just adding the new row to the matrix of parameter coefficients
            matrix_of_param_coefficients = np.vstack([matrix_of_params, newrow]) 

    #Carrying out OLS Regression                
    params = np.linalg.lstsq(matrix_of_params,spot_rate)[0] 
    residuals = np.sqrt(((spot_rate - matrix_of_params.dot(params))**2).sum())  

   #To keep track of which params are associated with which residuals
   values.append((tau_1, tau_2, residuals, params)) 
   return values
```

Thank You

## Answer by Tulio Carnelossi (score 1)

https://quant.stackexchange.com/a/19171

First of all you need to get the YTM. The parametric model has an economic interpretation so coupons would mess the estimation and the interpretation of your model. Because of that the values of both taus must be restricted so you can avoid multicoliniarity. If they are the same you are saying that both curvatures are on the same tenor but then your model is reduced to the Nelson Siegel.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.