def rolldate(dt, calendar, convention): """ Roll date to the business day Roll date of the date to the business day according to the convention. For 'follow' convention, if date falls on a holiday, finds first working day after it For 'previous' convention, if date falls on a holiday, finds latest working day before it For 'modfollow' convention, if date falls on a holiday, finds first working day after it, unless date is the end of month in which case previous workday is found For 'previous' convention, if date falls on a holiday, finds latest working day before it, unless date is the beginning of month in which case following workday is found Parameters ---------- dt: datetime.date, datetime.datetime or date string calendar: Calendar object that has is_holiday method convention: one of 'follow', 'previous', 'modfollow', 'modprevious' Returns ------- datetime.datetime of the next business day according to the convention """ convention = convention.lower() dt = dateutils.asdatetime(dt) rolled = dt if convention == "follow": rolled = _roll_forward(dt, calendar) elif convention == 'modfollow': rolled = _roll_forward(dt, calendar) if rolled.month > dt.month: rolled = _roll_backward(dt, calendar) elif convention == 'previous': rolled = _roll_backward(dt, calendar) elif convention == 'modprevious': rolled = _roll_backward(dt, calendar) if rolled.month < dt.month: rolled = _roll_forward(dt, calendar) return rolled
def is_holiday(self, dt): """ Check if specific date is holiday """ dt = dateutils.asdatetime(dt) year = dt.year if not year in self._holiday_cache: # create year cache self._holiday_cache[year] = set() current = datetime.datetime(year, 1, 1) end_year = datetime.datetime(year, 12, 31) while current <= end_year: b_holiday, name, move_day = self._verify_holiday(current) if b_holiday: self._holiday_cache[year].add(current) if move_day is not None: self._holiday_cache[year].add(move_day) current = current + datetime.timedelta(days = 1) return dt in self._holiday_cache[year]
def _daycount_parameters(dt1, dt2, convention, **kwargs): """ Return number of days and total number of days (i.e. numerator and denominator in the counting of year fraction between dates """ convention = _normalize_daycount_convention(convention) dt1 = dateutils.asdatetime(dt1) dt2 = dateutils.asdatetime(dt2) y1, m1, d1 = dt1.year, dt1.month, dt1.day y2, m2, d2 = dt2.year, dt2.month, dt2.day factor = None if convention in {'30/360 US', '30E/360', '30E/360 ISDA', '30E+/360'}: eom = 'eom' in kwargs and kwargs['eom'] if convention == '30/360 US': # US adjustments if eom and dt1.month == 2 and dateutils.iseom( dt1) and dt2.month == 2 and dateutils.iseom(dt2): d2 = 30 if eom and dt1.month == 2 and dateutils.iseom(dt1): d1 = 30 if d2 == 31 and d1 >= 30: d2 = 30 if d1 == 31: d1 = 30 elif convention == '30E/360': if d1 == 31: d1 = 30 if d2 == 31: d2 = 30 elif convention == '30E/360 ISDA': if dateutils.iseom(dt1): d1 = 30 if dateutils.iseom(dt2) and m2 != 2: d2 = 30 elif convention == '30E+/360': if d1 == 31: d1 = 30 if d2 == 31: m2 += 1 if m2 == 13: m2 = 1 y2 += 1 d2 = 1 num_days = (360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)) year_days = 360 elif convention == 'ACTUAL/ACTUAL ISDA': num_days = 0 year_days = 0 if y2 == y1: num_days = (dt2 - dt1).days year_days = dateutils.yeardays(y1) else: # we need to calculate factor properly factor = 0.0 # full years between y1 and y2 exclusive for y in range(y1 + 1, y2): yd = dateutils.yeardays(y) num_days += yd year_days += yd factor += float(num_days) / year_days # days in the remaining part of the first year num = (datetime.datetime(y1 + 1, 1, 1) - dt1).days den = dateutils.yeardays(y1) num_days += num year_days += den factor += float(num) / den # days in the beginning of the last year num = (dt2 - datetime.datetime(y2, 1, 1)).days den = dateutils.yeardays(y2) num_days += num year_days += den factor += float(num) / den elif convention == 'ACTUAL/365 FIXED': num_days = (dt2 - dt1).days year_days = 365 elif convention == 'ACTUAL/360': num_days = (dt2 - dt1).days year_days = 360 elif convention == 'ACTUAL/365L': yearly_frequency = 'frequency' in kwargs and kwargs[ 'frequency'] == 'yearly' if yearly_frequency: year_days = 366 if _period_has_29feb(dt1, dt2) else 365 else: year_days = 366 if dateutils.leapyear(dt2) else 365 num_days = (dt2 - dt1).days elif convention == 'ACTUAL/ACTUAL AFB': year_days = 366 if _period_has_29feb(dt1, dt2) else 365 num_days = (dt2 - dt1).days else: raise ValueError('Unknown daycount convention \'%s\'' % convention) if factor is None: factor = float(num_days) / year_days return num_days, year_days, factor
def _daycount_parameters(dt1, dt2, convention, **kwargs): """ Return number of days and total number of days (i.e. numerator and denominator in the counting of year fraction between dates """ convention = convention.upper() convention = _normalize_daycount_convention(convention) dt1 = dateutils.asdatetime(dt1) dt2 = dateutils.asdatetime(dt2) y1, m1, d1 = dt1.year, dt1.month, dt1.day y2, m2, d2 = dt2.year, dt2.month, dt2.day factor = None if convention in {'30/360 US', '30E/360', '30E/360 ISDA', '30E+/360'}: eom = 'eom' in kwargs and kwargs['eom'] if convention == '30/360 US': # US adjustments if eom and dt1.month == 2 and dateutils.iseom(dt1) and dt2.month == 2 and dateutils.iseom(dt2): d2 = 30 if eom and dt1.month == 2 and dateutils.iseom(dt1): d1 = 30 if d2 == 31 and d1>=30: d2 = 30 if d1 == 31: d1 = 30 elif convention == '30E/360': if d1 == 31: d1 = 30 if d2 == 31: d2 = 30 elif convention == '30E/360 ISDA': if dateutils.iseom(dt1): d1 = 30 if dateutils.iseom(dt2) and m2 != 2: d2 = 30 elif convention == '30E+/360': if d1 == 31: d1 = 30 if d2 == 31: m2 += 1 if m2 == 13: m2 = 1 y2 += 1 d2 = 1 num_days = (360*(y2-y1)+30*(m2-m1)+(d2-d1)) year_days = 360 elif convention == 'ACTUAL/ACTUAL ISDA': num_days = 0 year_days = 0 if y2 == y1: num_days = (dt2 - dt1).days year_days = dateutils.yeardays(y1) else: # we need to calculate factor properly factor = 0.0 # full years between y1 and y2 exclusive for y in range(y1+1, y2): yd = dateutils.yeardays(y) num_days += yd year_days += yd factor += float(num_days)/year_days # days in the remaining part of the first year num = (datetime.datetime(y1+1, 1, 1) - dt1).days den = dateutils.yeardays(y1) num_days += num year_days += den factor += float(num)/den # days in the beginning of the last year num = (dt2 - datetime.datetime(y2, 1, 1)).days den = dateutils.yeardays(y2) num_days += num year_days += den factor += float(num)/den elif convention == 'ACTUAL/365 FIXED': num_days = (dt2-dt1).days year_days = 365 elif convention == 'ACTUAL/360': num_days = (dt2-dt1).days year_days = 360 elif convention == 'ACTUAL/365L': yearly_frequency = 'frequency' in kwargs and kwargs['frequency'] =='yearly' if yearly_frequency: year_days = 366 if _period_has_29feb(dt1, dt2) else 365 else: year_days = 366 if dateutils.leapyear(dt2) else 365 num_days = (dt2-dt1).days elif convention == 'ACTUAL/ACTUAL AFB': year_days = 366 if _period_has_29feb(dt1, dt2) else 365 num_days = (dt2-dt1).days else: raise ValueError('Unknown daycount convention \'%s\'' % convention) if factor is None: factor = float(num_days)/year_days return num_days, year_days, factor