Nothing
test_that("yc_zspread: zero spread when price equals benchmark PV", {
curve <- yc_curve(c(1, 2, 5, 10), c(0.04, 0.042, 0.044, 0.045))
# Price the bond at the benchmark curve (no spread)
maturity <- 5
freq <- 2
step <- 1 / freq
cf_times <- seq(step, maturity, by = step)
coupon <- 100 * 0.04 / freq
cfs <- rep(coupon, length(cf_times))
cfs[length(cfs)] <- cfs[length(cfs)] + 100
z <- approx(c(1, 2, 5, 10), c(0.04, 0.042, 0.044, 0.045),
xout = cf_times, rule = 2)$y
benchmark_price <- sum(cfs * (1 + z)^(-cf_times))
result <- yc_zspread(price = benchmark_price, coupon_rate = 0.04,
maturity = 5, curve = curve, frequency = 2)
expect_equal(result$zspread, 0, tolerance = 1e-6)
})
test_that("yc_zspread: positive spread when price is below benchmark", {
curve <- yc_curve(c(1, 2, 5, 10), c(0.04, 0.042, 0.044, 0.045))
# Price the bond at the benchmark curve
maturity <- 5
freq <- 2
step <- 1 / freq
cf_times <- seq(step, maturity, by = step)
coupon <- 100 * 0.04 / freq
cfs <- rep(coupon, length(cf_times))
cfs[length(cfs)] <- cfs[length(cfs)] + 100
z <- approx(c(1, 2, 5, 10), c(0.04, 0.042, 0.044, 0.045),
xout = cf_times, rule = 2)$y
benchmark_price <- sum(cfs * (1 + z)^(-cf_times))
# Lower price means positive spread
result <- yc_zspread(price = benchmark_price - 2, coupon_rate = 0.04,
maturity = 5, curve = curve, frequency = 2)
expect_true(result$zspread > 0)
})
test_that("yc_zspread: negative spread when price is above benchmark", {
curve <- yc_curve(c(1, 2, 5, 10), c(0.04, 0.042, 0.044, 0.045))
maturity <- 5
freq <- 2
step <- 1 / freq
cf_times <- seq(step, maturity, by = step)
coupon <- 100 * 0.04 / freq
cfs <- rep(coupon, length(cf_times))
cfs[length(cfs)] <- cfs[length(cfs)] + 100
z <- approx(c(1, 2, 5, 10), c(0.04, 0.042, 0.044, 0.045),
xout = cf_times, rule = 2)$y
benchmark_price <- sum(cfs * (1 + z)^(-cf_times))
result <- yc_zspread(price = benchmark_price + 1, coupon_rate = 0.04,
maturity = 5, curve = curve, frequency = 2)
expect_true(result$zspread < 0)
})
test_that("yc_zspread: model_price matches input price", {
curve <- yc_curve(c(1, 2, 5, 10), c(0.04, 0.042, 0.044, 0.045))
result <- yc_zspread(price = 95, coupon_rate = 0.04, maturity = 5,
curve = curve, frequency = 2)
expect_equal(result$model_price, 95, tolerance = 1e-6)
})
test_that("yc_zspread works with annual frequency", {
curve <- yc_curve(c(1, 2, 5, 10), c(0.04, 0.042, 0.044, 0.045))
result <- yc_zspread(price = 98, coupon_rate = 0.04, maturity = 5,
curve = curve, frequency = 1)
expect_true(result$zspread > 0)
expect_equal(result$model_price, 98, tolerance = 1e-6)
})
test_that("yc_zspread accepts list-style curve", {
curve <- list(maturities = c(1, 2, 5, 10), rates = c(0.04, 0.042, 0.044, 0.045))
result <- yc_zspread(price = 95, coupon_rate = 0.04, maturity = 5,
curve = curve, frequency = 2)
expect_true(result$zspread > 0)
})
test_that("yc_zspread rejects bad inputs", {
curve <- yc_curve(c(1, 2, 5), c(0.04, 0.042, 0.044))
expect_error(yc_zspread(price = -1, coupon_rate = 0.04, maturity = 5,
curve = curve), "positive")
expect_error(yc_zspread(price = 95, coupon_rate = 0.04, maturity = 5,
curve = curve, frequency = 4), "frequency")
})
# Key rate duration tests
test_that("yc_key_rate_duration returns correct structure", {
curve <- yc_curve(c(1, 2, 5, 10, 30), c(0.03, 0.035, 0.04, 0.042, 0.045))
result <- yc_key_rate_duration(coupon_rate = 0.04, maturity = 10,
curve = curve)
expect_true(is.data.frame(result))
expect_true(all(c("tenor", "key_rate_duration") %in% names(result)))
expect_equal(nrow(result), 5)
})
test_that("yc_key_rate_duration: sum approximates total modified duration", {
curve <- yc_curve(c(1, 2, 5, 10, 30), c(0.04, 0.042, 0.044, 0.045, 0.046))
# Price the bond at the curve
maturity <- 10
freq <- 2
step <- 1 / freq
cf_times <- seq(step, maturity, by = step)
coupon <- 100 * 0.05 / freq
cfs <- rep(coupon, length(cf_times))
cfs[length(cfs)] <- cfs[length(cfs)] + 100
z <- approx(c(1, 2, 5, 10, 30), c(0.04, 0.042, 0.044, 0.045, 0.046),
xout = cf_times, rule = 2)$y
price <- sum(cfs * (1 + z)^(-cf_times))
krd <- yc_key_rate_duration(coupon_rate = 0.05, maturity = 10,
curve = curve, key_rates = c(1, 2, 5, 10, 30))
# Compute total modified duration via parallel shift
shift <- 0.0001
z_up <- z + shift
price_up <- sum(cfs * (1 + z_up)^(-cf_times))
total_mod_dur <- -(price_up - price) / (shift * price)
sum_krd <- sum(krd$key_rate_duration)
# Sum of KRDs should be close to total modified duration
expect_equal(sum_krd, total_mod_dur, tolerance = 0.5)
})
test_that("yc_key_rate_duration: all values non-negative for standard bond", {
curve <- yc_curve(c(1, 2, 5, 10, 30), c(0.04, 0.042, 0.044, 0.045, 0.046))
result <- yc_key_rate_duration(coupon_rate = 0.05, maturity = 10,
curve = curve)
# For a standard bond, all KRDs should be non-negative
expect_true(all(result$key_rate_duration >= -0.01))
})
test_that("yc_key_rate_duration: short bond has KRD concentrated at short end", {
curve <- yc_curve(c(1, 2, 5, 10, 30), c(0.04, 0.042, 0.044, 0.045, 0.046))
result <- yc_key_rate_duration(coupon_rate = 0.05, maturity = 2,
curve = curve)
# For a 2Y bond, KRDs at 10Y and 30Y should be near zero
expect_true(abs(result$key_rate_duration[result$tenor == 10]) < 0.01)
expect_true(abs(result$key_rate_duration[result$tenor == 30]) < 0.01)
})
test_that("yc_key_rate_duration rejects bad inputs", {
curve <- yc_curve(c(1, 2, 5), c(0.04, 0.042, 0.044))
expect_error(yc_key_rate_duration(coupon_rate = 0.04,
maturity = -5, curve = curve), "positive")
expect_error(yc_key_rate_duration(coupon_rate = 0.04,
maturity = 5, curve = curve,
frequency = 4), "frequency")
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.