Adams and Deventer Maximum Smoothness Forward Rate Curve using R code

[This article was first published on K & L Fintech Modeling, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

This post implements Adams and Deventer (1994) approach which generates the best fitting yield curve smoothing with the principle of maximum smoothness forward rate curve. This provides a powerful closed-form solution by which we can sidestep a kinked or zigzag problem of forward rate curve effectively.



Computing Maximum Smoothness Forward Rate Curve



To ensure a smoothness of the yield curve, the Adam-Deventer method can be used.

  • Adams K.J. and D. R. van Deventer (1994), Fitting yield curves and forward rate curves with maximum smoothness, Journal of Fixed Income 4-1, pp. 52-62.
  • Imai K. and D. R. van Deventer (1996), Financial Risk Analytics Book
  • van Deventer, D. R. (2010), Basic Building Blocks of Yield Curve Smoothing, Part 10: Maximum Smoothness Forward Rates and Related Yields versus Nelson-Siegel (Revised May 8, 2012)
  • Lim, K. G. and Q. Xiao (2002), Computing maximum smoothness forward rate curves, Statistics and Computing 12, pp. 275-279.


We use the Deventer (2010) revised methodology, from which we borrow input data also as follows.


1
2
3
4
5
6
7
8
9
10
11
12
13
——————————————
    Maturity   Spot Rate     ZCB Price
——————————————
       t           r          P(0,t)
——————————————
     0.25        4.75%          0.9882
       1         4.50%          0.9560
       3         5.50%          0.8479
       5         5.25%          0.7691
      10         6.50%          0.5220
——————————————
 
cs


Using a linear interpolation of the given market zero curve, we can calculate next monthly zero curve and forward curve. As can be seen in figure below, forward rate from linearly interpolated show discontinuity and no smoothness (some forward rates are omitted because they show too large deviations as outliers).

Maximum Smoothness Forward Rate Curves using R code

To ensure a smooth yield curve, Adams and Deventer (1994) use maximum smoothness forward rate approach. Their approach delivers a smooth yield curve like the following figure.

Maximum Smoothness Forward Rate Curves using R code source : Kamakura Corporation



Forward Rate


To evaluate smoothness numerically, Adams and Deventer (1994) calculate the second derivative of forward rate for each segments (intervals between observed maturities), the squared sum of them, and minimize this with some constraints which ensure smoothness of yield curve.

In each forward rate curve segment, each forward rate \(f(t)\) is assumed to have the following form.
\[\begin{align} f(t) &= a_i t^4 + b_i t^3 + c_i t^2 + d_i t + e_i \\ \\ &t_{i-1} \lt t \le t_{i}, \quad i=1,2,…,m \end{align}\]
The subscript i denotes the segment number. In case of the example data, as the number of coefficients are \(25 = 5 \times 5\), it is desirable to construct 25 constraints.


Objective Function


The i-th objective function maximizes a smoothness of forward rates, which is an integration of twice differenced forward rates. \[\begin{align} \int_{t_{i-1}}^{t_i} [f^{”}(t)]^2 dt &= \int_{t_{i-1}}^{t_i} \left[12a_i t^2 + 6 b_i t + 2 c_i \right]^2 dt \\ &= \frac{144}{5} \Delta t_i^5 a_i^2 + 36 \Delta t_i^4 a_i b_i + 12 \Delta t_i^3 b_i^2 \\ &+ 16 \Delta t_i^3 a_i c_i + 12 \Delta t_i^2 b_i c_i + 4 \Delta t_i c_i^2 \end{align}\]

Equality Linear Constraints


For \(i=1,2,…,m-1 \), restrictions on continuity of \(f\), \(f^{‘}\),\(f^{”}\) and \(f^{”’}\) are imposed.

\[\begin{align} &a_{i+1} t^4 + b_{i+1} t^3 + c_{i+1} t^2 + d_{i+1} t + e_{i+1} \\ &= a_i t^4 + b_i t^3 + c_i t^2 + d_i t + e_i \\ \\ & 4a_{i+1} t^3 + 3b_{i+1} t^2 + 2c_{i+1} t + d_{i+1} \\ &= 4a_i t^3 + 3b_i t^2 + 2c_i t + d_i\\ \\ &12a_{i+1} t^2 + 6b_{i+1} t + 2c_{i+1} = 12a_{i} t^2 + 6b_{i} t + 2c_{i} \\ \\ & 24 a_{i+1} t + 6b_{i+1} = 24a_{i} t + 6 b_{i} \end{align}\]
For \(i=1,2,…,m \), a set of integration of forward rates should fit a set of zero coupon bond price.

\[\begin{align} -log\left[ \frac{P_i}{P_{i-1}}\right] &= \frac{1}{5} a_i (t_i^5 – t_{i-1}^5) + \frac{1}{4} b_i (t_i^4 – t_{i-1}^4) \\ &+ \frac{1}{3} c_i (t_i^3 – t_{i-1}^3) + \frac{1}{2} d_i (t_i^2 – t_{i-1}^2) + e_i (t_i – t_{i-1}) \end{align}\]
These restrictions can be rewritten more compactly (\(\Delta k_{i+1}= k_{i+1} – k_{i}, \Delta t_i^n= t_i^n – t_{i-1}^n\)). \[\begin{align} 0 &= \Delta a_{i+1} t^4 + \Delta b_{i+1} t^3 + \Delta c_{i+1} t^2 + \Delta d_{i+1} t + \Delta e_{i+1} \\ 0 &= 4\Delta a_{i+1} t^3 + 3\Delta b_{i+1} t^2 + 2\Delta c_{i+1} t + d\Delta _{i+1} \\ 0 &= 12\Delta a_{i+1} t^2 + 6\Delta b_{i+1} t + 2\Delta c_{i+1} \\ 0 &= 24\Delta a_{i+1} t + 6\Delta b_{i+1} \end{align}\] \[\begin{align} -log\left[ \frac{P_i}{P_{i-1}}\right] = \frac{1}{5} a_i \Delta t_i^5 + \frac{1}{4} b_i \Delta t_i^4 + \frac{1}{3} c_i \Delta t_i^3 + \frac{1}{2} d_i \Delta t_i^2 + e_i \Delta t_i \end{align}\]

From these, we have \(21 = 4 \times 4 + 5\) constraints and additional 4 constraints are required.


Additional Constraints


The initial forward rate is assumed to be equal to the observed spot rate or given other rate at time zero. Three remaining constraints are related to the smoothness of forward rate at time 0 and T using the first or second derivative of forward rate.

\[\begin{align} f(0) = r(0) \\ f^{‘}(0) = 0 \\ f^{‘}(T) = 0 \\ f^{”}(T) = 0 \end{align}\]
Now we have a problem of 25 unknowns and 25 equations.


R code


We use optimization to solve for this 25 unknown variables problem using NlcOptim R package which have two main arguments : 1) objective function and 2) constraints function. In particular, constraints function can deal with both linear and non-linear constraints. The following R code estimate 25 coefficients of Adams-Deventer’s maximum smoothness forward rate curve.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#========================================================#
# Quantitative ALM, Financial Econometrics & Derivatives 
# ML/DL using R, Python, Tensorflow by Sang-Heon Lee 
#
# https://kiandlee.blogspot.com
#——————————————————–#
# Adams and Deventer Maximum Smoothness Forward Curve
# using non-linear optimization
#========================================================#
 
