# Computing yield-to-maturity

This example is taken from Chapter 14 of Gilli/Maringer/Schumann, 2011. The functions `ytm` and `vanillaBond` are included in the NMOF package (since version 0.27-1).

```require("NMOF")
```

A plain-vanilla bond can be represented as a list of cashflows, `cf`, with associated payment dates. The bond's theoretical price `b0` is the present value of these payments. As an example, we calculate `b0` with a single yield `y`.

```cf <- c(5, 5, 5, 5, 5, 105) ## cashflows
times <- 1:6                ## times to payment
y <- 0.047                  ## the "true" yield
b0 <- sum(cf/(1 + y)^times)
b0
```

Since `y` is below the coupon rate, the theoretical price should be higher than par.

``` 101.5374
```

The function `vanillaBond` shows a simple implementation for computing the present value of cashflows.

```vanillaBond <- function(cf, times, df, yields) {
if (missing(times))
times <- seq_len(length(cf))
if (missing(df))
df <- 1/(1+yields)^times
drop(cf %*% df)
}
```

Some examples.

```cf <- c(rep(5, 9), 105)
vanillaBond(cf, yields = 0.05)
vanillaBond(cf, yields = 0.03)
```
``` 100
 117.0604
```

If only a single yield is given, the function acts as if the term structure were flat. But we did not explicitly check for this case; R's recycling rule will handle this for us. Here is an example to show this more clearly:

```2^(1:5)
```
```  2  4  8 16 32
```

(the `^` operator has precedence over `:` which is why we need the parentheses.)

Another example; this time we value the bond according a Nelson-Siegel curve. With the given parameters, the curve should be flat.

```vanillaBond(cf, 1:10, yield = NS(c(0.03,0,0,2), 1:10))
```
``` 117.0604
```

Back to our problem: to recover `y` from `b0`, we append `b0` to the cashflow vector, but switch its sign (since we need to buy the bond). The is now to find discount factors for which the sum over all cashflows (the net present value) is just zero.

```cf <- c(-b0, cf); times <- c(0, times)
data.frame(times=times, cashflows=cf)
```
```
times cashflows
1     0 -101.5374
2     1    5.0000
3     2    5.0000
4     3    5.0000
5     4    5.0000
6     5    5.0000
7     6  105.0000
```

The function `ytm` evaluates the derivative of the discounted cashflows analytically; `ytm2` uses a finite difference.

```ytm <- function(cf, times, y0 = 0.05,
tol = 1e-05, h = 1e-05, maxit = 1000L) {
dr <- 1
for (i in seq_len(maxit)) {
y1 <- 1 + y0
g <- cf / y1 ^ times
g <- sum(g)
t1 <- times - 1
dg <- times * cf * 1/y1 ^ t1
dg <- sum(dg)
dr <- g/dg
y0 <- y0 + dr
if (abs(dr) < tol)
break
}
y0
}
ytm2 <- function(cf, times, y0 = 0.05,
tol = 1e-04, h = 1e-08, maxit = 1000L) {
dr <- 1
for (i in seq_len(maxit)) {
y1 <- 1 + y0
g <- sum(cf/y1^times)
y1 <- y1 + h
dg <- (sum(cf/y1^times) - g)/h
dr <- g/dg
y0 <- y0 - dr
if (abs(dr) < tol)
break
}
y0
}
system.time(for (i in 1:2000) ytm(cf, times, y0=0.06))
system.time(for (i in 1:2000) ytm2(cf, times, y0=0.06))
ytm(cf, times, y0=0.062, maxit = 5000)
ytm2(cf, times, y0=0.062, maxit = 5000)
```
```   user  system elapsed
0.064   0.000   0.064
user  system elapsed
0.052   0.000   0.051
 0.0470007
 0.047
```

The only reason for not using a finite difference is that with extreme rates or extremely far off starting values, the numerically-evaluated derivative is more stable. But note that far-off really means far-off: something like the true yield is 5 percent and we use a starting value of 50 percent. (A reasonable starting value is the coupon divided by the price.)

```(initial.value <- 5/b0)
```
```ytm(cf,  times, y0 = 0.7, maxit = 5000)
ytm(cf,  times, y0 = initial.value)
ytm2(cf, times, y0 = 0.7, maxit = 5000)
ytm2(cf, times, y0 = initial.value)
```
``` 0.04699928
 0.04700013
 Inf
 0.047
```