Coverage for tests / test_bonds_new.py: 100%

0 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-27 14:36 +0000

1# import unittest 

2# import datetime as dt 

3# from rivapy.marketdata.curves import DiscountCurve 

4# from rivapy.tools.datetools import Schedule, Period, DayCounter 

5# from rivapy.tools.enums import DayCounterType, InterpolationType, ExtrapolationType, SecuritizationLevel, RollConvention 

6# from rivapy.instruments.bond_specifications import FixedRateBond, FloatingRateBond 

7# from holidays.financial import ECB 

8 

9 

10# class TestFixedRateBonds(unittest.TestCase): 

11# def __init__(self, *args, **kwargs): 

12# super(TestFixedRateBonds, self).__init__(*args, **kwargs) 

13 

14# def test_annually_coupon_unadjusted(self): 

15# obj_id = "test_bond" 

16# issue_date = dt.datetime(2019, 1, 1) 

17# maturity_date = dt.datetime(2024, 1, 1) 

18# coupon_rate = 0.03 

19# coupon_payment = Period(years=1) 

20# notional = 100.0 

21# securitisation_level_val = SecuritizationLevel.NONE 

22# discount_rate = 0.02 

23# daycounter = DayCounterType.ACT_ACT 

24 

25# scheduler = Schedule( 

26# start_day=issue_date, 

27# end_day=maturity_date, 

28# time_period=coupon_payment, 

29# backwards=True, 

30# stub=True, 

31# business_day_convention=RollConvention.UNADJUSTED, 

32# calendar=ECB(years=range(issue_date.year, maturity_date.year + 1)), 

33# ) 

34# refdate = issue_date - dt.timedelta(days=1) 

35# dates = [issue_date, maturity_date] 

36# rates = [discount_rate] * len(dates) 

37 

38# discount_curve = DiscountCurve( 

39# id="test_curve", 

40# refdate=refdate, 

41# dates=dates, 

42# df=rates, 

43# interpolation=InterpolationType.LINEAR, 

44# extrapolation=ExtrapolationType.LINEAR, 

45# daycounter=daycounter, 

46# ) 

47 

48# bond = FixedRateBond( 

49# obj_id=obj_id, 

50# schedule=scheduler, 

51# notional=notional, 

52# currency="EUR", 

53# issue_date=issue_date, 

54# maturity_date=maturity_date, 

55# coupon_rate=coupon_rate, 

56# issuer="Test", 

57# securitization_level=securitisation_level_val, 

58# accrual_day_counter_type=daycounter, 

59# ) 

60 

61# target_cashflows = [ 

62# (dt.datetime(2020, 1, 1), 3.0), 

63# (dt.datetime(2021, 1, 1), 3.0), 

64# (dt.datetime(2022, 1, 1), 3.0), 

65# (dt.datetime(2023, 1, 1), 3.0), 

66# (dt.datetime(2024, 1, 1), 103.0), 

67# ] 

68# true_cashflows = bond.cashflows 

69 

70# for i in range(len(target_cashflows)): 

71# target_cashflow_tpl = target_cashflows[i] 

72# self.assertTupleEqual(target_cashflow_tpl, true_cashflows[i]) 

73 

74# # valuation 1 

75# value_date = dt.datetime(2019, 1, 1) 

76# target_ytm = 0.02 

77# target_clean_price = 104.7134595085042 

78# target_dirty_price = 104.7134595085042 

79# target_accrued_interest = 0.0 

80 

81# dirty_price = bond.compute_dirty_price(value_date=value_date, discount_curve=discount_curve) 

82# clean_price = bond.compute_clean_price(value_date=value_date, discount_curve=discount_curve) 

83# accrued_interest = bond.compute_accrued_interest(valuation_date=value_date) 

84# ytm = bond.compute_yield(dirty_price=dirty_price, val_date=value_date) 

85 

86# self.assertAlmostEqual(target_clean_price - clean_price, 0.0, places=8) 

87# self.assertAlmostEqual(target_dirty_price - dirty_price, 0.0, places=8) 

88# self.assertAlmostEqual(target_accrued_interest - accrued_interest, 0.0, places=8) 

89# self.assertAlmostEqual(target_ytm - ytm, 0, places=8) 

90 

91# # valuation 2 

92# value_date = dt.datetime(2023, 6, 1) 

93# target_accrued_interest = 1.2410958904109588 

94# target_dirty_price = 101.81105369821792 

95# target_clean_price = 100.58428398614735 