graphics.off()  # clear all graphs
rm(list = ls()) # remove all files from your workspace
 
library(NlcOptim) # optimization with non-linear constraints
 
# whole calculation process
f_calc < function(x0) {
    
    # number of maturity
    n < length(df.mkt$mat)
    
    # add 0-maturity zero rate (assumption)
    #df <- rbind(c(0, df.mkt$zrc[1]), df.mkt)
    df < rbind(c(00.04), df.mkt)
    
    # discount factor
    df$DF < with(df, exp(zrc*mat))
    
    # -ln(P(t(i)/t(i-1)))
    df$mln < c(NA,log(df$DF[1:n+1]/df$DF[1:n]))
    
    # ti^n
    df$t5 < df$mat^5
    df$t4 < df$mat^4
    df$t3 < df$mat^3
    df$t2 < df$mat^2
    df$t1 < df$mat^1
    
    # dti = ti^n-(ti-1)^n
    df$dt5 < c(NA,df$t5[1:n+1 df$t5[1:n])
    df$dt4 < c(NA,df$t4[1:n+1 df$t4[1:n])
    df$dt3 < c(NA,df$t3[1:n+1 df$t3[1:n])
    df$dt2 < c(NA,df$t2[1:n+1 df$t2[1:n])
    df$dt1 < c(NA,df$t1[1:n+1 df$t1[1:n])
    
    # parameters to be found
    df$e < df$d < df$c < df$b < df$a < c(NA,rep(0,n))
 
    df$a[1:n+1< x0[(0*n+1):(1*n)]
    df$b[1:n+1< x0[(1*n+1):(2*n)]
    df$c[1:n+1< x0[(2*n+1):(3*n)]
    df$d[1:n+1< x0[(3*n+1):(4*n)]
    df$e[1:n+1< x0[(4*n+1):(5*n)]
    
    # difference of coefficients : dki = ki – k(i-1)
    df$da < c(NA, df$a[2:n]  df$a[2:n+1], NA)
    df$db < c(NA, df$b[2:n]  df$b[2:n+1], NA)
    df$dc < c(NA, df$c[2:n]  df$c[2:n+1], NA)
    df$dd < c(NA, df$d[2:n]  df$d[2:n+1], NA)
    df$de < c(NA, df$e[2:n]  df$e[2:n+1], NA)
    
    # forward rate
    df$fwd < NA
    df$fwd[1:n+1< with(df[1:n+1,], a*t4 + b*t3 + c*t2 +d*t1 + e)
    df$fwd[1< df$e[2]
    
    # linear constraints
    df$cmln < df$cfp3 < df$cfp2 < df$cfp1< df$cfp0< NA
    df$cfp0[2:n] < with(df[2:n,], da*t4 + db*t3 +   dc*t2 +   dd*t1 +   de)
    df$cfp1[2:n] < with(df[2:n,],       4*da*t3 + 3*db*t2 + 2*dc*t1 +   dd)
    df$cfp2[2:n] < with(df[2:n,],                12*da*t2 + 6*db*t1 + 2*dc)
    df$cfp3[2:n] < with(df[2:n,],                          24*da*t1 + 6*db)
    df$cmln[2:(n+1)] < with(df[2:(n+1),],
                        a*dt5/5 + b*dt4/4 + c*dt3/3 + d*dt2/2 + e*dt1  mln)
    
    # additional 4 constraint
    # initial(0) forward rate(f) = r0, initial f’ = 0
    # terminal(T) f’, f” = 0
    const_f_0  < df$e[2  df$zrc[1]
    const_f1_0 < df$d[2]
    const_f1_T < with(df[n+1,], 4*a*t3 + 3*b*t2 + 2*c*t1 + d)
    const_f2_T < with(df[n+1,], 12*a*t2 + 6*b*t1 + 2*c)
    
    # objective function
    df$obj < NA
    df$obj[1:n+1< 
        with(df[1:n+1,],
             (144/5)*dt5*a^2 + 36*dt4*a*+ 12*dt3*b^2 +
                  16*dt3*a*+ 12*dt2*b*+  4*dt1*c^2)
    
    return(list(df.calc = df,
                fvalue  = sum(df$obj[1:n+1]),
                const   = c(
                    df$cfp0[2:n],
                    df$cfp1[2:n],
                    df$cfp2[2:n],
                    df$cfp3[2:n],
                    df$cmln[2:(n+1)],
                    const_f_0, const_f1_0, 
                    const_f1_T, const_f2_T)))
}
 
# objective function for solnl
obj < function(x) {
    lt.out < f_calc(x); return(lt.out$fvalue)
}
 
# constraint function for solnl
con < function(x) {
    lt.out < f_calc(x)
    return(list(ceq = lt.out$const, c = NULL))
}
 
# Input : market zero rate, maturity
df.mkt < data.frame(
    mat = c(0.2513510),
    zrc = c(4.754.55.55.256.5)/100
)
 
x0 < rep(0.001,25# initial guesses
 
out < solnl(x0, objfun = obj, confun = con)
 
# augment df.mkt with calibrated paramters
< length(df.mkt$mat)
 
df.mkt$a[1:n] < out$par[(0*n+1):(1*n)]
df.mkt$b[1:n] < out$par[(1*n+1):(2*n)]
df.mkt$c[1:n] < out$par[(2*n+1):(3*n)]
df.mkt$d[1:n] < out$par[(3*n+1):(4*n)]
df.mkt$e[1:n] < out$par[(4*n+1):(5*n)]
 
df.mkt
 
cs


Running the above R code, we can obtain calibrated parameters (\(a_i, b_i, c_i, d_i, e_i\), \(i=1,…,m \)) which characterize the maximum smoothness forward curve.

It is typical to set the time 0 spot rate to the shortest term rate but Deventer (2010) use 4% and we follow this setting.

1
2
3
4
5
6
7
    mat    zrc             a            b          c             d           e
1  0.25 0.0475  3.7020078185 -3.343981375  0.8481712  1.429488e-16  0.04000000
2  1.00 0.0450 -0.1354629771  0.493489420 -0.5908803  2.398419e-01  0.02500988
3  3.00 0.0550  0.0080049880 -0.080382440  0.2699275 -3.340299e-01  0.16847782
4  5.00 0.0525 -0.0024497294  0.045074169 -0.2946273  7.950796e-01 -0.67835429
5 10.00 0.0650  0.0002985362 -0.009891143  0.1176126 -5.790532e-01  1.03931172
 
cs


Substituting these calibrated parameters into the above equations for forward rate and yield curve, we can draw the next figure. Green line is the maximum smoothness forward rate. we can easily find that this forward rate is continuous and very smooth unlike the forward rate from the linearly interpolated zero rate (yellow line).



Concluding Remarks


From this post, we have implemented Adams and Deventer’s maximum smoothness forward rate curve. This is a powerful approach for fitting yield curve. But in principle, this problem is solved by closed form solution because it is a quadratic optimization with equality linear constraints. This topic will be covered in the next post. \(\blacksquare\)


To leave a comment for the author, please follow the link and comment on their blog: K & L Fintech Modeling.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)