Quaderni SEMeQ
Quaderno n. 13/2007
Daniele Marazzina
Interest Rate Modelling: A MATLAB
Implementation
Stampato in proprio presso la Segreteria del Dipartimento di Scienze Economiche e
Metodi Quantitativi, Università degli Studi del Piemonte Orientale “A. Avogadro”
I Lavori pubblicati nella collana riflettono esclusivamente le opinioni degli autori e
non impegnano la responsabilità del Dipartimento SEMeQ.
Dipartimento di Scienze Economiche e Metodi Quantitativi
Via E. Perrone 18 – 28100 Novara
Tel. 39 (0) 321- 375310 Fax 39 (0) 321 - 375305
e-mail: [email protected]
Interest Rate Modeling: A M
ATLAB
Implementation
Daniele Marazzina
SEMeQ Department, Università del Piemonte Orientale Via Perrone 18, 28100 Novara, Italy
e-mail: [email protected] Avanade Italy
Corso Venezia 46, 20121 Milano, Italy e-mail: [email protected]
Abstract
The aim of this work is to present a MATLABimplementation of different methods for estimating the term structure of interest rate. More precisely, we implement the exponential functional form of Nelson-Siegel and polynomial spline methods (with or without penalty term), considering both coupon bonds, like Italian Btp, and Libor and Swap interest rates. Furthermore, we compare the models’performances, considering both computational costs and approximation results.
1
Introduction
An important tool in the development and testing of financial theory is the term structure of interest rate as it provides a characterization of interest rates as a function of maturity. The yield curve, or term structure of interest rate, is the set of interest rates for different investment periods or maturities. Yield curve can display a wide variety of shapes. Typically, a yield curve will slope upwards, with longer term rates being higher, although there are plenty of examples of inverted term structures where long rates are less then short rates.
In the bond markets the yield curve is found from bond data, while in the money market the yield curve is derived from the prices of a variety of different types of instrument. Cash rates provide information about the short term rates, from one week to about 18 months. These are investments or borrowings where the entire sum of interest plus principal is due at the end of the period, with no interim payments. After about two-year point, Swaps become the dominant instrument: from there it is possible to derive the yield curve until 15-year point. After that, bond can be used. See [5] for further details.
Information about the yield curve becomes steadily more sparse as the maturity increases. A good interest rate curve is the most basic requirement for pricing interest rate derivatives: if the curve is bad, the prices it returns will be bad (see [1]). The objective in empirical fitting of the term structure is to find a curve that fits the data sufficiently well and it is a sufficiently smooth function: the second requirement is at least as important as the first, even if it is less quantifiable (see, for example, [12]).
In this paper we consider the methodology of fitting the discount function d (or related function, as, for example, the instantaneous forward rate function) by the exponential model of Nelson-Siegel (see [9] and [11]) and polynomial spline methods (see [6] and [7]) with or without penalty term (see [2] and [4]). Moreover, we consider a MATLAB[13] code written for both Italian Btp data and Libor and Swap data in order to obtain the curves.
The outline of the paper is the following: in Section 2 we describe how we can extract the necessary data from coupon bonds and Libor and Swap rates; in Section 3 we introduce the data-fitting techniques: more precisely, we describe the Nelson-Siegel model and some methods based on cubic spline. In Sec-tion 4 we explain how our MATLAB code works using the theoretical results introduced in the previous
sections. Finally, Section 5 deals with the models’performances, considering both computational cost and approximation results.
2
The Discount Function
The term structure of interest rate can be identified with any of a number of related concepts. In this section we consider the discount function d(t), which describes the present value of 1 Euro repayable in t years. The aim of the algorithm described in this paper is to extract the term structure from a set of coupon bonds (such as the Italian Btp considered), whose prices are largely determined by the present value of their stated payments (see [2]), or a set of Libor and Swap rates. More precisely, in this section we show a way to obtain the values of the discount function at the payments’dates for both kind of data. For further details, see the examples reported in Appendix A.
2.1
Coupon Bonds
Following [2] and [8], let P (i) be the price of a coupon bond i at a fixed date t (value date) and A(i, j) be its jthpayment made at time BP (i, j); moreover, let m
ibe the number of remaining payments for the
bond i and n be the number of bonds. Then, if we set for i = 1, · · · , n and j = 1, · · · , mi
T (i, j) := α0[t, BP (i, j)],
where α0[t1, t2] is the distance between the two dates t1 and t2, computed according to the actual/365
day-count convention (i.e., the day-count convention for bonds), we have P (i) =
mi
X
j=1
A(i, j)d(T (i, j)) ∀ i = 1, · · · , n. (1) Since the coupon bond i is characterized by a periodic coupon Ciand a facial value Fi, we set
(
A(i, j) = Ci if j < mi
A(i, j) = Ci+ Fi if j = mi,
for i = 1, · · · , n. For simplicity, in this paper we consider coupons paid every six months (semi-annual coupons) and we define Ci = bCi/2, where bCiis the annual payment for the bond i, i = 1, · · · , n.
If we now define T T as the vector containing all the elements of T , sorted in ascending order, and we define the matrix AA such that AA(i, j) is the payment of the bond i at time T T (j) (thus it could be equal to 0 if T T (j) is not a time to payment for the considered bond), then (1) becomes
AA D = P , (2) where D(i) = d(T T (i)), i = 0, · · · , m, and m is the number of elements of T T (i.e., the number of the global payment times).
Considering the bond i, i = 1, · · · , n, its price P (i) (i.e., the price that purchaser pays when buying the bond) is equal to the quoted price (clean price) plus the accrued interest AI (i.e., the interest accumulated between the most recent interest payment and the present time). In order to compute AI, let t−1be the date
of the previous coupon payment, t be the value date and t1be the date of the next coupon payment, then
the accrued interest is
AI = Cbi 2 α0[t−1, t] α0[t−1, t1] = Ci α0[t−1, t] α0[t−1, t1] .
2.2
Libor and Swap Rates
First of all we consider Libor (London Interbank Offered Rate) rates, which are daily reference rates based on the interest rates at which banks offer to lend funds to other banks.
Let n1be the number of Libors considered, LM be the vector containing the times to maturity of the n1
Libors and LR(i) be the interest rate of the Libor with time to maturity LM (i), i = 1, · · · , n1: this means
that 1 Euro bought at time t (i.e., the value date) gives
1 + LR(i) α1[t, LP (i)]
Euros at time LP (i), where:
• LP (i) is the payment date, i.e., LP (i) = t + LM (i) if it is a business day, otherwise LP (i) is the first business day after t + LM (i);
• α1[t1, t2] is the distance between the two dates t1 and t2, computed according to the actual/360
day-count convention, (i.e., the day-count convention for Libors). Thus, if we set T1(i) := α1[t, LP (i)], we have d(T1(i)) = 1 1 + LR(i) α1[t, LP (i)] = 1 1 + LR(i) T1(i) , and so, reasoning as in the previous subsection, if we define for each Libor rate i
A1(i) = 1 + LR(i) T1(i),
P1(i) = 1,
we obtain
A1(i) d(T1(i)) = P1(i) ∀ i = 1, · · · , n1. (3)
Now we consider Swaps, which are agreement to exchange one set of cash flow for another. The most commonly traded Swap requires one side to pay a fixed rate and the other to pay a floating rate. In order to derive the discount function from Swap rates, we suppose that they have cash flow at one-year intervals. Let t be the value date, SR(i) be the interest rate of the Swap with expiration at i years and SP (i, j), with 0 < j ≤ i, be its jthpayment date; we define
T2(i, j) := α2[t, SP (i, j)],
where α2[t1, t2] is the distance between the two dates t1and t2, computed according to the 30/360
day-count convention (i.e., the day-day-count convention for Swaps considering the Euro market). Thus, following [5], we have
d(T2(i)) =
1 − SR(i)Pi−1
j=1[T2(i, j) − T2(i, j − 1)] d(T2(i, j))
1 + SR(i) [T2(i) − T2(i, i − 1)]
where n2 is the maximum expiration time of the Swap considered and we have set T2(i, 0) = 0 and
T2(i) := T2(i, i), i.e., its time to maturity.
If we define for i = 1, · · · , n2
A2(i, j) = SR(i) [T2(i, j) − T2(i, j − 1)] if j < i,
A2(i, i) = 1 + SR(i) [T2(i) − T2(i, i − 1)],
P2(i) = 1,
then (4) is equivalent to
i
X
j=1
A2(i, j) d(T2(i, j)) = P2(i) ∀ i = 1, · · · , n2. (5)
It is easy to see that in order to use equations (4) and (5) it is necessary to know each Swap rates with expi-ration from 1 to n2years. If it is not possible, we obtain the missing Swap rates using linear interpolation,
i.e., if we suppose that the values of SR(i − 1) and SR(i + 1) are known, while SR(i) is unknown, we set
SR(i) = SR(i − 1) + SR(i + 1) 2 .
See the MATLABroutineMISSING_SWAPin Section 4.1 for further details and [1] for other interpolation techniques.
Reasoning as above, if we now define n as the total number of financial instruments considered (i.e., n = n1+ n2), T T as the vector containing all the times to payment of both the Libor and the Swap rates
(i.e., all the elements T1(l), l = 1, · · · , n1and T2(i, j), i = 1, · · · , n2, j = 1, · · · , i, sorted in ascending
order), we can define
• AA(i, j) = A1(i) if i ≤ n1and T T (j) = T1(i),
• AA(i, j) = A2(i − n1, p) if i > n1and T T (j) = T2(i − n1, p),
• P (i) = P1(i) if i ≤ n1,
• P (i) = P2(i − n1) if i > n1,
for i = 0, · · · , n, j = 1, · · · , m, where m is the size of T T , i.e., the number of payment dates, and p = 1, · · · , n2.
Thus equations (3) and (5) become
AA D = P , (6) where D(i) = d(T T (i)), i = 0, · · · , n.
Notice that equation (2) (i.e., the coupon bonds case) and equation (6) (i.e., the Libor and Swap rates case) are of the same type. Thus we will consider their resolution through a fitting procedure without any distinction between the kind of data considered.
3
The Fitting Procedure
The aim of this section is to introduce the data-fitting techniques: we start with the Nelson-Siegel model, introduced in [9], then we present six models related with the cubic spline method.
3.1
Nelson-Siegel Model
The Nelson-Siegel model uses a single exponential functional form over the entire maturity range (see [8]). The discount function associated with (1), (3) and (4) is given as
d(t) = e−α1t−β(α2+α3)(1−e−t/β)+α3te−t/β (7)
with the constraints
α1> 0, α1+ α2> 0, β > 0. (8)
Therefore, considering equations (2) and (6), we impose that
D(i) := d(T T (i)) = e−α1T T (i)−β(α2+α3)(1−e−T T (i)/β)+α3T T (i)e−T T (i)/β.
It is easy to understand that we are not sure that it is possible to find values of α1, α2, α3and β which
satisfies
AA D = P ;
thus the estimation of the discount function requires searching the unknown parameters Θ := {α1, α2, α3, β}
which minimize the sum of the squared errors, i.e., we consider the system AA D = P + E,
where E is the errors’vector (which depends on Θ) and we have to solve the following least square mini-mization problem:
min
Θ E
T
E. (9)
3.2
Cubic Spline Method
The term spline is used to refer to a wide class of functions that are used in applications requiring data interpolation and/or smoothing (see, for example, [10] for further details). Cubic splines are piecewise polynomial, joined at so-called knot points, characterized by the fact that their first and second derivatives are continuous functions.
A B-spline (or bell spline) is a spline function that has minimal support with respect to a given degree and domain partition; each spline function can be represented as a linear combination of B-splines of the same degree and over the same partition. In Figure 1 we show an example of B-spline basis function (see Appendix B and [2] for further details).
Following [2], we have implemented the term structure fitting techniques below. 3.2.1 B-spline
Let {φi}si=1 be the set of the B-spline basis functions; first of all we consider the case in which we
parametrize d as a cubic spline, i.e.,
d(τ ) =
s
X
i=1
Figure 1: 0 0.5 1 1.5 2 2.5 3 3.5 4 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
B−spline Basis Functions (Knot Points: 0; 2; 4)
φ1 φ2 φ3 φ4 φ5
Therefore, if we define for i = 1, · · · , m and j = 1, · · · , s G(i, j) = φj(T T (i)),
we obtain
D := G β, (10) where β = (β1, · · · , βs)T.
Then we consider the function
g(τ ) := −log(d(τ )) and we parametrize it as a cubic spline, i.e.,
g(τ ) = s X i=1 βiφi(τ ), As it holds d(τ ) = −eg(τ ), (11) we have D := −exp(G β), (12) where exp is the exponential function which operates element-wise on arrays.
Finally we consider the instantaneous forward rate function f (τ ) = − d
reasoning as above, it holds
d(τ ) = e−R0τf (t) dt. (13)
Thus, if we parametrize f as a cubic spline, i.e., f (τ ) =
s
X
i=1
βiφi(τ ),
and we define the matrix
IG(i, j) = Z T T (i) 0 φj(t) dt, then we have D := −exp(IG β). (14)
Reasoning as for the Nelson-Siegel model, the fitting procedure for the three cases above consists of solving the minimization problem associated to the following system
AA D = P + E,
i.e., since we have to search β which minimize ETE, we have to solve the least square minimization problem
min
β [(P − AA D)
T(P − AA D)]. (15)
3.2.2 B-spline with Penalty Term
In a spline the number of basis functions s is determined by the number of knot points: too few or too many parameters can lead to poor estimates. The strategy proposed in [2] is to penalize excess variability in the estimated discount function, reducing the effective number of parameters since the penalty forces implicit relationship between the spline basis functions.
If the domain of the spline is [0, T ], we define the penalty term H as the matrix of dimension s × s such that
H(i, j) = Z T
0
φ00i(t)φ00j(t) dt.
The minimization problem associated with the B-spline with penalty fitting technique is min
β [(P − AA D)
T(P − AA D) + λβTHβ],
(16) where λ is the penalty parameter (i.e., a positive real number) and D is defined as in (10), (12) or (14), depending on the function to be parametrized.
The penalty parameter λ controls the penalty matrix H; naturally, λ moves inversely to the effective number of parameters: the more λ becomes a large number, the more the penalty matrix H becomes important in (16). In order to choose the appropriate λ, we consider the value of λ which minimizes the so-called Generalized Cross Validation(GCV) function
γ(λ) := ((I − Q)Y )
T((I − Q)Y )
where
Q := X(XTX + λH)−1XT,
I is the s × s identity matrix, tr is the trace operator and X and Y are defined in Table 1. Table 1: Choice of X and Y (Xidenotes the ithrow of X)
Function to Parametrize W Xi Y
d - (AA G)i P
g AA exp(−G β) −W (i) (AA G)i P − W + X β
f AA exp(−IG β) −W (i) (AA IG)i P − W + X β
Intuitively, cross validation starts by looking at a leave-one-out estimator for each data point. If data is not scarce, then the set of available input-output measurements can be divided into two parts: one part for training and one part for testing. A better method, which is intended to avoid the possible bias introduced by relying on any particular division into test and train components, is to partition the original set in several different ways and to compute an average score over the different partitions. This is called “leave-one-out” cross-validation. The residual values from actual data point and the fitted data point from the “leave-one-out” estimation are averaged to construct the cross validation measure. With few parameters in the estimation the residual will tend to be large, due to a poor overall fit, while with an interpolant the perfect fit will tend to produce spurious movements and hence large out of sample residuals. Somewhere in between is the lowest value of the cross validation measure and thus the best estimate. See [2] for further details.
Notice that in Table 1 β is the solution of (16) and thus it depends also on λ: therefore, in the second and the third case (i.e., when we parametrize g or f ), in order to find the best λ we have to find β solving the minimization problem (16) at each step of the GCV-minimization procedure.
4
The M
ATLAB
Code
The aim of this section is to present our code; below we report the main file. For further information on the MATLABroutines used in the following code, such asFMINSEARCHorDAYSDIF, see Appendix C. function main(flag1,flag2)
% INPUT
% flag1: kind of data % flag1 = 0 -> Btp
% flag1 = 1 -> Swap + Libor %
% flag2: kind of curve
% flag2 = 0 -> Nelson-Siegel
% flag2 = 1 -> B-spline (function: d)
% flag2 = 2 -> B-spline + Penalty (function: d) % flag2 = 3 -> B-spline (function: g)
% flag2 = 4 -> B-spline + Penalty (function: g) % flag2 = 5 -> B-spline (function: f)
% flag2 = 6 -> B-spline + Penalty (function: f) %%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FIRST STEP: Getting Data % %%%%%%%%%%%%%%%%%%%%%%%%%%%%
if flag1==0 [Btp,value_date]=Btp_data; elseif flag1==1 [Libor,Swap,value_date]=Libor_Swap_data; Swap=missing_Swap(Swap); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SECOND STEP: Building Matrices and Vectors % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % TT-> vector of global payments (time)
% AA-> matrix of global payments (value) % MAT->vector of maturity dates
if flag1==0 [P,T,A]=matrix_vector_Btp(value_date,Btp); elseif flag1==1 [P,T,A]=matrix_vector_LS(value_date,Libor,Swap); end [TT,AA,MAT]=matrix_vector(T,A); %%%%%%%%%%%%%%%%%%%%%%%%%%%%% % THIRD STEP: Curve Fitting % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Find the vector x of the parameters of the curve %
% Options for FMINSEARCH
options=optimset(’TolFun’,1e-9,’TolX’,1e-9,’MaxIter’,1e8,’MaxFunEvals’,1e8); %
if flag2==0 %NELSON-SIEGEL
x_0=[0.04;-0.02;0.001;2]; %initial value
x=fminsearch(’NelsonSiegel’,x_0,options,AA,P,TT); else %B-SPLINE METHODS
K=length(MAT);
s=max(3,round(sqrt(K))); %number of knot points + 1 KP=knot_points(s,K,MAT);
s=length(KP)+2; %number of B-spline basis function
KPa=[KP(1),KP(1),KP(1),KP,KP(end),KP(end),KP(end)];%Augmented knot points if flag2==1 %B-SPLINE (function d)
for i=1:length(TT) for j=1:s
G(i,j)=BsplineBasis(j,4,TT(i),KPa); %B-spline matrix end
end
x_0=ones(s-1,1); %initial value
x=fminsearch(’Bspline’,x_0,options,AA*G,P); x=[1;x];
elseif flag2==2 %B-SPLINE + PENALTY (function d) for i=1:length(TT)
G(i,j)=BsplineBasis(j,4,TT(i),KPa); end
end
H=penalty(s,KPa); % Penalty Term % Find the optimal Penalty Parameter lambda=10^10; %initial value
lambda=fminsearch(’GCV’,lambda,options,AA*G,P,H); % Find x
x_0=ones(s-1,1); %initial value
x=fminsearch(’Bspline_penalty’,x_0,options,AA*G,P,H,lambda); x=[1;x];
elseif flag2==3 %B-SPLINE (function g) for i=1:length(TT) for j=1:s G(i,j)=BsplineBasis(j,4,TT(i),KPa); end end x_0=ones(s-1,1); x=fminsearch(’Bspline_g’,x_0,options,AA,G,P); x=[0;x];
elseif flag2==4 %B-SPLINE+PENALTY (function g) for i=1:length(TT)
for j=1:s
G(i,j)=BsplineBasis(j,4,TT(i),KPa); end
end
H=penalty(s,KPa); % Penalty Term % Find the optimal Penalty Parameter x_0=ones(s-1,1); %initial value lambda=10^3; % initial value
lambda=fminsearch(’GCV_g’,lambda,options,AA,G,P,H,x_0,s,options); % Find x
x=fminsearch(’Bspline_penalty_g’,x_0,options,AA,G,P,H,lambda); x=[0;x];
elseif flag2==5 %B-SPLINE (function f)
IG=intBsplineBasis(TT,s,KP,KPa); % integral of the B-spline basis x_0=ones(s,1); %initial value
x=fminsearch(’Bspline_f’,x_0,options,AA,IG,P); elseif flag2==6 %B-SPLINE+PENALTY (function f)
IG=intBsplineBasis(TT,s,KP,KPa); % integral of the B-spline basis H=penalty(s,KPa); % Penalty Term
% Find the optimal Penalty Parameter x_0=ones(s,1); %initial value
lambda=10^3; %initial value
lambda=fminsearch(’GCV_f’,lambda,options,AA,IG,P,H,x_0,s,options); % Find x
end end
4.1
First Step: Getting Data
The first step of the MATLABprogram is to get the Btp or the Libor and Swap data. If we consider Btp, the program calls the BTP_DATAroutine, which provides all the necessary data.
function [Btp,value_date]=Btp_data
% Btp -> Btp struct --> [nominal value, clean value, maturity, % rate, number of coupons (for year)] % value_date=’12/19/2006’; % Btp=[ struct(’n_val’,100,’val’,99.94,’mat’,’01/15/2007’,’rate’,2.75,’coupons’,2) ... struct(’n_val’,100,’val’,96.3,’mat’,’02/01/2037’,’rate’,4.00,’coupons’,2) ];
If instead we consider Libor and Swap, the program calls the LIBOR_SWAP_DATA routine for the necessary data and thenMISSING_SWAP, which uses linear interpolation for the missing Swap rates (see Section 2.2).
function [Libor,Swap,value_date]=Libor_Swap_data %
value_date=’09/21/2006’; %%%%%%%%%%%%%%%%%%%%%%
% Libor -> duration (in days) - duration (in months) - rate Libor=[ 1, 0, 3.04188; ... 0, 3, 3.37025; ]; %%%%%%%%%%%%%%%%%%%%%%
% Swap -> duration (in years) - rate Swap=[ 1, 3.808; ... 30, 4.214; ]; function Swap1=missing_Swap(Swap)
% Compute missing Swap using linear interpolation %
n=length(Swap); %number of Swaps difference=zeros(n,1); %initialize
% difference(i)=n means that between the duration of swap i and i+1 % there are n years-> if n=1 we do nothing, otherwise we create the
% missing Swap/Swaps using linear interpolation for i=1:n-1 difference(i)=Swap(i+1,1)-Swap(i,1); end % count=0; for i=1:n count=count+1; Swap1(count,:)=Swap(i,:); for j=1:difference(i)-1 count=count+1; Swap1(count,1)=Swap(i,1)+j; t=Swap1(count,1); t1=Swap(i,1); t2=Swap(i+1,1); s1=Swap(i,2); s2=Swap(i+1,2); Swap1(count,2)=(s1*(t2-t)+s2*(t-t1))/(t2-t1); end end
4.2
Second Step: Building Matrices and Vectors
In the second step, following Section 2, we build the matrix AA and the vector P necessary for the third step: the curve fitting.
If we consider Btp, the main program calls theMATRIX_VECTOR_BTProutine, which computes matrices A and T and the vector P (see Section 2.1).
function [P,T,A]=matrix_vector_Btp(Date,Btp) % Compute: P -> price vector
% T -> matrix of coupons’payments (time)
% T[i,j]=time to the jth payment of the ith Btp % A -> matrix of coupons’payments (value)
% A[i,j]=jth payment of the ith Btp % Date is taken as time 0
%
% Initialize Matrices and Vector n=length(Btp); % number of Btp P=zeros(n,1);
A=[]; T=[]; for i=1:n
% 1-compute Previous coupon and Next coupons [Pcoupon,Ncoupons]=PNcoupons(Btp(i),Date); % 2-compute Accrued Interest
AI=Btp(i).n_val*(Btp(i).rate/100)*(1/(Btp(i).coupons))*... (daysdif(Pcoupon,Date,3)/daysdif(Pcoupon,Ncoupons(1),3));
% 3-compute P
P(i)=Btp(i).val+AI; % 4-compute A and T
m=length(Ncoupons); % number of next coupons for j=1:m
T(i,j)=daysdif(Date,Ncoupons(j),3)/365;
A(i,j)=((Btp(i).rate/100)/Btp(i).coupons)*Btp(i).n_val; end
A(i,m)=A(i,m)+Btp(i).n_val; %pay the nominal value at maturity end
The above routine calls PNCOUPON, which computes the previous coupon date and the dates of the next coupons.
function [Pcoupon,Ncoupons]=PNcoupons(Btp,Date);
% Compute the Previous coupon and the Next coupons date %
maturity=Btp.mat;
numcoup=Btp.coupons;%number of coupons paid on each year monthcoup=12/numcoup; %months between two coupons
%
coupon=datenum(maturity);
%DATENUM converts date string into serial date number cday=day(maturity);%day of the maturity date
cmonth=month(maturity);%month of the maturity date cyear=year(maturity);%year of the maturity date count=0; %number of next coupons
while( daysdif(Date,coupon,3)>0 )
%DAYSDIF->days between dates with day-count basis 3(=actual/365) count=count+1; coup(count)=coupon;%next coupons’date temp=cmonth-monthcoup; if temp<=0 cyear=cyear-1; cmonth=12+temp; else cmonth=temp; end coupon=datenum(cyear,cmonth,cday); end Pcoupon=coupon;
while( isbusday(Pcoupon)==0 ) % if it is not a business day Pcoupon=Pcoupon+1;
end %
for i=1:count temp=coup(i);
temp=temp+1; end
Ncoupons(count-i+1)=temp; % ascending order end
In the same way, if we consider Libor and Swap, we compute matrix A, T and vector P using the
MATRIX_VECTOR_LS. Notice that A, T and P contain the elements of A1and A2, T1and T2, P1and
P2, respectively (see Section 2.2).
function [P,T,A]=matrix_vector_LS(Date,Libor,Swap) % Compute: P -> price vector
% T -> matrix of payments (time) % A -> matrix of payments (value) % Date is taken as time 0
%
n1=length(Libor); % number of Libors n2=length(Swap); % number of Swaps cday=day(Date); cmonth=month(Date); cyear=year(Date); % %%%%% % P % %%%%% P=ones(n1+n2,1); % %%%%%%%%%%% % A and T % %%%%%%%%%%% % Libor
% daycount -> actual/360 (basis 2 in daysdif) for i=1:n1
% temp is the maturity of the i-th Libor if Libor(i,1)>0 % if the duration is in days
temp=datenum(cyear,cmonth,cday+Libor(i,1)); else % if the duration is in months
temp=datenum(cyear,cmonth+Libor(i,2),cday); end
while( isbusday(temp)==0 ) %if it is not a business day temp=temp+1; end T(i,1)=daysdif(Date,temp,2)/360; A(i,1)=1+(Libor(i,3)/100)*T(i,1); end % % Swap
% daycount -> 30/360 (basis 6 in daysdif) for i=1:n2
for j=1:Swap(i,1)
% temp is the j-th payment date of the i-th Swap temp=datenum(cyear+j,cmonth,cday);
while( isbusday(temp)==0 ) %if it is not a business day temp=temp+1;
end
T(n1+i,j)=daysdif(Date,temp,6)/360; if j==1 %if it is the first payment
t=T(n1+i,j); else t=T(n1+i,j)-T(n1+i,j-1); end A(n1+i,j)=(Swap(i,2)/100)*t; end A(n1+i,Swap(i,1))=A(n1+i,Swap(i,1))+1; end
At the end, using routineMATRIX_VECTOR, we compute matrix AA and vector T T (see Section 2) for both the Btp case or the Libor-Swap case.
function [TT,AA,MAT]=matrix_vector(T,A) % Compute:
% TT-> vector of global payments (time) % AA-> matrix of global payments (value) % MAT->vector of times to maturity
% %%%%%% % TT % %%%%%% count=1; for i=1:size(T,2) for j=1:size(T,1) a=T(end+1-j,i);
if a==0 %all the remaining element of the column are 0 break % break the j-loop
else
flag=0; % the date a is not present in TT for k=1:count-1
if TT(k)==a
flag=1; % a is already present in T break % break the k-loop
end end
if flag==0 % if the date a is not present in TT... TT(count)=a; % add a to TT
count=count+1; end
end end
TT=sort(TT); %Sort in ascending order. %%%%%% % AA % %%%%%% for k=1:length(TT) for i=1:size(A,1) for j=1:size(A,2)
if T(i,j)==TT(k) %if the payment date are equal AA(i,k)=A(i,j); break; elseif T(i,j)>TT(k) break; end end end end %%%%%%% % MAT % %%%%%%% for i=1:size(T,1) for j=1:size(T,2) if T(i,j)==0 MAT(i)=T(i,j-1); break; elseif j==size(T,2) MAT(i)=T(i,j); break; end end end MAT=sort(MAT);
For further details see the examples reported in Appendix A.
4.3
Third Step: Curve Fitting
In this last step we compute the curve’s parameters. Notice that both the Btp case and the Libor and Swap case are considered without any distinction.
4.3.1 Nelson Siegel Model From theMAINroutine we have
x_0=[0.04;-0.02;0.001;2]; %initial value
x=fminsearch(’NelsonSiegel’,x_0,options,AA,P,TT); where the NELSONSIEGELroutine is the following
function [f]=NelsonSiegel(x,A,P,T) % f is the quantity to minimize %
if x(1)<=0 % x(1) must be positive... f=10^15;
elseif x(1)+x(2)<=0 % x(1)+x(2) must be positive... f=10^15;
elseif x(4)<=0 % x(4) must be positive... f=10^15; else % if x(1), x(1)+x(2), x(4) is positive... for i=1:length(T) t(i,1)=exp(-x(1)*T(i)-x(4)*(x(2)+x(3))*(1-exp(-T(i)/x(4)))+... x(3)*T(i)*exp(-T(i)/x(4))); end temp=P-A*t; f=0; for i=1:length(temp) f=f+temp(i)^2; end end
If we consider Section 3.1, we have
x(1):= α1, x(2):= α2, x(3):= α3, x(4):= β.
Notice that, in order to solve the constrain minimization (9), we impose f=10^15 if (8) is not satisfied. 4.3.2 B-spline methods
For the B-spline methods, in order to define the basis functions, in theMAINprogram we have K=length(MAT);
s=max(3,round(sqrt(K))); %number of knot points + 1 KP=knot_points(s,K,MAT);
s=length(KP)+2; %number of B-spline basis functions
KPa=[KP(1),KP(1),KP(1),KP,KP(end),KP(end),KP(end)]; %Augmented KP where theKNOT_POINTSroutine is as follows
function T=knot_points(s,k,MAT)
% Compute the knot points following McCulloch (1971)
% These points are used to compute the B-spline basis functions %
T(1)=0; for i=2:s-2
h=fix(((i-1)*k/(s-2)));
% fix(X) rounds X to the nearest integers towards zero. theta=((i-1)*k/(s-2))-h;
T(i)=MAT(h)+theta*(MAT(h+1)-MAT(h)); end
As in [6] and [8], the number of knot points (and thus the number of basis functions) depends on√K. In Appendix B we report the following routines:
• BSPLINEBASISreturns the value of B-spline basis functions; this routine is used to compute matrix G,
• DBSPLINEBASISreturns the value of the first derivative of the B-spline basis functions, • DDBSPLINEBASISreturns the value of the second derivative of the B-spline basis functions,
• INTBSPLINEBASISreturns the integrals of the B-spline basis functions: this routine is used to com-pute matrix IG.
Following Sections 3.2.1 and 3.2.2, in order to compute the set of parameters β, we have to consider the minimization problem below.
• B-spline (parametrized function: d) From theMAINroutine we have
x_0=ones(s-1,1); %initial value
x=fminsearch(’Bspline’,x_0,options,AA*G,P); x=[1;x];
where
function [f]=Bspline(x,A,b) % B-spline
% f is the quantity to minimize %
x=[1;x]; %the first term of the vector must be equal to 1 temp=b-A*x;
f=0;
for i=1:length(temp) f=f+temp(i)^2; end
Notice that d(0) = 1, since the present value of 1 Euro repayable in 0 years is actually 1; thus we have to impose
β1= 1
since 0 is the first knot point and it holds
φ1(0) = 1, φi(0) = 0 if i = 2, · · · s,
where we recall that s is the number of basis functions (see Figure 1 and [2]). For this reason, the minimization procedure involves only the parameters β2, · · · , βs.
• B-spline with Penalty (parametrized function: d) From theMAINroutine we have
H=penalty(s,KPa); % Penalty Term % Find the optimal Penalty Parameter lambda=10^10; %initial value
lambda=fminsearch(’GCV’,lambda,options,AA*G,P,H); % Find x
x_0=ones(s-1,1); %initial value
x=fminsearch(’Bspline_penalty’,x_0,options,AA*G,P,H,lambda); x=[1;x];
The penalty term, which is unique for each B-spline with penalty method, is computed by the PENALTYroutine (see Section 3.2.2).
function H=penalty(s,KPa) % Compute the Penalty Term % K=length(KPa)-6; H=sparse(s,s); for k=1:K-1 [x,w]=gauleg(KPa(k+3),KPa(k+4),4); for t=1:length(x) f=[]; for i=1:s f(i)=ddBsplineBasis(i,4,x(t),KPa); end for i=1:s for j=1:s H(i,j)=H(i,j)+w(t)*f(i)*f(j); end end end end
For theGAULEGroutine, which computes the nodes and weights of the Gauss-Legendre quadrature formula, see Appendix B.5.
The minimization procedures call the following routines: function f=GCV(lambda,X,Y,H)
% GENERAL CROSS VALIDATION %
Q=X*inv(X’*X+lambda*H)*X’; n=length(Y);
I=eye(n); %identity matrix of size n T=(I-Q)*Y;
theta=2;
f=((T’)*T)/((n-theta*trace(Q))^2); and
function [f]=usa_Bspline_penalty(x,A,b,H,lambda) % f is the quantity to minimize
%
x=[1;x];%the first term of the vector must be equal to 1 f=((b-A*x)’)*(b-A*x)+lambda*((x’)*H*x);
As above, the second minimization procedure involves only the parameters β2, · · · , βs.
• B-spline (parametrized function: g) From theMAINroutine we have x_0=ones(s-1,1);
x=fminsearch(’Bspline_g’,x_0,options,AA,G,P); x=[0;x];
where
function [f]=Bspline_g(x,C,F,b) % f is the quantity to minimize %
x=[0;x];
Pi=C*exp(-F*x); f=((b-Pi)’)*(b-Pi);
Notice that we have imposed β1= 0 since g(0) = 0 implies d(0) = 1 (see (11) ).
• B-spline with Penalty (parametrized function: g) From theMAINroutine we have
H=penalty(s,KPa); % Penalty Term % Find the optimal Penalty Parameter x_0=ones(s-1,1); %initial value lambda=10^3; % initial value
lambda=fminsearch(’GCV_g’,lambda,options,AA,G,P,H,x_0,s,options); % Find x x=fminsearch(’Bspline_penalty_g’,x_0,options,AA,G,P,H,lambda); x=[0;x]; where function e=GCV_g(lambda,AA,G,P,H,b_0,s,options) % GENERAL CROSS VALIDATION
% if lambda>0 b=fminsearch(’Bspline_penalty_g’,b_0,options,AA,G,P,H,lambda); b=[0;b]; W=AA*exp(-G*b); temp=AA*G;
for i=1:length(W)
Xg(i,:)=(-W(i)).*(temp(i,:)); end
Yg=P-W+Xg*b;
e=GCV(lambda,Xg,Yg,H); else %constrain: lambda>0
e=10^12; end
and
function [f]=Bspline_penalty_g(x,C,F,b,H,lambda) % f is the quantity to minimize
%
x=[0;x];
Pi=C*exp(-F*x);
f=(((b-Pi)’)*(b-Pi))+lambda*((x’)*H*x);
Notice that GCV_Gcall B-SPLINE_PENALTY_Gin a minimization procedure. Moreover, as above, we have imposed β1= 0 and the minimization procedures involve only parameters β2, · · · , βs.
• B-spline (parametrized function: f ) From theMAINroutine we have
x_0=zeros(s,1); %initial value
x=fminsearch(’Bspline_f’,x_0,options,AA,IG,P); where
function [f]=Bspline_f(x,C,F,b) % f is the quantity to minimize %
Pi=C*exp(-F*x); f=((b-Pi)’)*(b-Pi);
Notice that (13) implies d(0) = 1; thus we do not impose any restriction to β1and the minimization
procedure involves β1, · · · , βs.
• B-spline with Penalty (parametrized function: f )
This case is similar to the B-spline with Penalty (parametrized function: g) case. We have H=penalty(s,KPa); % Penalty Term
% Find the optimal Penalty Parameter x_0=ones(s,1); %initial value
lambda=10^3; %initial value
lambda=fminsearch(’GCV_f’,lambda,options,AA,IG,P,H,x_0,s,options); % Find x
where
function e=GCV_f(lambda,X,IG,P,H,b_0,s,options) % GENERAL CROSS VALIDATION
% if lambda>0 b=fminsearch(’Bspline_penalty_f’,b_0,options,X,IG,P,H,lambda); W=X*exp(-IG*b); temp=X*IG; for i=1:length(W) Xf(i,:)=(-W(i)).*(temp(i,:)); end Yf=P-W+Xf*b; e=GCV(lambda,Xf,Yf,H); else %constrain: lambda>0
e=10^12; end
and
function [f]=Bspline_penalty_f(x,C,F,b,H,lambda) % f is the quantity to minimize
%
Pi=C*exp(-F*x);
f=(((b-Pi)’)*(b-Pi))+lambda*((x’)*H*x);
5
Numerical Results
The aim of this section is to present the models’performances, considering both computational cost and approximation results. Numerical experiments have been carried out on a Personal Computer with a Pentium IV (2.40 Ghz) processor and 768 MB of RAM, and using MATLAB7.0 (R14).
5.1
Btp Case
In this section we deal with the Btp case, which are Italian coupon bonds with semi-annual payments. In the numerical experiments we have considered the Btp quotation with value date 19thDecember 2006 (see Table 2).
First of all we consider the computational cost: the “Building Matrices and Vectors” step, which does not depend on the fitting model considered, takes approximately 8.5 seconds, while in Table 3 we report the elapsed time of the third step of theMAIN routine (i.e., the “Curve Fitting” steps) for each method considered. Moreover, in the same table we present the residuals |E| of the minimization problems (9) for the Nelson Siegel model, (15) for the B-spline methods, and (16) for the B-spline with penalty methods; for this last class of methods, we also report the best λ computed with the GCV procedure.
From this table, it seems that the best choice from a computational point of view is the B-spline model with the parametrization of the discount function, while if we consider the residual, the best choice seems to be the B-spline method (f ), i.e., we parametrize the instantaneous forward rate function f using B-spline basis functions. Moreover, the B-spline methods with penalty (g) and (f ) have a great computational cost: this
Table 2: Btp
Value Clean Price Expiration Annual Rate Value Clean Price Expiration Annual Rate
(%) (%) 100 99.94 01/15/2007 2.75 100 105.91 08/01/2011 5.25 100 100.36 02/01/2007 6.75 100 99.57 09/15/2011 3.75 100 100.17 03/01/2007 4.50 100 105.31 02/01/2012 5.00 100 99.7 06/01/2007 3.00 100 104.81 02/01/2013 4.75 100 101.6 07/01/2007 6.75 100 102.22 08/01/2013 4.25 100 101 10/15/2007 5.00 100 102.31 08/01/2014 4.25 100 101.89 11/01/2007 6.00 100 102.27 02/01/2015 4.25 100 99.77 01/15/2008 3.50 100 98.6 08/01/2015 3.75 100 98.92 02/01/2008 2.75 100 98.19 08/01/2016 3.75 100 101.63 05/01/2008 5.00 100 110.78 08/01/2017 5.25 100 98.18 06/15/2008 2.50 100 101.96 02/01/2019 4.25 100 99.69 09/15/2008 3.50 100 104.24 02/01/2020 4.50 100 98.37 04/15/2009 3.00 100 96.16 08/01/2021 3.75 100 98.44 02/01/2009 3.00 100 158.78 11/01/2023 9.00 100 101.6 05/01/2009 4.50 100 141.42 11/01/2026 7.25 100 99.91 06/15/2009 3.75 100 131.7 11/01/2027 6.50 100 101.25 11/01/2009 4.25 100 115.18 11/01/2029 5.25 100 97.74 01/15/2010 3.00 100 127.09 05/01/2031 6.00 100 96.59 06/15/2010 2.75 100 124.27 02/01/2033 5.75 100 106.03 11/01/2010 5.50 100 112.94 08/01/2034 5.00 100 98.73 03/15/2011 3.50 100 96.3 02/01/2037 4.00
Table 3: Btp Data: Timing and Residual
Method Curve Fitting Residual Best λ (elapsed time)
Nelson Siegel 0.406 seconds 1.058905 -Bspline (d) 0.313 seconds 0.599565 -Bspline+Penalty(d) 0.531 seconds 0.858068 15442.197764 Bspline (g) 0.437 seconds 0.544416 -Bspline+Penalty (g) 28.922 seconds 0.560438 1273.437583 Bspline (f ) 3.234 seconds 0.471925 -Bspline+Penalty (f ) 80.062 seconds 1.641775 52148137.527466
is due to the GCV-minimization procedure, which call another minimization procedure at each step (see Section 3.2.2).
Then we show the plots of the discount function d (Figure 2), of the spot rate function h (Figure 3), where
h(τ ) := −log(d(τ )) τ , and of the instantaneous forward rate function f (Figure 4).
These figures suggest the following observations, which are important in order to understand the behavior of the different data-fitting techniques:
Figure 2: Btp Case 0 5 10 15 20 25 30 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 Discount Function Nelson−Siegel Bspline (d) Bspline + Penalty (d) Bspline (g) Bspline + Penalty (g) Bspline (f) Bspline + Penalty (f) Figure 3: Btp Case 0 5 10 15 20 25 30 0.036 0.037 0.038 0.039 0.04 0.041 0.042 0.043 Spot Rate Nelson−Siegel Bspline (d) Bspline + Penalty (d) Bspline (g) Bspline + Penalty (g) Bspline (f) Bspline + Penalty (f)
Figure 4: Btp Case 0 5 10 15 20 25 30 0.034 0.036 0.038 0.04 0.042 0.044 0.046
Instantaneous Forward Rate
Nelson−Siegel Bspline (d) Bspline + Penalty (d) Bspline (g) Bspline + Penalty (g) Bspline (f) Bspline + Penalty (f) Figure 5: Btp Case 0 5 10 15 20 25 30 35 40 −0.8 −0.6 −0.4 −0.2 0 0.2 0.4 0.6 Comparison Nelson−Siegel Bspline (d) Bspline + Penalty (d) Bspline (g) Bspline + Penalty (g) Bspline (f) Bspline + Penalty (f)
• discount function
from Figure 2 it seems that the different methods for curve construction produce very similar plots; • spot rate function
from Figure 3 it seems that the Nelson Siegel and the B-spline with penalty (f ) models behave quite different from the other methods after the 15-years point. If we consider the last method, this behavior is due to the large penalty parameter computed with the GCV procedure (see Table 3);
• instantaneous forward rate function
the plots presented in Figure 4 are quite different; the best choices seem to be the Nelson-Siegel and the B-spline with penalty (f ) models, since for the other methods the plotted curves decrease too rapidly after the 20-years point (see [3] for further details).
Finally we compare our models with the initial data. More precisely, let eP be the vector containing the prices of the considered Btp at the value date, computed using the discount function obtained with a data-fitting technique, then in Figure 5 we plot the values
P (i) − eP (i), i = 1, · · · , 42, where 42 is the number of Btp considered.
5.2
Libor and Swap Case
In this section we present the same data as above, considering Libor and Swap rates with value date 21thSeptember 2006 (see Tables 4 and 5).
Table 4: Libor
Duration (days) Rate (%) Duration (months) Rate (%) 1 day 3.04188 1 month 3.19325 7 days 3.06275 2 months 3.2935 15 days 3.06563 3 months 3.37025
Table 5: Swap
Duration (year) Rate (%) Duration (year) Rate (%) 1 3.808 8 3.973 2 3.88 9 4.011 3 3.883 10 4.029 4 3.892 15 4.066 5 3.906 20 4.202 6 3.925 25 4.218 7 3.947 30 4.214
As above, in Table 6 we compare the different methods considering both the computational cost and the residuals |E|, respectively. The results are in agreement with the Btp case. Notice that the elapsed time for the “Building Matrices and Vectors” step is approximately 3.5 seconds.
Moreover, in Figures 6, 7 and 8 we show the plots of the discount function, of the spot rate function and of the instantaneous forward rate function, respectively. These figures suggest the following observations:
• discount function
Figure 6: Libor and Swap Case 0 5 10 15 20 25 30 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 Discount Function Nelson−Siegel Bspline (d) Bspline + Penalty (d) Bspline (g) Bspline + Penalty (g) Bspline (f) Bspline + Penalty (f)
Figure 7: Libor and Swap Case
0 5 10 15 20 25 30 0.036 0.037 0.038 0.039 0.04 0.041 0.042 0.043 Spot Rate Nelson−Siegel Bspline (d) Bspline + Penalty (d) Bspline (g) Bspline + Penalty (g) Bspline (f) Bspline + Penalty (f)
Figure 8: Libor and Swap Case 0 5 10 15 20 25 30 0.034 0.036 0.038 0.04 0.042 0.044 0.046 0.048
Instantaneous Forward Rate
Nelson−Siegel Bspline (d) Bspline + Penalty (d) Bspline (g) Bspline + Penalty (g) Bspline (f) Bspline + Penalty (f)
Figure 9: Libor and Swap Case
0 5 10 15 20 25 30 35 40 −8 −6 −4 −2 0 2 4 x 10−3 Comparison Nelson−Siegel Bspline (d) Bspline + Penalty (d) Bspline (g) Bspline + Penalty (g) Bspline (f) Bspline + Penalty (f)
Table 6: Libor and Swap Data: Timing and Residual Method Curve Fitting Residual Best λ
(elapsed time)
Nelson Siegel 0.125 seconds 0.011997 -Bspline (d) 0.157 seconds 0.006932 -Bspline+Penalty(d) 0.360 seconds 0.011889 4.089915 Bspline (g) 0.157 seconds 0.007672 -Bspline+Penalty (g) 18.828 seconds 0.012356 22.205400 Bspline (f ) 0.920 seconds 0.005384 -Bspline+Penalty (f ) 44.875 seconds 0.006086 1.419115 • spot rate function
from Figure 7 it seems that the B-spline with penalty (g) method (and not the B-spline with penalty (f ), as in the Btp case) behaves like the Nelson Siegel model: this is due to the different penalty parameter value, since in the Btp case the largest parameter is the one of the B-spline with penalty (f ) method, while in the Libor and Swap case it is the one computed for the B-spline with penalty (g) model (see Table 6);
• instantaneous forward rate function
Figure 8 suggests that the best plots are the ones derived from the Nelson Siegel and the B-spline with penalty methods, since the classical B-spline methods produce curves which increase or decrease too rapidly after the 20-years points.
Finally, as in the previous subsection, in Figure 9 we compare our models with the initial data.
6
Conclusion
In this paper we have shown how to fit smoothing splines or exponential curves (like the Nelson Siegel model) to the term structure of interest rates. We have described the MATLABcode and we have presented numerical results, comparing the different models. From the numerical experiments, it is clear that it is not possible to find the best model: the choice of which method to use is subjective and need to be decided on a case by case basis. In fact, if we consider the residual, the best choice is to use smoothing splines which parametrized the instantaneous forward rate function f : this result is in agreement with [2]. Moreover, if we are interested in the instantaneous forward rate function, the best choice seems to be the Nelson Siegel model. Following [2], we have also presented methods with a penalty term: these methods behaves better than the classical B-spline methods if we consider the instantaneous forward rate function, but they are characterized by high computational costs due to the GCV procedure in order to compute the best penalty parameter. Notice that in [2] the authors use a great number of knot points and thus the penalty term is necessary in order to obtain well-behaved curves, while in this paper we have followed [6] and [8], using few knot points.
A
Building Matrices and Vectors: Examples
The aim of this section is to explain Sections 2 and 4.2 with two examples: one for the Btp case and the other for the Libor and Swap case.
A.1
Btp Case
In this section we consider the Btp quotation with value date 19thDecember 2006 reported in Table 7. Table 7: Btp
Facial Value Clean Price Expiration Annual Interest Rate (%) 100 99.94 01/15/2007 2.75
100 99.7 06/01/2007 3.00 100 99.77 01/15/2008 3.50 100 101.63 05/01/2008 5.00 100 98.18 06/15/2008 2.50 We now consider the first bond of Table 7: following Section 2.1, we have • m1= 1, i.e, there is only 1 payment date for the first bond;
• F1= 100, i.e., the facial value;
• C1= 1002.75%2 = 1.375, i.e., the facial value multiplied by the semi-annual interest rate.
Reasoning in the same way for all the elements of Table 7, we have
A = 101.375 0 0 101.5 0 0 1.75 1.75 101.75 2.5 2.5 102.5 1.25 1.25 101.25 . Moreover, we have BP = 01/16/07 06/01/07 01/16/07 07/16/07 01/15/08 05/01/07 11/01/07 05/01/08 06/15/07 12/17/07 06/16/08 and thus T = 28 0 0 164 0 0 28 209 392 133 317 499 178 363 545 ,
where we recall that T contains the days between the value date and the payment dates of the Btp BP , computed according to the actual/365 day-count convention.
Now we can compute vector T T , i.e., the vector containing all the non-zero elements of T T T = 28 133 164 178 209 317 363 392 499 545 ;
and thus we define matrix AA as follows AA = 101.375 0 0 0 0 0 0 0 0 0 0 0 101.5 0 0 0 0 0 0 0 1.75 0 0 0 1.75 0 0 101.75 0 0 0 2.5 0 0 0 2.5 0 0 102.5 0 0 0 0 1.25 0 0 1.25 0 0 101.25 .
Notice that in the MATLABcode (see Section 4) both the elements of T and T T are computed in years and not in days. Moreover, in order to compute the payment dates (and thus BP ), we have to check if each date is a business day: in our code, we have used the MATLABfunctionISBUSDAY, which is based on the US calendar (that is why the 15thJanuary 2007 is considered as holiday and thus the payment date of the first bond is 16thJanuary 2007).
Finally, if we sum the clean prices and the accrued interests, we obtain
P = 101.104617486339 99.848351648352 101.252240437158 102.292983425414 98.207472527473 .
A.2
Libor and Swap
Now we consider the Libor and Swap interest rates with value date 21thSeptember 2006 reported in
Tables 8 and 9, respectively.
Table 8: Libor Duration Interest Rate (%)
1 day 3.04188 7 days 3.06275 15 days 3.06563
Table 9: Swap Duration Interest Rate (%)
1 year 3.808 2 years 3.88 3 years 3.883 First of all, we consider the Libor case: from Table 8, we obtain
LR = 0.0304188 0.0306275 0.0306563 , LM = 1 7 15 ;
thus, following Section 2.2, we have
LP = 09/22/06 09/28/06 10/06/06 , T1=
and so
A1=
1.000084 1.000595 1.001278 ;
notice that in this example the elements of LM are computed in days, while the elements of T1 are
computed in years, using the actual/360 day-count convention. Now we consider the Swap case: from Table 9 we obtain
SR = 0.03808 0.0388 0.03883 and SP = 09/21/07 09/21/07 09/22/08 09/21/07 09/22/08 09/21/09 .
Recalling that the value date is 09/21/06, using the 30/360 day-count convention, we have
T2= 1 0 0 1 2.002778 0 1 2.002778 3 .
We recall that in order to compute the payment dates, we have to check if each date is a business day, otherwise we have to look for the next business day: this is the reason why T2(2, 2) = 2.002778.
Finally, following Section 2.2, we calculate matrix A2
A2= 1.03808 0 0 0.0388 1.038908 0 0.03883 0.038938 1.038722 .
If we now compute vector T T , i.e., the vector containing all the non-zero elements of T1and T2
T T = 0.002778 0.019444 0.041667 1 2.002778 3 , we can define matrix AA as follows
AA = 1.000084 0 0 0 0 0 0 1.000595 0 0 0 0 0 0 1.001277 0 0 0 0 0 0 1.03808 0 0 0 0 0 0.0388 1.038908 0 0 0 0 0.03883 0.038938 1.038722 ;
Notice that in theMATRIX_VECTOR_LS routine (see Section 4.2) the elements of A1and A2are stored
in a unique matrix A A = 1.000084 0 0 1.000595 0 0 1.001278 0 0 1.03808 0 0 0.0388 1.038908 0 0.03883 0.038938 1.038722 ;
in the same way, the elements of T1and T2are stored in T as follows T = 0.002778 0 0 0.019444 0 0 0.041667 0 0 1 0 0 1 2.002778 0 1 2.002778 3 .
B
Bspline Routines
In this section, we present the following routines:
1. BSPLINEBASIScomputes the value of B-spline basis functions;
2. DBSPLINEBASIScomputes the value of the first derivative of the B-spline basis functions; 3. DDBSPLINEBASIScomputes the value of the second derivative of the B-spline basis functions; 4. INTBSPLINEBASISreturns the integrals of the B-spline basis functions;
5. GAULEGcomputes the nodes and weights of the Gauss-Legendre quadrature formula.
B.1
B
SPLINEB
ASISfunction F=BsplineBasis(k,r,x,d) % B-spline Basis Function
% k -> consider the kth basis function % r -> r-1 is the degree of the spline
% x -> is the the point in which I evaluate the function % d -> augmented knot points
% if (k+r-1<5 & k<5) if (x<d(5)) F=((d(5)-x)/(d(5)-d(1)))^(r-1); else F=0; end
elseif (k+1>length(d)-4 & k+r>length(d)-4) if (x>=d(end-4)) F=((x-d(end-4))/(d(end)-d(end-4)))^(r-1); else F=0; end else if r==1 if (x>=d(k) & x<d(k+1)) F=1; else
F=0; end else f1=BsplineBasis(k,r-1,x,d); f2=BsplineBasis(k+1,r-1,x,d); F=((f1*(x-d(k)))/(d(k+r-1)-d(k)))+... ((f2*(d(k+r)-x))/(d(k+r)-d(k+1))); end end
B.2
DB
SPLINEB
ASIS function F=dBsplineBasis(k,r,x,d)% First Derivative of B-spline Basis Function % k -> consider the kth basis function
% r -> r-1 is the degree of the spline
% x -> is the vector of the points in which I evaluate the function % d -> augmented knot points (necessary to define the basis function) % if r<2 F=0; else if (k+r-1<5 & k<5) if (x<d(5)) F=(r-1)*(((d(5)-x)/(d(5)-d(1)))^(r-2))*(-1/(d(5)-d(1))); else F=0; end
elseif (k+1>length(d)-4 & k+r>length(d)-4) if (x>=d(end-4)) F=(r-1)*(((x-d(end-4))/(d(end)-d(end-4)))^(r-2))*... (1/(d(end)-d(end-4))); else F=0; end else f1=BsplineBasis(k,r-1,x,d); f2=BsplineBasis(k+1,r-1,x,d); d1=dBsplineBasis(k,r-1,x,d); d2=dBsplineBasis(k+1,r-1,x,d); F=((f1+(x-d(k))*d1)/((d(k+r-1)-d(k))))+... ((-f2+(d(k+r)-x)*d2)/(d(k+r)-d(k+1))); end end end
B.3
DDB
SPLINEB
ASISfunction F=ddBsplineBasis(k,r,x,d)
% Second Derivative of B-spline Basis Function % k -> consider the kth basis function
% r -> r-1 is the degree of the spline
% x -> is the vector of the points in which I evaluate the function % d -> augmented knot points (necessary to define the basis function) % if r<3 F=0; else if (k+r-1<5 & k<5) if (x<d(5)) F=(r-1)*(r-2)*(((d(5)-x)/(d(5)-d(1)))^(r-3))*... ((-1/(d(5)-d(1)))^2); else F=0; end
elseif (k+1>length(d)-4 & k+r>length(d)-4) if (x>=d(end-4)) F=(r-1)*(r-2)*(((x-d(end-4))/(d(end)-d(end-4)))^(r-3))*... ((1/(d(end)-d(end-4)))^2); else F=0; end else d1=dBsplineBasis(k,r-1,x,d); d2=dBsplineBasis(k+1,r-1,x,d); dd1=ddBsplineBasis(k,r-1,x,d); dd2=ddBsplineBasis(k+1,r-1,x,d); F=((d1+(x-d(k))*dd1+d1)/((d(k+r-1)-d(k))))+... ((-d2+(d(k+r)-x)*dd2-d2)/(d(k+r)-d(k+1))); end end end
B.4
INTB
SPLINEB
ASISfunction IG=intBsplineBasis(T,s,KP,d)
% Integral of the cubic B-spline basis function % IG[i,j]=int_{0}^{T(i)} B(j) dx
% where B(j) is the jth basis function % KP -> Knot Points
% d -> Augmented Knot Points %
IG=zeros(length(T),s); for i=1:length(T)
for ii=1:length(KP) if T(i)>KP(ii) if ii<length(KP) t=min(T(i),KP(ii+1)); else t=T(i); end [x,w]=gauleg(KP(ii),t,4); for j=1:s for jj=1:length(x) temp=BsplineBasis(j,4,x(jj),d); IG(i,j)=IG(i,j)+temp*w(jj); end end
else %the integral between 0 and T(i) has already been computed break %stop the ii-loop and change the value of i
end end end
B.5
GAULEGfunction [xx,ww]=gauleg(a,b,n) % Gauss-Legendre quadrature formula
% Computes the Gauss-Legendre nodes xx and weights ww % of order n for the interval (a,b)
% m=(n+1)/2; xm=0.5*(b+a); xl=0.5*(b-a); for i=1:m z=cos(pi*(i-0.25)/(n+0.5)); while 1 % do untill break
p1=1.0; p2=0.0; for j=1:n p3=p2; p2=p1; p1=((2.0*j-1.0)*z*p2-(j-1.0)*p3)/j; end pp=n*(z*p1-p2)/(z*z-1.0); z1=z; z=z1-p1/pp;
if (abs(z-z1)<eps), break, end end
xx(i)=xm-xl*z; xx(n+1-i)=xm+xl*z;
ww(i)=2.0*xl/((1.0-z*z)*pp*pp); ww(n+1-i)=ww(i);
end
C
M
ATLAB
Routines
The following informations are taken from the MATLAB[13] manual.
C.1
DAYSDIF- Days between dates for any day count basis
DAYSDIF returns the number of days between D1 and D2 using the given day count basis. Enter dates as serial date numbers or date strings.
D = daysdif(D1, D2) D = daysdif(D1, D2, Basis) Inputs:
D1 - [Scalar or Vector] of dates. D2 - [Scalar or Vector] of dates.
Optional Inputs:
Basis - [Scalar or Vector] of day-count basis. Valid Basis are:
0 = actual/actual (default) 1 = 30/360 2 = actual/360 3 = actual/365 4 - 30/360 (PSA compliant) 5 - 30/360 (ISDA compliant) 6 - 30/360 (European) 7 - act/365 (Japanese)
C.2
DATENUM- Serial date number
N = DATENUM(S) converts the string or date vector S into a serial date number. Date numbers are serial days where 1 corresponds to 1-Jan-0000.
Certain formats may not contain enough information to compute a date number. In those cases, missing values will in general default to 0 for hours, minutes and seconds, will default to January for the month, and 1 for the day of month. The year will default to the current year. Date strings with 2 character years are interpreted to be within the 100 years centered around the current year.
N = DATENUM(Y,M,D) and N = DATENUM([Y,M,D]) return the serial date numbers for correspond-ing elements of the Y,M,D (year,month,day) arrays. Y, M, and D must be arrays of the same size (or any can be a scalar).
Examples:
n = datenum(2001,12,19) returns n = 731204.
n = datenum(2001,12,19,18,0,0) returns n = 731204.75.
C.3
FMINSEARCH- Multidimensional unconstrained nonlinear minimization
X = FMINSEARCH(FUN,X0) starts at X0 and attempts to find a local minimizer X of the function FUN. FUN accepts input X and returns a scalar function value F evaluated at X. X0 can be a scalar, vector or matrix.
X = FMINSEARCH(FUN,X0,OPTIONS) minimizes with the default optimization parameters replaced by values in the structure OPTIONS, created with the OPTIMSET function. FMINSEARCH uses these options: Display, TolX, TolFun, MaxFunEvals, MaxIter, FunValCheck, and OutputFcn.
[X,FVAL]= FMINSEARCH(...) returns the value of the objective function, described in FUN, at X. [X,FVAL,EXITFLAG] = FMINSEARCH(...) returns an EXITFLAG that describes the exit condition of FMINSEARCH. Possible values of EXITFLAG and the corresponding exit conditions are:
1 - FMINSEARCH converged to a solution X.
0 - Maximum number of function evaluations or iterations reached. -1 - Algorithm terminated by the output function.
FMINSEARCH uses the Nelder-Mead simplex (direct search) method.
C.4
ISBUSDAY- True for dates that are business days
T = ISBUSDAY(D,HOL,WEEKEND)Inputs:
D - a vector of dates in question. Optional Inputs:
HOL - a user-defined vector of holidays. The default is a predefined US holidays (in holidays.m).
WEEKEND - a vector of length 7, containing 0 and 1, with the value of 1 to indicate weekend day(s). The first element of this vector corresponds to Sunday. Thus, when Saturday and Sunday are weekend then WEEKDAY = [1 0 0 0 0 0 1]. The default is Saturday and Sunday weekend.
Outputs:
T = - 1 if D is business day and 0 if otherwise.
C.5
OPTIMSET- Create/alter OPTIM OPTIONS structure
OPTIONS = OPTIMSET(’PARAM1’,VALUE1,’PARAM2’,VALUE2,...) creates an optimization options structure OPTIONS in which the named parameters have the specified values. Any unspecified parameters are set to [] (parameters with value [] indicate to use the default value for that parameter when OPTIONS is passed to the optimization function).
OPTIMSET PARAMETERS for MATLAB: Display - Level of display [ off | iter | notify | final ]
MaxIter - Maximum number of iterations allowed [ positive scalar ] TolFun - Termination tolerance on the function value [ positive scalar ] TolX - Termination tolerance on X [ positive scalar ]
FunValCheck - Check for invalid values, such as NaN or complex, from user-supplied functions [ off | on ] OutputFcn - Name of installable output function [ function ]. This output function is called by the solver after each iteration.
References
[1] Revisiting “The Art and Science of Curve Building”, FINCAD News, December 2005 Issue.
[2] M. Fisher, D. Nychka, D. Zervos, “Fitting the Term Structure of Interest Rates with Smoothing Splines”, Board of Governors of the Federal Reserve System (U.S.), Finance and Economics Dis-cussion Series, Vol. 95-1, 1994.
[3] P. Hagan, G. West, “Interpolation Methods for Curve Construction”, Applied Mathematical Finance, Vol. 13-2, 2006.
[4] M. Ioannides, “A Comparison of Yield Curve Estimation Techniques Using UK Data”, Journal of Banking & Finance, Vol. 27, 2003.
[5] J. James, N. Webber, Interest Rate Modelling, John Wiley & Sons, 2000.
[6] J.H. McCulloch, “Measuring the Term Structure of Interest Rate”, Journal of Business, Vol. XLIV, 1971.
[7] J.H. McCulloch, “The Tax-Adjusted Yield Curve”, The Journal of Finance, Vol. XXX-3, 1975. [8] S.K. Nawalkha, G.M. Soto, N.K. Beliaeva, Interest Rate Risk Modeling: The Fixed Income Valuation
Course, John Wiley & Sons, 2005.
[9] C.R. Nelson, A.F. Siegel, “Parsimonious Modeling of Yield Curves”, Journal of Business, Vol. 60-4, 1987.
[10] A. Quarteroni, R. Sacco, F. Saleri, Numerical Mathematics, Springer, 2000.
[11] A.F. Siegel, C.R. Nelson, “Long-Term Behaviour of Yield Curves”, Journal of Financial and Quanti-tative Analysis, Vol. 23-1, 1988.
[12] O.A. Vasicek, H.G. Fong, “Term Structure Modeling Using Exponential Splines”, The Journal of Finance, Vol. XXXVII-2, 1982.