96 

97# dirty_price = bond.compute_dirty_price(value_date=value_date, discount_curve=discount_curve) 

98# clean_price = bond.compute_clean_price(value_date=value_date, discount_curve=discount_curve) 

99# accrued_interest = bond.compute_accrued_interest(valuation_date=value_date) 

100 

101# self.assertAlmostEqual(target_clean_price - clean_price, 0.0, places=8) 

102# self.assertAlmostEqual(target_dirty_price - dirty_price, 0.0, places=8) 

103# self.assertAlmostEqual(target_accrued_interest - accrued_interest, 0.0, places=8) 

104 

105# def test_semi_annually_coupon_following(self): 

106# obj_id = "test_bond" 

107# issue_date = dt.datetime(2023, 1, 1) 

108# maturity_date = dt.datetime(2024, 1, 1) 

109# coupon_rate = 0.03 

110# coupon_payment = Period(months=6) 

111# notional = 100.0 

112# securitisation_level_val = SecuritizationLevel.NONE 

113# discount_rate = 0.02 

114# daycounter = DayCounterType.ActActICMA 

115 

116# scheduler = Schedule( 

117# start_day=issue_date, 

118# end_day=maturity_date, 

119# time_period=coupon_payment, 

120# backwards=True, 

121# stub=True, 

122# business_day_convention=RollConvention.FOLLOWING, 

123# calendar=ECB(years=range(issue_date.year, maturity_date.year + 1)), 

124# ) 

125 

126# refdate = issue_date - dt.timedelta(days=1) 

127# dates = [issue_date, maturity_date] 

128# rates = [discount_rate] * len(dates) 

129 

130# discount_curve = DiscountCurve( 

131# id="test_curve", 

132# refdate=refdate, 

133# dates=dates, 

134# df=rates, 

135# interpolation=InterpolationType.LINEAR, 

136# extrapolation=ExtrapolationType.LINEAR, 

137# daycounter=daycounter, 

138# ) 

139# bond = FixedRateBond( 

140# obj_id=obj_id, 

141# schedule=scheduler, 

142# notional=notional, 

143# currency="EUR", 

144# issue_date=issue_date, 

145# maturity_date=maturity_date, 

146# coupon_rate=coupon_rate, 

147# issuer="Test", 

148# securitization_level=securitisation_level_val, 

149# accrual_day_counter_type=daycounter, 

150# ) 

151 

152# value_date = dt.datetime(2023, 6, 1) 

153# target_clean_price = 100.58844590976388 

154# target_dirty_price = 101.82255931875143 

155# target_accrued_interest = 1.2362637362637363 

156 

157# target_cashflows = [ 

158# (dt.datetime(2023, 7, 3), 1.5), 

159# (dt.datetime(2024, 1, 2), 101.5), 

160# ] 

161# true_cashflows = bond.cashflows 

162 

163# for i in range(len(target_cashflows)): 

164# target_cashflow_tpl = target_cashflows[i] 

165# self.assertTupleEqual(target_cashflow_tpl, true_cashflows[i]) 

166 

167# dirty_price = bond.compute_dirty_price(value_date=value_date, discount_curve=discount_curve) 

168# clean_price = bond.compute_clean_price(value_date=value_date, discount_curve=discount_curve) 

169# accrued_interest = bond.compute_accrued_interest(valuation_date=value_date) 

170 

171# self.assertAlmostEqual(target_clean_price - clean_price, 0.0, places=8) 

172# self.assertAlmostEqual(target_dirty_price - dirty_price, 0.0, places=8) 

173# self.assertAlmostEqual(target_accrued_interest - accrued_interest, 0.0, places=8) 

174 

175 

176# class TestFloatingRateBonds(unittest.TestCase): 

177# def __init__(self, *args, **kwargs): 

178# super(TestFloatingRateBonds, self).__init__(*args, **kwargs) 

179 

180# def test_semi_annually_coupon_unadjusted(self): 

181# obj_id = "test_bond" 

182# issue_date = dt.datetime(2023, 1, 1) 

183# maturity_date = dt.datetime(2024, 1, 1) 

184# coupon_rate = 0.03 

185# coupon_payment = Period(months=6) 

186# notional = 100.0 

187# securitisation_level_val = SecuritizationLevel.NONE 

188 

189# daycounter = DayCounterType.ACT_ACT 

190 

191# scheduler = Schedule( 

192# start_day=issue_date, 

193# end_day=maturity_date, 

