Introduction and Plain Vanilla Bond Valuation

Time Value of Money

Computing a bond’s future value calculate the future value of $100 one year from now and two years from now assuming an opportunity cost of 10%.

# Create pv
pv <- 100

# Create r
r <- 0.1

# Calculate fv1
fv1 <- pv* (1 + r)
print(fv1)
## [1] 110
# Calculate fv2
fv2 <- pv*(1+r)*(1+r)
print(fv2)
## [1] 121

calculate the present value of fv1 and fv2 assuming an r (interest rate) of 0.10, where fv1 is a future value one year from now and fv2 is a future value two years from now.

# Calculate pv1
pv1 <- fv1 / (1 + r)

# Calculate pv2
pv2 <- fv2/((1+r)*(1+r))

# Print pv1 and pv2
pv1
## [1] 100
pv2
## [1] 100

Bond valuation

Laying out the bond’s cash flows Suppose you are considering investing in a bond that has a $100 par value, 5% coupon rate, and 5 years to maturity.
The coupon rate is the interest that you earn from holding the bond, so prior to the bond’s maturity you would receive USD5 of coupon payments each year. At maturity, you will also receive the USD100 par value back.
Step 1: Calculate the present value of each cash flow
step 2: Calculate the value of the bond
Recall that this bond has a yiel of 6%, which acts as the discount rate.

# Create vector of cash flows
cf <- c(5,5,5,5,105)

# Convert to data frame
cf <- data.frame(cf)
cf
##    cf
## 1   5
## 2   5
## 3   5
## 4   5
## 5 105
# Add column t to cf
cf$t <- as.numeric(c(1,2,3,4,5))
rownames(cf)
## [1] "1" "2" "3" "4" "5"
# Calculate pv_factor
cf$pv_factor <- 1 / (1 + 0.06)^cf$t

# Calculate pv
cf$pv <- cf$cf* cf$pv_factor

# Calculate the bond price
sum(cf$pv)
## [1] 95.78764

Convert the code to a function
p for par value
r for coupon rate
ttm for time to maturity
y for yield.

# Create function
bondprc <- function(p, r, ttm, y) {
  cf <- c(rep(p * r, ttm - 1), p * (1 + r))
  cf <- data.frame(cf)
  cf$t <- as.numeric(rownames(cf))
  cf$pv_factor <- 1 / (1 + y)^cf$t
  cf$pv <- cf$cf * cf$pv_factor
  sum(cf$pv)
}

# Verify prior result
bondprc(95.79, 100, 0.05,5)
## [1] 1612.465

Yield to Maturity

The yield on the Moody’s Baa index

a bond’s yield can be estimated by looking at the yield of comparable bonds. ssume that the bond you valued has a Baa credit rating by Moody’s and the bond will be issued on September 30, 2016. With this information, you can use the Quandl() package in R to obtain the yield of the Moody’s Baa index (use the ticker “FED/RIMLPBAAR_N_M”“) on September 30, 2016.

# Load Quandl package
library(Quandl)
## Warning: package 'Quandl' was built under R version 3.4.4
## Loading required package: xts
## Warning: package 'xts' was built under R version 3.4.4
## Loading required package: zoo
## Warning: package 'zoo' was built under R version 3.4.4
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
# Obtain Moody's Baa index data
baa <- Quandl("FED/RIMLPBAAR_N_M")

# Identify 9/30/16 yield
baa_yield <- subset(baa, baa$Date == "2016-09-30")

# Convert yield to decimals and view
baa_yield <-baa_yield$Value/100
baa_yield
## [1] 0.0431

Value the 5% bond using the Baa yield you found

use the yield to find the value of a Baa-rated bond on September 30, 2016 with a $100 par value, 5% coupon rate, and 5 years to maturity.Use the bondprc function

 # Value bond
bondprc(p = 100, r = 0.05, ttm = 5, y = 0.0429)
## [1] 103.1352

