Skip to content

Commit a3efa49

Browse files
committed
Upgraded the computation of multiple metrics
1. Use the class member fixed_leg_type(SwapTypes.PAY or SwapTypes.RECEIVE) to determine whether a counterparty is a buyer or seller, and calculate the Macaulay duration, modified duration, basis point value (BPV), and present value change of the interest rate swap (IRS). 2. Updated the notebook: FINIBORSWAP_ReplicationBONDMATHDurationExample.ipynb.
1 parent ff0750a commit a3efa49

File tree

2 files changed

+159
-462
lines changed

2 files changed

+159
-462
lines changed

financepy/products/rates/ibor_swap.py

Lines changed: 16 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -392,42 +392,16 @@ def print_payments(self):
392392

393393
###########################################################################
394394

395-
def payer_side_macaulay_duration(self, value_dt, discount_curve, payment_periods: float):
396-
"""Calculation of the Payer's Macaulay Duration in an Interest Rate Swap
397-
Based on Bond Math: The Theory Behind the Formulas, Second Edition by
398-
Donald J. Smith
399-
"""
400-
# Get coupon frequency
401-
coupon_frequency = self.fixed_leg.freq_type.value
402-
403-
# Get swap rate
404-
swap_rate_val = self.swap_rate(value_dt,discount_curve)
405-
406-
y = swap_rate_val / coupon_frequency
407-
# payment_periods is the number of periods to maturity as of the beginning of the period;
408-
# for instance, If the start date of the interest rate swap is 2024-01-01,
409-
# the end date is 2025-12-31, the valuation date is 2024-03-31,
410-
# and the frequency type is QUARTERLY,
411-
# then the number of payment periods is 8; it is independent of the valuation date.
412-
N = payment_periods
413-
c = swap_rate_val / coupon_frequency
414-
md1 = (1 + y) / y
415-
md2 = 1 + y + (N * (c - y))
416-
md3 = c * ((1 + y) ** N - 1) + y
417-
mac_duration = 1 - (md1 - md2 / md3)
418-
return mac_duration
419-
420-
421395
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
396+
"""Calculation of the Macaulay Duration in an Interest Rate Swap
423397
Based on Bond Math: The Theory Behind the Formulas, Second Edition by
424398
Donald J. Smith
425399
"""
426400
# Get coupon frequency
427401
coupon_frequency = self.fixed_leg.freq_type.value
428402

429403
# Get swap rate
430-
swap_rate_val = self.swap_rate(value_dt,discount_curve)
404+
swap_rate_val = self.swap_rate(value_dt, discount_curve)
431405

432406
y = swap_rate_val / coupon_frequency
433407
# payment_periods is the number of periods to maturity as of the beginning of the period;
@@ -447,37 +421,16 @@ def macaulay_duration(self, value_dt, discount_curve, swap_type, payment_periods
447421
The fixed‐rate payer and floating‐rate receiver,
448422
sometimes is said to be the “buyer” of the swap, or is “long” the swap.
449423
"""
450-
if swap_type == SwapTypes.PAY:
424+
if swap_type == SwapTypes.PAY:
451425
mac_duration = 1 - (md1 - md2 / md3)
452426

453-
elif swap_type == SwapTypes.RECEIVE:
454-
mac_duration = (1 - (md1 - md2 / md3))*(-1)
427+
elif swap_type == SwapTypes.RECEIVE:
428+
mac_duration = (1 - (md1 - md2 / md3)) * (-1)
455429

456430
return mac_duration
457431

458432
###########################################################################
459433

460-
def receiver_side_macaulay_duration(self, value_dt, discount_curve,payment_periods: float):
461-
"""Calculation of the Receiver's Macaulay Duration in an Interest Rate Swap
462-
Based on Bond Math: The Theory Behind the Formulas, Second Edition by
463-
Donald J. Smith
464-
"""
465-
return self.payer_side_macaulay_duration(value_dt, discount_curve,payment_periods)*(-1)
466-
467-
###########################################################################
468-
469-
def payer_side_modified_duration(self, value_dt, discount_curve,payment_periods: float):
470-
"""Computation of the Modified Duration for the Fixed-Rate
471-
Payer's Perspective in Interest Rate Swap
472-
"""
473-
# Get coupon frequency
474-
coupon_frequency = self.fixed_leg.freq_type.value
475-
476-
# Get swap rate
477-
swap_rate_val = self.swap_rate(value_dt,discount_curve)
478-
479-
return self.payer_side_macaulay_duration(value_dt, discount_curve,payment_periods)/(1+swap_rate_val/coupon_frequency)
480-
481434
def modified_duration(self, value_dt, discount_curve, swap_type, payment_periods: float):
482435
"""Computation of the Modified Duration for the Fixed-Rate
483436
Payer's Perspective in Interest Rate Swap
@@ -488,50 +441,13 @@ def modified_duration(self, value_dt, discount_curve, swap_type, payment_periods
488441
# Get swap rate
489442
swap_rate_val = self.swap_rate(value_dt, discount_curve)
490443

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))
444+
modified_duration = (self.macaulay_duration(value_dt, discount_curve, swap_type, payment_periods) /
445+
(1 + swap_rate_val / coupon_frequency))
498446

