示例#1
0
    def __init__(self, year=None, month=None, day=None, serialNumber=None):
        # do the input validation
        if serialNumber:
            self.__serialNumber__ = serialNumber

            y = (int(serialNumber / 365)) + 1900
            if serialNumber <= _YearOffset[y - 1900]:
                y -= 1

            d = serialNumber - _YearOffset[y - 1900]
            m = int(d / 30) + 1
            leap = Date.isLeap(y)
            while d <= _month_offset(m, leap):
                m -= 1

            py_assert(
                1900 < y < 2200, ValueError,
                "year {0:d} is out of bound. It must be in [1901, 2199]".
                format(y))
            self._year = y
            self._month = m
            self._day = d - _month_offset(m, leap)
            return
        elif serialNumber and (year or month or day):
            raise ValueError(
                "When serial number is offered, no year or month or day number should be entered"
            )
        elif not (year and month and day):
            raise ValueError(
                "year: {0}, month: {1}, day: {2} can't be null value included".
                format(year, month, day))

        self._calculate_date(year, month, day)
示例#2
0
    def nthWeekday(nth, dayOfWeek, m, y):
        py_assert(nth > 0, ValueError,
                  "zeroth day of week in a given (month, year) is undefined")
        py_assert(nth < 6, ValueError,
                  "no more than 5 weekday in a given (month, year)")

        first = Date(y, m, 1).weekday()
        skip = nth - (1 if dayOfWeek >= first else 0)
        return Date(y, m, (1 + dayOfWeek + skip * 7) - first)
示例#3
0
 def bizDatesList(self, fromDate, toDate):
     py_assert(
         fromDate <= toDate, ValueError,
         "from date ({0} must be earlier than to date {1}".format(
             fromDate, toDate))
     result = []
     d = fromDate
     while d <= toDate:
         if self.isBizDay(d):
             result.append(d)
         d += 1
     return result
示例#4
0
 def holDatesList(self, fromDate, toDate, includeWeekEnds=True):
     py_assert(
         fromDate <= toDate, ValueError,
         "from date ({0} must be earlier than to date {1}".format(
             fromDate, toDate))
     result = []
     d = fromDate
     while d <= toDate:
         if self.isHoliday(d) and (includeWeekEnds
                                   or not self.isWeekEnd(d.weekday())):
             result.append(d)
         d += 1
     return result
示例#5
0
    def __div__(self, n):
        resunits = self.units
        reslength = self.length

        if reslength % n == 0:
            reslength /= n
        else:
            if resunits == TimeUnits.Years:
                reslength *= 12
                resunits = TimeUnits.Months
            elif resunits == TimeUnits.Weeks:
                reslength *= 7
                resunits = TimeUnits.Days

            py_assert(reslength % n == 0, ValueError,
                      "{0} cannot be divided by {1:d}".format(self, n))

            reslength //= n

        return Period(length=reslength, units=resunits)
示例#6
0
def _advance(date, n, units):
    if units == TimeUnits.Days or units == TimeUnits.BDays:
        return Date(serialNumber=date.__serialNumber__ + n)
    elif units == TimeUnits.Weeks:
        return Date(serialNumber=date.__serialNumber__ + 7 * n)
    elif units == TimeUnits.Months:
        d = date._day
        m = date._month + n
        y = date._year
        added_year = int(floor(m / 12.))
        month_left = m % 12
        if month_left == 0:
            month_left = 12
            added_year -= 1
        y += added_year
        leap_flag = Date.isLeap(y)

        py_assert(
            1900 < y < 2200, ValueError,
            'year {0:d} is out of bound. It must be in [1901, 2199]'.format(y))

        length = _MonthLeapLength[month_left -
                                  1] if leap_flag else _MonthLength[month_left
                                                                    - 1]
        if d > length:
            d = length

        return Date(y, month_left, d)
    elif units == TimeUnits.Years:
        d = date._day
        m = date._month
        y = date._year + n
        leap_flag = Date.isLeap(y)

        if y <= 1900 or y >= 2200:
            return Date(1900, 1, 1)

        if d == 29 and m == 2 and not leap_flag:
            d = 28

        return Date(y, m, d)