194# time_period=coupon_payment, 

195# backwards=True, 

196# stub=True, 

197# business_day_convention=RollConvention.UNADJUSTED, 

198# calendar=ECB(years=range(issue_date.year, maturity_date.year + 1)), 

199# ) 

200 

201# refdate = issue_date - dt.timedelta(days=1) 

202# dates = [issue_date, maturity_date] 

203# rates = [0.02, 0.04] 

204 

205# discount_curve = DiscountCurve( 

206# id="test_curve", 

207# refdate=refdate, 

208# dates=dates, 

209# df=rates, 

210# interpolation=InterpolationType.LINEAR, 

211# extrapolation=ExtrapolationType.LINEAR, 

212# daycounter=daycounter, 

213# ) 

214# value_date = dt.datetime(2023, 6, 1) 

215# refdate = value_date - dt.timedelta(days=1) 

216# dates = [value_date, maturity_date] 

217# rates = [0.03, 0.06] 

218 

219# ref_curve = DiscountCurve( 

220# id="test_curve", 

221# refdate=refdate, 

222# dates=dates, 

223# df=rates, 

224# interpolation=InterpolationType.LINEAR, 

225# extrapolation=ExtrapolationType.LINEAR, 

226# daycounter=daycounter, 

227# ) 

228# margin = 0.01 

229# fixing_coupon_rate = 0.02 

230# bond = FloatingRateBond( 

231# obj_id=obj_id, 

232# schedule=scheduler, 

233# notional=notional, 

234# currency="EUR", 

235# issue_date=issue_date, 

236# maturity_date=maturity_date, 

237# coupon_ref_curve=ref_curve, 

238# margin=margin, 

239# fixing_date=value_date, 

240# fixing_coupon_rate=fixing_coupon_rate, 

241# issuer="Test", 

242# securitization_level=securitisation_level_val, 

243# accrual_day_counter_type=daycounter, 

244# ) 

245 

246# target_cashflows = [ 

247# (dt.datetime(2023, 7, 1), DayCounter.yf_ActAct(d1=dt.datetime(2023, 1, 1), d2=dt.datetime(2023, 7, 1)) * 0.02 * notional), 

248# ( 

249# dt.datetime(2024, 1, 1), 

250# DayCounter.yf_ActAct(d1=dt.datetime(2023, 7, 1), d2=dt.datetime(2024, 1, 1)) * (0.06 + margin) * notional + notional, 

251# ), 

252# ] 

253 

254# true_cashflows = bond.cashflows 

255 

256# for i in range(len(target_cashflows)): 

257# target_cashflow_tpl = target_cashflows[i] 

258# self.assertTupleEqual(target_cashflow_tpl, true_cashflows[i]) 

259 

260# target_accrued_interest = 0.8273972602739728 

261# target_dirty_price = 102.16465071930195 

262# target_clean_price = 101.33925575930313 

263 

264# dirty_price = bond.compute_dirty_price(value_date=value_date, discount_curve=discount_curve) 

265# clean_price = bond.compute_clean_price(value_date=value_date, discount_curve=discount_curve) 

266# accrued_interest = bond.compute_accrued_interest(valuation_date=value_date) 

267 

268# self.assertAlmostEqual(target_clean_price - clean_price, 0.0, places=8) 

269# self.assertAlmostEqual(target_dirty_price - dirty_price, 0.0, places=8) 

270# self.assertAlmostEqual(target_accrued_interest - accrued_interest, 0.0, places=8) 

271 

272# def test_assertion(self): 

273# obj_id = "test_bond" 

274# issue_date = dt.datetime(2023, 1, 1) 

275# maturity_date = dt.datetime(2025, 1, 1) 

276# coupon_payment = Period(months=6) 

277# notional = 100.0 

278# securitisation_level_val = SecuritizationLevel.NONE 

279 

280# daycounter = DayCounterType.ACT_ACT 

281 

282# scheduler = Schedule( 

283# start_day=issue_date, 

284# end_day=maturity_date, 

285# time_period=coupon_payment, 

286# backwards=True, 

287# stub=True, 

288# business_day_convention=RollConvention.UNADJUSTED, 

289# calendar=ECB(years=range(issue_date.year, maturity_date.year + 1)), 

290# ) 

291 

292# refdate = issue_date - dt.timedelta(days=1) 

293# dates = [issue_date, maturity_date] 

294# rates = [0.02, 0.04] 

295 

296# discount_curve = DiscountCurve( 

