rlquantdata 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rlquantdata might be problematic. Click here for more details.
- rlquantdata-0.1.0.dist-info/METADATA +24 -0
- rlquantdata-0.1.0.dist-info/RECORD +22 -0
- rlquantdata-0.1.0.dist-info/WHEEL +5 -0
- rlquantdata-0.1.0.dist-info/licenses/LICENSE +21 -0
- rlquantdata-0.1.0.dist-info/top_level.txt +1 -0
- src/__init__.py +0 -0
- src/rlquantdata/DateUtilities/Asserts.py +26 -0
- src/rlquantdata/DateUtilities/Calendar.py +282 -0
- src/rlquantdata/DateUtilities/Date.py +427 -0
- src/rlquantdata/DateUtilities/DateUtilities.py +86 -0
- src/rlquantdata/DateUtilities/Date_enum.py +47 -0
- src/rlquantdata/DateUtilities/DayCounter.py +141 -0
- src/rlquantdata/DateUtilities/Period.py +319 -0
- src/rlquantdata/DateUtilities/Schedule.py +309 -0
- src/rlquantdata/DateUtilities/Settings.py +39 -0
- src/rlquantdata/DateUtilities/__init__.py +24 -0
- src/rlquantdata/__init__.py +0 -0
- src/rlquantdata/client.py +665 -0
- src/rlquantdata/utilities/__init__.py +0 -0
- src/rlquantdata/utilities/trade_date_utils.py +65 -0
- src/rlquantdata/utilities/utils.py +197 -0
- src/rlquantdata/version.py +1 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import datetime as dt
|
|
4
|
+
from math import floor
|
|
5
|
+
|
|
6
|
+
import six
|
|
7
|
+
from src.rlquantdata.DateUtilities.Date_enum import TimeUnits
|
|
8
|
+
from src.rlquantdata.DateUtilities.Asserts import require
|
|
9
|
+
from src.rlquantdata.DateUtilities.Period import Period
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _monthLength(month, isLeap: bool):
|
|
13
|
+
return _MonthLeapLength[month - 1] if isLeap else _MonthLength[month - 1]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _monthOffset(month, isLeap):
|
|
17
|
+
return _MonthLeapOffset[month - 1] if isLeap else _MonthOffset[month - 1]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _advance(date, n, units):
|
|
21
|
+
d: int
|
|
22
|
+
m: int
|
|
23
|
+
y: int
|
|
24
|
+
addedYear: int
|
|
25
|
+
monthLeft: int
|
|
26
|
+
length: int
|
|
27
|
+
leapFlag: int
|
|
28
|
+
|
|
29
|
+
if units == TimeUnits.Days or units == TimeUnits.BDays:
|
|
30
|
+
return Date(serialNumber=date.__serialNumber__ + n)
|
|
31
|
+
elif units == TimeUnits.Weeks:
|
|
32
|
+
return Date(serialNumber=date.__serialNumber__ + 7 * n)
|
|
33
|
+
elif units == TimeUnits.Months:
|
|
34
|
+
d = date._day
|
|
35
|
+
m = date._month + n
|
|
36
|
+
y = date._year
|
|
37
|
+
addedYear = int(floor(m / 12.))
|
|
38
|
+
monthLeft = m % 12
|
|
39
|
+
if monthLeft == 0:
|
|
40
|
+
monthLeft = 12
|
|
41
|
+
addedYear -= 1
|
|
42
|
+
y += addedYear
|
|
43
|
+
leapFlag = Date.isLeap(y)
|
|
44
|
+
|
|
45
|
+
require(1900 < y < 2200, ValueError, 'year {0:d} is out of bound. It must be in [1901, 2199]'.format(y))
|
|
46
|
+
|
|
47
|
+
length = _MonthLeapLength[monthLeft - 1] if leapFlag else _MonthLength[monthLeft - 1]
|
|
48
|
+
if d > length:
|
|
49
|
+
d = length
|
|
50
|
+
|
|
51
|
+
return Date(y, monthLeft, d)
|
|
52
|
+
elif units == TimeUnits.Years:
|
|
53
|
+
d = date._day
|
|
54
|
+
m = date._month
|
|
55
|
+
y = date._year + n
|
|
56
|
+
leapFlag = Date.isLeap(y)
|
|
57
|
+
|
|
58
|
+
if y <= 1900 or y >= 2200:
|
|
59
|
+
return Date(1900, 1, 1)
|
|
60
|
+
|
|
61
|
+
if d == 29 and m == 2 and not leapFlag:
|
|
62
|
+
d = 28
|
|
63
|
+
|
|
64
|
+
return Date(y, m, d)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class Date(object):
|
|
68
|
+
def __init__(self, year=0, month=0, day=0, serialNumber=0):
|
|
69
|
+
leap: bool
|
|
70
|
+
y: int
|
|
71
|
+
m: int
|
|
72
|
+
d: int
|
|
73
|
+
|
|
74
|
+
if serialNumber:
|
|
75
|
+
self.__serialNumber__ = serialNumber
|
|
76
|
+
|
|
77
|
+
y = (int(serialNumber / 365)) + 1900
|
|
78
|
+
if serialNumber <= _YearOffset[y - 1900]:
|
|
79
|
+
y -= 1
|
|
80
|
+
|
|
81
|
+
d = serialNumber - _YearOffset[y - 1900]
|
|
82
|
+
m = int(d / 30) + 1
|
|
83
|
+
leap = Date.isLeap(y)
|
|
84
|
+
while d <= _monthOffset(m, leap):
|
|
85
|
+
m -= 1
|
|
86
|
+
|
|
87
|
+
require(1900 < y < 2200, ValueError, 'year {0:d} is out of bound. It must be in [1901, 2199]'.format(y))
|
|
88
|
+
self._year = y
|
|
89
|
+
self._month = m
|
|
90
|
+
self._day = d - _monthOffset(m, leap)
|
|
91
|
+
return
|
|
92
|
+
elif serialNumber and (year or month or day):
|
|
93
|
+
raise ValueError("When serial number is offered, no year or month or day number should be entered")
|
|
94
|
+
elif not (year and month and day):
|
|
95
|
+
raise ValueError("year: {0}, month: {1}, day: {2} can't be null value included".format(year, month, day))
|
|
96
|
+
|
|
97
|
+
self._calculate_date(year, month, day)
|
|
98
|
+
|
|
99
|
+
def dayOfMonth(self):
|
|
100
|
+
return self._day
|
|
101
|
+
|
|
102
|
+
def dayOfYear(self):
|
|
103
|
+
return self.__serialNumber__ - _YearOffset[self.year() - 1900]
|
|
104
|
+
|
|
105
|
+
def year(self):
|
|
106
|
+
return self._year
|
|
107
|
+
|
|
108
|
+
def month(self):
|
|
109
|
+
return self._month
|
|
110
|
+
|
|
111
|
+
def weekday(self):
|
|
112
|
+
w: int
|
|
113
|
+
w = self.__serialNumber__ % 7
|
|
114
|
+
return 7 if w == 0 else w
|
|
115
|
+
|
|
116
|
+
def toDateTime(self):
|
|
117
|
+
return dt.datetime(self.year(), self.month(), self.dayOfMonth())
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def serialNumber(self):
|
|
121
|
+
return self.__serialNumber__
|
|
122
|
+
|
|
123
|
+
def __lt__(self, other):
|
|
124
|
+
return self.__serialNumber__ < other.__serialNumber__
|
|
125
|
+
|
|
126
|
+
def __le__(self, other):
|
|
127
|
+
return self.__serialNumber__ <= other.__serialNumber__
|
|
128
|
+
|
|
129
|
+
def __eq__(self, other):
|
|
130
|
+
return self.__serialNumber__ == other.__serialNumber__
|
|
131
|
+
|
|
132
|
+
def __ne__(self, other):
|
|
133
|
+
return self.__serialNumber__ != other.__serialNumber__
|
|
134
|
+
|
|
135
|
+
def __gt__(self, other):
|
|
136
|
+
return self.__serialNumber__ > other.__serialNumber__
|
|
137
|
+
|
|
138
|
+
def __ge__(self, other):
|
|
139
|
+
return self.__serialNumber__ >= other.__serialNumber__
|
|
140
|
+
|
|
141
|
+
def __add__(self, period):
|
|
142
|
+
if isinstance(period, Period):
|
|
143
|
+
return _advance(self, period.length(), period.units())
|
|
144
|
+
elif isinstance(period, int):
|
|
145
|
+
return Date(serialNumber=self.__serialNumber__ + period)
|
|
146
|
+
else:
|
|
147
|
+
period = Period(period)
|
|
148
|
+
return _advance(self, period.length(), period.units())
|
|
149
|
+
|
|
150
|
+
def __sub__(self, period):
|
|
151
|
+
if isinstance(period, Period):
|
|
152
|
+
return _advance(self, -period.length(), period.units())
|
|
153
|
+
elif isinstance(period, int):
|
|
154
|
+
return Date(serialNumber=self.__serialNumber__ - period)
|
|
155
|
+
elif isinstance(period, Date):
|
|
156
|
+
return self.__serialNumber__ - period.__serialNumber__
|
|
157
|
+
else:
|
|
158
|
+
period = Period(period)
|
|
159
|
+
return _advance(self, -period.length(), period.units())
|
|
160
|
+
|
|
161
|
+
def __hash__(self):
|
|
162
|
+
return self.__serialNumber__
|
|
163
|
+
|
|
164
|
+
def __str__(self):
|
|
165
|
+
d: int
|
|
166
|
+
m: int
|
|
167
|
+
y: int
|
|
168
|
+
d = self.dayOfMonth()
|
|
169
|
+
m = self.month()
|
|
170
|
+
y = self.year()
|
|
171
|
+
return "{0:d}-{1:02d}-{2:02d}".format(y, m, d)
|
|
172
|
+
|
|
173
|
+
def __repr__(self):
|
|
174
|
+
d: int
|
|
175
|
+
m: int
|
|
176
|
+
y: int
|
|
177
|
+
d = self.dayOfMonth()
|
|
178
|
+
m = self.month()
|
|
179
|
+
y = self.year()
|
|
180
|
+
return "Date({0:d}, {1:d}, {2:d})".format(y, m, d)
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def minDate():
|
|
184
|
+
return Date(1901, 1, 1)
|
|
185
|
+
|
|
186
|
+
@staticmethod
|
|
187
|
+
def maxDate():
|
|
188
|
+
return Date(2199, 12, 31)
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def endOfMonth(date):
|
|
192
|
+
m: int
|
|
193
|
+
y: int
|
|
194
|
+
m = date.month()
|
|
195
|
+
y = date.year()
|
|
196
|
+
return Date(y, m, _monthLength(m, Date.isLeap(y)))
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def isEndOfMonth(date):
|
|
200
|
+
m: int
|
|
201
|
+
y: int
|
|
202
|
+
m = date.month()
|
|
203
|
+
y = date.year()
|
|
204
|
+
return date.dayOfMonth() == _monthLength(m, Date.isLeap(y))
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def nextWeekday(date, dayOfWeek):
|
|
208
|
+
wd = date.weekday()
|
|
209
|
+
return date + ((7 if wd > dayOfWeek else 0) - wd + dayOfWeek)
|
|
210
|
+
|
|
211
|
+
@staticmethod
|
|
212
|
+
def nthWeekday(nth, dayOfWeek, m, y):
|
|
213
|
+
require(nth > 0, ValueError, "zeroth day of week in a given (month, year) is undefined")
|
|
214
|
+
require(nth < 6, ValueError, "no more than 5 weekday in a given (month, year)")
|
|
215
|
+
|
|
216
|
+
skip: int
|
|
217
|
+
first: int
|
|
218
|
+
|
|
219
|
+
first = Date(y, m, 1).weekday()
|
|
220
|
+
skip = nth - (1 if dayOfWeek >= first else 0)
|
|
221
|
+
return Date(y, m, (1 + dayOfWeek + skip * 7) - first)
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def todaysDate():
|
|
225
|
+
today = dt.date.today()
|
|
226
|
+
return Date(today.year, today.month, today.day)
|
|
227
|
+
|
|
228
|
+
@staticmethod
|
|
229
|
+
def isLeap(year):
|
|
230
|
+
return _YearIsLeap[year - 1900]
|
|
231
|
+
|
|
232
|
+
@staticmethod
|
|
233
|
+
def fromExcelSerialNumber(serialNumber):
|
|
234
|
+
return Date(serialNumber=serialNumber)
|
|
235
|
+
|
|
236
|
+
@staticmethod
|
|
237
|
+
def fromDateTime(dateTime):
|
|
238
|
+
return Date(dateTime.year, dateTime.month, dateTime.day)
|
|
239
|
+
|
|
240
|
+
@staticmethod
|
|
241
|
+
def parseISO(dateStr):
|
|
242
|
+
return Date(int(dateStr[0:4]), int(dateStr[5:7]), int(dateStr[8:10]))
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def strptime(dateStr, dateFormat='%Y-%m-%d'):
|
|
246
|
+
pydt = dt.datetime.strptime(dateStr, dateFormat)
|
|
247
|
+
return Date(pydt.year, pydt.month, pydt.day)
|
|
248
|
+
|
|
249
|
+
@staticmethod
|
|
250
|
+
def westernStyle(day, month, year):
|
|
251
|
+
return Date(year, month, day)
|
|
252
|
+
|
|
253
|
+
def __deepcopy__(self, memo):
|
|
254
|
+
return Date(self._year, self._month, self._day)
|
|
255
|
+
|
|
256
|
+
def __reduce__(self):
|
|
257
|
+
d = {}
|
|
258
|
+
|
|
259
|
+
return Date, (self._year, self._month, self._day), d
|
|
260
|
+
|
|
261
|
+
def __setstate__(self, state):
|
|
262
|
+
pass
|
|
263
|
+
|
|
264
|
+
def _calculate_date(self, year, month, day):
|
|
265
|
+
length: int
|
|
266
|
+
offset: int
|
|
267
|
+
isLeap: bool
|
|
268
|
+
|
|
269
|
+
isLeap = Date.isLeap(year)
|
|
270
|
+
|
|
271
|
+
length = _monthLength(month, isLeap)
|
|
272
|
+
offset = _monthOffset(month, isLeap)
|
|
273
|
+
|
|
274
|
+
self.__serialNumber__ = day + offset + _YearOffset[year - 1900]
|
|
275
|
+
self._day = day
|
|
276
|
+
self._month = month
|
|
277
|
+
self._year = year
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def check_date(date):
|
|
281
|
+
if isinstance(date, six.string_types):
|
|
282
|
+
return Date.parseISO(date)
|
|
283
|
+
else:
|
|
284
|
+
return Date.fromDateTime(date)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
_YearIsLeap = [
|
|
288
|
+
# 1900 is leap in agreement with Excel's bug
|
|
289
|
+
# 1900 is out of valid date range anyway
|
|
290
|
+
# 1900-1909
|
|
291
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
292
|
+
# 1910-1919
|
|
293
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
294
|
+
# 1920-1929
|
|
295
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
296
|
+
# 1930-1939
|
|
297
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
298
|
+
# 1940-1949
|
|
299
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
300
|
+
# 1950-1959
|
|
301
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
302
|
+
# 1960-1969
|
|
303
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
304
|
+
# 1970-1979
|
|
305
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
306
|
+
# 1980-1989
|
|
307
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
308
|
+
# 1990-1999
|
|
309
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
310
|
+
# 2000-2009
|
|
311
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
312
|
+
# 2010-2019
|
|
313
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
314
|
+
# 2020-2029
|
|
315
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
316
|
+
# 2030-2039
|
|
317
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
318
|
+
# 2040-2049
|
|
319
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
320
|
+
# 2050-2059
|
|
321
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
322
|
+
# 2060-2069
|
|
323
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
324
|
+
# 2070-2079
|
|
325
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
326
|
+
# 2080-2089
|
|
327
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
328
|
+
# 2090-2099
|
|
329
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
330
|
+
# 2100-2109
|
|
331
|
+
False, False, False, False, True, False, False, False, True, False,
|
|
332
|
+
# 2110-2119
|
|
333
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
334
|
+
# 2120-2129
|
|
335
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
336
|
+
# 2130-2139
|
|
337
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
338
|
+
# 2140-2149
|
|
339
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
340
|
+
# 2150-2159
|
|
341
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
342
|
+
# 2160-2169
|
|
343
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
344
|
+
# 2170-2179
|
|
345
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
346
|
+
# 2180-2189
|
|
347
|
+
True, False, False, False, True, False, False, False, True, False,
|
|
348
|
+
# 2190-2199
|
|
349
|
+
False, False, True, False, False, False, True, False, False, False,
|
|
350
|
+
# 2200
|
|
351
|
+
False
|
|
352
|
+
]
|
|
353
|
+
|
|
354
|
+
_YearOffset = [
|
|
355
|
+
# 1900-1909
|
|
356
|
+
0, 366, 731, 1096, 1461, 1827, 2192, 2557, 2922, 3288,
|
|
357
|
+
# 1910-1919
|
|
358
|
+
3653, 4018, 4383, 4749, 5114, 5479, 5844, 6210, 6575, 6940,
|
|
359
|
+
# 1920-1929
|
|
360
|
+
7305, 7671, 8036, 8401, 8766, 9132, 9497, 9862, 10227, 10593,
|
|
361
|
+
# 1930-1939
|
|
362
|
+
10958, 11323, 11688, 12054, 12419, 12784, 13149, 13515, 13880, 14245,
|
|
363
|
+
# 1940-1949
|
|
364
|
+
14610, 14976, 15341, 15706, 16071, 16437, 16802, 17167, 17532, 17898,
|
|
365
|
+
# 1950-1959
|
|
366
|
+
18263, 18628, 18993, 19359, 19724, 20089, 20454, 20820, 21185, 21550,
|
|
367
|
+
# 1960-1969
|
|
368
|
+
21915, 22281, 22646, 23011, 23376, 23742, 24107, 24472, 24837, 25203,
|
|
369
|
+
# 1970-1979
|
|
370
|
+
25568, 25933, 26298, 26664, 27029, 27394, 27759, 28125, 28490, 28855,
|
|
371
|
+
# 1980-1989
|
|
372
|
+
29220, 29586, 29951, 30316, 30681, 31047, 31412, 31777, 32142, 32508,
|
|
373
|
+
# 1990-1999
|
|
374
|
+
32873, 33238, 33603, 33969, 34334, 34699, 35064, 35430, 35795, 36160,
|
|
375
|
+
# 2000-2009
|
|
376
|
+
36525, 36891, 37256, 37621, 37986, 38352, 38717, 39082, 39447, 39813,
|
|
377
|
+
# 2010-2019
|
|
378
|
+
40178, 40543, 40908, 41274, 41639, 42004, 42369, 42735, 43100, 43465,
|
|
379
|
+
# 2020-2029
|
|
380
|
+
43830, 44196, 44561, 44926, 45291, 45657, 46022, 46387, 46752, 47118,
|
|
381
|
+
# 2030-2039
|
|
382
|
+
47483, 47848, 48213, 48579, 48944, 49309, 49674, 50040, 50405, 50770,
|
|
383
|
+
# 2040-2049
|
|
384
|
+
51135, 51501, 51866, 52231, 52596, 52962, 53327, 53692, 54057, 54423,
|
|
385
|
+
# 2050-2059
|
|
386
|
+
54788, 55153, 55518, 55884, 56249, 56614, 56979, 57345, 57710, 58075,
|
|
387
|
+
# 2060-2069
|
|
388
|
+
58440, 58806, 59171, 59536, 59901, 60267, 60632, 60997, 61362, 61728,
|
|
389
|
+
# 2070-2079
|
|
390
|
+
62093, 62458, 62823, 63189, 63554, 63919, 64284, 64650, 65015, 65380,
|
|
391
|
+
# 2080-2089
|
|
392
|
+
65745, 66111, 66476, 66841, 67206, 67572, 67937, 68302, 68667, 69033,
|
|
393
|
+
# 2090-2099
|
|
394
|
+
69398, 69763, 70128, 70494, 70859, 71224, 71589, 71955, 72320, 72685,
|
|
395
|
+
# 2100-2109
|
|
396
|
+
73050, 73415, 73780, 74145, 74510, 74876, 75241, 75606, 75971, 76337,
|
|
397
|
+
# 2110-2119
|
|
398
|
+
76702, 77067, 77432, 77798, 78163, 78528, 78893, 79259, 79624, 79989,
|
|
399
|
+
# 2120-2129
|
|
400
|
+
80354, 80720, 81085, 81450, 81815, 82181, 82546, 82911, 83276, 83642,
|
|
401
|
+
# 2130-2139
|
|
402
|
+
84007, 84372, 84737, 85103, 85468, 85833, 86198, 86564, 86929, 87294,
|
|
403
|
+
# 2140-2149
|
|
404
|
+
87659, 88025, 88390, 88755, 89120, 89486, 89851, 90216, 90581, 90947,
|
|
405
|
+
# 2150-2159
|
|
406
|
+
91312, 91677, 92042, 92408, 92773, 93138, 93503, 93869, 94234, 94599,
|
|
407
|
+
# 2160-2169
|
|
408
|
+
94964, 95330, 95695, 96060, 96425, 96791, 97156, 97521, 97886, 98252,
|
|
409
|
+
# 2170-2179
|
|
410
|
+
98617, 98982, 99347, 99713, 100078, 100443, 100808, 101174, 101539, 101904,
|
|
411
|
+
# 2180-2189
|
|
412
|
+
102269, 102635, 103000, 103365, 103730, 104096, 104461, 104826, 105191, 105557,
|
|
413
|
+
# 2190-2199
|
|
414
|
+
105922, 106287, 106652, 107018, 107383, 107748, 108113, 108479, 108844, 109209,
|
|
415
|
+
# 2200
|
|
416
|
+
109574
|
|
417
|
+
]
|
|
418
|
+
|
|
419
|
+
_MonthLength = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
420
|
+
_MonthLeapLength = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
421
|
+
|
|
422
|
+
_MonthOffset = [0, 31, 59, 90, 120, 151, # Jan - Jun
|
|
423
|
+
181, 212, 243, 273, 304, 334, # Jun - Dec
|
|
424
|
+
365]
|
|
425
|
+
_MonthLeapOffset = [0, 31, 60, 91, 121, 152, # Jan - Jun
|
|
426
|
+
182, 213, 244, 274, 305, 335, # Jun - Dec
|
|
427
|
+
366]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from src.rlquantdata.DateUtilities import Schedule
|
|
4
|
+
from src.rlquantdata.DateUtilities import check_period
|
|
5
|
+
from src.rlquantdata.DateUtilities.Calendar import Calendar
|
|
6
|
+
from src.rlquantdata.DateUtilities.Date import Date, check_date
|
|
7
|
+
from src.rlquantdata.DateUtilities.Date_enum import BizDayConventions, DateGeneration, TimeUnits
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def isBizDay(holidayCenter, ref):
|
|
11
|
+
cal = Calendar(holidayCenter)
|
|
12
|
+
ref = check_date(ref)
|
|
13
|
+
return cal.isBizDay(ref)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def datesList(fromDate, toDate):
|
|
17
|
+
fromDate = check_date(fromDate)
|
|
18
|
+
toDate = check_date(toDate)
|
|
19
|
+
return [Date.fromExcelSerialNumber(serial).toDateTime() for serial in
|
|
20
|
+
range(fromDate.serialNumber, toDate.serialNumber + 1)]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def bizDatesList(holidayCenter, fromDate, toDate):
|
|
24
|
+
cal = Calendar(holidayCenter)
|
|
25
|
+
fromDate = check_date(fromDate)
|
|
26
|
+
toDate = check_date(toDate)
|
|
27
|
+
return [d.toDateTime() for d in cal.bizDatesList(fromDate, toDate)]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def holDatesList(holidayCenter, fromDate, toDate, includeWeekend=True):
|
|
31
|
+
cal = Calendar(holidayCenter)
|
|
32
|
+
fromDate = check_date(fromDate)
|
|
33
|
+
toDate = check_date(toDate)
|
|
34
|
+
return [d.toDateTime() for d in cal.holDatesList(fromDate, toDate, includeWeekend)]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def advanceDate(referenceDate, period):
|
|
38
|
+
d = check_date(referenceDate) + period
|
|
39
|
+
return d.toDateTime()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def adjustDateByCalendar(holidayCenter, referenceDate, convention=BizDayConventions.Following):
|
|
43
|
+
cal = Calendar(holidayCenter)
|
|
44
|
+
refer = check_date(referenceDate)
|
|
45
|
+
return cal.adjustDate(refer, convention).toDateTime()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def advanceDateByCalendar(holidayCenter, referenceDate, period, convention=BizDayConventions.Following):
|
|
49
|
+
cal = Calendar(holidayCenter)
|
|
50
|
+
refer = check_date(referenceDate)
|
|
51
|
+
period = check_period(period)
|
|
52
|
+
return cal.advanceDate(refer, period, convention).toDateTime()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def nthWeekDay(nth, dayOfWeek, month, year):
|
|
56
|
+
date = Date.nthWeekday(nth, dayOfWeek, month, year)
|
|
57
|
+
return date.toDateTime()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def makeSchedule(firstDate,
|
|
61
|
+
endDate,
|
|
62
|
+
tenor,
|
|
63
|
+
calendar='NullCalendar',
|
|
64
|
+
dateRule=BizDayConventions.Following,
|
|
65
|
+
dateGenerationRule=DateGeneration.Forward):
|
|
66
|
+
cal = Calendar(calendar)
|
|
67
|
+
firstDate = check_date(firstDate)
|
|
68
|
+
endDate = check_date(endDate)
|
|
69
|
+
tenor = check_period(tenor)
|
|
70
|
+
|
|
71
|
+
if tenor.units() == TimeUnits.BDays:
|
|
72
|
+
schedule = []
|
|
73
|
+
if dateGenerationRule == DateGeneration.Forward:
|
|
74
|
+
d = cal.adjustDate(firstDate, dateRule)
|
|
75
|
+
while d <= endDate:
|
|
76
|
+
schedule.append(d)
|
|
77
|
+
d = cal.advanceDate(d, tenor, dateRule)
|
|
78
|
+
elif dateGenerationRule == DateGeneration.Backward:
|
|
79
|
+
d = cal.adjustDate(endDate, dateRule)
|
|
80
|
+
while d >= firstDate:
|
|
81
|
+
schedule.append(d)
|
|
82
|
+
d = cal.advanceDate(d, -tenor, dateRule)
|
|
83
|
+
schedule = sorted(schedule)
|
|
84
|
+
else:
|
|
85
|
+
schedule = Schedule(firstDate, endDate, tenor, cal, convention=dateRule, dateGenerationRule=dateGenerationRule)
|
|
86
|
+
return [d.toDateTime() for d in schedule]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
class Weekdays:
|
|
2
|
+
Sunday = 1
|
|
3
|
+
Monday = 2
|
|
4
|
+
Tuesday = 3
|
|
5
|
+
Wednesday = 4
|
|
6
|
+
Thursday = 5
|
|
7
|
+
Friday = 6
|
|
8
|
+
Saturday = 7
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TimeUnits:
|
|
12
|
+
BDays = 0
|
|
13
|
+
Days = 1
|
|
14
|
+
Weeks = 2
|
|
15
|
+
Months = 3
|
|
16
|
+
Years = 4
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Months:
|
|
20
|
+
January = 1
|
|
21
|
+
February = 2
|
|
22
|
+
March = 3
|
|
23
|
+
April = 4
|
|
24
|
+
May = 5
|
|
25
|
+
June = 6
|
|
26
|
+
July = 7
|
|
27
|
+
August = 8
|
|
28
|
+
September = 9
|
|
29
|
+
October = 10
|
|
30
|
+
November = 11
|
|
31
|
+
December = 12
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class BizDayConventions:
|
|
35
|
+
Following = 0
|
|
36
|
+
ModifiedFollowing = 1
|
|
37
|
+
Preceding = 2
|
|
38
|
+
ModifiedPreceding = 3
|
|
39
|
+
Unadjusted = 4
|
|
40
|
+
HalfMonthModifiedFollowing = 5
|
|
41
|
+
Nearest = 6
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DateGeneration:
|
|
45
|
+
Zero = 0
|
|
46
|
+
Backward = 1
|
|
47
|
+
Forward = 2
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from Period import Period
|
|
4
|
+
from Date_enum import TimeUnits
|
|
5
|
+
from Date_enum import Months
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DayCounter(object):
|
|
9
|
+
|
|
10
|
+
def __init__(self, dcName):
|
|
11
|
+
|
|
12
|
+
dcName = dcName.lower()
|
|
13
|
+
if dcName in _dcDict:
|
|
14
|
+
self.impl_ = _dcDict[dcName]()
|
|
15
|
+
else:
|
|
16
|
+
raise ValueError('{0} is not a valid day counter name'.format(dcName))
|
|
17
|
+
|
|
18
|
+
def name(self):
|
|
19
|
+
return self.impl_.name()
|
|
20
|
+
|
|
21
|
+
def dayCount(self, d1, d2):
|
|
22
|
+
return self.impl_.dayCount(d1, d2)
|
|
23
|
+
|
|
24
|
+
def yearFraction(self, d1, d2, refPeriodStart=None, refPeriodEnd=None):
|
|
25
|
+
return self.impl_.yearFraction(d1, d2, refPeriodStart, refPeriodEnd)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DayCounterImpl(object):
|
|
29
|
+
|
|
30
|
+
def name(self):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
def dayCount(self, d1, d2):
|
|
34
|
+
return d2 - d1
|
|
35
|
+
|
|
36
|
+
def yearFraction(self, d1, d2, refPeriodStart, refPeriodEnd):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Actual360(DayCounterImpl):
|
|
41
|
+
|
|
42
|
+
def name(self):
|
|
43
|
+
return 'Actual/360'
|
|
44
|
+
|
|
45
|
+
def yearFraction(self, d1, d2, refPeriodStart, refPeriodEnd2):
|
|
46
|
+
return float(d2 - d1) / 360.
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Actual365Fixed(DayCounterImpl):
|
|
50
|
+
|
|
51
|
+
def name(self):
|
|
52
|
+
return 'Actual/365 (Fixed)'
|
|
53
|
+
|
|
54
|
+
def yearFraction(self, d1, d2, refPeriodStart, refPeriodEnd2):
|
|
55
|
+
return float(d2 - d1) / 365.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Actual365NoLeap(DayCounterImpl):
|
|
59
|
+
MonthOffset = [
|
|
60
|
+
0, 31, 59, 90, 120, 151, # Jan - Jun
|
|
61
|
+
181, 212, 243, 273, 304, 334 # Jun - Dec
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
def name(self):
|
|
65
|
+
return 'Actual/365 (NL)'
|
|
66
|
+
|
|
67
|
+
def dayCount(self, d1, d2):
|
|
68
|
+
|
|
69
|
+
s1 = d1.dayOfMonth() + self.MonthOffset[d1.month() - 1] + (d1.year() * 365)
|
|
70
|
+
s2 = d2.dayOfMonth() + self.MonthOffset[d2.month() - 1] + (d2.year() * 365)
|
|
71
|
+
|
|
72
|
+
if d1.month() == Months.Feb and d1.dayOfMonth() == 29:
|
|
73
|
+
s1 -= 1
|
|
74
|
+
|
|
75
|
+
if d2.month() == Months.Feb and d2.dayOfMonth() == 29:
|
|
76
|
+
s2 -= 1
|
|
77
|
+
|
|
78
|
+
return s2 - s1
|
|
79
|
+
|
|
80
|
+
def yearFraction(self, d1, d2, refPeriodStart, refPeriodEnd2):
|
|
81
|
+
return self.dayCount(d1, d2) / 365.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class ActualActualISMAImpl(DayCounterImpl):
|
|
85
|
+
|
|
86
|
+
def name(self):
|
|
87
|
+
return 'Actual/Actual (ISMA)'
|
|
88
|
+
|
|
89
|
+
def yearFraction(self, d1, d2, d3, d4):
|
|
90
|
+
|
|
91
|
+
if d1 == d2:
|
|
92
|
+
return 0.
|
|
93
|
+
|
|
94
|
+
if d1 > d2:
|
|
95
|
+
return -self.yearFraction(d2, d1, d3, d4)
|
|
96
|
+
|
|
97
|
+
refPeriodStart = d3 if d3 else d1
|
|
98
|
+
refPeriodEnd = d4 if d4 else d2
|
|
99
|
+
|
|
100
|
+
months = int(0.5 + 12. * (refPeriodEnd - refPeriodStart) / 365.)
|
|
101
|
+
|
|
102
|
+
if months == 0:
|
|
103
|
+
refPeriodStart = d1
|
|
104
|
+
refPeriodEnd = d1 + '1y'
|
|
105
|
+
months = 12
|
|
106
|
+
|
|
107
|
+
period = months / 12.
|
|
108
|
+
|
|
109
|
+
if d2 <= refPeriodEnd:
|
|
110
|
+
if d1 >= refPeriodStart:
|
|
111
|
+
return period * (d2 - d1) / (refPeriodEnd - refPeriodStart)
|
|
112
|
+
else:
|
|
113
|
+
previousRef = refPeriodStart - Period(length=months, units=TimeUnits.Months)
|
|
114
|
+
|
|
115
|
+
if d2 > refPeriodStart:
|
|
116
|
+
return self.yearFraction(d1, refPeriodStart, previousRef, refPeriodStart) \
|
|
117
|
+
+ self.yearFraction(refPeriodStart, d2, refPeriodStart, refPeriodEnd)
|
|
118
|
+
else:
|
|
119
|
+
return self.yearFraction(d1, d2, previousRef, refPeriodStart)
|
|
120
|
+
|
|
121
|
+
else:
|
|
122
|
+
sum = self.yearFraction(d1, refPeriodEnd, refPeriodStart, refPeriodEnd)
|
|
123
|
+
i = 0
|
|
124
|
+
while True:
|
|
125
|
+
newRefStart = refPeriodEnd + Period(length=i*months, units=TimeUnits.Months)
|
|
126
|
+
newRefEnd = refPeriodEnd + Period(length=(i+1)*months, units=TimeUnits.Months)
|
|
127
|
+
|
|
128
|
+
if d2 < newRefEnd:
|
|
129
|
+
break
|
|
130
|
+
else:
|
|
131
|
+
sum += period
|
|
132
|
+
i += 1
|
|
133
|
+
|
|
134
|
+
sum += self.yearFraction(newRefStart, d2, newRefStart, newRefEnd)
|
|
135
|
+
return sum
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
_dcDict = {'actual/360': Actual360,
|
|
139
|
+
'actual/365 (fixed)': Actual365Fixed,
|
|
140
|
+
'actual/365 (nl)': Actual365NoLeap,
|
|
141
|
+
'actual/actual (isma)': ActualActualISMAImpl}
|