├── LICENSE
├── main.py
├── README.md
└── nelsonsiegelsvensson.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Qnity
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | """ The Nelson-Siegel-Svensson is a popular extension of the 4-parameter Nelson-Siegel method to 6 parameters. It is an algorithm for interpolating/extrapolating the yield curve among other uses.
2 | Scennson introduces two extra parameters to better fit the variety of shapes of either the instantaneous forward rate or yield curves that are observed in practice.
3 | A desirable property of the model is that it produces a smooth and well-behaved forward rate curve.
4 | Another desirable property is the intuitive explanation of the parameters. beta0 is the long-term interest rate and beta0+beta1 is the instantaneous short-term rate.
5 | To find the optimal value of the parameters, the Nelder-Mead simplex algorithm is used (Already implemented in the scipy package). The link to the optimization algorithm is
6 | Gao, F. and Han, L. Implementing the Nelder-Mead simplex algorithm with adaptive parameters. 2012. Computational Optimization and Applications. 51:1, pp. 259-277
7 | """
8 | from nelsonsiegelsvensson import *
9 | import numpy as np
10 |
11 | ## Inputs
12 | # - Observed yield rates (YieldVec)
13 | # - Maturity of each observed yield (TimeVec)
14 | # - Initial guess for parameters (beta0, beta1, beta2,beta3, labda0 , and lambda1)
15 | # - Target maturities (TimeResultVec)
16 |
17 | TimeVec = np.array([1,2,5,10,25])
18 | YieldVec = np.array([0.0039, 0.0061, 0.0166, 0.0258, 0.0332])
19 | beta0 = 0.1 # initial guess
20 | beta1 = 0.1 # initial guess
21 | beta2 = 0.1 # initial guess
22 | beta3 = 0.1 # initial guess
23 | lambda0 = 1 # initial guess
24 | lambda1 = 1 # initial guess
25 |
26 | TimeResultVec = np.array([1,2,5,10,25,30,31]) # Maturities for yields that we are interested in
27 |
28 | ## Implementation
29 | OptiParam = NSSMinimize(beta0, beta1, beta2, beta3, lambda0, lambda1, TimeVec, YieldVec) # The Nelder-Mead simplex algorithem is used to find the parameters that result in a curve with the minimum residuals compared to the market data.
30 |
31 | # Print the yield curve with optimal parameter to compare with the data provided
32 | print(NelsonSiegelSvansson(TimeResultVec, OptiParam[0], OptiParam[1], OptiParam[2], OptiParam[3], OptiParam[4], OptiParam[5]))
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 🐍 Nelson-Siegel-Svannson algorithm 🐍
4 |
5 |
6 |
7 |
8 |
9 | Popular algorithm for fitting a yield curve to observed data.
10 |
11 | ## Problem
12 | Data on bond yields is usually available only for a small set of maturities, while the user is normally interested in a wider range of yields.
13 |
14 | ## Solution
15 | A popular solution is to use an algorithm to find a function that fits the existing datapoints. This way, the function can be used to interpolate/extrapolate any other point. The Nelson-Siegel-Svannson model is a curve-fitting-algorithm that is flexible enough to approximate most real-world applications.
16 |
17 | The Nelson-Siegel-Svensson is an extension of the 4-parameter Nelson-Siegel method to 6 parameters. Scennson introduced two extra parameters to better fit the variety of shapes of either the instantaneous forward rate or yield curves that are observed in practice.
18 |
19 | Advantages:
20 | - It produces a smooth and well-behaved forward rate curve.
21 | - The intuitive explanation of the parameters. `beta0` is the long-term interest rate and `beta0+beta1` is the instantaneous short-term rate.
22 |
23 | To find the optimal value of the parameters, the Nelder-Mead simplex algorithm is used (Already implemented in the scipy package). The link to the optimization algorithm is Gao, F. and Han, L. Implementing the Nelder-Mead simplex algorithm with adaptive parameters. 2012. Computational Optimization and Applications. 51:1, pp. 259-277.
24 |
25 | The formula for the yield curve (Value of the yield for a maturity at time 't') is given by the formula:
26 |
27 | =\beta_{1}) +
28 | 
29 | }{\frac{t}{\lambda_1}}\big)) +
30 | 
31 | }{\frac{t}{\lambda_1}}-exp(\frac{-t}{\lambda_1})\big)) +
32 | 
33 | }{\frac{t}{\lambda_2}}-exp(\frac{-t}{\lambda_2})\big))
34 |
35 | ### Parameters
36 |
37 | - Observed yield rates `YieldVec`.
38 | - Maturity of each observed yield `TimeVec`.
39 | - Initial guess for parameters `beta0`, `beta1`, `beta2`, `beta3`, `labda0`, and `lambda1`.
40 | - Target maturities `TimeResultVec`.
41 |
42 | ### Desired output
43 |
44 | - Calculated yield rates for maturities of interest `TimeResultVec`.
45 |
46 | ## Getting started
47 |
48 | The user is interested in the projected yield for government bonds with a maturity in 1,2,5,10,25,30, and 31 years. They have data on government bonds maturing in
49 | 1, 2, 5, 10, and 25 years. The calculated yield for those bonds is 0.39%, 0.61%, 1.66%, 2.58%, and 3.32%.
50 |
51 | ```bash
52 | from nelsonsiegelsvensson import *
53 | import numpy as np
54 |
55 | TimeVec = np.array([1, 2, 5, 10, 25])
56 | YieldVec = np.array([0.0039, 0.0061, 0.0166, 0.0258, 0.0332])
57 | beta0 = 0.1 # initial guess
58 | beta1 = 0.1 # initial guess
59 | beta2 = 0.1 # initial guess
60 | beta3 = 0.1 # initial guess
61 | lambda0 = 1 # initial guess
62 | lambda1 = 1 # initial guess
63 |
64 | TimeResultVec = np.array([1, 2, 5, 10, 25, 30, 31]) # Maturities for yields that we are interested in
65 |
66 | ## Implementation
67 | OptiParam = NSSMinimize(beta0, beta1, beta2, beta3, lambda0, lambda1, TimeVec, YieldVec) # The Nelder-Mead simplex algorithm is used to find the parameters that result in a curve with the minimum residuals compared to the market data.
68 |
69 | # Print the yield curve with optimal parameter to compare with the data provided
70 | print(NelsonSiegelSvansson(TimeResultVec, OptiParam[0], OptiParam[1], OptiParam[2], OptiParam[3], OptiParam[4], OptiParam[5]))
71 | ```
72 |
--------------------------------------------------------------------------------
/nelsonsiegelsvensson.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy.optimize import minimize
3 |
4 | def NelsonSiegelSvansson(T, beta0: float, beta1: float, beta2: float, beta3: float, lambda0: float, lambda1: float):
5 | """
6 | NelsonSiegelSvansson calculates the interpolated/extrapolated curve at points in the array "T" using the Nelson-Siegel-Svannson algorithm,
7 | parameterized with parameters beta0, beta1, beta2, beta3, lambda0, lambda1. It returns a numpy ndarray of points.
8 |
9 | Arguments:
10 | T: n x 1 ndarray of maturities for which the user wants to calculate the corresponding rate.
11 | beta0: 1 x 1 floating number, representing the first factor of the NSS parametrization.
12 | beta1: 1 x 1 floating number, representing the second factor of the NSS parametrization.
13 | beta2: 1 x 1 floating number, representing the third factor of the NSS parametrization.
14 | beta3: 1 x 1 floating number, representing the fourth factor of the NSS parametrization.
15 | lambda0: 1 x 1 floating number, representing the first shape parameter lambda of the NSS parametrization.
16 | lambda1: 1 x 1 floating number, representing the second shape parameter lambda of the NSS parametrization.
17 |
18 | Returns:
19 | n x 1 ndarray of interpolated/extrapolated points corresponding to maturities inside T. Where n is the length of the vector T.
20 |
21 | Implemented by Gregor Fabjan from Qnity Consultants on 11/07/2023
22 | """
23 | alpha1 = (1-np.exp(-T/lambda0)) / (T/lambda0)
24 | alpha2 = alpha1 - np.exp(-T/lambda0)
25 | alpha3 = (1-np.exp(-T/lambda1)) / (T/lambda1) - np.exp(-T/lambda1)
26 |
27 | return beta0 + beta1*alpha1 + beta2*alpha2 + beta3*alpha3
28 |
29 | def NSSGoodFit(params: list, TimeVec, YieldVec):
30 | """
31 | NSSGoodFit calculates the residuals between the yield predicted by the NSS algorithm with the specified parameterization and the market observed ones.
32 |
33 | Arguments:
34 | params: 6 x 1 tuple containing the 6 parameters of the NSS algorithm. The sequence of parameters needs to be (beta0, ..., beta4, lambda0, lambda1).
35 | TimeVec: n x 1 ndarray of maturities for which the yields in YieldVec were observed.
36 | YieldVec: n x 1 ndarray of observed yields.
37 |
38 | Returns:
39 | 1 x 1 float number Euclidean distance between the calculated points and observed data.
40 |
41 | Implemented by Gregor Fabjan from Qnity Consultants on 11/07/2023
42 | """
43 |
44 | return np.sum((NelsonSiegelSvansson(TimeVec, params[0], params[1], params[2], params[3], params[4], params[5])-YieldVec)**2)
45 |
46 | def NSSMinimize(beta0: float, beta1: float, beta2: float, beta3: float, lambda0: float, lambda1: float, TimeVec, YieldVec) -> list:
47 | """
48 | NSSMinimize uses the built-in minimize function from the Python's scipy package. The function sets up the parameters and the function NSSGoodFit as to make it
49 | compatible with the way minimize requires its arguments. If the optimization does not converge, the output is an empty array.
50 |
51 | Arguments:
52 | beta0: 1 x 1 floating number, representing the first factor of the NSS parametrization.
53 | beta1: 1 x 1 floating number, representing the second factor of the NSS parametrization.
54 | beta2: 1 x 1 floating number, representing the third factor of the NSS parametrization.
55 | beta3: 1 x 1 floating number, representing the fourth factor of the NSS parametrization.
56 | lambda0: 1 x 1 floating number, representing the first shape parameter lambda of the NSS parametrization.
57 | lambda1: 1 x 1 floating number, representing the second shape parameter lambda of the NSS parametrization.
58 | TimeVec: n x 1 ndarray of maturities for which the yields in YieldVec were observed.
59 | YieldVec: n x 1 ndarray of observed yields.
60 |
61 | Returns:
62 | 6 x 1 array of parameters and factors that best fit the observed yields (or an empty array if the optimization was not successful).
63 |
64 | Source:
65 | - https://docs.scipy.org/doc/scipy/reference/optimize.minimize-neldermead.html
66 | - https://en.wikipedia.org/wiki/Nelder%E2%80%93Mead_method
67 |
68 | Implemented by Gregor Fabjan from Qnity Consultants on 11/07/2023
69 | """
70 |
71 | opt_sol = minimize(NSSGoodFit, x0=np.array([beta0, beta1, beta2, beta3, lambda0, lambda1]), args = (TimeVec, YieldVec), method="Nelder-Mead")
72 | if (opt_sol.success):
73 | return opt_sol.x
74 | else:
75 | return []
76 |
--------------------------------------------------------------------------------