示例#7
0
    def __add__(self, p2):
        reslength = self.length
        resunits = self.units
        p2length = p2.length
        p2units = p2.units

        if reslength == 0:
            return Period(length=p2length, units=p2units)
        elif resunits == p2units:
            reslength += p2length
            return Period(length=reslength, units=resunits)
        else:
            if resunits == TimeUnits.Years:
                if p2units == TimeUnits.Months:
                    resunits = TimeUnits.Months
                    reslength = reslength * 12 + p2length
                elif p2units == TimeUnits.Weeks or p2units == TimeUnits.Days or p2units == TimeUnits.BDays:
                    py_assert(
                        p2length == 0, ValueError,
                        "impossible addition between {0} and {1}".format(
                            self, p2))
                else:
                    raise ValueError(
                        "unknown time unit ({0:d})".format(p2units))
                return Period(length=reslength, units=resunits)
            elif resunits == TimeUnits.Months:
                if p2units == TimeUnits.Years:
                    reslength += 12 * p2length
                elif p2units == TimeUnits.Weeks or p2units == TimeUnits.Days or p2units == TimeUnits.BDays:
                    py_assert(
                        p2length == 0, ValueError,
                        "impossible addition between {0} and {1}".format(
                            self, p2))
                else:
                    raise ValueError(
                        "unknown time unit ({0:d})".format(p2units))
                return Period(length=reslength, units=resunits)
            elif resunits == TimeUnits.Weeks:
                if p2units == TimeUnits.Days:
                    resunits = TimeUnits.Days
                    reslength = reslength * 7 + p2length
                elif p2units == TimeUnits.Years or p2units == TimeUnits.Months or p2units == TimeUnits.BDays:
                    py_assert(
                        p2length == 0, ValueError,
                        "impossible addition between {0} and {1}".format(
                            self, p2))
                else:
                    raise ValueError(
                        "unknown time unit ({0:d})".format(p2units))
                return Period(length=reslength, units=resunits)
            elif resunits == TimeUnits.Days:
                if p2units == TimeUnits.Weeks:
                    reslength += 7 * p2length
                elif p2units == TimeUnits.Years or p2units == TimeUnits.Months or p2units == TimeUnits.BDays:
                    py_assert(
                        p2length == 0, ValueError,
                        "impossible addition between {0} and {1}".format(
                            self, p2))
                else:
                    raise ValueError(
                        "unknown time unit ({0:d})".format(p2units))
                return Period(length=reslength, units=resunits)
            elif resunits == TimeUnits.BDays:
                if p2units == TimeUnits.Years or p2units == TimeUnits.Months or p2units == TimeUnits.Weeks or p2units == TimeUnits.Days:
                    py_assert(
                        p2length == 0, ValueError,
                        "impossible addition between {0} and {1}".format(
                            self, p2))
                else:
                    raise ValueError(
                        "unknown time unit ({0:d})".format(p2units))
                return Period(length=reslength, units=resunits)