Plotting the Price/Yield relationship
changes in price due to change in yield could differ substantially depending on whether the yield goes up or down, we should keep in mind that inverse relationship is non-linear.
Assume that you have a bond with $100 par value, 10% coupon rate, and 20 years to maturity.

# Generate prc_yld
prc_yld <- seq(0.02, 0.4, 0.01)

# Convert prc_yld to data frame
prc_yld <- data.frame(prc_yld)

# Calculate bond price given different yields
for (i in 1:nrow(prc_yld)) {
     prc_yld$price[i] <- bondprc(100, 0.10, 20, prc_yld$prc_yld[i])  
}
print(prc_yld$price)
##  [1] 230.81147 204.14232 181.54196 162.31105 145.87968 131.78204 119.63629
##  [8] 109.12855 100.00000  92.03667  85.06111  78.92575  73.50748  68.70334
## [15]  64.42695  60.60563  57.17803  54.09224  51.30420  48.77640  46.47681
## [22]  44.37799  42.45640  40.69175  39.06651  37.56550  36.17551  34.88507
## [29]  33.68412  32.56386  31.51656  30.53540  29.61436  28.74811  27.93191
## [36]  27.16154  26.43322  25.74360  25.08964
# Plot P/YTM relationship
plot(prc_yld, prc_yld$price,
     type = "l",
     col = "blue",
     main = "Price/YTM Relationship")


Components of Yield
Plotting US Treasury yields Since corporate bonds are riskier than US Treasuries, the risk-free rate is the baseline rate we would expect corporate bonds to yield. The getSymbols() command in quantmod allows you to access the FRED database by specifying the Symbols argument (in this case, to “DGS10” for 10-Year US Treasury Bonds). You’ll also need to set the src argument equal to “FRED”, and set auto.assign to FALSE.

# Load quantmod package
library(quantmod)
## Warning: package 'quantmod' was built under R version 3.4.4
## Loading required package: TTR
## Version 0.4-0 included new data defaults. See ?getSymbols.
# Obtain Treasury yield data
t10yr <- getSymbols(Symbols = "DGS10", src = "FRED", auto.assign = FALSE)
## 'getSymbols' currently uses auto.assign=TRUE by default, but will
## use auto.assign=FALSE in 0.5-0. You will still be able to use
## 'loadSymbols' to automatically load data. getOption("getSymbols.env")
## and getOption("getSymbols.auto.assign") will still be checked for
## alternate defaults.
## 
## This message is shown once per session and may be disabled by setting 
## options("getSymbols.warning4.0"=FALSE). See ?getSymbols for details.
# Subset data
t10yr <-t10yr["2006-01/2016-09"]

# Plot yields
plot(x = index(t10yr),
     y = t10yr$DGS10,
     xlab = "Date",
     ylab = "Yield (%)",
     type = "l",
     col = "red",
     main = "10-Year US Treasury Yields")

Plotting the investment grade spread
The size of the credit spread, also changes through time. One way to observe this spread is to compare the yield on the highest rated investment grade bonds (Aaa rating) and the yield on the lowest investment grade bonds (Baa rating).

spread<-read.csv("spread.csv", sep=";")
# Examine first and last six elements in spread
head(spread)
##         date  aaa  baa
## 1 30/09/2016 3.41 4.31
## 2 31/08/2016 3.32 4.24
## 3 31/07/2016 3.28 4.22
## 4 30/06/2016 3.50 4.53
## 5 31/05/2016 3.65 4.68
## 6 30/04/2016 3.62 4.79
tail(spread)
##           date  aaa  baa
## 124 30/06/2006 5.89 6.78
## 125 31/05/2006 5.95 6.75
## 126 30/04/2006 5.84 6.68
## 127 31/03/2006 5.53 6.41
## 128 28/02/2006 5.35 6.27
## 129 31/01/2006 5.29 6.24
# Calculate spread$diff
spread$diff <-(spread$baa-spread$aaa)*100

