#!/usr/bin/python
#coding = utf-8
import pandas as pd
import QuantLib as ql
from typing import List, Optional, Sequence
from RiskQuantLib.Tool.decoratorTool import stringTimestampConverter
#<import>
#</import>
[docs]class calendar(object):
unitKey = ['W', 'w', 'M', 'm', 'D', 'd', 'Y', 'y']
unitValue = [ql.Weeks, ql.Weeks, ql.Months, ql.Months, ql.Days, ql.Days, ql.Years, ql.Years]
unitMap = dict(zip(unitKey, unitValue))
[docs] @staticmethod
def parsePeriod(period: str) -> (int, int):
n, unit = int(period[:-1]), period[-1]
assert n > 0
assert unit in set(calendar.unitKey)
return n, calendar.unitMap[unit]
[docs] @staticmethod
def timestampToDate(date: pd.Timestamp) -> ql.Date:
return ql.Date(date.day, date.month, date.year)
[docs] @staticmethod
def dateToTimestamp(date: ql.Date) -> pd.Timestamp:
return pd.Timestamp(date.to_date())
@stringTimestampConverter(argIterableIndexList=[2], argIterableKeyList=['otherHolidays'])
def __init__(self, holidayRule: ql.Calendar, otherHolidays: Optional[Sequence[pd.Timestamp]] = None) -> None:
self.updateCalendar(holidayRule, otherHolidays)
[docs] @stringTimestampConverter(argIterableIndexList=[2], argIterableKeyList=['otherHolidays'])
def updateCalendar(self, holidayRule: ql.Calendar, otherHolidays: Optional[Sequence[pd.Timestamp]] = None) -> None:
"""Create a custom calendar using QuantLib."""
otherHolidays = otherHolidays if otherHolidays else []
self.addHolidayRule(holidayRule)
self.addHolidays(otherHolidays)
[docs] def addHolidayRule(self, holidayRule: ql.Calendar) -> None:
"""Add customized holiday rule, this will overwrite current holiday settings."""
self._otherHolidays = []
self._qlCalendar = holidayRule
self._holidayRule = self._qlCalendar.isHoliday
[docs] @stringTimestampConverter(argIterableIndexList=[1], argIterableKeyList=['holidays'])
def addHolidays(self, holidays: Sequence[pd.Timestamp]) -> None:
"""Add customized holiday."""
self._otherHolidays.extend(holidays)
[self._qlCalendar.addHoliday(calendar.timestampToDate(i)) for i in holidays]
self._holidayRule = self._qlCalendar.isHoliday
[docs] @stringTimestampConverter(argIndexList=[1],argKeyList=['date'])
def isTrading(self, date: pd.Timestamp) -> bool:
"""Return True if a day is trading day."""
return not self._holidayRule(calendar.timestampToDate(date))
[docs] @stringTimestampConverter(argIndexList=[1,2], argKeyList=['start', 'end'])
def tradingDaysBetween(self, start: pd.Timestamp, end: pd.Timestamp, startPoint: bool = True, endPoint: bool = True) -> List[pd.Timestamp]:
"""
Return a list of trading days between *start* and *end*. Endpoints
are counted only if they are trading.
"""
startAdjust = start if startPoint else start + pd.Timedelta(days=1)
startQlDate = calendar.timestampToDate(startAdjust)
endAdjust = end if endPoint else end + pd.Timedelta(days=-1)
endQlDate = calendar.timestampToDate(endAdjust)
return [calendar.dateToTimestamp(i) for i in self._qlCalendar.businessDayList(startQlDate, endQlDate)]
[docs] @stringTimestampConverter(argIndexList=[1], argKeyList=['date'])
def offset(self, date: pd.Timestamp, n: int, period: int = ql.Days) -> pd.Timestamp:
"""Return date of trading day *n* days before or after *date*."""
qlDate = calendar.timestampToDate(date)
offsetDate = self._qlCalendar.advance(qlDate, n, period)
return calendar.dateToTimestamp(offsetDate)
[docs] @stringTimestampConverter(argIndexList=[1, 2], argKeyList=['start', 'end'])
def makeSchedule(self, start: pd.Timestamp, end: pd.Timestamp, period: str, forwardAdjust: bool = True, forwardScale: bool = True, forwardAdjustEndDay: bool = False):
"""Generate periodical trading dates between start and end, given period."""
n, periodQl = calendar.parsePeriod(period)
convention = ql.Following if forwardAdjust else ql.Preceding
terminalDateConvention = ql.Following if forwardAdjustEndDay else ql.Preceding
forwards, backwards = (True, False) if forwardScale else (False, True)
startQlDate, endQlDate = calendar.timestampToDate(start), calendar.timestampToDate(end)
schedule = ql.MakeSchedule(startQlDate, endQlDate, ql.Period(n, periodQl), calendar=self._qlCalendar, convention=convention, terminalDateConvention=terminalDateConvention,forwards=forwards, backwards=backwards)
return [calendar.dateToTimestamp(i) for i in schedule]
[docs] @stringTimestampConverter(argIndexList=[1], argKeyList=['start'])
def makeScheduleByPeriod(self, start: pd.Timestamp, period: str, count: int, forwardAdjust: bool = True) -> List[pd.Timestamp]:
"""
Generate periodical trading dates.
Warnings: If the first day is not a trading day, the next trading day will be used, thus leading
to duplicated elements. So make sure the given start date is a trading day.
"""
n, periodQl = calendar.parsePeriod(period)
convention = ql.Following if forwardAdjust else ql.Preceding
dateQl = calendar.timestampToDate(start)
tradingDates = [self._qlCalendar.advance(dateQl, i*n, periodQl, convention) for i in range(count)]
return [calendar.dateToTimestamp(i) for i in tradingDates]
[docs] @stringTimestampConverter(argIndexList=[1, 2], argKeyList=['start', 'end'])
def numTradingDaysBetween(self, start: pd.Timestamp, end: pd.Timestamp, countStart: bool = True, countEnd: bool = True) -> int:
"""Return number of trading days between two dates."""
startQlDate, endQlDate = calendar.timestampToDate(start), calendar.timestampToDate(end)
return self._qlCalendar.businessDaysBetween(startQlDate, endQlDate, countStart, countEnd)
[docs] @stringTimestampConverter(argIndexList=[1], argKeyList=['start'], argIterableIndexList=[2], argIterableKeyList=['dateList'])
def numTradingDaysBetweenGrid(self, start: pd.Timestamp, dateList: Sequence[pd.Timestamp], countStart: bool = True, countEnd: bool = True) -> List[int]:
"""Return number of trading days between a date and a date list."""
return [self.numTradingDaysBetween(start, end, countStart, countEnd) for end in dateList]
#<calendar>
#</calendar>
[docs]@stringTimestampConverter(argIndexList=[2], argKeyList=['on'])
def getNthWeekday(count:int, weekdayCode:int, on:pd.Timestamp):
"""
This function will return the n-th weekday of the month, which contains the given day.
Parameters
----------
count : int
The n-th weekday you want to get
weekdayCode : int
The weekday code, use int from 1 to 7
on : pd.Timestamp
The standing point, calculation will be done based on this day.
Returns
-------
result : pd.Timestamp
"""
firstDayAtThatMonth = pd.Timestamp(on.year, on.month, 1)
weekdayCodeBenchMark = firstDayAtThatMonth.dayofweek + 1
if count == 0:
raise Exception("Error: N can't be 0!\n")
elif count<0:
count = count+1
else:
pass
if weekdayCode>=weekdayCodeBenchMark:
days = (count-1)*7 + weekdayCode-weekdayCodeBenchMark
else:
days = count * 7 + weekdayCode - weekdayCodeBenchMark
resultDay = firstDayAtThatMonth + pd.Timedelta(days=days)
return resultDay
[docs]@stringTimestampConverter(argIndexList=[0,1], argKeyList=['start', 'end'])
def generateBusinessDateList(start: pd.Timestamp, end: pd.Timestamp, freq:str='D'):
"""
This function will generate a list of business day.
Parameters
----------
start : pd.Timestamp
The range start.
end : pd.Timestamp
The range end
freq : str
The frequency used to generate date list.
Returns
-------
list
"""
businessList = pd.date_range(start.strftime("%Y-%m-%d"),end.strftime("%Y-%m-%d"),freq=freq).to_list()
return [i for i in businessList if i.dayofweek<5]
[docs]@stringTimestampConverter(argIndexList=[0], argKeyList=['start'])
def generateNextNWeekday(start: pd.Timestamp, count: int):
"""
This function will return a list, whose elements are the next n weekdays.
"""
startDate_FormedStr = start.strftime("%Y-%m-%d")
endDate_FormedStr = (start + pd.Timedelta(days=int(count/5*7)+10)).strftime("%Y-%m-%d")
return [i for i in pd.date_range(startDate_FormedStr,endDate_FormedStr,freq='D').to_list() if i.dayofweek<5][:count]
#<dateTool>
#</dateTool>
# Quick link for non-QuantLib users.
[docs]class null(calendar):
holidayRule = ql.NullCalendar()
"""Quick way to initialize a null calendar."""
@stringTimestampConverter(argIterableIndexList=[1], argIterableKeyList=['otherHolidays'])
def __init__(self, otherHolidays: Optional[Sequence[pd.Timestamp]] = None) -> None:
super().__init__(self.holidayRule, otherHolidays)
[docs]class argentinaMerval(null):
"""Quick way to initialize calendar of Argentina Merval."""
holidayRule = ql.Argentina(ql.Argentina.Merval)
[docs]class australiaSettlement(null):
"""Quick way to initialize calendar of Australia Settlement."""
holidayRule = ql.Australia(ql.Australia.Settlement)
[docs]class australiaASX(null):
"""Quick way to initialize calendar of Australia ASX."""
holidayRule = ql.Australia(ql.Australia.ASX)
[docs]class austriaSettlement(null):
"""Quick way to initialize calendar of Austria Settlement."""
holidayRule = ql.Austria(ql.Austria.Settlement)
[docs]class austriaExchange(null):
"""Quick way to initialize calendar of Austria Exchange."""
holidayRule = ql.Austria(ql.Austria.Exchange)
[docs]class botswana(null):
"""Quick way to initialize calendar of Botswana."""
holidayRule = ql.Botswana()
[docs]class brazilSettlement(null):
"""Quick way to initialize calendar of Brazil Settlement."""
holidayRule = ql.Brazil(ql.Brazil.Settlement)
[docs]class brazilExchange(null):
"""Quick way to initialize calendar of Brazil Exchange."""
holidayRule = ql.Brazil(ql.Brazil.Exchange)
[docs]class canadaSettlement(null):
"""Quick way to initialize calendar of Canada Settlement."""
holidayRule = ql.Canada(ql.Canada.Settlement)
[docs]class canadaTSX(null):
"""Quick way to initialize calendar of Canada TSX."""
holidayRule = ql.Canada(ql.Canada.TSX)
[docs]class chileSSE(null):
"""Quick way to initialize calendar of Chile SSE."""
holidayRule = ql.Chile(ql.Chile.SSE)
[docs]class chinaSSE(null):
"""Quick way to initialize calendar of China SSE."""
holidayRule = ql.China(ql.China.SSE)
[docs]class chinaIB(null):
"""Quick way to initialize calendar of China IB."""
holidayRule = ql.China(ql.China.IB)
[docs]class czechRepublicPSE(null):
"""Quick way to initialize calendar of CzechRepublic PSE."""
holidayRule = ql.CzechRepublic(ql.CzechRepublic.PSE)
[docs]class denmark(null):
"""Quick way to initialize calendar of Denmark."""
holidayRule = ql.Denmark()
[docs]class finland(null):
"""Quick way to initialize calendar of Finland."""
holidayRule = ql.Finland()
[docs]class franceSettlement(null):
"""Quick way to initialize calendar of France Settlement."""
holidayRule = ql.France(ql.France.Settlement)
[docs]class franceExchange(null):
"""Quick way to initialize calendar of France Exchange."""
holidayRule = ql.France(ql.France.Exchange)
[docs]class germanySettlement(null):
"""Quick way to initialize calendar of Germany Settlement."""
holidayRule = ql.Germany(ql.Germany.Settlement)
[docs]class germanyFrankfurtStockExchange(null):
"""Quick way to initialize calendar of Germany FrankfurtStockExchange."""
holidayRule = ql.Germany(ql.Germany.FrankfurtStockExchange)
[docs]class germanyXetra(null):
"""Quick way to initialize calendar of Germany Xetra."""
holidayRule = ql.Germany(ql.Germany.Xetra)
[docs]class germanyEurex(null):
"""Quick way to initialize calendar of Germany Eurex."""
holidayRule = ql.Germany(ql.Germany.Eurex)
[docs]class hongKongHKEx(null):
"""Quick way to initialize calendar of HongKong HKEx."""
holidayRule = ql.HongKong(ql.HongKong.HKEx)
[docs]class hungary(null):
"""Quick way to initialize calendar of Hungary."""
holidayRule = ql.Hungary()
[docs]class icelandICEX(null):
"""Quick way to initialize calendar of Iceland ICEX."""
holidayRule = ql.Iceland(ql.Iceland.ICEX)
[docs]class indiaNSE(null):
"""Quick way to initialize calendar of India NSE."""
holidayRule = ql.India(ql.India.NSE)
[docs]class indonesiaBEJ(null):
"""Quick way to initialize calendar of Indonesia BEJ."""
holidayRule = ql.Indonesia(ql.Indonesia.BEJ)
[docs]class indonesiaJSX(null):
"""Quick way to initialize calendar of Indonesia JSX."""
holidayRule = ql.Indonesia(ql.Indonesia.JSX)
[docs]class israelSettlement(null):
"""Quick way to initialize calendar of Israel Settlement."""
holidayRule = ql.Israel(ql.Israel.Settlement)
[docs]class israelTASE(null):
"""Quick way to initialize calendar of Israel TASE."""
holidayRule = ql.Israel(ql.Israel.TASE)
[docs]class israelSHIR(null):
"""Quick way to initialize calendar of Israel SHIR."""
holidayRule = ql.Israel(ql.Israel.SHIR)
[docs]class italySettlement(null):
"""Quick way to initialize calendar of Italy Settlement."""
holidayRule = ql.Italy(ql.Italy.Settlement)
[docs]class italyExchange(null):
"""Quick way to initialize calendar of Italy Exchange."""
holidayRule = ql.Italy(ql.Italy.Exchange)
[docs]class japan(null):
"""Quick way to initialize calendar of Japan."""
holidayRule = ql.Japan()
[docs]class mexicoBMV(null):
"""Quick way to initialize calendar of Mexico BMV."""
holidayRule = ql.Mexico(ql.Mexico.BMV)
[docs]class newZealandWellington(null):
"""Quick way to initialize calendar of NewZealand Wellington."""
holidayRule = ql.NewZealand(ql.NewZealand.Wellington)
[docs]class newZealandAuckland(null):
"""Quick way to initialize calendar of NewZealand Auckland."""
holidayRule = ql.NewZealand(ql.NewZealand.Auckland)
[docs]class norway(null):
"""Quick way to initialize calendar of Norway."""
holidayRule = ql.Norway()
[docs]class polandSettlement(null):
"""Quick way to initialize calendar of Poland Settlement."""
holidayRule = ql.Poland(ql.Poland.Settlement)
[docs]class polandWSE(null):
"""Quick way to initialize calendar of Poland WSE."""
holidayRule = ql.Poland(ql.Poland.WSE)
[docs]class romaniaPublic(null):
"""Quick way to initialize calendar of Romania Public."""
holidayRule = ql.Romania(ql.Romania.Public)
[docs]class romaniaBVB(null):
"""Quick way to initialize calendar of Romania BVB."""
holidayRule = ql.Romania(ql.Romania.BVB)
[docs]class russiaSettlement(null):
"""Quick way to initialize calendar of Russia Settlement."""
holidayRule = ql.Russia(ql.Russia.Settlement)
[docs]class russiaMOEX(null):
"""Quick way to initialize calendar of Russia MOEX."""
holidayRule = ql.Russia(ql.Russia.MOEX)
[docs]class saudiArabiaTadawul(null):
"""Quick way to initialize calendar of SaudiArabia Tadawul."""
holidayRule = ql.SaudiArabia(ql.SaudiArabia.Tadawul)
[docs]class singaporeSGX(null):
"""Quick way to initialize calendar of Singapore SGX."""
holidayRule = ql.Singapore(ql.Singapore.SGX)
[docs]class slovakiaBSSE(null):
"""Quick way to initialize calendar of Slovakia BSSE."""
holidayRule = ql.Slovakia(ql.Slovakia.BSSE)
[docs]class southAfrica(null):
"""Quick way to initialize calendar of SouthAfrica."""
holidayRule = ql.SouthAfrica()
[docs]class southKoreaSettlement(null):
"""Quick way to initialize calendar of SouthKorea Settlement."""
holidayRule = ql.SouthKorea(ql.SouthKorea.Settlement)
[docs]class southKoreaKRX(null):
"""Quick way to initialize calendar of SouthKorea KRX."""
holidayRule = ql.SouthKorea(ql.SouthKorea.KRX)
[docs]class sweden(null):
"""Quick way to initialize calendar of Sweden."""
holidayRule = ql.Sweden()
[docs]class switzerland(null):
"""Quick way to initialize calendar of Switzerland."""
holidayRule = ql.Switzerland()
[docs]class taiwanTSEC(null):
"""Quick way to initialize calendar of Taiwan TSEC."""
holidayRule = ql.Taiwan(ql.Taiwan.TSEC)
[docs]class thailand(null):
"""Quick way to initialize calendar of Thailand."""
holidayRule = ql.Thailand()
[docs]class turkey(null):
"""Quick way to initialize calendar of Turkey."""
holidayRule = ql.Turkey()
[docs]class ukraineUSE(null):
"""Quick way to initialize calendar of Ukraine USE."""
holidayRule = ql.Ukraine(ql.Ukraine.USE)
[docs]class unitedKingdomSettlement(null):
"""Quick way to initialize calendar of UnitedKingdom Settlement."""
holidayRule = ql.UnitedKingdom(ql.UnitedKingdom.Settlement)
[docs]class unitedKingdomExchange(null):
"""Quick way to initialize calendar of UnitedKingdom Exchange."""
holidayRule = ql.UnitedKingdom(ql.UnitedKingdom.Exchange)
[docs]class unitedStatesSettlement(null):
"""Quick way to initialize calendar of UnitedStates Settlement."""
holidayRule = ql.UnitedStates(ql.UnitedStates.Settlement)
[docs]class unitedStatesNYSE(null):
"""Quick way to initialize calendar of UnitedStates NYSE."""
holidayRule = ql.UnitedStates(ql.UnitedStates.NYSE)
[docs]class unitedStatesGovernmentBond(null):
"""Quick way to initialize calendar of UnitedStates GovernmentBond."""
holidayRule = ql.UnitedStates(ql.UnitedStates.GovernmentBond)
[docs]class unitedStatesNERC(null):
"""Quick way to initialize calendar of UnitedStates NERC."""
holidayRule = ql.UnitedStates(ql.UnitedStates.NERC)
[docs]class unitedStatesLiborImpact(null):
"""Quick way to initialize calendar of UnitedStates LiborImpact."""
holidayRule = ql.UnitedStates(ql.UnitedStates.LiborImpact)
[docs]class unitedStatesFederalReserve(null):
"""Quick way to initialize calendar of UnitedStates FederalReserve."""
holidayRule = ql.UnitedStates(ql.UnitedStates.FederalReserve)
[docs]class unitedStatesSOFR(null):
"""Quick way to initialize calendar of UnitedStates SOFR."""
holidayRule = ql.UnitedStates(ql.UnitedStates.SOFR)