def advanceDateByCalendar(holidayCenter, referenceDate, period, convention=BizDayConventions.Following): cal = Calendar(holidayCenter) refer = check_date(referenceDate) return cal.advanceDate(refer, period, convention).toDateTime()
def testDailySchedule(self): # Jan 2 and Jan 3 are skipped as New Year holiday # Jan 7 is skipped as weekend # Jan 8 is adjusted to Jan 9 with following convention startDate = Date(2012, 1, 1) s = Schedule(startDate, startDate + 7, Period(length=1, units=TimeUnits.Days), Calendar("China.SSE"), BizDayConventions.Preceding) expected = [ Date(2011, 12, 30), Date(2012, 1, 4), Date(2012, 1, 5), Date(2012, 1, 6), Date(2012, 1, 9) ] self.checkDates(s, expected) # The schedule should skip Saturday 21st and Sunday 22rd. # Previously, it would adjust them to Friday 20th, resulting # in three copies of the same date. startDate = Date(2012, 1, 17) s = Schedule(startDate, startDate + 7, Period(length=1, units=TimeUnits.Days), Calendar("Target"), BizDayConventions.Preceding) expected = [ Date(2012, 1, 17), Date(2012, 1, 18), Date(2012, 1, 19), Date(2012, 1, 20), Date(2012, 1, 23), Date(2012, 1, 24) ] self.checkDates(s, expected)
def testChinaIB(self): # China Inter Bank working weekend list in the year 2014 expectedWorkingWeekEnd = [Date(2014, 1, 26), Date(2014, 2, 8), Date(2014, 5, 4), Date(2014, 9, 28), Date(2014, 10, 11), # China Inter Bank working weekend list in the year 2015 Date(2015, 1, 4), Date(2015, 2, 15), Date(2015, 2, 28), Date(2015, 9, 6), Date(2015, 10, 10), # China Inter Bank working weekend list in the year 2016 Date(2016, 2, 6), Date(2016, 2, 14), Date(2016, 6, 12), Date(2016, 9, 18), Date(2016, 10, 8), Date(2016, 10, 9), # China Inter Bank working weekend list in the year 2017 Date(2017, 1, 22), Date(2017, 2, 4), Date(2017, 4, 1), Date(2017, 5, 27), Date(2017, 9, 30)] cal = Calendar('China.IB') for day in expectedWorkingWeekEnd: self.assertEqual(cal.isHoliday(day), False, "{0} is not expected to be a holiday in {1}".format(day, cal)) self.assertEqual(cal.isBizDay(day), True, "{0} is expected to be a working day in {1} ".format(day, cal))
def testChinaSSE(self): # China Shhanghai Securities Exchange holiday list in the year 2014 expectedHol = [Date(2014, 1, 1), Date(2014, 1, 31), Date(2014, 2, 1), Date(2014, 2, 2), Date(2014, 2, 3), Date(2014, 2, 4), Date(2014, 2, 5), Date(2014, 2, 6), Date(2014, 2, 8), Date(2014, 4, 5), Date(2014, 4, 6), Date(2014, 4, 7), Date(2014, 5, 1), Date(2014, 5, 2), Date(2014, 5, 3), Date(2014, 5, 4), Date(2014, 5, 31), Date(2014, 6, 1), Date(2014, 6, 2), Date(2014, 9, 6), Date(2014, 9, 7), Date(2014, 9, 8), Date(2014, 9, 28), Date(2014, 10, 1), Date(2014, 10, 2), Date(2014, 10, 3), Date(2014, 10, 4), Date(2014, 10, 5), Date(2014, 10, 6), Date(2014, 10, 7), Date(2014, 10, 11), # China Shhanghai Securities Exchange holiday list in the year 2015 Date(2015, 1, 1), Date(2015, 1, 2), Date(2015, 1, 3), Date(2015, 1, 4), Date(2015, 2, 15), Date(2015, 2, 18), Date(2015, 2, 19), Date(2015, 2, 20), Date(2015, 2, 21), Date(2015, 2, 22), Date(2015, 2, 23), Date(2015, 2, 24), Date(2015, 2, 28), Date(2015, 4, 5), Date(2015, 4, 6), Date(2015, 5, 1), Date(2015, 5, 2), Date(2015, 5, 3), Date(2015, 6, 20), Date(2015, 6, 21), Date(2015, 6, 22), Date(2015, 9, 3), Date(2015, 9, 4), Date(2015, 9, 27), Date(2015, 10, 1), Date(2015, 10, 2), Date(2015, 10, 3), Date(2015, 10, 4), Date(2015, 10, 5), Date(2015, 10, 6), Date(2015, 10, 7), Date(2015, 10, 10)] cal = Calendar('China.SSE') for day in expectedHol: self.assertEqual(cal.isHoliday(day), True, "{0} is expected to be a holiday in {1}".format(day, cal)) self.assertEqual(cal.isBizDay(day), False, "{0} is expected not to be a working day in {1} ".format(day, cal))
def bizDatesList(holidayCenter, fromDate, toDate): cal = Calendar(holidayCenter) fromDate = check_date(fromDate) toDate = check_date(toDate) assert fromDate <= toDate, "from date ({0} must be earlier than to date {1}".format( fromDate, toDate) return [d.toDateTime() for d in cal.bizDatesList(fromDate, toDate)]
def _filter_sec_on_tiaocang_date(self, tiaocang_date, sec_id): sse_cal = Calendar('China.SSE') tiaocang_date_prev = sse_cal.advanceDate( Date.strptime(str(tiaocang_date)[:10]), Period('-1b')).toDateTime() tiaocang_date_prev2 = sse_cal.advanceDate( Date.strptime(str(tiaocang_date)[:10]), Period('-2b')).toDateTime() price_data = get_sec_price(start_date=tiaocang_date_prev2, end_date=tiaocang_date, sec_ids=sec_id, data_source=self._data_source, csv_path=self._csv_path) price_data = price_data.transpose() price_data.index.name = 'secID' # 去除涨幅过大可能买不到的 price_data['returnFilter'] = price_data[tiaocang_date] / price_data[ tiaocang_date_prev] > 1 + self._filter_return_on_tiaocang_date # 去除有NaN的, 新股 price_data['ipoFilter'] = pd.isnull(price_data[tiaocang_date] * price_data[tiaocang_date_prev] * price_data[tiaocang_date_prev2]) # 去除停牌的,此处判断标准就是连续三天收盘价格一样 price_data['tingpaiFilter'] = ( (price_data[tiaocang_date] == price_data[tiaocang_date_prev]) & (price_data[tiaocang_date_prev] == price_data[tiaocang_date_prev2])) price_data['filters'] = 1 - (1 - price_data['returnFilter']) * ( 1 - price_data['ipoFilter']) * (1 - price_data['tingpaiFilter']) return price_data['filters']
def holDatesList(holidayCenter, fromDate, toDate, includeWeekend=True): cal = Calendar(holidayCenter) fromDate = check_date(fromDate) toDate = check_date(toDate) return [ d.toDateTime() for d in cal.holDatesList(fromDate, toDate, includeWeekend) ]
def testCalendarConstructionIsInsensitiveOfCase(self): cal1 = Calendar('NullCalendar') cal2 = Calendar('nullcalendar') shouldBeTrue = cal1 == cal2 self.assertTrue(shouldBeTrue) caltoBeDifferent = Calendar('China.SSE') shouldBeFalse = cal1 == caltoBeDifferent self.assertFalse(shouldBeFalse)
def holDatesList(holidayCenter, fromDate, toDate, includeWeekend=True): cal = Calendar(holidayCenter) fromDate = Date.fromDateTime(fromDate) toDate = Date.fromDateTime(toDate) assert fromDate <= toDate, "from date ({0} must be earlier than to date {1}".format( fromDate, toDate) return [ d.toDateTime() for d in cal.holDatesList(fromDate, toDate, includeWeekend) ]
def get_pos_adj_date(start_date, end_date, formats="%Y-%m-%d", calendar='China.SSE', freq='m', return_biz_day=False): """ :param start_date: str/datetime.datetime, start date of strategy :param end_date: str/datetime.datetime, end date of strat egy :param formats: optional, formats of the string date :param calendar: str, optional, name of the calendar to use in dates math :param freq: str, optional, the frequency of data :param return_biz_day: bool, optional, if the return dates are biz days :return: list of datetime.datetime, pos adjust dates """ if isinstance(start_date, str) and isinstance(end_date, str): d_start_date = Date.strptime(start_date, formats) d_end_date = Date.strptime(end_date, formats) elif isinstance(start_date, datetime.datetime) and isinstance( end_date, datetime.datetime): d_start_date = Date.fromDateTime(start_date) d_end_date = Date.fromDateTime(end_date) cal = Calendar(calendar) pos_adjust_date = Schedule(d_start_date, d_end_date, Period(length=1, units=_freqDict[freq]), cal, BizDayConventions.Unadjusted) # it fails if setting dStartDate to be first adjustment date, then use Schedule to compute the others # so i first compute dates list in each period, then compute the last date of each period # last day of that period(month) is the pos adjustment date if _freqDict[freq] == TimeUnits.Weeks: pos_adjust_date = [ Date.nextWeekday(date, Weekdays.Friday) for date in pos_adjust_date[:-1] ] elif _freqDict[freq] == TimeUnits.Months: pos_adjust_date = [ cal.endOfMonth(date) for date in pos_adjust_date[:-1] ] elif _freqDict[freq] == TimeUnits.Years: pos_adjust_date = [ Date(date.year(), 12, 31) for date in pos_adjust_date[:-1] ] if return_biz_day: pos_adjust_date = [ cal.adjustDate(date, BizDayConventions.Preceding) for date in pos_adjust_date ] pos_adjust_date = [Date.toDateTime(date) for date in pos_adjust_date] pos_adjust_date = [ date for date in pos_adjust_date if date <= d_end_date.toDateTime() ] return pos_adjust_date
def forward_date(date, tenor, date_format='%Y-%m-%d'): try: # use pyfin instead to get more accurate and flexible date math start_date = Date.strptime(date, date_format) sseCal = Calendar('China.SSE') ret = sseCal.advanceDate(start_date, Period('-' + tenor), endOfMonth=True) # 此处返回的是上一期期末日期,再向后调整一天,以避免区间日期重叠 ret = sseCal.advanceDate(ret, Period('1b')) return str(ret) except NameError: pass
def __init__(self, start_date, end_date, factor, industry, data_source=DataSource.WIND, calendar='China.SSE'): """ :param factor: pd.Series, multi index=[tradeDate, secID] columns = [factor] :param industry: pd.Series/dict, Either A MultiIndex Series indexed by date and asset, containing the period wise group codes for each asset, or a dict of asset to group mappings. If a dict is passed, it is assumed that group mappings are unchanged for the entire time period of the passed factor data. :param data_source: enum, DataSource type :param calendar: PyFin.Calendar type :return: """ self._calendar = Calendar(calendar) self._startDate = start_date self._endDate = end_date self._factor = factor self._industry = industry self._dataSource = data_source self._factor.index = self._factor.index.rename(_alphaLensFactorIndexName) self._factor.name = _alphaLensFactorColName self._factor = self._factor.loc[ self._factor.index.get_level_values(_alphaLensFactorIndexName[0]) >= self._startDate] self._factor = self._factor.loc[ self._factor.index.get_level_values(_alphaLensFactorIndexName[0]) <= self._endDate] self._tradeDate = sorted(set(self._factor.index.get_level_values(_alphaLensFactorIndexName[0]))) self._secID = sorted(set(self._factor.index.get_level_values(_alphaLensFactorIndexName[1]).tolist())) self._price = self._get_price_data()
def makeSchedule(firstDate, endDate, tenor): cal = Calendar('NullCalendar') firstDate = check_date(firstDate) endDate = check_date(endDate) tenor = Period(tenor) schedule = Schedule(firstDate, endDate, tenor, cal) return [d.toDateTime() for d in schedule]
def testAdjustDate(self): # April 30, 2005 is a working day under IB, but a holiday under SSE referenceDate = Date(2005, Months.April, 30) sseCal = Calendar('China.SSE') ibCal = Calendar('China.IB') bizDayConv = BizDayConventions.Unadjusted self.assertEqual(sseCal.adjustDate(referenceDate, bizDayConv), referenceDate) self.assertEqual(ibCal.adjustDate(referenceDate, bizDayConv), referenceDate) bizDayConv = BizDayConventions.Following self.assertEqual(sseCal.adjustDate(referenceDate, bizDayConv), Date(2005, Months.May, 9)) self.assertEqual(ibCal.adjustDate(referenceDate, bizDayConv), Date(2005, Months.April, 30)) bizDayConv = BizDayConventions.ModifiedFollowing self.assertEqual(sseCal.adjustDate(referenceDate, bizDayConv), Date(2005, Months.April, 29)) self.assertEqual(ibCal.adjustDate(referenceDate, bizDayConv), Date(2005, Months.April, 30))
def testScheduleInitializeWithYearly(self): startDate = Date(2012, 2, 29) endDate = Date(2013, 3, 1) tenor = Period('1y') cal = Calendar('NullCalendar') sch = Schedule(startDate, endDate, tenor, cal) expected = [Date(2012, 2, 29), Date(2013, 2, 28), Date(2013, 3, 1)] for i in range(sch.size()): self.assertEqual(expected[i], sch[i])
def testScheduleDeepCopy(self): startDate = Date(2013, 3, 31) endDate = Date(2013, 6, 30) tenor = Period('1m') cal = Calendar('NullCalendar') sch = Schedule(startDate, endDate, tenor, cal) copied_sch = copy.deepcopy(sch) self.assertEqual(sch, copied_sch)
def testNullCalendar(self): cal = Calendar("Null") testDate = Date(2015, 1, 1) self.assertTrue(cal.isBizDay(testDate)) self.assertTrue(not cal.isHoliday(testDate)) self.assertTrue(cal.isWeekEnd(Weekdays.Saturday)) self.assertTrue(cal.isWeekEnd(Weekdays.Sunday)) self.assertTrue(not cal.isWeekEnd(Weekdays.Friday))
def testCalendarPickle(self): sseCal = Calendar('China.SSE') f = tempfile.NamedTemporaryFile('w+b', delete=False) pickle.dump(sseCal, f) f.close() with open(f.name, 'rb') as f2: pickledCal = pickle.load(f2) self.assertEqual(sseCal, pickledCal) os.unlink(f.name)
def makeSchedule(firstDate, endDate, tenor, calendar='NullCalendar', dateRule=BizDayConventions.Following): cal = Calendar(calendar) firstDate = check_date(firstDate) endDate = check_date(endDate) tenor = Period(tenor) schedule = Schedule(firstDate, endDate, tenor, cal, convention=dateRule) return [d.toDateTime() for d in schedule]
def makeSchedule(firstDate, endDate, tenor, calendar='NullCalendar', dateRule=BizDayConventions.Following, dateGenerationRule=DateGeneration.Forward): cal = Calendar(calendar) firstDate = check_date(firstDate) endDate = check_date(endDate) tenor = check_period(tenor) if tenor.units() == TimeUnits.BDays: schedule = [] if dateGenerationRule == DateGeneration.Forward: d = cal.adjustDate(firstDate, dateRule) while d <= endDate: schedule.append(d) d = cal.advanceDate(d, tenor, dateRule) elif dateGenerationRule == DateGeneration.Backward: d = cal.adjustDate(endDate, dateRule) while d >= firstDate: schedule.append(d) d = cal.advanceDate(d, -tenor, dateRule) schedule = sorted(schedule) else: schedule = Schedule(firstDate, endDate, tenor, cal, convention=dateRule, dateGenerationRule=dateGenerationRule) return [d.toDateTime() for d in schedule]
def _filter_sec_on_tiaocang_date(self, tiaocang_date, sec_id): sse_cal = Calendar('China.SSE') tiaocang_date_prev = sse_cal.advanceDate(Date.strptime(str(tiaocang_date)[:10]), '-1b').toDateTime() tiaocang_date_prev2 = sse_cal.advanceDate(Date.strptime(str(tiaocang_date)[:10]), '-2b').toDateTime() price_data = WindMarketDataHandler.get_sec_price_on_date(start_date=tiaocang_date_prev2, end_date=tiaocang_date, sec_ids=sec_id) price_data = price_data.transpose() price_data.index.name = 'sec_id' # 去除涨幅过大可能买不到的 price_data['returnFilter'] = price_data[tiaocang_date] / price_data[ tiaocang_date_prev] > 1 + self._filterReturnOnTiaoCangDate # 去除有NaN的, 新股 price_data['ipoFilter'] = pd.isnull( price_data[tiaocang_date] * price_data[tiaocang_date_prev] * price_data[tiaocang_date_prev2]) # 去除停牌的,此处判断标准就是连续三天收盘价格一样 price_data['tingpaiFilter'] = ((price_data[tiaocang_date] == price_data[tiaocang_date_prev]) & ( price_data[tiaocang_date_prev] == price_data[tiaocang_date_prev2])) price_data['filters'] = 1 - (1 - price_data['returnFilter']) * (1 - price_data['ipoFilter']) * ( 1 - price_data['tingpaiFilter']) return price_data['filters']
def get_report_date(act_date, return_biz_day=True): """ :param act_date: str/datetime.datetime, 任意日期 :param return_biz_day: bool, 是否返回交易日 :return: datetime, 对应应使用的报告日期, 从wind数据库中爬取 此函数的目的是要找到,任意时刻可使用最新的季报数据的日期,比如2-20日可使用的最新季报是去年的三季报(对应日期为9-30), """ if isinstance(act_date, str): act_date = Date.strptime(act_date) elif isinstance(act_date, datetime.datetime): act_date = Date.fromDateTime(act_date) act_month = act_date.month() act_year = act_date.year() if 1 <= act_month <= 3: # 第一季度使用去年三季报的数据 year = act_year - 1 month = 9 day = 30 elif 4 <= act_month <= 7: # 第二季度使用当年一季报 year = act_year month = 3 day = 31 elif 8 <= act_month <= 9: # 第三季度使用当年中报 year = act_year month = 6 day = 30 else: year = act_year # 第四季度使用当年三季报 month = 9 day = 30 if return_biz_day: date_adj = Calendar('China.SSE').adjustDate( Date(year, month, day), BizDayConventions.Preceding) ret = date_adj.toDateTime() else: ret = datetime.datetime(year, month, day) return ret
def testScheduleInitialize(self): startDate = Date(2013, 3, 31) endDate = Date(2013, 6, 30) tenor = Period('1m') cal = Calendar('NullCalendar') sch = Schedule(startDate, endDate, tenor, cal) expected = [ Date(2013, 3, 31), Date(2013, 4, 30), Date(2013, 5, 31), Date(2013, 6, 30) ] for i in range(sch.size()): self.assertEqual(expected[i], sch[i])
def testDatesList(self): fromDate = Date(2014, 1, 31) toDate = Date(2014, 2, 28) sseCal = Calendar('China.SSE') ibCal = Calendar('China.IB') benchmarkHol = [Date(2014, 1, 31), Date(2014, 2, 3), Date(2014, 2, 4), Date(2014, 2, 5), Date(2014, 2, 6)] sseHolList = sseCal.holDatesList(fromDate, toDate, False) self.assertEqual(sseHolList, benchmarkHol) ibHolList = ibCal.holDatesList(fromDate, toDate, False) self.assertEqual(ibHolList, benchmarkHol) sseHolList = sseCal.holDatesList(fromDate, toDate, True) benchmarkHol = [Date(2014, 1, 31), Date(2014, 2, 1), Date(2014, 2, 2), Date(2014, 2, 3), Date(2014, 2, 4), Date(2014, 2, 5), Date(2014, 2, 6), Date(2014, 2, 8), Date(2014, 2, 9), Date(2014, 2, 15), Date(2014, 2, 16), Date(2014, 2, 22), Date(2014, 2, 23)] self.assertEqual(sseHolList, benchmarkHol) ibHolList = ibCal.holDatesList(fromDate, toDate, True) benchmarkHol = [Date(2014, 1, 31), Date(2014, 2, 1), Date(2014, 2, 2), Date(2014, 2, 3), Date(2014, 2, 4), Date(2014, 2, 5), Date(2014, 2, 6), Date(2014, 2, 9), Date(2014, 2, 15), Date(2014, 2, 16), Date(2014, 2, 22), Date(2014, 2, 23)] self.assertEqual(ibHolList, benchmarkHol) sseWorkingDayList = sseCal.bizDatesList(fromDate, toDate) d = fromDate while d <= toDate: if sseCal.isBizDay(d): self.assertTrue(d in sseWorkingDayList and d not in sseHolList) d += 1 ibWorkingDayList = ibCal.bizDatesList(fromDate, toDate) d = fromDate while d <= toDate: if ibCal.isBizDay(d): self.assertTrue(d in ibWorkingDayList and d not in ibHolList) d += 1
def testSchedulePickle(self): startDate = Date(2013, 3, 31) endDate = Date(2013, 6, 30) tenor = Period('1m') cal = Calendar('NullCalendar') sch = Schedule(startDate, endDate, tenor, cal) f = tempfile.NamedTemporaryFile('w+b', delete=False) pickle.dump(sch, f) f.close() with open(f.name, 'rb') as f2: pickled_sch = pickle.load(f2) self.assertEqual(sch, pickled_sch) os.unlink(f.name)
def testAdvanceDate(self): referenceDate = Date(2014, 1, 31) sseCal = Calendar('China.SSE') ibCal = Calendar('China.IB') bizDayConv = BizDayConventions.Following # test null period self.assertEqual(sseCal.advanceDate(referenceDate, '0b', bizDayConv), Date(2014, 2, 7)) # test negative period self.assertEqual(sseCal.advanceDate(referenceDate, '-5b', bizDayConv), Date(2014, 1, 24)) # The difference is caused by Feb 8 is SSE holiday but a working day for IB market self.assertEqual(sseCal.advanceDate(referenceDate, '2b', bizDayConv), Date(2014, 2, 10)) self.assertEqual(sseCal.advanceDate(referenceDate, '2d', bizDayConv), Date(2014, 2, 7)) self.assertEqual(ibCal.advanceDate(referenceDate, '2b', bizDayConv), Date(2014, 2, 8)) self.assertEqual(ibCal.advanceDate(referenceDate, '2d', bizDayConv), Date(2014, 2, 7)) bizDayConv = BizDayConventions.ModifiedFollowing # May 31, 2014 is a holiday self.assertEqual(sseCal.advanceDate(referenceDate, '4m', bizDayConv, True), Date(2014, 5, 30))
def testBasicFunctions(self): testDate = Date(2015, 7, 11) cal = Calendar('China.SSE') self.assertTrue(cal.isWeekEnd(testDate.weekday()), "{0} is expected to be a weekend".format(testDate)) testDate = Date(2015, 7, 13) self.assertTrue(not cal.isWeekEnd(testDate.weekday()), "{0} is expected not to be a weekend".format(testDate)) testDate = Date(2015, 5, 29) cal = Calendar('China.SSE') self.assertTrue(cal.isEndOfMonth(testDate), "{0} is expected to be a end of month".format(testDate)) testDate = Date(2015, 5, 1) cal = Calendar('China.SSE') endOfMonth = cal.endOfMonth(testDate) self.assertEqual(endOfMonth, Date(2015, 5, 29), "The month end of 2015/5 is expected to be {0}".format(Date(2015, 5, 29))) bizDates1 = cal.bizDaysBetween(Date(2015, 1, 1), Date(2015, 12, 31), True, False) bizDates2 = cal.bizDaysBetween(Date(2015, 12, 31), Date(2015, 1, 1), False, True) self.assertEqual(bizDates1, bizDates2)
def map_to_biz_day(date_series, calendar='China.SSE', convention=BizDayConventions.Preceding): """ :param date_series: pd.Sereis, datetime.datetime :param calendar: str, optional, name of the calendar to use in dates math :param convention: str, optional, pyFin date conventions :return: pd.Series, datetime.datetime 用更快的方式计算, 避免对每个日期进行循环 """ unique_date_list = sorted(set(date_series)) py_date_list = [Date.fromDateTime(date) for date in unique_date_list] py_date_list = [ Calendar(calendar).adjustDate(date, convention) for date in py_date_list ] biz_day_list = [Date.toDateTime(date) for date in py_date_list] dict_date_map = dict(zip(unique_date_list, biz_day_list)) ret = date_series.map(dict_date_map) return ret
def testCalendarWithDayConvention(self): sseCal = Calendar('China.SSE') referenceDate = Date(2015, 2, 14) testDate = sseCal.adjustDate(referenceDate, BizDayConventions.HalfMonthModifiedFollowing) self.assertEqual(testDate, Date(2015, 2, 13)) referenceDate = Date(2014, 2, 4) testDate = sseCal.adjustDate(referenceDate, BizDayConventions.ModifiedPreceding) self.assertEqual(testDate, Date(2014, 2, 7)) referenceDate = Date(2014, 2, 3) testDate = sseCal.adjustDate(referenceDate, BizDayConventions.Nearest) self.assertEqual(testDate, Date(2014, 2, 7)) referenceDate = Date(2014, 2, 2) testDate = sseCal.adjustDate(referenceDate, BizDayConventions.Nearest) self.assertEqual(testDate, Date(2014, 1, 30)) with self.assertRaises(ValueError): _ = sseCal.adjustDate(referenceDate, -1)
def isBizDay(holidayCenter, ref): cal = Calendar(holidayCenter) ref = check_date(ref) return cal.isBizDay(ref)
def holDatesList(holidayCenter, fromDate, toDate, includeWeekend=True): cal = Calendar(holidayCenter) fromDate = check_date(fromDate) toDate = check_date(toDate) assert fromDate <= toDate, "from date ({0} must be earlier than to date {1}".format(fromDate, toDate) return [d.toDateTime() for d in cal.holDatesList(fromDate, toDate, includeWeekend)]
def isBizDay(holidayCenter, ref): cal = Calendar(holidayCenter) ref = Date.fromDateTime(ref) return cal.isBizDay(ref)
def bizDatesList(holidayCenter, fromDate, toDate): cal = Calendar(holidayCenter) fromDate = Date.fromDateTime(fromDate) toDate = Date.fromDateTime(toDate) assert fromDate <= toDate, "from date ({0} must be earlier than to date {1}".format(fromDate, toDate) return [d.toDateTime() for d in cal.bizDatesList(fromDate, toDate)]
def testBasicFunctions(self): testDate = Date(2015, 7, 11) cal = Calendar('China.SSE') self.assertTrue(cal.isWeekEnd(testDate.weekday()), "{0} is expected to be a weekend".format(testDate)) testDate = Date(2015, 7, 13) self.assertTrue(not cal.isWeekEnd(testDate.weekday()), "{0} is expected not to be a weekend".format(testDate)) testDate = Date(2015, 5, 29) cal = Calendar('China.SSE') self.assertTrue( cal.isEndOfMonth(testDate), "{0} is expected to be a end of month".format(testDate)) testDate = Date(2015, 5, 1) cal = Calendar('China.SSE') endOfMonth = cal.endOfMonth(testDate) self.assertEqual( endOfMonth, Date(2015, 5, 29), "The month end of 2015/5 is expected to be {0}".format( Date(2015, 5, 29))) bizDates1 = cal.bizDaysBetween(Date(2015, 1, 1), Date(2015, 12, 31), True, False) bizDates2 = cal.bizDaysBetween(Date(2015, 12, 31), Date(2015, 1, 1), False, True) self.assertEqual(bizDates1, bizDates2)
def adjustDateByCalendar(holidayCenter, referenceDate, convention=BizDayConventions.Following): cal = Calendar(holidayCenter) refer = Date.fromDateTime(referenceDate) return cal.adjustDate(refer, convention).toDateTime()
def testCalendarDeepCopy(self): sseCal = Calendar('China.SSE') copiedCal = copy.deepcopy(sseCal) self.assertEqual(sseCal, copiedCal)
def testDatesList(self): fromDate = Date(2014, 1, 31) toDate = Date(2014, 2, 28) sseCal = Calendar('China.SSE') ibCal = Calendar('China.IB') benchmarkHol = [ Date(2014, 1, 31), Date(2014, 2, 3), Date(2014, 2, 4), Date(2014, 2, 5), Date(2014, 2, 6) ] sseHolList = sseCal.holDatesList(fromDate, toDate, False) self.assertEqual(sseHolList, benchmarkHol) ibHolList = ibCal.holDatesList(fromDate, toDate, False) self.assertEqual(ibHolList, benchmarkHol) sseHolList = sseCal.holDatesList(fromDate, toDate, True) benchmarkHol = [ Date(2014, 1, 31), Date(2014, 2, 1), Date(2014, 2, 2), Date(2014, 2, 3), Date(2014, 2, 4), Date(2014, 2, 5), Date(2014, 2, 6), Date(2014, 2, 8), Date(2014, 2, 9), Date(2014, 2, 15), Date(2014, 2, 16), Date(2014, 2, 22), Date(2014, 2, 23) ] self.assertEqual(sseHolList, benchmarkHol) ibHolList = ibCal.holDatesList(fromDate, toDate, True) benchmarkHol = [ Date(2014, 1, 31), Date(2014, 2, 1), Date(2014, 2, 2), Date(2014, 2, 3), Date(2014, 2, 4), Date(2014, 2, 5), Date(2014, 2, 6), Date(2014, 2, 9), Date(2014, 2, 15), Date(2014, 2, 16), Date(2014, 2, 22), Date(2014, 2, 23) ] self.assertEqual(ibHolList, benchmarkHol) sseWorkingDayList = sseCal.bizDatesList(fromDate, toDate) d = fromDate while d <= toDate: if sseCal.isBizDay(d): self.assertTrue(d in sseWorkingDayList and d not in sseHolList) d += 1 ibWorkingDayList = ibCal.bizDatesList(fromDate, toDate) d = fromDate while d <= toDate: if ibCal.isBizDay(d): self.assertTrue(d in ibWorkingDayList and d not in ibHolList) d += 1
def __init__(self, effectiveDate, terminationDate, tenor, calendar, convention=BizDayConventions.Following, terminationConvention=BizDayConventions.Following, dateGenerationRule=DateGeneration.Forward, endOfMonth=False, firstDate=None, nextToLastDate=None): # Initialize private data 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 = Settings.evaluationDate pyFinAssert(evalDate < terminationDate, ValueError, "null effective date") if nextToLastDate is not None: y = int((nextToLastDate - evalDate) / 366) + 1 effectiveDate = nextToLastDate - Period(y, TimeUnits.Years) else: y = int((terminationDate - evalDate) / 366) + 1 effectiveDate = terminationDate - Period(y, TimeUnits.Years) else: pyFinAssert(effectiveDate is not None, ValueError, "null effective date") pyFinAssert(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: pyFinAssert(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: pyFinAssert(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: pyFinAssert(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(0, 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(-periods * self._tenor.length, 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(-periods * self._tenor.length, 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(periods * self._tenor.length, 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(periods * self._tenor.length, 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). if len(self._dates) >= 2 and self._dates[len(self._dates) - 2] >= self._dates[-1]: self._isRegular[len(self._dates) - 2] = (self._dates[len(self._dates) - 2] == self._dates[-1]) self._dates[len(self._dates) - 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:] pyFinAssert(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))