from ..utils import *
from ..utils.literal import Literal
from ..utils.payment_schedule import PaymentSchedule
from typing import Union, List
import datetime
[docs]
class FixedCouponLegBase:
def __init__(
self,
coupon_rate: float,
payment_dates: List,
accrual_start: List,
accrual_end: List,
notional: float = Literal.ONE_HUNDRED.value,
day_count_type: DayCountTypes = DayCountTypes.THIRTY_360,
) -> None:
"""
NOTE: with_additional_day is a dangerous parameter. It is used to add an additional day \
at the last of the accrual period. This is used in the case of CDS pricing.\
"""
self.payment_dates = Date.convert_from_datetimes(payment_dates)
self.accrual_start = Date.convert_from_datetimes(accrual_start)
self.accrual_end = Date.convert_from_datetimes(accrual_end)
self.coupon_rate = coupon_rate
self.notional = notional
self.day_count_type = day_count_type
# derived attributes
self.day_counter = DayCount(self.day_count_type)
self.accrual_days = []
self.accrual_factors = []
self.payment_amounts = []
self.generate_cashflows()
@property
def cashflows(self) -> list:
return list(zip(self.payment_dates, self.payment_amounts))
[docs]
def generate_cashflows(self) -> None:
"""Generate the cashflows for the fixed coupon leg"""
num_payments = len(self.payment_dates)
for i in range(num_payments):
days, frac = self.day_counter.days_between(
self.accrual_start[i], self.accrual_end[i])
self.accrual_days.append(days)
self.accrual_factors.append(frac)
self.payment_amounts.append(
self.coupon_rate * self.accrual_factors[i] * self.notional)
[docs]
def print_cashflows(self):
d = {
"Payment Date": self.payment_dates,
"Cashflow": self.cashflows,
"Accrual Start": self.accrual_start,
"Accrual End": self.accrual_end,
"Accrual Days": self.accrual_days,
"Accrual Factor": self.accrual_factors,
}
prettyTableByColumn(d)
[docs]
class FixedCouponLeg(ClassUtil, FixedCouponLegBase):
def __init__(
self,
start_date: Union[Date, datetime.date],
maturity_date_or_tenor: Union[Date, datetime.date, str],
coupon_rate: float,
notional: float = Literal.ONE_HUNDRED.value,
freq_type: FrequencyTypes = FrequencyTypes.SEMI_ANNUAL,
day_count_type: DayCountTypes = DayCountTypes.THIRTY_360,
calendar_type: CalendarTypes = CalendarTypes.WEEKEND,
bus_day_adj_type: BusDayAdjustTypes = BusDayAdjustTypes.NONE,
date_gen_rule_type: DateGenRuleTypes = DateGenRuleTypes.BACKWARD,
) -> None:
self.save_attributes(ignore=["start_date", "maturity_date_or_tenor"])
self.resolve_dates(start_date, maturity_date_or_tenor)
# derived attributes
self.calendar = Calendar(self.calendar_type)
# generate schedule
self._payment_schedule_helper = PaymentSchedule(
self.start_date,
self.maturity_date,
self.freq_type,
self.calendar_type,
self.bus_day_adj_type,
self.date_gen_rule_type,
extended=True,
).payment_dates
self.maturity_date = self.calendar.adjust(
self.maturity_date, self.bus_day_adj_type
)
self.payment_dates = self._payment_schedule_helper[1:]
self.accrual_start = self._payment_schedule_helper[:-1]
self.accrual_end = self._payment_schedule_helper[1:]
super().__init__(
self.coupon_rate,
self.payment_dates,
self.accrual_start,
self.accrual_end,
self.notional,
day_count_type,
)
[docs]
@classmethod
def from_cashflows(
cls,
coupon_rate: float,
payment_dates: List,
accrual_start: List,
accrual_end: List,
notional: float = Literal.ONE_HUNDRED.value,
day_count_type: DayCountTypes = DayCountTypes.THIRTY_360,
) -> FixedCouponLegBase:
return FixedCouponLegBase(
coupon_rate,
payment_dates,
accrual_start,
accrual_end,
notional,
day_count_type,
)