Skip to content
All library documents

Estimating Nelson–Siegel Yield-Curve Factors with Fixed Decay Parameters

Article Quant Q&A · Author: Jojo

Summary

The document presents a bond-pricing question about estimating Nelson–Siegel–Svensson parameters through nonlinear optimization. The author reports extremely small discount factors and shares a calculation that converts payment dates into year fractions before applying an exponential yield formula. The response notes that the cited procedure concerns the Nelson–Siegel model rather than its Svensson extension, so the model specification should be checked when adapting it.

As a diagnostic, the response proposes fixing the decay parameter and estimating the remaining Nelson–Siegel factors by ordinary least squares. It includes an example that constructs factor loadings across maturities and estimates factors from observed yields, with plots comparing estimated factors to yield-based proxies. This provides a way to check whether the factor-loading setup behaves plausibly before relying on nonlinear optimization. The answer does not directly isolate the source of the tiny discount factors in the original code, and its example is for Nelson–Siegel, not a complete Svensson solution or a validated fix for the reported calculation.

Key ideas

  • Check that the cited estimation procedure matches the Nelson–Siegel or Svensson model being implemented.
  • Fixing the decay parameter allows the remaining Nelson–Siegel factors to be estimated by ordinary least squares.
  • Factor loadings can be constructed across maturities and compared with yield-based proxies.
  • The proposed diagnostic example does not directly identify the discount-factor error in the supplied code.

Tags

Full text
# Wrong discount factors when finding Nelson Siegel Svensson model parameters


# Wrong discount factors when finding Nelson Siegel Svensson model parameters












I am trying to determine the parameters for the Nelson Siegel Svensson model and am solving a Non- Linear Optimization problem to do this. Some of the code I have written is below and this is where my problem lies as I find that some of my discount factors are of the order 10^-30 and I don't know where I am going wrong. I am using the procedure presented in Pg. 5 of this paper. `df` is a dataframe which consists of observed bond data. All coupon payments will take place in 6 month intervals.

```
def calculatingprices(df, beta_0, beta_1, beta_2, beta_3, tau_1, tau_2):
    #I am just creating a dict 'd' which will have the months to maturity for each bond as keys and 
    #a list as its values. The lists will contain the months to maturity - payments associated 
    #with the bond 
    months_to_maturity_matrix = df['months_to_maturity'].values
    number_of_rows = df.shape[0]
    theoretical_price_for_bonds = np.zeros((number_of_rows, 1))
    d = {}
    for months_to_maturity_bond in months_to_maturity_matrix: 
        l = []
        #Adding the difference in dates to the dict d. Differences, will just be multiples of 6 upto 360 (i.e. the 30 year bond)
        for i in range(6, 360, 6):                  
            if i <= months_to_maturity_bond:
                l.append(months_to_maturity_bond - i)
            else:
                l.append(0)    
                d[months_to_maturity_bond] = l                  
                d_ordered = collections.OrderedDict(sorted(d.items()))

#I then use the formula for the discount factor as follows
for x, value in np.ndenumerate(months_to_maturity_matrix):
    diff_between_maturity_and_payments_array = np.array(d_ordered[value])
    years_diff_between_maturity_and_payments_array = diff_between_maturity_and_payments_array/12
    theoretical_discount_factor = np.exp(-years_diff_between_maturity_and_payments_array * (beta_0 + ((beta_1 + beta_2)*((1-np.exp(-years_diff_between_maturity_and_payments_array/tau_1))/years_diff_between_maturity_and_payments_array/tau_1))-(beta_2*np.exp(-years_diff_between_maturity_and_payments_array/tau_1)) + (beta_3*(((1-np.exp(-years_diff_between_maturity_and_payments_array/tau_2))/years_diff_between_maturity_and_payments_array/tau_2) -np.exp(-years_diff_between_maturity_and_payments_array/tau_2)))))
```

Edit: Please let me know if I need to provide more information. I realize in the paper I have cited they just consider the Nelson Siegel model, however in the code above I have accounted for the extra parameters associated with the Nelson Siegel Svensson model.

Thank You

## Answer by phdstudent (score 1)

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

if I read it correctly the paper you mention is about Nelson-Siegel and not Nelson-Siegel Svensson. The easier way to cross check it is to simply start by fixing theta and estimate the remaining parameters by OLS. If that works, then the optimization algorithm should work similarly. Below I attach a simple matlab code that does it and that you can adapt for your purposes:

```
    % Define yield structure used in the estimation
maturity = [ 1/12; 2/12; 3/12; 4/12; 5/12; 6/12; 9/12; 12/12; 18/12; 24/12;];

%************************    PARAMETERS     *****************************

yn = size(maturity,1);  % number of yields
lamda=0.3;         % as in Diebold, Rudebusch and Aruoba(2003)
Roll=1;              % Rolling estimation (1) or recursive estimation (0).
length = size(xdata,1); % data length

%% Ex 2-1).Calculate and plot Nelson-Siegel factor loadings, given lamda.
mat = 1:1:120; 
pNS1 = ones(1,120); 
pNS2 = zeros(1,120); 
pNS3 = zeros(1,120);
for imat = 1:120;
    pNS2(imat) = exp(-(imat/12)/lamda);
    pNS3(imat) = ((imat/12)/lamda)*exp(-(imat/12)/lamda);
end;

CCw = [pNS1; pNS2; pNS3]'; %Coefficient matrix for all maturity 1:120
CC = CCw((maturity')*12,:);

% Extract (Estimate) Nelson-Siegel factors for yields by OLS.
yield= xdata(1:length,1:10);  
NS = ((CC'*CC)\eye(size(CC,2)))*CC'*yield';

   % Plot the NS factors and their empirical proxies
   tm = dates;
   tm = tm';
   figure(2)
   subplot(3,1,1)
     ymin = min(NS(1,:))-0.05; ymax = max(NS(1,:))+0.05;
     plot(tm,NS(1,:),'r-', tm, yield(:,10), 'b:');
     h = legend('NS 1st factor','2year yield',1);
     axis([tm(1),tm(length),ymin,ymax])  
     %xlabel('Time')
     ylabel('%')
     %title('');

   subplot(3,1,2)
     plot(tm,NS(2,:),'r-', tm, -(yield(:,10)-yield(:,1)), 'b:');
     h = legend('NS 2nd factor',' -(2year - 1month)',1);
     axis([tm(1),tm(length),min(NS(2,:))-0.05,max(NS(2,:))+0.05])
     %xlabel('Time')
     ylabel('%')
     %title('');

   subplot(3,1,3)
     plot(tm,NS(3,:),'r-', tm, 2*yield(:,3)-(yield(:,1)+yield(:,10)), 'b:');
     h = legend('NS 3rd factor','2*3month - (1month + 24month)',1);
      axis([tm(1),tm(length),min(NS(2,:))-0.05,max(NS(2,:))+0.05])
     xlabel('Time')
     ylabel('%')
```

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.