Source code for finpricing.utils.calendar
from enum import Enum
from .error import NotSupportedError
from .date import Date
from .bus_day_adj import BusDayAdjustTypes
from .holiday import Holiday, CalendarTypes
[docs]
class DateGenRuleTypes(Enum):
FORWARD = 1
BACKWARD = 2
[docs]
class Calendar:
def __init__(self, calendarType: CalendarTypes) -> None:
"""
I think holiday should be a class dependent on the calendar type.
"""
if calendarType not in CalendarTypes:
raise NotSupportedError("CalendarTypes is not supported")
self._calendarType = calendarType
self._holiday = Holiday(calendarType)
[docs]
def is_holiday(self, date: Date) -> bool:
return self._holiday.is_holiday(date)
[docs]
def is_business_day(self, date: Date) -> bool:
"""Return True if the date is a business day
Weekend or not is determined solely by the date itself.
Holiday or not is determined by the calendar.
"""
if date.is_weekend is False and self.is_holiday(date) is False:
return True
else:
return False
[docs]
def add_business_days(self, date: Date, num_days: int) -> Date:
"""Return a new Date object by adding num_days business days to the date"""
if num_days == 0:
return date
if num_days > 0:
date = date.add_days(1)
while self.is_business_day(date) is False:
date = date.add_days(1)
return self.add_business_days(date, num_days - 1)
else:
date = date.add_days(-1)
while self.is_business_day(date) is False:
date = date.add_days(-1)
return self.add_business_days(date, num_days + 1)
[docs]
def adjust(self, date: Date, busDayAdjType: BusDayAdjustTypes) -> Date:
"""Return a new Date object by adjusting the date according to the Business Day Convention
For details, https://jollycontrarian.com/index.php?title=Business_Day_Convention_-_ISDA_Definition
"""
# no need to adjust if no calendar is specified
if self._calendarType == CalendarTypes.NONE:
return date
# need to adjust but depend on the BusDayAdjustTypes
if busDayAdjType == BusDayAdjustTypes.NONE:
return date
elif busDayAdjType == BusDayAdjustTypes.FOLLOWING:
while self.is_business_day(date) is False:
date = date.add_days(1)
return date
elif busDayAdjType == BusDayAdjustTypes.MODIFIED_FOLLOWING:
initial_date = Date.from_tuple(date.to_tuple())
while self.is_business_day(date) is False:
date = date.add_days(1)
# if the following business day is in the different month, find the previous business day
if date.month != initial_date.month:
date = initial_date
while self.is_business_day(date) is False:
date = date.add_days(-1)
return date
elif busDayAdjType == BusDayAdjustTypes.PREVIOUS:
while self.is_business_day(date) is False:
date = date.add_days(-1)
return date
elif busDayAdjType == BusDayAdjustTypes.MODIFIED_PREVIOUS:
initial_date = Date.from_tuple(date.to_tuple())
while self.is_business_day(date) is False:
date = date.add_days(-1)
# if the previous business day is in the different month, find the next business day
if date.month != initial_date.month:
date = initial_date
while self.is_business_day(date) is False:
date = date.add_days(1)
return date
else:
raise NotSupportedError(
"BusDayAdjustTypes is not supported, cannot adjust date")