@@ -417,6 +417,44 @@ def payer_side_macaulay_duration(self, value_dt, discount_curve, payment_periods
417417 mac_duration = 1 - (md1 - md2 / md3 )
418418 return mac_duration
419419
420+
421+ def macaulay_duration (self , value_dt , discount_curve , swap_type , payment_periods : float ):
422+ """Calculation of the Payer's Macaulay Duration in an Interest Rate Swap
423+ Based on Bond Math: The Theory Behind the Formulas, Second Edition by
424+ Donald J. Smith
425+ """
426+ # Get coupon frequency
427+ coupon_frequency = self .fixed_leg .freq_type .value
428+
429+ # Get swap rate
430+ swap_rate_val = self .swap_rate (value_dt ,discount_curve )
431+
432+ y = swap_rate_val / coupon_frequency
433+ # payment_periods is the number of periods to maturity as of the beginning of the period;
434+ # for instance, If the start date of the interest rate swap is 2024-01-01,
435+ # the end date is 2025-12-31, the valuation date is 2024-03-31,
436+ # and the frequency type is QUARTERLY,
437+ # then the number of payment periods is 8; it is independent of the valuation date.
438+ N = payment_periods
439+ c = swap_rate_val / coupon_frequency
440+ md1 = (1 + y ) / y
441+ md2 = 1 + y + (N * (c - y ))
442+ md3 = c * ((1 + y ) ** N - 1 ) + y
443+
444+ """
445+ The party who pays the fixed rate(ie,SwapTypes.PAY) is known as the payer
446+ and the party who receives the fixed rate is known as the receiver.
447+ The fixed‐rate payer and floating‐rate receiver,
448+ sometimes is said to be the “buyer” of the swap, or is “long” the swap.
449+ """
450+ if swap_type == SwapTypes .PAY :
451+ mac_duration = 1 - (md1 - md2 / md3 )
452+
453+ elif swap_type == SwapTypes .RECEIVE :
454+ mac_duration = (1 - (md1 - md2 / md3 ))* (- 1 )
455+
456+ return mac_duration
457+
420458 ###########################################################################
421459
422460 def receiver_side_macaulay_duration (self , value_dt , discount_curve ,payment_periods : float ):
@@ -440,6 +478,27 @@ def payer_side_modified_duration(self, value_dt, discount_curve,payment_periods:
440478
441479 return self .payer_side_macaulay_duration (value_dt , discount_curve ,payment_periods )/ (1 + swap_rate_val / coupon_frequency )
442480
481+ def modified_duration (self , value_dt , discount_curve , swap_type , payment_periods : float ):
482+ """Computation of the Modified Duration for the Fixed-Rate
483+ Payer's Perspective in Interest Rate Swap
484+ """
485+ # Get coupon frequency
486+ coupon_frequency = self .fixed_leg .freq_type .value
487+
488+ # Get swap rate
489+ swap_rate_val = self .swap_rate (value_dt , discount_curve )
490+
491+ if swap_type == SwapTypes .PAY :
492+ modified_duration = (self .macaulay_duration (value_dt , discount_curve , swap_type , payment_periods ) /
493+ (1 + swap_rate_val / coupon_frequency ))
494+
495+ elif swap_type == SwapTypes .RECEIVE :
496+ modified_duration = (self .macaulay_duration (value_dt , discount_curve , swap_type , payment_periods ) /
497+ (1 + swap_rate_val / coupon_frequency ))
498+
499+ return modified_duration
500+
501+
443502 ###########################################################################
444503
445504 def receiver_side_modified_duration (self , value_dt , discount_curve ,payment_periods : float ):
@@ -472,6 +531,25 @@ def receiver_side_profits(self,
472531 """
473532 return self .payer_side_profits (value_dt , discount_curve ,payment_periods ,swap_rate_changes )* (- 1 )
474533
534+
535+ def change_in_market_value (self ,
536+ value_dt , discount_curve , swap_type , payment_periods : float ,
537+ swap_rate_changes : float ):
538+ """Computation of the Profits for the Fixed-Rate Payer's Perspective in Interest Rate Swap
539+ """
540+ # Get coupon frequency
541+ coupon_frequency = self .fixed_leg .freq_type .value
542+
543+ annualized_modi_dur = self .modified_duration (value_dt , discount_curve ,swap_type ,payment_periods )/ coupon_frequency
544+
545+ if swap_type == SwapTypes .PAY :
546+ delta_mv = (- 1 )* (annualized_modi_dur * self .fixed_leg .notional * swap_rate_changes )
547+
548+ elif swap_type == SwapTypes .RECEIVE :
549+ delta_mv = (annualized_modi_dur * self .fixed_leg .notional * swap_rate_changes )
550+
551+ return delta_mv
552+
475553 ###########################################################################
476554
477555 def payer_side_BPV (self , value_dt , discount_curve ,payment_periods : float ,):
@@ -499,6 +577,27 @@ def receiver_side_BPV(self, value_dt, discount_curve,payment_periods: float,):
499577
500578 ###########################################################################
501579
580+ def basis_point_value (self ,value_dt , discount_curve ,swap_type , payment_periods : float ):
581+ """
582+ calculate the basis‐point‐value (BPV) for the payer_side of the swap,
583+ which is swap's modified duration times the notional principal,
584+ times one basis point (0.0001)
585+ """
586+ bp = 0.0001
587+ # Get coupon frequency
588+ coupon_frequency = self .fixed_leg .freq_type .value
589+ modi_dur = self .modified_duration (value_dt , discount_curve , swap_type , payment_periods )
590+ annualized_modi_dur = modi_dur / coupon_frequency
591+
592+ if swap_type == SwapTypes .PAY :
593+ BPV = annualized_modi_dur * self .fixed_leg .notional * bp
594+
595+ elif swap_type == SwapTypes .RECEIVE :
596+ BPV = annualized_modi_dur * self .fixed_leg .notional * bp * (- 1 )
597+
598+ return BPV
599+
600+
502601 def __repr__ (self ):
503602
504603 s = label_to_string ("OBJECT TYPE" , type (self ).__name__ )
0 commit comments