tests/testthat/test_convertibles.R

library(ragtop)
library(futile.logger)
context("Convertibles")

flog.threshold(WARN, name="ragtop")
flog.threshold(WARN)
flog.threshold(ERROR, name='ragtop.implicit.setup.width')

pct4 = function(T,t=0) {
  exp(-0.04*(T-t))
}
pct0 = function(T,t=0) {
  exp(-0.0*(T-t))
}

zccb0 = ConvertibleBond(conversion_ratio=0, maturity=1.3, notional=100,
                        discount_factor_fcn=pct0, name='ConvertibleBondZeroCoup')
zcb = ZeroCouponBond(maturity=zccb0$maturity, notional=zccb0$notional,
                     discount_factor_fcn=pct0, name='EquivalentStraightZCB')
gvs_zc_const_h = form_present_value_grid(100, 2,
                                      instruments=list(zcb=zcb, convertible_bond=zccb0),
                                      const_default_intensity=0.06,
                                      const_short_rate = 0)
gvs_zc_const_r = form_present_value_grid(100, 2,
                                         instruments=list(zcb=zcb, convertible_bond=zccb0),
                                         const_short_rate=0.11)
gvs_zc_const_rh = form_present_value_grid(100, 2,
                                          instruments=list(zcb=zcb, convertible_bond=zccb0),
                                          const_default_intensity=0.05,
                                          const_short_rate=0.11)
differing_zc_const_h = gvs_zc_const_h[,'zcb'] - gvs_zc_const_h[,'convertible_bond']
differing_zc_const_r = gvs_zc_const_r[,'zcb']  - gvs_zc_const_r[,'convertible_bond']
differing_zc_const_rh = gvs_zc_const_rh[,'zcb']  - gvs_zc_const_rh[,'convertible_bond']
test_that("Straight Zero Coupon Bond Equivalents", {
  expect_equal(max(abs(differing_zc_const_h)), 0.0, tolerance=1.e-6)
  expect_equal(max(abs(differing_zc_const_r)), 0.0, tolerance=1.e-6)
  expect_equal(max(abs(differing_zc_const_rh)), 0.0, tolerance=1.e-6)
})


cb0 = ConvertibleBond(conversion_ratio=0, maturity=1, notional=100,
                      coupons=data.frame(payment_time=0.875, payment_size=1),
                      discount_factor_fcn=pct0, name='ConvertibleBond')
split_coup = ZeroCouponBond(maturity=0.875, notional=1, discount_factor_fcn=pct0)
no_coup = ZeroCouponBond(maturity=1, notional=100)
gvs_0_rh = form_present_value_grid(100, 2,
                                      instruments=list(zcb=no_coup, coupon=split_coup, convertible_bond=cb0),
                                      const_default_intensity=0,
                                      const_short_rate = 0)
gvs_const_h = form_present_value_grid(100, 2,
                                      instruments=list(zcb=no_coup, coupon=split_coup, convertible_bond=cb0),
                                      const_default_intensity=0.05862091,
                                      const_short_rate = 0)
gvs_const_r = form_present_value_grid(100, 2,
                                      instruments=list(zcb=no_coup, coupon=split_coup, convertible_bond=cb0),
                                      const_short_rate=0.11)
gvs_const_rh = form_present_value_grid(100, 2,
                                       instruments=list(zcb=no_coup, coupon=split_coup, convertible_bond=cb0),
                                       const_default_intensity=0.05,
                                       const_short_rate=0.11)
differing_const_h = gvs_const_h[,'zcb'] + gvs_const_h[,'coupon'] - gvs_const_h[,'convertible_bond']
differing_const_r = gvs_const_r[,'zcb'] + gvs_const_r[,'coupon'] - gvs_const_r[,'convertible_bond']
differing_0_rh = gvs_0_rh[,'zcb'] + gvs_0_rh[,'coupon'] - gvs_0_rh[,'convertible_bond']
differing_const_rh = gvs_const_rh[,'zcb'] + gvs_const_rh[,'coupon'] - gvs_const_rh[,'convertible_bond']
test_that("Straight Coupon Bond Equivalents", {
  expect_equal(max(abs(differing_const_h)), 0.0, tolerance=1.e-6)
  expect_equal(max(abs(differing_const_r)), 0.0, tolerance=1.e-6)
  expect_equal(max(abs(differing_const_rh)), 0.0, tolerance=1.e-6)
})


cbeq = ConvertibleBond(conversion_ratio=1, maturity=1.5, notional=0,
                       discount_factor_fcn=pct0, name='ConvertibleBond')
euro_zero_strike_call = EuropeanOption(maturity=cbeq$maturity, strike=0, callput=1, name="CallStrike0")
S0 = 100
equity_equiv_prices = find_present_value(S0=S0,
                                         instruments=list(call=euro_zero_strike_call, convertible_bond=cbeq),
                                         num_time_steps=200, const_default_intensity=0.07,
                                         const_volatility = 0.4, const_short_rate=0.03, std_devs_width=4)
test_that("Trivial equity-equivalent", {
  expect_equal(S0, equity_equiv_prices$call, tolerance=1.e-1)
  expect_equal(S0, equity_equiv_prices$convertible_bond, tolerance=1.e-1)
})




cbopt = ConvertibleBond(conversion_ratio=5./3., maturity=1.5, notional=100,
                       discount_factor_fcn=pct0, name='ConvertibleBond')
S0 = 0.95 * cbopt$notional/cbopt$conversion_ratio
euro_simple_call = EuropeanOption$new(maturity=cbopt$maturity,
                                      strike=cbopt$notional/cbopt$conversion_ratio,
                                      callput=1, name="EquivCall")
opt_equiv_prices = find_present_value(S0=S0, instruments=list(call=euro_simple_call, convertible_bond=cbopt),
                                          num_time_steps=200, const_default_intensity=0.0,
                                          const_volatility = 0.4, const_short_rate=0.0, std_devs_width=4)
test_that("Option-equivalent convertible", {
  expect_equal(cbopt$conversion_ratio * opt_equiv_prices$call+cbopt$notional, opt_equiv_prices$convertible_bond, tolerance=1.e-2)
})




cb_zero_recov = ConvertibleBond(maturity=2.87,
                     conversion_ratio=2.7788,
                     notional=1000,
                     coupons=data.frame(payment_time=seq(2.8,0, by=-0.25),
                                        payment_size=1000*0.0025/4),
                     discount_factor_fcn = pct4,
                     name='NoRecov');
cb_40_recov = ConvertibleBond(maturity=2.87,
                      conversion_ratio=2.7788,
                      notional=1000,
                      recovery_rate=0.4,
                      coupons=data.frame(payment_time=seq(2.8,0, by=-0.25),
                                         payment_size=1000*0.0025/4),
                      discount_factor_fcn = pct4,
                      name='StdRecov');
vs_with_and_without = find_present_value(S0=252,
                                         instruments=list(NoRecov=cb_zero_recov, StdRecov=cb_40_recov),
                                         num_time_steps=25,
                                         const_default_intensity=0.1,
                                         const_short_rate = 0.04,
                                         const_volatility=0.5)
test_that("Recovery rates matter", {
  expect_gt(vs_with_and_without$StdRecov, 1.05 * vs_with_and_without$NoRecov)
})

Try the ragtop package in your browser

Any scripts or data that you put into this service are public.

ragtop documentation built on March 26, 2020, 7:28 p.m.