# Plot spread
plot(x = spread$date,
     y = spread$diff,
     type = "l",
     xlab = "Date",
     ylab = "Spread (bps)",
     col = "red",
     main = "Baa - Aaa Spread")

### Estimating the Yield of a Bond Finding a Bond’s Yield Suppose someone is offering $95.79 to purchase a bond you own with a $100 par value, 5% coupon rate, and 5 years to maturity. In this situation, you would like to know what the yield is of that bond, so you can compare the yield to the yield of other comparable securities to know whether the price being offered is too high or too low. calculate the price using a 5%, 6%, and 7% yield using your bondprc() function,

# Value bond using 5% yield
bondprc(p = 100, r = 0.05, ttm = 5, y = 0.05)
## [1] 100
# Value bond using 7% yield
bondprc(p=100, r=0.05,ttm=5, y=0.07)
## [1] 91.79961
# Value bond using 6% yield
bondprc(p=100, r=0.05,ttm=5,y=0.06)
## [1] 95.78764

By guessing upper and lower bounds, you were able to triangulate the correct yield of 6%.

Use uniroot function to find YTM
The uniroot() function requires us to setup a vector of cash flows, cf, that begins with the price of the bond (as a negative number) as the first element and the cash flows you expect to receive from the bond (i.e., coupon and principal payments) as the remaining elements.

Recall that the price of the bond is $95.79 and the bond has a $100 par value, 5% coupon rate, and 5 years to maturity.

# Create cash flow vector
cf <- c(-95.79, 5, 5, 5, 5, 105)

# Create bond valuation function
bval <- function(i, cf,
     t=seq(along = cf))
     sum(cf / (1 + i)^t)

# Create ytm() function using uniroot
ytm <- function(cf) {
    uniroot(bval, c(0, 1), cf = cf)$root
}

# Use ytm() function to find yield
ytm(cf)
## [1] 0.05999552

Your custom ytm() function produced an output of approximately 0.06, representing a 6% yield.

Duration and Convexity

. When interest rates rise, bond prices fall. Because of this, much attention is paid to how sensitive a particular bond’s price is to changes in interest rates.
Price value of a basis point represents a Dollar value of an 01 press

Calculate PV01 of a 10% bond alculate the PV01 of a bond with a $100 par value, 10% coupon, and 20 years to maturity assuming 10% yield to maturity.

# Calculate the PV01
abs(bondprc(p = 100, r = 0.10, ttm = 20, y = 0.1001) 
  - bondprc(p = 100, r = 0.10, ttm = 20, y = 0.10))
## [1] 0.08507756

Duration Duration of a zero-coupon bond can sometimes be thought of as the weighted-average time to maturity of the bond. what is the duration of a zero-coupon bond with three years to maturity?Without a coupon to adjust for, the duration would equal the regular maturity (in this case, three).

Calculate approximate duration for a bond calculate the approximate duration of a bond with $100 par value, 10% coupon rate, 20 years to maturity, 10% yield to maturity, and a 1% expected change in yield.

# Calculate bond price today
px <- bondprc(p = 100, r = 0.1, ttm = 20, y = 0.1)
px
## [1] 100
# Calculate bond price if yields increase by 1%
px_up <- bondprc(p=100, r=0.1, ttm=20, y=0.11)
px_up
## [1] 92.03667
# Calculate bond price if yields decrease by 1%
px_down <- bondprc(p=100, r=0.1, ttm=20, y=0.09)
px_down
## [1] 109.1285
# Calculate approximate duration
duration <- (px_down - px_up) / (2 * 100 * 0.01)
duration
## [1] 8.545937

Estimating effect on bond price using duration