297# id="test_curve", 

298# refdate=refdate, 

299# dates=dates, 

300# df=rates, 

301# interpolation=InterpolationType.LINEAR, 

302# extrapolation=ExtrapolationType.LINEAR, 

303# daycounter=daycounter, 

304# ) 

305 

306# value_date = dt.datetime(2024, 6, 1) 

307# refdate = value_date - dt.timedelta(days=1) 

308# dates = [value_date, maturity_date] 

309# rates = [0.03, 0.06] 

310 

311# ref_curve = DiscountCurve( 

312# id="test_curve", 

313# refdate=refdate, 

314# dates=dates, 

315# df=rates, 

316# interpolation=InterpolationType.LINEAR, 

317# extrapolation=ExtrapolationType.LINEAR, 

318# daycounter=daycounter, 

319# ) 

320 

321# # the coupon payment at 2024-01-01 falls between the fixed coupon payment and the start of the coupon reference curve 

322# with self.assertRaises(ValueError): 

323# FloatingRateBond( 

324# obj_id=obj_id, 

325# schedule=scheduler, 

326# notional=notional, 

327# currency="EUR", 

328# issue_date=issue_date, 

329# maturity_date=maturity_date, 

330# coupon_ref_curve=ref_curve, 

331# margin=0.01, 

332# fixing_date=dt.datetime(2023, 6, 1), 

333# fixing_coupon_rate=0.02, 

334# issuer="Test", 

335# securitization_level=securitisation_level_val, 

336# accrual_day_counter_type=daycounter, 

337# ) 

338 

339# def test_cashflow_dates_fixing_date(self): 

340# obj_id = "test_bond" 

341# issue_date = dt.datetime(2023, 1, 1) 

342# maturity_date = dt.datetime(2025, 1, 1) 

343# coupon_rate = 0.03 

344# coupon_payment = Period(months=6) 

345# notional = 100.0 

346# securitisation_level_val = SecuritizationLevel.NONE 

347 

348# daycounter = DayCounterType.ACT_ACT 

349 

350# scheduler = Schedule( 

351# start_day=issue_date, 

352# end_day=maturity_date, 

353# time_period=coupon_payment, 

354# backwards=True, 

355# stub=True, 

356# business_day_convention=RollConvention.UNADJUSTED, 

357# calendar=ECB(years=range(issue_date.year, maturity_date.year + 1)), 

358# ) 

359 

360# refdate = issue_date - dt.timedelta(days=1) 

361# dates = [issue_date, maturity_date] 

362# rates = [0.02, 0.04] 

363 

364# discount_curve = DiscountCurve( 

365# id="test_curve", 

366# refdate=refdate, 

367# dates=dates, 

368# df=rates, 

369# interpolation=InterpolationType.LINEAR, 

370# extrapolation=ExtrapolationType.LINEAR, 

371# daycounter=daycounter, 

372# ) 

373 

374# value_date = dt.datetime(2023, 6, 1) 

375# refdate = value_date - dt.timedelta(days=1) 

376# dates = [value_date, maturity_date] 

377# rates = [0.03, 0.06] 

378 

379# ref_curve = DiscountCurve( 

380# id="test_curve", 

381# refdate=refdate, 

382# dates=dates, 

383# df=rates, 

384# interpolation=InterpolationType.LINEAR, 

385# extrapolation=ExtrapolationType.LINEAR, 

386# daycounter=daycounter, 

387# ) 

388 

389# bond = FloatingRateBond( 

390# obj_id=obj_id, 

391# schedule=scheduler, 

392# notional=notional, 

393# currency="EUR", 

394# issue_date=issue_date, 

395# maturity_date=maturity_date, 

396# coupon_ref_curve=ref_curve, 

397# margin=0.01, 

398# fixing_date=dt.datetime(2024, 6, 1), # <- fixing date is after the valuation date, therefore the valuation starts after the fixing date 

399# fixing_coupon_rate=0.02, 

400# issuer="Test", 

401# securitization_level=securitisation_level_val, 

402# accrual_day_counter_type=daycounter, 

403# ) 

404 

405# target_payment_dates = [dt.datetime(2024, 7, 1), dt.datetime(2025, 1, 1)] 

406 

407# true_cashflows = bond.cashflows 

408 

409# for i in range(len(true_cashflows)): 

410# self.assertEqual(target_payment_dates[i], true_cashflows[i][0]) 

411 

412 

413# if __name__ == "__main__": 

414# unittest.main()