499447
return modified_duration
500448

501-
502449
###########################################################################
503450

504-
def receiver_side_modified_duration(self, value_dt, discount_curve,payment_periods: float):
505-
"""Computation of the Modified Duration for the Fixed-Rate
506-
Receiver's Perspective in Interest Rate Swap
507-
"""
508-
return self.payer_side_modified_duration(value_dt, discount_curve,payment_periods)*(-1)
509-
510-
###########################################################################
511-
512-
def payer_side_profits(self,
513-
value_dt,
514-
discount_curve,payment_periods: float,
515-
swap_rate_changes: float ):
516-
"""Computation of the Profits for the Fixed-Rate Payer's Perspective in Interest Rate Swap
517-
"""
518-
# Get coupon frequency
519-
coupon_frequency = self.fixed_leg.freq_type.value
520-
521-
annualized_modi_dur = self.payer_side_modified_duration(value_dt, discount_curve,payment_periods)/coupon_frequency
522-
return (-1)*(annualized_modi_dur*self.fixed_leg.notional*swap_rate_changes)
523-
524-
###########################################################################
525-
526-
def receiver_side_profits(self,
527-
value_dt,
528-
discount_curve,payment_periods: float,
529-
swap_rate_changes: float):
530-
"""Computation of the Profits for the Fixed-Rate Receiver's Perspective in Interest Rate Swap
531-
"""
532-
return self.payer_side_profits(value_dt, discount_curve,payment_periods,swap_rate_changes)*(-1)
533-
534-
535451
def change_in_market_value(self,
536452
value_dt, discount_curve, swap_type, payment_periods: float,
537453
swap_rate_changes: float):
@@ -540,63 +456,31 @@ def change_in_market_value(self,
540456
# Get coupon frequency
541457
coupon_frequency = self.fixed_leg.freq_type.value
542458

543-
annualized_modi_dur = self.modified_duration(value_dt, discount_curve,swap_type,payment_periods)/coupon_frequency
459+
annualized_modi_dur = self.modified_duration(value_dt, discount_curve, swap_type,
460+
payment_periods) / coupon_frequency
544461

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)
462+
delta_mv = (-1) * (annualized_modi_dur * self.fixed_leg.notional * swap_rate_changes)
550463

551464
return delta_mv
552465

553466
###########################################################################
554467

555-
def payer_side_BPV(self, value_dt, discount_curve,payment_periods: float,):
468+
def basis_point_value(self, value_dt, discount_curve, swap_type, payment_periods: float):
556469
"""
557-
calculate the basis‐point‐value (BPV) for the payer_side of the swap,
470+
calculate the basis‐point‐value (BPV) of the swap,
558471
which is swap's modified duration times the notional principal,
559472
times one basis point (0.0001)
560473
"""
561-
bp=0.0001
562-
# Get coupon frequency
563-
coupon_frequency = self.fixed_leg.freq_type.value
564-
modi_dur = self.payer_side_modified_duration(value_dt, discount_curve,payment_periods)
565-
annualized_modi_dur= modi_dur/coupon_frequency
566-
return annualized_modi_dur*self.fixed_leg.notional*bp
567-
568-
###########################################################################
569-
570-
def receiver_side_BPV(self, value_dt, discount_curve,payment_periods: float,):
571-
"""
572-
calculate the basis‐point‐value (BPV) for receiver_side of the swap,
573-
which is swap's modified duration times the notional principal,
574-
times one basis point (0.0001)
575-
"""
576-
return self.payer_side_BPV(value_dt, discount_curve,payment_periods)*(-1)
577-
578-
###########################################################################
579-
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
474+
bp = 0.0001
587475
# Get coupon frequency
588476
coupon_frequency = self.fixed_leg.freq_type.value
589477
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
478+
annualized_modi_dur = modi_dur / coupon_frequency
479+
BPV = annualized_modi_dur * self.fixed_leg.notional * bp
597480

598481
return BPV
599482

483+
###########################################################################
600484

601485
def __repr__(self):
602486

0 commit comments

Comments
 (0)