Oct 13, 2021

Adams and Deventer Maximum Smoothness Forward Rate Curve using R

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 minput zero curve, we can calculate following monthly zero curve and forward curve.

Maximum Smoothness Forward Rate Curves using R code

As can be seen in figure above, the forward rate from the linearly interpolated zero curve shows discontinuity and no smoothness at each knot (some forward rates are omitted because they show too large deviations as outliers).

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. It is for an illustrative purpose and not from the input data.

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\)).

1) continuity or smoothness at the joints (internal knots) \[\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}\]
2) market price fitting at the observed points \[\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


For \(i=1,...,m\), zero coupon bond price at time \(t_i\) is \[\begin{align} P(0,t_i) = \exp\left( -\int_{0}^{t_i} f(s) ds \right)= \exp\left(-y(t_i)t_i\right) \end{align}\]
From the above equation, we can get the following relation. \[\begin{align} \int_{0}^{t_i} f(s) ds = y(t_i) t_i \end{align}\]
In a piece-wise linear form, the spot rate and forward rate are as follows.

\[\begin{align} f(t_i) &= a_i t^4 + b_i t^3 + c_i t^2 + d_i t + e_i \\ y(t_i) &= \frac{1}{t_i}\sum_{j=1}^{i} \int_{t_{j-1}}^{t_j} f(t_s) ds \\ y(t_i) &= \frac{1}{t_i}\sum_{j=1}^{i} \int_{t_{j-1}}^{t_j} \left( a_j s^4 + b_j s^3 + c_j s^2 + d_j s + e_j \right) ds \\ y(t_i) &= \frac{1}{t_i}\sum_{j=1}^{i}\left( \frac{1}{5} a_j \Delta t_j^5 + \frac{1}{4} b_j \Delta t_j^4 + \frac{1}{3} c_j \Delta t_j^3 + \frac{1}{2} d_j \Delta t_j^2 + e_j \Delta t_j \right) \end{align}\]
Substituting calibrated parameters into the above equations for forward rate and yield curve, we can draw the next figure with monthly frequency. The green line is the maximum smoothness forward rate. we can easily find that this forward rate is continuous and very smooth unlike kinked forward rate from the linearly interpolated zero rate (the yellow line).



Concluding Remarks


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


No comments:

Post a Comment