Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions financepy/products/rates/ibor_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ def __init__(
float_spread: float = 0.0,
float_freq_type: FrequencyTypes = FrequencyTypes.QUARTERLY,
float_dc_type: DayCountTypes = DayCountTypes.THIRTY_E_360,
cal_type: CalendarTypes = CalendarTypes.WEEKEND,
bd_type: BusDayAdjustTypes = BusDayAdjustTypes.FOLLOWING,
dg_type: DateGenRuleTypes = DateGenRuleTypes.BACKWARD,
):
cal_type: CalendarTypes = CalendarTypes.WEEKEND,
bd_type: BusDayAdjustTypes = BusDayAdjustTypes.FOLLOWING,
dg_type: DateGenRuleTypes = DateGenRuleTypes.BACKWARD,
end_of_month: bool = False,
):
"""Create an interest rate swap contract giving the contract start
date, its maturity, fixed cpn, fixed leg frequency, fixed leg day
count convention and notional. The floating leg parameters have default
Expand Down Expand Up @@ -97,10 +98,11 @@ def __init__(
notional,
principal,
payment_lag,
cal_type,
bd_type,
dg_type,
)
cal_type,
bd_type,
dg_type,
end_of_month,
)

self.float_leg = SwapFloatLeg(
effective_dt,
Expand All @@ -112,10 +114,11 @@ def __init__(
notional,
principal,
payment_lag,
cal_type,
bd_type,
dg_type,
)
cal_type,
bd_type,
dg_type,
end_of_month,
)

###########################################################################

Expand Down
94 changes: 94 additions & 0 deletions unit_tests/test_FinIborSwap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from financepy.market.curves import InterpTypes
from financepy.products.rates.ibor_single_curve import IborSingleCurve
from financepy.products.rates.ibor_swap import IborSwap
from financepy.utils import (
BusDayAdjustTypes,
Date,
DayCountTypes,
FrequencyTypes,
SwapTypes,
)


def test_ibor_swap_end_of_month_aligns_coupon_grid():
valuation_date = Date(31, 5, 2023)

swaps = []
for tenor in ["6M", "1Y"]:
swaps.append(
IborSwap(
effective_dt=valuation_date,
term_dt_or_tenor=tenor,
fixed_leg_type=SwapTypes.PAY,
fixed_cpn=0.01,
fixed_freq_type=FrequencyTypes.QUARTERLY,
fixed_dc_type=DayCountTypes.ACT_360,
float_dc_type=DayCountTypes.ACT_360,
bd_type=BusDayAdjustTypes.NONE,
end_of_month=True,
)
)

assert swaps[0].fixed_leg.payment_dts == [
Date(31, 8, 2023),
Date(30, 11, 2023),
]
assert swaps[1].fixed_leg.payment_dts[:2] == swaps[0].fixed_leg.payment_dts

IborSingleCurve(
value_dt=valuation_date,
ibor_deposits=[],
ibor_fras=[],
ibor_swaps=swaps,
interp_type=InterpTypes.FLAT_FWD_RATES,
)


def test_ibor_swap_end_of_month_handles_non_eom_effective_date():
swap = IborSwap(
effective_dt=Date(15, 5, 2023),
term_dt_or_tenor="1Y",
fixed_leg_type=SwapTypes.PAY,
fixed_cpn=0.01,
fixed_freq_type=FrequencyTypes.QUARTERLY,
fixed_dc_type=DayCountTypes.ACT_360,
float_dc_type=DayCountTypes.ACT_360,
bd_type=BusDayAdjustTypes.NONE,
end_of_month=True,
)

assert swap.fixed_leg.payment_dts == [
Date(31, 5, 2023),
Date(31, 8, 2023),
Date(30, 11, 2023),
Date(29, 2, 2024),
Date(15, 5, 2024),
]


def test_ibor_swap_end_of_month_applies_to_float_leg_schedule():
swap = IborSwap(
effective_dt=Date(31, 5, 2023),
term_dt_or_tenor="1Y",
fixed_leg_type=SwapTypes.PAY,
fixed_cpn=0.01,
fixed_freq_type=FrequencyTypes.ANNUAL,
fixed_dc_type=DayCountTypes.ACT_360,
float_freq_type=FrequencyTypes.QUARTERLY,
float_dc_type=DayCountTypes.ACT_360,
bd_type=BusDayAdjustTypes.NONE,
end_of_month=True,
)

assert swap.float_leg.payment_dts == [
Date(31, 8, 2023),
Date(30, 11, 2023),
Date(29, 2, 2024),
Date(31, 5, 2024),
]
assert swap.float_leg.start_accrued_dts == [
Date(31, 5, 2023),
Date(31, 8, 2023),
Date(30, 11, 2023),
Date(29, 2, 2024),
]