Skip to content
All library documents

Weekly Coupon Bond Valuation with Interpolated Zero Rates

Article Quant Q&A · Author: PK1998

Summary

The document describes how to revalue a coupon bond each week using a small set of zero-rate observations. It expresses present value as the sum of future coupon and principal cash flows, each multiplied by a discount factor. Under the stated assumption of continuously compounded actual/365 zero rates, the example linearly interpolates rates between available tenor nodes and converts each interpolated rate into a discount factor.

For each valuation date, it computes the remaining time to every payment, assigns zero contribution to payments already made, and sums the discounted future schedule. A worked R example applies this method to a bond with annual coupons and reports weekly present values. The result depends on the assumed rate convention and interpolation approach. The answer explicitly leaves clean-versus-dirty pricing and curve-construction business-day adjustments untreated, and the supplied tenor set requires a flat extrapolation rule beyond its final node.

Key ideas

  • Bond value is calculated by discounting each future coupon and principal payment to the valuation date.
  • The example assumes continuously compounded actual/365 zero rates and linearly interpolates rates between tenor nodes.
  • Payment dates and valuation dates determine each cash flow's remaining time and whether it contributes to the price.
  • The example does not address clean-versus-dirty pricing or business-day adjustments used in curve construction.
  • Results depend on the selected interpolation, extrapolation, compounding, and day-count assumptions.

Tags

Full text
# Pricing coupon bond on weekly basis effectively


# Pricing coupon bond on weekly basis effectively












I have a coupon bond with $NV=20 000 000$ and coupon $4\% p.a.$, assumed the coupon is paid annually (I don't have this stated explicitly). Let's assume, the starting date is 27.4.2015, so the first coupon will be paid next year. My goal is to price this bond every week from the starting date, given $1Y,3Y$ and $10Y$ zero rates:

```
        Date  EUR1Y  EUR3Y  EUR10Y
1 2015-04-27  -0.27  -0.14    0.15
2 2015-05-04  -0.24   0.01    0.40
3 2015-05-11  -0.24   0.08    0.60
4 2015-05-18  -0.24   0.09    0.67
5 2015-05-25  -0.24   0.00    0.53
6 2015-06-01  -0.24   0.00    0.53
```

I know, that there is going to be a lot of interpolation and messy date counting, and I do not know how to proceed effectively in R:

```
nv <- 20000000
c <- nv*0.04

coupons <- rep(c,10)
payments <- seq.Date(as.Date("2015-04-27"),as.Date("2025-04-27"), by='year')
payments2 <- bizdays::adjust.previous(payments, 'weekends')[2:10]
```

## Answer by Kermittfrog (score 1, accepted)

https://quant.stackexchange.com/a/54139

Note: In this text, I will not touch on the topic of dirty vs. clean price. Neither on business day adjustments for the curve construction.

### Definition

The present value of a bond, or its clean price, can be defined is

$$ P(t) = \sum_i^ncD(t,T_i)+D(T_n) $$ Where $c$ is the coupon on the bond (potentially scaled to correct payment frequency), $D(t,T)$ is the discount factor for a cash-flow at $T$, as valued at date $t$.

Effectively, your problem 'boils down' to a curve construction exercise.

### Curve Construction

The valuation of your bond boils down to the definition and repeated calibration / application of the discount curve. From here on, I am assuming that the zero rates you have quoted are 'quoted' as actual/365 continuous compounding. First, we need a function that returns a 'calibrated' discount curve given zero rates and tenors. to this end, let

```
discount_factor_generator <- function(nodes, rates){
  function(t){exp( -t * approxfun(x = nodes, y = rates,rule = 2)(t))}
}
```

This function takes in nodes `c(1,3,10)` and zero rates `c(-0.0027, -0.0014, 0.0015)` and returns a function `f(t)`. This function `f(t)`will yield the discount factor for a cashflow in $t$, with linear interpolation between the rates.

Let's say you stored your rates in a table called, well, `rates`, each row indexing 1 week, and your dates are stored in the first column (as in your example)

Then:

```
sapply(1:nrow(rates),function(i){
  ttm <- as.numeric(pmax(payment_dates  - rates[i,1],0)/365)
  dfs <- discount_factor_generator(c(1,3,10),current_rates <- rates[i,-1])(ttm)
  sum(schedule * (payment_dates>=rates[i,1]) * dfs)
})
```

Will result in a list of PVs, one for each valuation date.

Explanation: `date_diffs`stores the remaining time until each cash flow in year fractions. It will hold a zero at each point once that payment date is passed over. `dfs`hold the discount factor values for the corresponding vector of cash flow dates. Ultimately, the `notional * sum(...)` term yields the present value of all future coupon payments and the ultimate payment of 100% at maturity.

HTH

### Worked example:

```
discount_factor_generator <- function(nodes, rates){
  function(t){exp( -t * approxfun(x = nodes, y = rates,rule = 2)(t))}
}

payment_dates <- seq.Date(as.Date("2015-04-27"),as.Date("2025-04-27"), by='year')
coupon   <- 0.04
notional <- 20000000

schedule <- notional * c(rep(coupon,length(payment_dates)-1),1+coupon)

rates <- data.frame(
           Date   = seq.Date(as.Date("2015-04-27"),as.Date("2015-06-01"),by="week"),
           EUR1Y  = c( -0.27, -0.24, -0.24, -0.24, -0.24, -0.24)/100,
           EUR3Y  = c( -0.14,  0.01,  0.08,  0.09,  0.00,  0.00)/100,
           EUR10Y = c(  0.15,  0.40,  0.60,  0.67,  0.53,  0.53)/100)

sapply(1:nrow(rates),function(i){
  ttm <- as.numeric(pmax(payment_dates  - rates[i,1],0)/365)
  dfs <- discount_factor_generator(c(1,3,10),current_rates <- rates[i,-1])(ttm)
  sum(schedule * (payment_dates>=rates[i,1]) * dfs)
})
```

With output

```
[1] 28491519 27119430 26684695 26541992 26858125 26863656
```

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.