0
$\begingroup$

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]
$\endgroup$
1
$\begingroup$

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_diffsstores 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. dfshold 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
| improve this answer | |
$\endgroup$
  • $\begingroup$ There is somewhere a misunderstanding in the calculations, as by following your code I came up with the bond valuation at $2.7e^{13}$ for some random valuation date 2015-06-29, which just does not seem to be true: valuedate <- as.Date("2015-06-29") current_rates <- c(-0.27,0.12,0.87)/100 payments <- seq.Date(as.Date("2015-04-27"),as.Date("2025-04-27"), by='year') date_diffs <- as.numeric(pmax(payments - valuedate,0)/365) dfs <- discount_factor_generator(c(1,3,10),current_rates)(date_diffs) nv * sum(c*dfs + rev(dfs)) $\endgroup$ – PK1998 May 16 at 8:18
  • $\begingroup$ I've added a working example. Shouldn't be too hard to add adjustments from here on, no? $\endgroup$ – Kermittfrog May 20 at 11:29
  • $\begingroup$ Thanks, it is fine now, I managed to adjust the discount factor function to my needs $\endgroup$ – PK1998 May 22 at 8:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.