Coverage for rivapy / pricing / fra_pricing.py: 88%

56 statements  

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

1from datetime import datetime, date 

2from scipy.optimize import brentq 

3from rivapy.tools.interfaces import BaseDatedCurve 

4from rivapy.instruments.bond_specifications import DeterministicCashflowBondSpecification 

5from rivapy.marketdata import DiscountCurveParametrized, ConstantRate, DiscountCurve 

6from rivapy.pricing.bond_pricing import DeterministicCashflowPricer 

7from rivapy.pricing.pricing_request import PricingRequest 

8from rivapy.pricing._logger import logger 

9from rivapy.instruments.deposit_specifications import DepositSpecification 

10from rivapy.instruments.fra_specifications import ForwardRateAgreementSpecification 

11from typing import List as _List, Union as _Union, Tuple 

12from rivapy.tools.datetools import DayCounter, roll_day 

13 

14 

15class ForwardRateAgreementPricer: 

16 

17 def __init__( 

18 self, 

19 val_date: _Union[date, datetime], 

20 fra_spec: ForwardRateAgreementSpecification, 

21 discount_curve: DiscountCurve, 

22 forward_curve: DiscountCurve = None, 

23 ): 

24 """Initializes the FRA pricer with valuation date, FRA specification, discount curve and forward curve. 

25 

26 Args: 

27 val_date (_Union[date, datetime]): specific date for which the value of the financial instrument is calculated. 

28 fra_spec (ForwardRateAgreementSpecification): Specification object with FRA specific parameters. 

29 discount_curve (DiscountCurve): Discount curve used for discounting. 

30 forward_curve(): from underlying index... 

31 

32 """ 

33 

34 self._val_date = val_date 

35 self._fra_spec = fra_spec 

36 self._discount_curve = discount_curve 

37 

38 if forward_curve == None: 

39 # generate forward curve from given discount curve? 

40 self._forward_curve = discount_curve # TODO implement functionality 

41 else: 

42 self._forward_curve = forward_curve 

43 

44 @staticmethod 

45 def get_expected_cashflows( 

46 specification: ForwardRateAgreementSpecification, 

47 val_date: _Union[datetime.date, datetime], 

48 fwdcurve: DiscountCurve, 

49 ) -> _List[Tuple[datetime, float]]: 

50 """Calculate expected cashflows for the FRA specification based on the valuation date and discount curve. 

51 

52 Args: 

53 specification (ForwardRateAgreementSpecification): The FRA specification. 

54 val_date (_Union[datetime.date, datetime]): The data as of which the cashflows are calculated. 

55 fwdcurve (_Union[DiscountCurve, None]): The forward curve. 

56 

57 Returns: 

58 List[Tuple[datetime, float]]: List of tuples containing payment dates and amounts. 

59 """ 

60 

61 cashflows = [] 

62 # using curve daycount convention to get fwd-rate data 

63 dcc_rate = DayCounter(fwdcurve.daycounter) 

64 fwdrateDF = fwdcurve.value_fwd(val_date, specification._rate_start_date, specification._rate_end_date) 

65 dt_rate = dcc_rate.yf(specification._rate_start_date, specification._rate_end_date) 

66 fwdrate = (1.0 / fwdrateDF - 1) / dt_rate 

67 print(f"Day count fraction (yf): {dt_rate}, Forward rate: {fwdrate}") 

68 

69 # using instrument daycount convention to calculate delta t for cf amount calculation and discouting 

70 dcc = DayCounter(specification.day_count_convention) 

71 dt = dcc.yf(specification._start_date, specification._end_date) 

72 amount = specification._notional * (fwdrate - specification._rate) * dt 

73 print(f"dt: {dt}, Specification_Rate: {specification._rate}, Amount: {amount}") 

74 cf = amount / (1 + fwdrate * dt) 

75 print(f"Cashflow: {cf}") 

76 

77 payment_date = roll_day( 

78 specification._start_date, specification._calendar, specification._business_day_convention, settle_days=specification._payment_days 

79 ) 

80 cashflows.append((payment_date, cf)) 

81 

82 return cashflows 

83 

84 def expected_cashflows(self): 

85 """Calculate expected cashflows for the FRA specification. 

86 

87 Returns: 

88 List[Tuple[datetime, float]]: List of tuples containing payment dates and amounts. 

89 """ 

90 return ForwardRateAgreementPricer.get_expected_cashflows(self._fra_spec, self._val_date, self._forward_curve) 

91 

92 @staticmethod 

93 def get_price( 

94 val_date: _Union[datetime.date, datetime], 

95 specification: ForwardRateAgreementSpecification, 

96 discount_curve: DiscountCurve, 

97 forward_curve: _Union[DiscountCurve, None] = None, 

98 ) -> float: 

99 """Calculate the present value of the specified FRA given a discount curve and forward curve 

100 

101 Args: 

102 val_date (_Union[datetime.date, datetime]): The valuation date. 

103 specification (ForwardRateAgreementSpecification): The FRA specification. 

104 discount_curve (DiscountCurve): The discount curve. 

105 forward_curve (_Union[DiscountCurve, None]): The forward curve. 

106 

107 Returns: 

108 float: The present value of the FRA. 

109 """ 

110 expected_cashflows = ForwardRateAgreementPricer.get_expected_cashflows(specification, val_date, forward_curve) 

111 price = discount_curve.value(val_date, expected_cashflows[0][0]) * expected_cashflows[0][1] 

112 # DeterministicCashflowPricer.get_pv_cashflows(val_date, specification, discount_curve, expected_cashflows) 

113 return price 

114 

115 def price(self): 

116 """Calculate the present value of the specified FRA given a discount curve and forward curve 

117 

118 Returns: 

119 float: present value of a deposit based on simple compounding 

120 """ 

121 price = ForwardRateAgreementPricer.get_price(self._val_date, self._fra_spec, self._discount_curve, self._forward_curve) 

122 

123 return price 

124 

125 @staticmethod 

126 def compute_fair_rate( 

127 val_date: _Union[datetime, date], 

128 specification: ForwardRateAgreementSpecification, 

129 discount_curve: DiscountCurve, 

130 ): 

131 """Computes the fair rate such that the when used in the specification of the FRA gives a net value of zero. 

132 A discount curve is given, from which the Forward Rate is determined between the two dates. 

133 Assuming simple compounding 

134 Forward rate = (DF_1/DF_2 -1 )/ time_interval 

135 = (1 /FWD_DF -1 )/ time_interval 

136 

137 Args: 

138 val_date (_Union[datetime, date]): specific date as of which the value of the financial instrument is calculated. 

139 forward_curve (DiscountCurve): Forward curve used for projecting rates 

140 rate_start_date (_Union[datetime, date]): start date for the forward period 

141 rate_end_date (_Union[datetime, date]): end date for the forward period 

142 

143 Returns: 

144 float: _description_ 

145 """ 

146 

147 rate_start_date = specification._rate_start_date 

148 rate_end_date = specification._rate_end_date 

149 

150 dcc = DayCounter(discount_curve.daycounter) 

151 yf = dcc.yf(rate_start_date, rate_end_date) 

152 fwd_df = discount_curve.value_fwd(val_date, rate_start_date, rate_end_date) # REF DATE is = 

153 

154 fair_rate = (1.0 / fwd_df - 1) / yf 

155 

156 return fair_rate