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
« 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
10# class TestFixedRateBonds(unittest.TestCase):
11# def __init__(self, *args, **kwargs):
12# super(TestFixedRateBonds, self).__init__(*args, **kwargs)
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
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)
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# )
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# )
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
70# for i in range(len(target_cashflows)):
71# target_cashflow_tpl = target_cashflows[i]
72# self.assertTupleEqual(target_cashflow_tpl, true_cashflows[i])
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
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)
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)
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
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)
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)
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
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# )
126# refdate = issue_date - dt.timedelta(days=1)
127# dates = [issue_date, maturity_date]
128# rates = [discount_rate] * len(dates)
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# )
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
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
163# for i in range(len(target_cashflows)):
164# target_cashflow_tpl = target_cashflows[i]
165# self.assertTupleEqual(target_cashflow_tpl, true_cashflows[i])
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)
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)
176# class TestFloatingRateBonds(unittest.TestCase):
177# def __init__(self, *args, **kwargs):
178# super(TestFloatingRateBonds, self).__init__(*args, **kwargs)
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
189# daycounter = DayCounterType.ACT_ACT
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# )
201# refdate = issue_date - dt.timedelta(days=1)
202# dates = [issue_date, maturity_date]
203# rates = [0.02, 0.04]
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]
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# )
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# ]
254# true_cashflows = bond.cashflows
256# for i in range(len(target_cashflows)):
257# target_cashflow_tpl = target_cashflows[i]
258# self.assertTupleEqual(target_cashflow_tpl, true_cashflows[i])
260# target_accrued_interest = 0.8273972602739728
261# target_dirty_price = 102.16465071930195
262# target_clean_price = 101.33925575930313
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)
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)
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
280# daycounter = DayCounterType.ACT_ACT
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# )
292# refdate = issue_date - dt.timedelta(days=1)
293# dates = [issue_date, maturity_date]
294# rates = [0.02, 0.04]
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# )
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]
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# )
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# )
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
348# daycounter = DayCounterType.ACT_ACT
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# )
360# refdate = issue_date - dt.timedelta(days=1)
361# dates = [issue_date, maturity_date]
362# rates = [0.02, 0.04]
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# )
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]
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# )
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# )
405# target_payment_dates = [dt.datetime(2024, 7, 1), dt.datetime(2025, 1, 1)]
407# true_cashflows = bond.cashflows
409# for i in range(len(true_cashflows)):
410# self.assertEqual(target_payment_dates[i], true_cashflows[i][0])
413# if __name__ == "__main__":
414# unittest.main()