Source code for finpricing.utils.date_generator

import datetime
from bisect import bisect_left
from typing import Union, List

from finpricing.utils.date import TimeInterval, Date
from finpricing.utils.cds_style import CDSStyle, CDSStubType, CDSTermStyle
from finpricing.utils.tools import datetimeToDates
from finpricing.utils.calendar import Calendar

[docs] class DateGenerator: def __init__(self): pass
[docs] @staticmethod @datetimeToDates def generate_extended( start_date: Union[datetime.date, Date], maturity_date: Union[datetime.date, Date], time_interval: Union[TimeInterval, str], stub_at_end: bool ): """generate a list of dates from start_date to maturity_date with given time_interval NOTE: if stub_at_end is True, the dates will be generated from start_date and roll forward until and after maturity_date if stub_at_end is False, the dates will be generated from maturity_date and roll backward until and before start_date Args: start_date (Union[datetime.date, Date]): start date maturity_date (Union[datetime.date, Date]): maturity date time_interval (Union[TimeInterval, str]): time interval stub_at_end (bool): whether the stub is at the end of the period """ if isinstance(time_interval, str): time_interval = TimeInterval.from_string(time_interval) dates = list() if stub_at_end: next_date = start_date while next_date < maturity_date: dates.append(next_date) next_date = next_date.add_interval(time_interval) dates.append(next_date) else: prev_date = maturity_date while prev_date > start_date: dates.append(prev_date) prev_date = prev_date.add_interval(-time_interval) dates.append(prev_date) dates.reverse() return dates
[docs] @staticmethod @datetimeToDates def generate_cds( start_date, maturity_date, cds_style: CDSStyle, stub_at_end: bool=False ): """generate a list of dates from start_date to maturity_date with given cds_style NOTE: Most likely, the stub is at the end of the period. In a NO_STUB case, the first date will be removed. \ So the first date will be after start_date. """ time_interval = TimeInterval(int(12 / cds_style.frequency_type.value), "m") unadjust_dates = DateGenerator.generate_extended(start_date, maturity_date, time_interval, stub_at_end) if cds_style.cds_stub_length == CDSStubType.NO_STUB: return unadjust_dates[1:] else: raise NotImplementedError("Given CDS Stub Type is not implemented yet.")
[docs] @staticmethod @datetimeToDates def generate_cds_adjust( start_date, maturity_date, cds_style: CDSStyle, stub_at_end: bool=False ): """generate accrual start dates, accrual end dates, and calendar adjusted payment dates for a cds contract NOTE only the payment dates are adjusted! """ # generate unadjusted dates dates = DateGenerator.generate_cds(start_date, maturity_date, cds_style, stub_at_end) # calendar type considers the holidays, and business day adjustment type considers the following, previous, etc. calendar = Calendar(cds_style.calendar_type) bus_adj_type = cds_style.bus_day_adj_type accrual_start_dates = list() accrual_end_dates = list() payment_dates = list() this_accrual_start_date = start_date for x in dates: this_accrual_end_date = x this_payment_date = calendar.adjust(this_accrual_end_date, bus_adj_type) if this_accrual_end_date < maturity_date: this_accrual_end_date = this_payment_date accrual_start_dates.append(this_accrual_start_date) accrual_end_dates.append(this_accrual_end_date) payment_dates.append(this_payment_date) this_accrual_start_date = this_accrual_end_date assert this_accrual_start_date == maturity_date return accrual_start_dates, accrual_end_dates, payment_dates
[docs] @staticmethod def generate_cds_maturity_date(market_date: Union[Date, datetime.date], maturity_date: Union[Date, datetime.date], term_style: str=None): """return the adjusted maturity date for a cds contract by its term style FIXME This is not effectively implemented yet. It seems IMM CORPORATE returns the same maturity date as the input. """ if term_style == CDSTermStyle.IMM_CORPORATE: return maturity_date else: raise NotImplementedError("CDS maturity date: given CDS Term Style is not implemented yet.")
[docs] @staticmethod @datetimeToDates def generate_cds_effective_date(market_date: Union[Date, datetime.date], maturity_date: Union[Date, datetime.date], cds_style: Union[CDSStyle, str]): """generate the effective date for a cds contract when market date is in between coupon dates """ step_in_date = market_date.add_tenor("1d") time_interval = TimeInterval(int(12 / cds_style.frequency_type.value), "m") safe_start_date = step_in_date.add_interval(-3 * time_interval) dates = DateGenerator.generate_cds(safe_start_date, maturity_date, cds_style, stub_at_end=False) # business day adjustment calendar = Calendar(cds_style.calendar_type) bus_adj_type = cds_style.bus_day_adj_type dates = [calendar.adjust(x, bus_adj_type) for x in dates] # find the previous valid date before step_in_date idx_effective_date = bisect_left(dates, step_in_date) - 1 return dates[idx_effective_date]