Coverage for rivapy / pricing / deposit_pricing.py: 72%
43 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
1from datetime import datetime, date
2from rivapy.marketdata import DiscountCurve
3from rivapy.pricing.pricing_request import PricingRequest
4from rivapy.pricing._logger import logger
5from rivapy.instruments.deposit_specifications import DepositSpecification
6from rivapy.tools.datetools import DayCounter
7from rivapy.pricing.bond_pricing import DeterministicCashflowPricer
8from typing import Tuple, Union as _Union, List as _List
9from rivapy.tools._validators import _check_start_at_or_before_end
12class DepositPricer(DeterministicCashflowPricer):
14 def __init__(
15 self,
16 val_date: _Union[date, datetime],
17 deposit_spec: DepositSpecification,
18 discount_curve: DiscountCurve,
19 ):
20 """Create a pricer for a deposit instrument.
22 Args:
23 val_date (date | datetime): Valuation date used for pricing.
24 deposit_spec (DepositSpecification): Deposit specification containing schedule
25 and contractual parameters.
26 discount_curve (DiscountCurve): Discount curve used for discounting cashflows.
27 """
29 self._val_date = val_date
30 self._spec = deposit_spec
31 self._discount_curve = discount_curve
32 self._validate_pricer_dates()
34 def _validate_pricer_dates(self):
35 """Validate that the pricer valuation date is consistent with the discount curve refdate.
37 This updates ``self._discount_curve.refdate`` and ``self._val_date`` to a
38 canonical ordering using the project's date validators.
39 """
40 self._discount_curve.refdate, self._val_date = _check_start_at_or_before_end(self._discount_curve.refdate, self._val_date)
42 def expected_cashflows(self) -> _List[Tuple[datetime, float]]:
43 """Return expected cashflows for the configured deposit and valuation date.
45 Returns:
46 List[Tuple[datetime, float]]: List of (pay_date, amount) tuples.
47 """
48 return DeterministicCashflowPricer.expected_cashflows(self._spec, self._val_date)
50 @staticmethod
51 def get_expected_cashflows(
52 specification: DepositSpecification, val_date: _Union[datetime.date, datetime, None] = None
53 ) -> _List[Tuple[datetime, float]]:
54 """Static helper: get expected cashflows for a deposit specification.
56 Args:
57 specification (DepositSpecification): The deposit specification.
58 val_date (date | datetime, optional): Valuation date to use. If ``None``,
59 the specification's default behavior is used.
61 Returns:
62 List[Tuple[datetime, float]]: List of (pay_date, amount) tuples.
63 """
64 if val_date is None:
65 return DeterministicCashflowPricer.get_expected_cashflows(specification)
66 else:
67 return DeterministicCashflowPricer.get_expected_cashflows(specification, val_date)
69 def price(self) -> float:
70 """Return the present value of the configured deposit using the provided curves.
72 Returns:
73 float: Present value.
74 """
75 return self.get_price(self._val_date, self._spec, self._discount_curve)
77 @staticmethod
78 def get_price(val_date: datetime, specification: DepositSpecification, discount_curve: DiscountCurve) -> float:
79 """Calculate present value of a deposit using the provided discount curve.
81 Args:
82 val_date (date | datetime): Valuation date.
83 specification (DepositSpecification): Deposit contract specification.
84 discount_curve (DiscountCurve): Discount curve used for discounting.
86 Returns:
87 float: Present value (PV) computed from discounted expected cashflows.
88 """
90 return DeterministicCashflowPricer.get_pv_cashflows(val_date, specification, discount_curve)
92 def implied_simply_compounded_rate(self) -> float:
93 """Return the implied simply compounded rate for the configured deposit.
95 The implied simply compounded rate is the rate that makes the deposit's PV
96 equal to zero under a simple-compounding assumption.
98 Returns:
99 float: Implied simply compounded rate.
100 """
101 return DepositPricer.get_implied_simply_compounded_rate(self._val_date, self._spec, self._discount_curve)
103 @staticmethod
104 def get_implied_simply_compounded_rate(val_date: datetime, specification: DepositSpecification, discount_curve: DiscountCurve) -> float:
105 """Compute the implied simply compounded rate that sets deposit PV to zero.
107 The implementation assumes simple compounding: D(t) = 1 / (1 + rate * dt).
109 Args:
110 val_date (date | datetime): Valuation date.
111 specification (DepositSpecification): Deposit specification with start/end dates
112 and day count convention.
113 discount_curve (DiscountCurve): Discount curve used to obtain forward discount
114 factor between start and end.
116 Returns:
117 float: The implied simply compounded rate.
119 Raises:
120 ValueError: If ``discount_curve`` is not an instance of DiscountCurve.
121 """
123 start_date = specification.start_date
124 # maturity_date = specification.maturity_date # date of legal end of the deposit and when payment needs to be made
125 end_date = (
126 specification.end_date
127 ) # period over which interest is calculated # this is the period we need to use as it is the actual accrual period, regardless of when payment is due
128 daycountconvention = specification.day_count_convention
130 if isinstance(discount_curve, DiscountCurve):
131 cont_df = discount_curve.value_fwd(val_date, start_date, end_date)
132 else:
133 raise ValueError("Discount curve must be of type DiscountCurve")
135 dcc = DayCounter(daycountconvention)
136 dt = dcc.yf(start_date, end_date)
137 simple_rate = ((1 / cont_df) - 1) / dt
138 return simple_rate