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.

@@ -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}