When we know a bond’s duration, we can come up with an estimate of the bond’s price assuming some expected change in yield. the percentage change using duration, recall the formula: \(???D?????y\) where D is the duration and ??y is the change in yield. Estimate percentage change (duration_pct_change) based on duration if yields are expected to decrease by 1%. Estimate dollar change (duration_dollar_change) based on duration_pct_change and px if yields are expected to decrease by 1%.

# Estimate percentage change
duration_pct_change <- -duration*(-0.01)
duration_pct_change
## [1] 0.08545937
# Estimate dollar change
duration_dollar_change <- duration_pct_change*100
duration_dollar_change
## [1] 8.545937

Convexity

Calculate approximate convexity for a bond

# Calculate approximate convexity
convexity <- (px_up + px_down - 2 * px) / (px * (0.01)^2)
convexity
## [1] 116.5218

Estimating effect of convexity on bond price \(0.5???convexity???(??y)^2\) ??y is the expected change in yield. The dollar change based on convexity is equal to the percentage change multiplied by the current price P.

# Estimate percentage change
convexity_pct_change <- 0.5 * convexity * (0.01)^2
convexity_pct_change
## [1] 0.005826088
# Estimate dollar change
convexity_dollar_change <- convexity_pct_change * px
convexity_dollar_change
## [1] 0.5826088
#Estimating the bond price using duration and convexity
# Estimate change in price
price_change <- duration_dollar_change + convexity_dollar_change

# Estimate price
price <- duration_dollar_change + convexity_dollar_change + px

Comprehensive Example

Find AAA bond yields as of September 30, 2016

# Load Quandl package
library(Quandl)

# Obtain Moody's Aaa yield
aaa <-Quandl("FED/RIMLPAAAR_N_M")

# identify yield on September 30, 2016
aaa_yield <- subset(aaa, aaa$Date == "2016-09-30")

# Convert yield into decimals
aaa_yield <-as.numeric(aaa_yield$Value)/100
aaa_yield
## [1] 0.0341
# Layout the bond's cash flows
cf <- c(3, 3, 3, 3, 3, 3, 3, 103)

# Convert to data.frame
cf <-data.frame(cf)

# Add time indicator
cf$t <- seq(1, 8, 1)

# Calculate PV factor
cf$pv_factor <- 1 / (1 + aaa_yield)^cf$t

# Calculate PV
cf$pv <-cf$cf*cf$pv_factor

# Price bond
sum(cf$pv)
## [1] 97.17106
# Code cash flow function
alt_cf <- function(r, p, ttm) {
  c(rep(p * r, ttm - 1), p * (1 + r))
}

# Generate cf vector
alt_cf(r = 0.03, p = 100, ttm = 8)
## [1]   3   3   3   3   3   3   3 103
# Calculate bond price when yield increases
px_up <- bondprc(p = 100, r = 0.03, ttm = 8, y = aaa_yield + 0.01)

# Calculate bond price when yield decreases
px_down <- bondprc(p = 100, r = 0.03, ttm = 8, y = aaa_yield - 0.01)

# Calculate duration
duration <- (px_down - px_up) / (2 * px* 0.01)

# Calculate percentage effect of duration on price
duration_pct_change <- - duration * 0.01
duration_pct_change
## [1] -0.06790577
# Calculate dollar effect of duration on price
duration_dollar_change <- duration_pct_change * px
duration_dollar_change
## [1] -6.790577
# Calculate convexity measure
convexity <-((px_up+px_down)-2*px)/(px*0.01^2)

# Calculate percentage effect of convexity on price
convexity_pct_change <-0.5 * convexity * (0.01)^2
convexity_pct_change
## [1] -0.02543915
# Calculate dollar effect of convexity on price
convexity_dollar_change <-convexity_pct_change*px
convexity_dollar_change
## [1] -2.543915
# Estimate price_change
price_change <- duration_dollar_change + convexity_dollar_change
price_change
## [1] -9.334492
# Estimate new_price
new_price <- duration_dollar_change + convexity_dollar_change + px
new_price
## [1] 90.66551