Skip to content
All library documents

Estimating Nelson–Siegel–Svensson Curves from Bond Prices

Article Quant Q&A · Author: Jojo

Summary

The document explains how to fit Nelson–Siegel–Svensson (NSS) parameters to observed bond prices. Each bond is represented by its cash flows and their times to payment; each cash flow is discounted using the spot-rate curve implied by a candidate parameter vector. The resulting discounted cash flows are summed to obtain a fitted dirty price, which can be compared with the observed price.

Parameter estimation can then minimize the sum of squared pricing errors with a numerical optimizer. The answer emphasizes that payment times should be measured from the valuation date, and that the face value is included as a final cash flow. It also explains why fitting spot rates to bond prices is more consistent than modeling coupon yields directly. The exchange provides a conceptual setup rather than a worked implementation: it does not resolve numerical issues in the questioner's code or specify optimizer settings, data requirements, or constraints on the NSS parameters.

Key ideas

  • Represent each bond by its cash flows and payment times measured from the valuation date.
  • Discount each cash flow using the spot rate for its payment horizon, then sum the present values to obtain the fitted dirty price.
  • Estimate NSS parameters by minimizing the differences between observed and fitted bond prices.
  • Bond face value is a cash flow at maturity, alongside coupon payments.
  • Fitting spot rates to bond prices is more consistent than fitting nominal coupon yields directly.

Tags

Full text
# Understanding how to obtain Nelson Siegel Svensson parameters


# Understanding how to obtain Nelson Siegel Svensson 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. I am using the procedure presented in this paper.

The way I am currently thinking of the set-up is as follows:

Bond 1 is associated with vector $m_1$ which contains all the differences between times to maturity and times of each of the payments for Bond 1. For example, if Bond 1 has 24 months to maturity and has semi_annual coupons $m_1 = [18,12,6,0]$. However, the paper suggests that even the face value needs to be included in $c_{ij}$. But the equation $\sum\limits_{j=1}^{l_i}c_{ij}*d(m_{ij}, \theta)$ gives a very low value for the price of the bond as the $d(m_{ij}, \theta)$ values are turning out to be very small (of the order 10^-34 through to 10^-1, for each bond, as the differences between times to maturity and time of the payment decreases).

So, am I correct in thinking that each bond is associated with a different discount factor vector as $m_{ij}$ changes for each bond?

I guess my main issue is, am I interpreting $m_{ij}$ correctly and hence correctly calculating the discount factors the right way, as I find that they are very small and hence the prices are very small and only got a reasonable answer for the parameters when I take the discount factor to be 1 when $c_{ij} = Face value + Coupon$

Also, I realize in the paper I have cited they just consider the Nelson Siegel model, however in the code I have written below I account for the extra parameters associated with the Nelson Siegel Svensson model.

This is the code that I am using to determine the prices, where `df` contains all the observed market data:

```
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)))))
```

Thank You

## Answer by Physcs Envy (score 1)

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

Say at time $t$ , the cash flows of some bond $b$ can be described by the two vectors $\textbf{c}$ and $\textbf{t}$, containing information about the value of the nominal cash flows and cash flow times in years, respectively. Similarly, if we have a range of bonds $B = \{ b_1, ..., b_n\}$ that trade on a market, the matrices $\textbf{C}$ and $\textbf{T}$ contain information about the cash flows and cash flow times for all bonds on the market (in this example, assume that each bond occupy one row).

Assuming no-arbitrage and term-invariant default risk, the vector $\textbf{p}$ of dirty prices for the bonds trading on the market can be recovered using the expression $$ \textbf{p} = diag(\textbf{D(T)} \textbf{C}^T) $$ where $\textbf{D(T)}$ is a matrix of observed discount factors corresponding the the individual entries in $\textbf{T}$; for instance $\textbf{d}_{i, j} = e^{-r(\textbf{t}_{i,j})\textbf{t}_{i,j}}$, where $r(t)$ is some function for the spot (or zero coupon) term structure.

In the real world, instead of solving for $\textbf{p}$, we can in fact observe $\textbf{p}$ through our broker or on our Bloomberg terminals, and so we wish to estimate $\textbf{D(T)}$ instead.

So, you've chosen to model $r(t)$ using the NSS parametric model (sometimes referred to as a parsimonious model when comparing with other models of potentially many more predictors). We then choose an objective function, say $$ \min_\theta{\sum{(p_i - \hat p_i)^2}}. $$ Here, $p_i$ is the observed dirty price of the $i^{th}$ bond and $\hat p_i$ is the fitted dirty price of the $i^{th}$ bond and $\theta$ is a vector of estimation parameters in our model. We then simply employ some numerical optimization routine on our parameter vector $\theta$ until we find an optimum.

I hope this helps to not only see how to estimate parameters in the NSS model, but also why it is theoretically more consistent to model spot rates over nominal coupon YTMs (the latter of which I believe you've tried to model, if I interpret your question correctly).

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.