示例#8
0
    def __init__(self,
                 effectiveDate,
                 terminationDate,
                 tenor,
                 calendar,
                 convention=BizDayConventions.Following,
                 terminationConvention=BizDayConventions.Following,
                 dateGenerationRule=DateGeneration.Forward,
                 endOfMonth=False,
                 firstDate=None,
                 nextToLastDate=None,
                 evaluationDate=None):

        # Initialize private data
        self._effectiveDate = effectiveDate
        self._terminationDate = terminationDate
        self._tenor = tenor
        self._cal = calendar
        self._convention = convention
        self._terminationConvention = terminationConvention
        self._rule = dateGenerationRule
        self._dates = []
        self._isRegular = []

        if tenor < Period("1M"):
            self._endOfMonth = False
        else:
            self._endOfMonth = endOfMonth

        if firstDate is None or firstDate == effectiveDate:
            self._firstDate = None
        else:
            self._firstDate = firstDate

        if nextToLastDate is None or nextToLastDate == terminationDate:
            self._nextToLastDate = None
        else:
            self._nextToLastDate = nextToLastDate

        # in many cases (e.g. non-expired bonds) the effective date is not
        # really necessary. In these cases a decent placeholder is enough
        if effectiveDate is None and firstDate is None and dateGenerationRule == DateGeneration.Backward:
            evalDate = evaluationDate
            py_assert(evalDate < terminationDate, ValueError,
                      "null effective date")
            if nextToLastDate is not None:
                y = int((nextToLastDate - evalDate) / 366) + 1
                effectiveDate = nextToLastDate - Period(length=y,
                                                        units=TimeUnits.Years)
            else:
                y = int((terminationDate - evalDate) / 366) + 1
                effectiveDate = terminationDate - Period(length=y,
                                                         units=TimeUnits.Years)
        else:
            py_assert(effectiveDate is not None, ValueError,
                      "null effective date")

        py_assert(
            effectiveDate < terminationDate, ValueError,
            "effective date ({0}) "
            "later than or equal to termination date ({1}".format(
                effectiveDate, terminationDate))

        if tenor.length == 0:
            self._rule = DateGeneration.Zero
        else:
            py_assert(
                tenor.length > 0, ValueError,
                "non positive tenor ({0:d}) not allowed".format(tenor.length))

        if self._firstDate is not None:
            if self._rule == DateGeneration.Backward or self._rule == DateGeneration.Forward:
                py_assert(
                    effectiveDate < self._firstDate < terminationDate,
                    ValueError,
                    "first date ({0}) out of effective-termination date range [{1}, {2})"
                    .format(self._firstDate, effectiveDate, terminationDate))
                # we should ensure that the above condition is still
                # verified after adjustment
            elif self._rule == DateGeneration.Zero:
                raise ValueError(
                    "first date incompatible with {0:d} date generation rule".
                    format(self._rule))
            else:
                raise ValueError("unknown rule ({0:d})".format(self._rule))

        if self._nextToLastDate is not None:
            if self._rule == DateGeneration.Backward or self._rule == DateGeneration.Forward:
                py_assert(
                    effectiveDate < self._nextToLastDate < terminationDate,
                    ValueError,
                    "next to last date ({0}) out of effective-termination date range [{1}, {2})"
                    .format(self._nextToLastDate, effectiveDate,
                            terminationDate))
                # we should ensure that the above condition is still
                # verified after adjustment
            elif self._rule == DateGeneration.Zero:
                raise ValueError(
                    "next to last date incompatible with {0:d} date generation rule"
                    .format(self._rule))
            else:
                raise ValueError("unknown rule ({0:d})".format(self._rule))

        # calendar needed for endOfMonth adjustment
        nullCalendar = Calendar("Null")
        periods = 1

        if self._rule == DateGeneration.Zero:
            self._tenor = Period(length=0, units=TimeUnits.Years)
            self._dates.extend([effectiveDate, terminationDate])
            self._isRegular.append(True)
        elif self._rule == DateGeneration.Backward:
            self._dates.append(terminationDate)
            seed = terminationDate
            if self._nextToLastDate is not None:
                self._dates.insert(0, self._nextToLastDate)
                temp = nullCalendar.advanceDate(
                    seed,
                    Period(length=-periods * self._tenor.length,
                           units=self._tenor.units), convention,
                    self._endOfMonth)
                if temp != self._nextToLastDate:
                    self._isRegular.insert(0, False)
                else:
                    self._isRegular.insert(0, True)
                seed = self._nextToLastDate

            exitDate = effectiveDate
            if self._firstDate is not None:
                exitDate = self._firstDate

            while True:
                temp = nullCalendar.advanceDate(
                    seed,
                    Period(length=-periods * self._tenor.length,
                           units=self._tenor.units), convention,
                    self._endOfMonth)
                if temp < exitDate:
                    if self._firstDate is not None and self._cal.adjustDate(
                            self._dates[0],
                            convention) != self._cal.adjustDate(
                                self._firstDate, convention):
                        self._dates.insert(0, self._firstDate)
                        self._isRegular.insert(0, False)
                    break
                else:
                    # skip dates that would result in duplicates
                    # after adjustment
                    if self._cal.adjustDate(
                            self._dates[0],
                            convention) != self._cal.adjustDate(
                                temp, convention):
                        self._dates.insert(0, temp)
                        self._isRegular.insert(0, True)
                    periods += 1

            if self._cal.adjustDate(self._dates[0],
                                    convention) != self._cal.adjustDate(
                                        effectiveDate, convention):
                self._dates.insert(0, effectiveDate)
                self._isRegular.insert(0, False)

        elif self._rule == DateGeneration.Forward:
            self._dates.append(effectiveDate)

            seed = self._dates[-1]

            if self._firstDate is not None:
                self._dates.append(self._firstDate)
                temp = nullCalendar.advanceDate(
                    seed,
                    Period(length=periods * self._tenor.length,
                           units=self._tenor.units), convention,
                    self._endOfMonth)
                if temp != self._firstDate:
                    self._isRegular.append(False)
                else:
                    self._isRegular.append(True)
                seed = self._firstDate

            exitDate = terminationDate
            if self._nextToLastDate is not None:
                exitDate = self._nextToLastDate

            while True:
                temp = nullCalendar.advanceDate(
                    seed,
                    Period(length=periods * self._tenor.length,
                           units=self._tenor.units), convention,
                    self._endOfMonth)
                if temp > exitDate:
                    if self._nextToLastDate is not None and self._cal.adjustDate(
                            self._dates[-1],
                            convention) != self._cal.adjustDate(
                                self._nextToLastDate, convention):
                        self._dates.append(self._nextToLastDate)
                        self._isRegular.append(False)
                    break
                else:
                    # skip dates that would result in duplicates
                    # after adjustment
                    if self._cal.adjustDate(
                            self._dates[-1],
                            convention) != self._cal.adjustDate(
                                temp, convention):
                        self._dates.append(temp)
                        self._isRegular.append(True)
                    periods += 1

            if self._cal.adjustDate(
                    self._dates[-1],
                    terminationConvention) != self._cal.adjustDate(
                        terminationDate, terminationConvention):
                self._dates.append(terminationDate)
                self._isRegular.append(False)
        else:
            raise ValueError("unknown rule ({0:d})".format(self._rule))

        # adjustments
        if self._endOfMonth and self._cal.isEndOfMonth(seed):
            # adjust to end of month
            if convention == BizDayConventions.Unadjusted:
                for i in range(len(self._dates) - 1):
                    self._dates[i] = Date.endOfMonth(self._dates[i])
            else:
                for i in range(len(self._dates) - 1):
                    self._dates[i] = self._cal.endOfMonth(self._dates[i])

            if terminationConvention != BizDayConventions.Unadjusted:
                self._dates[0] = self._cal.endOfMonth(self._dates[0])
                self._dates[-1] = self._cal.endOfMonth(self._dates[-1])
            else:
                if self._rule == DateGeneration.Backward:
                    self._dates[-1] = Date.endOfMonth(self._dates[-1])
                else:
                    self._dates[0] = Date.endOfMonth(self._dates[0])
        else:
            for i in range(len(self._dates) - 1):
                self._dates[i] = self._cal.adjustDate(self._dates[i],
                                                      convention)

            if terminationConvention != BizDayConventions.Unadjusted:
                self._dates[-1] = self._cal.adjustDate(self._dates[-1],
                                                       terminationConvention)

        # Final safety checks to remove extra next-to-last date, if
        # necessary.  It can happen to be equal or later than the end
        # date due to EOM adjustments (see the Schedule test suite
        # for an example).

        dateLen = len(self._dates)

        if dateLen >= 2 and self._dates[dateLen - 2] >= self._dates[-1]:
            self._isRegular[dateLen - 2] = (self._dates[dateLen -
                                                        2] == self._dates[-1])
            self._dates[dateLen - 2] = self._dates[-1]
            self._dates.pop()
            self._isRegular.pop()

        if len(self._dates) >= 2 and self._dates[1] <= self._dates[0]:
            self._isRegular[1] = (self._dates[1] == self._dates[0])
            self._dates[1] = self._dates[0]
            self._dates = self._dates[1:]
            self._isRegular = self._isRegular[1:]

        py_assert(
            len(self._dates) >= 1, ValueError,
            "degenerate single date ({0}) schedule\n"
            "seed date: {1}\n"
            "exit date: {2}\n"
            "effective date: {3}\n"
            "first date: {4}\n"
            "next to last date: {5}\n"
            "termination date: {6}\n"
            "generation rule: {7}\n"
            "end of month: {8}\n".format(self._dates[0], seed, exitDate,
                                         effectiveDate, firstDate,
                                         nextToLastDate, terminationDate,
                                         self._rule, self._endOfMonth))