RATS 10.1
RATS 10.1

BONDS.RPF is an illustration of the use of a FUNCTION to evaluate a complicated objective function. In this case, it evaluates the present value of a bond with multiple coupon payments as part of the estimation of a yield curve using a portfolio of bonds. See also the BONDSPLINE.RPF example which does a similar analysis, but with a cubic spline.

 

Suppose that we want to estimate a yield-curve using data from many bonds. For each bond, we have the following information:

          Periods to maturity (series MATURITY).

          The coupon (series COUPON).

          The current market value (series VALUE).

 

The data set consists of US Treasury bonds, which have semi-annual coupons, but with the coupon stated in annual terms. The maturity series also provides annual information. The coupon is divided by two and the maturity multiplied by 2 to give values for the actual coupon period.

 

open data bonds.xls

data(format=xls,org=cols) / coupon value maturity

compute nbonds=%allocend()

*

set coupon   = coupon / 2

set maturity = maturity * 2

 

 

We will model the function \(\delta \left( T \right)\), which gives the present value of $1 at \(T\), by

 

\begin{equation} \delta \left( T \right) = \exp \left( { - T\left( {a_0 + a_1 T + a_2 \max \left( {0,T - C} \right)} \right)} \right) \label{eq:BondsDiscount} \end{equation}

 

The term multiplying \(–T\) is a piecewise linear function with a join point at \(C\) (which will be called CUSP in the program).

We need to minimize over the parameters of \eqref{eq:BondsDiscount} the gaps between the present values predicted by the model and the actual market values. However, the present value for a bond (which is a single entry in the data set) requires a sum over all the coupons due until maturity. We can’t use annuity formulas for this because the interest rate isn't fixed. So we create a FUNCTION that takes as its argument the number of the bond and uses the data in the three series described above to compute the present value of the bond given the parameter settings being evaluated. The start of is:

 

function BondPV bond

type real BondPV

type integer bond

local real mdate cdate

 

In practice, you would start with those first three lines and add the LOCAL after you've written the rest, once you know what other local variables you will need. The FUNCTION code needs to give a value to BondPV before it returns. In this case, we will keep setting and resetting the value of this as we add terms to the present value. To start, we take the discounted value of the redemption at maturity:

 

compute mdate=maturity(bond)

compute BondPV=100.0 * $

    exp(-mdate*(a0+mdate*a1+%plus(mdate-cusp)*a2))

 

The next part of this walks backwards through the coupon payments as long as there are full coupon periods, adding the discounted value to what's in BondPV.

 

compute cdate=mdate

while (cdate > 0.0) {

   compute BondPV=BondPV + coupon(bond) * $

     exp(-cdate*(a0+cdate*a1+%plus(cdate-cusp)*a2))

   compute cdate=cdate-1

}

 

And finally we adjust for the simple interest payable by the purchaser for the initial coupon. CDATE will be –(fraction of period). Note that you don’t need a RETURN, just END to indicate that the function is finished.

 

compute BondPV=BondPV+coupon(bond)*cdate

end

 

With that done, we still need a FRML which gives the value for each entry. Here, all this FRML needs to do is return the value of the function for entry T. The first three lines are best placed before the FUNCTION since it needs A0, A1, A2 and CUSP:

 

Before the FUNCTION definition:

 

nonlin a0 a1 a2

compute a0=.030,a1=a2=0.0

compute cusp=2.0

 

After the FUNCTION definition:

 

frml BondPrice value = BondPV(t)

*

nlls(robusterrors,frml=BondPrice) value


This graphs the estimated yield curve from maturities of 0->10 periods (5 years). (TESTM is the "test" maturity, which has values running over (0,10]. After the present value function is evaluated, the maturity is switched to years and the annual yield computed.

 

set testm 1 50  = .20*t

set pvalue 1 50 = $

   exp(-testm*(a0+testm*a1+%plus(testm-cusp)*a2))

set testm 1 50  = testm/2

set yield 1 50  = -log(pvalue)/testm

scatter(style=line,header="Yield Curve",hlabel="Years",vlabel="Yield")

# testm yield
 

 

Full Program

 

open data bonds.xls

data(format=xls,org=cols) / coupon value maturity

compute nbonds=%allocend()

*

* The data set consists of US Treasury bonds, which have semi-annual

* coupons, but with the coupon stated in annual terms. The maturity

* series also provides annual information. The coupon is divided by two

* and the maturity multiplied by 2 to give values for the actual coupon

* period.

*

set coupon   = coupon / 2

set maturity = maturity * 2

*

nonlin a0 a1 a2

compute a0=.030,a1=a2=0.0

compute cusp=2.0

*

function BondPV bond

type real BondPV

type integer bond

*

local real mdate cdate

*

compute mdate=maturity(bond)

compute BondPV=100.0 * $

    exp(-mdate*(a0+mdate*a1+%plus(mdate-cusp)*a2))

*

* Walk backwards through the coupons.

*

compute cdate=mdate

while (cdate > 0.0) {

   compute BondPV=BondPV + coupon(bond) * $

     exp(-cdate*(a0+cdate*a1+%plus(cdate-cusp)*a2))

   compute cdate=cdate-1

}

*

* Adjust for simple interest payable by the purchaser for the initial

* coupon. cdate will be -(fraction of period).

*

compute BondPV=BondPV+coupon(bond)*cdate

end

*

frml BondPrice value = BondPV(t)

*

nlls(robusterrors,frml=BondPrice) value

*

* Graph the estimated yield curve from maturities of 0->10 periods (5

* years). After the present value function is evaluated, the maturity is

* switched to years and the annual yield computed.

*

set testm 1 50  = .20*t

set pvalue 1 50 = $

   exp(-testm*(a0+testm*a1+%plus(testm-cusp)*a2))

set testm 1 50  = testm/2

set yield 1 50  = -log(pvalue)/testm

scatter(style=line,header="Yield Curve",hlabel="Years",vlabel="Yield")

# testm yield

 

 

Output

Nonlinear Least Squares - Estimation by Gauss-Newton

Convergence in     3 Iterations. Final criterion was  0.0000000 <=  0.0000100

 

With Heteroscedasticity-Consistent (Eicker-White) Standard Errors

Dependent Variable VALUE

Usable Observations                        20

Degrees of Freedom                         17

Centered R^2                        0.9997669

R-Bar^2                             0.9997394

Uncentered R^2                      0.9999990

Mean of Dependent Variable       106.99531250

Std Error of Dependent Variable    7.35723688

Standard Error of Estimate         0.11876009

Sum of Squared Residuals         0.2397673034

Log Likelihood                        15.8594

Durbin-Watson Statistic                2.3282

 

    Variable                        Coeff      Std Error      T-Stat      Signif

************************************************************************************

1.  A0                            0.028560456  0.000620441     46.03253  0.00000000

2.  A1                            0.001772348  0.000338740      5.23217  0.00000017

3.  A2                           -0.001926927  0.000349390     -5.51511  0.00000003

 

Graph


 


Copyright © 2025 Thomas A. Doan