openseries 1.2.2__py3-none-any.whl → 1.2.4__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.
openseries/datefixer.py CHANGED
@@ -1,20 +1,22 @@
1
- """
2
- Date related utilities
3
- """
1
+ """Date related utilities."""
4
2
  from __future__ import annotations
3
+
5
4
  import datetime as dt
6
- from typing import Optional, Union
5
+ from typing import Optional, Union, cast
6
+
7
7
  from dateutil.relativedelta import relativedelta
8
8
  from holidays import country_holidays, list_supported_countries
9
- from numpy import array, busdaycalendar, datetime64, is_busday, where, timedelta64
10
- from pandas import DataFrame, date_range, Timestamp, Series, DatetimeIndex, concat
9
+ from numpy import array, busdaycalendar, datetime64, is_busday, where
10
+ from pandas import DataFrame, DatetimeIndex, Series, Timestamp, concat, date_range
11
11
  from pandas.tseries.offsets import CustomBusinessDay
12
12
 
13
13
  from openseries.types import (
14
14
  CountriesType,
15
- TradingDaysType,
15
+ DateType,
16
+ HolidayType,
16
17
  LiteralBizDayFreq,
17
18
  LiteralPandasResampleConvention,
19
+ TradingDaysType,
18
20
  )
19
21
 
20
22
 
@@ -22,15 +24,10 @@ def holiday_calendar(
22
24
  startyear: int,
23
25
  endyear: int,
24
26
  countries: CountriesType = "SE",
25
- custom_holidays: Optional[
26
- Union[
27
- dict[Union[dt.date, dt.datetime, str, float, int], str],
28
- list[Union[dt.date, dt.datetime, str, float, int]],
29
- Union[dt.date, dt.datetime, str, float, int],
30
- ]
31
- ] = None,
27
+ custom_holidays: Optional[HolidayType] = None,
32
28
  ) -> busdaycalendar:
33
- """Function to generate a business calendar
29
+ """
30
+ Generate a business calendar.
34
31
 
35
32
  Parameters
36
33
  ----------
@@ -38,19 +35,16 @@ def holiday_calendar(
38
35
  First year in date range generated
39
36
  endyear: int
40
37
  Last year in date range generated
41
- countries: list[str] | str, default: "SE"
38
+ countries: CountriesType, default: "SE"
42
39
  (List of) country code(s) according to ISO 3166-1 alpha-2
43
- custom_holidays: Union[
44
- dict[Union[dt.date, dt.datetime, str, float, int], str],
45
- list[Union[dt.date, dt.datetime, str, float, int]],
46
- Union[dt.date, dt.datetime, str, float, int]], optional
40
+ custom_holidays: HolidayType, optional
47
41
  Argument where missing holidays can be added as
48
42
  {"2021-02-12": "Jack's birthday"} or ["2021-02-12"]
49
43
 
50
44
  Returns
51
45
  -------
52
46
  numpy.busdaycalendar
53
- Numpy busdaycalendar object
47
+ Generate a business calendar
54
48
  """
55
49
  startyear -= 1
56
50
  endyear += 1
@@ -73,25 +67,25 @@ def holiday_calendar(
73
67
  if i == 0 and custom_holidays is not None:
74
68
  staging.update(custom_holidays)
75
69
  countryholidays += list(staging)
76
- hols = array(sorted(list(set(countryholidays))), dtype="datetime64[D]")
70
+ hols = array(sorted(set(countryholidays)), dtype="datetime64[D]")
77
71
  else:
78
72
  raise ValueError(
79
73
  "Argument countries must be a string country code or a list "
80
- "of string country codes according to ISO 3166-1 alpha-2."
74
+ "of string country codes according to ISO 3166-1 alpha-2.",
81
75
  )
82
76
 
83
77
  return busdaycalendar(holidays=hols)
84
78
 
85
79
 
86
80
  def date_fix(
87
- fixerdate: Union[str, dt.date, dt.datetime, datetime64, Timestamp],
81
+ fixerdate: DateType,
88
82
  ) -> dt.date:
89
- """Function to parse from different date formats into datetime.date
83
+ """
84
+ Parse different date formats into datetime.date.
90
85
 
91
86
  Parameters
92
87
  ----------
93
- fixerdate: str | datetime.date | datetime.datetime |
94
- numpy.datetime64 | pandas.Timestamp
88
+ fixerdate: DateType
95
89
  The data item to parse
96
90
 
97
91
  Returns
@@ -99,44 +93,42 @@ def date_fix(
99
93
  datetime.date
100
94
  Parsed date
101
95
  """
102
-
103
96
  if isinstance(fixerdate, (Timestamp, dt.datetime)):
104
97
  return fixerdate.date()
105
98
  if isinstance(fixerdate, dt.date):
106
99
  return fixerdate
107
100
  if isinstance(fixerdate, datetime64):
108
- unix_epoch = datetime64(0, "s")
109
- one_second = timedelta64(1, "s")
110
- seconds_since_epoch = (fixerdate - unix_epoch) / one_second
111
- return dt.datetime.utcfromtimestamp(float(seconds_since_epoch)).date()
101
+ return (
102
+ dt.datetime.strptime(str(fixerdate)[:10], "%Y-%m-%d")
103
+ .replace(tzinfo=dt.timezone.utc)
104
+ .date()
105
+ )
112
106
  if isinstance(fixerdate, str):
113
- return dt.datetime.strptime(fixerdate, "%Y-%m-%d").date()
107
+ return (
108
+ dt.datetime.strptime(fixerdate, "%Y-%m-%d")
109
+ .replace(tzinfo=dt.timezone.utc)
110
+ .date()
111
+ )
114
112
  raise TypeError(
115
- f"Unknown date format {str(fixerdate)} of "
116
- f"type {str(type(fixerdate))} encountered"
113
+ f"Unknown date format {fixerdate!s} of "
114
+ f"type {type(fixerdate)!s} encountered",
117
115
  )
118
116
 
119
117
 
120
118
  def date_offset_foll(
121
- raw_date: Union[str, dt.date, dt.datetime, datetime64, Timestamp],
119
+ raw_date: DateType,
122
120
  months_offset: int = 12,
123
121
  adjust: bool = False,
124
122
  following: bool = True,
125
123
  countries: CountriesType = "SE",
126
- custom_holidays: Optional[
127
- Union[
128
- dict[Union[dt.date, dt.datetime, str, float, int], str],
129
- list[Union[dt.date, dt.datetime, str, float, int]],
130
- Union[dt.date, dt.datetime, str, float, int],
131
- ]
132
- ] = None,
124
+ custom_holidays: Optional[HolidayType] = None,
133
125
  ) -> dt.date:
134
- """Function to offset dates according to a given calendar
126
+ """
127
+ Offset dates according to a given calendar.
135
128
 
136
129
  Parameters
137
130
  ----------
138
- raw_date: str | datetime.date | datetime.datetime | numpy.datetime64 |
139
- pandas.Timestamp
131
+ raw_date: DateType
140
132
  The date to offset from
141
133
  months_offset: int, default: 12
142
134
  Number of months as integer
@@ -144,12 +136,9 @@ def date_offset_foll(
144
136
  Determines if offset should adjust for business days
145
137
  following: bool, default: True
146
138
  Determines if days should be offset forward (following) or backward
147
- countries: list[str] | str, default: "SE"
139
+ countries: CountriesType, default: "SE"
148
140
  (List of) country code(s) according to ISO 3166-1 alpha-2
149
- custom_holidays: Union[
150
- dict[Union[dt.date, dt.datetime, str, float, int], str],
151
- list[Union[dt.date, dt.datetime, str, float, int]],
152
- Union[dt.date, dt.datetime, str, float, int]], optional
141
+ custom_holidays: HolidayType, optional
153
142
  Argument where missing holidays can be added as
154
143
  {"2021-02-12": "Jack's birthday"} or ["2021-02-12"]
155
144
 
@@ -158,7 +147,6 @@ def date_offset_foll(
158
147
  datetime.date
159
148
  Off-set date
160
149
  """
161
-
162
150
  raw_date = date_fix(raw_date)
163
151
  month_delta = relativedelta(months=months_offset)
164
152
 
@@ -187,26 +175,18 @@ def date_offset_foll(
187
175
  def get_previous_business_day_before_today(
188
176
  today: Optional[dt.date] = None,
189
177
  countries: CountriesType = "SE",
190
- custom_holidays: Optional[
191
- Union[
192
- dict[Union[dt.date, dt.datetime, str, float, int], str],
193
- list[Union[dt.date, dt.datetime, str, float, int]],
194
- Union[dt.date, dt.datetime, str, float, int],
195
- ]
196
- ] = None,
178
+ custom_holidays: Optional[HolidayType] = None,
197
179
  ) -> dt.date:
198
- """Function to bump backwards to find the previous business day before today
180
+ """
181
+ Bump date backwards to find the previous business day.
199
182
 
200
183
  Parameters
201
184
  ----------
202
185
  today: datetime.date, optional
203
186
  Manual input of the day from where the previous business day is found
204
- countries: list[str] | str, default: "SE"
187
+ countries: CountriesType, default: "SE"
205
188
  (List of) country code(s) according to ISO 3166-1 alpha-2
206
- custom_holidays: Union[
207
- dict[Union[dt.date, dt.datetime, str, float, int], str],
208
- list[Union[dt.date, dt.datetime, str, float, int]],
209
- Union[dt.date, dt.datetime, str, float, int]], optional
189
+ custom_holidays: HolidayType, optional
210
190
  Argument where missing holidays can be added as
211
191
  {"2021-02-12": "Jack's birthday"} or ["2021-02-12"]
212
192
 
@@ -215,9 +195,8 @@ def get_previous_business_day_before_today(
215
195
  datetime.date
216
196
  The previous business day
217
197
  """
218
-
219
198
  if today is None:
220
- today = dt.date.today()
199
+ today = dt.datetime.now(tz=dt.timezone.utc).date()
221
200
 
222
201
  return date_offset_foll(
223
202
  today - dt.timedelta(days=1),
@@ -233,17 +212,13 @@ def offset_business_days(
233
212
  ddate: dt.date,
234
213
  days: int,
235
214
  countries: CountriesType = "SE",
236
- custom_holidays: Optional[
237
- Union[
238
- dict[Union[dt.date, dt.datetime, str, float, int], str],
239
- list[Union[dt.date, dt.datetime, str, float, int]],
240
- Union[dt.date, dt.datetime, str, float, int],
241
- ]
242
- ] = None,
215
+ custom_holidays: Optional[HolidayType] = None,
243
216
  ) -> dt.date:
244
- """Function to bump a date by business days instead of calendar days.
217
+ """
218
+ Bump date by business days.
219
+
245
220
  It first adjusts to a valid business day and then bumps with given
246
- number of business days from there
221
+ number of business days from there.
247
222
 
248
223
  Parameters
249
224
  ----------
@@ -252,12 +227,9 @@ def offset_business_days(
252
227
  days: int
253
228
  The number of business days to offset from the business day that is
254
229
  the closest preceding the day given
255
- countries: list[str] | str, default: "SE"
230
+ countries: CountriesType, default: "SE"
256
231
  (List of) country code(s) according to ISO 3166-1 alpha-2
257
- custom_holidays: Union[
258
- dict[Union[dt.date, dt.datetime, str, float, int], str],
259
- list[Union[dt.date, dt.datetime, str, float, int]],
260
- Union[dt.date, dt.datetime, str, float, int]], optional
232
+ custom_holidays: HolidayType, optional
261
233
  Argument where missing holidays can be added as
262
234
  {"2021-02-12": "Jack's birthday"} or ["2021-02-12"]
263
235
 
@@ -309,7 +281,7 @@ def offset_business_days(
309
281
 
310
282
  idx = where(array(local_bdays) == ddate)[0]
311
283
 
312
- return date_fix(local_bdays[idx[0] + days])
284
+ return cast(dt.date, local_bdays[idx[0] + days])
313
285
 
314
286
 
315
287
  def generate_calender_date_range(
@@ -318,18 +290,19 @@ def generate_calender_date_range(
318
290
  end: Optional[dt.date] = None,
319
291
  countries: CountriesType = "SE",
320
292
  ) -> list[dt.date]:
321
- """Generates a list of business day calender dates
322
-
323
- Parameters
324
- ----------
325
- trading_days: TradingDaysType
326
- Number of days to generate
327
- start: datetime.date, optional
328
- Date when the range starts
329
- end: datetime.date, optional
330
- Date when the range ends
331
- countries: CountriesType, default: "SE"
332
- (List of) country code(s) according to ISO 3166-1 alpha-2
293
+ """
294
+ Generate a list of business day calender dates.
295
+
296
+ Parameters
297
+ ----------
298
+ trading_days: TradingDaysType
299
+ Number of days to generate
300
+ start: datetime.date, optional
301
+ Date when the range starts
302
+ end: datetime.date, optional
303
+ Date when the range ends
304
+ countries: CountriesType, default: "SE"
305
+ (List of) country code(s) according to ISO 3166-1 alpha-2
333
306
 
334
307
  Returns
335
308
  -------
@@ -338,10 +311,14 @@ def generate_calender_date_range(
338
311
  """
339
312
  if start and not end:
340
313
  tmp_range = date_range(
341
- start=start, periods=trading_days * 365 // 252, freq="D"
314
+ start=start,
315
+ periods=trading_days * 365 // 252,
316
+ freq="D",
342
317
  )
343
318
  calendar = holiday_calendar(
344
- startyear=start.year, endyear=tmp_range[-1].year, countries=countries
319
+ startyear=start.year,
320
+ endyear=tmp_range[-1].year,
321
+ countries=countries,
345
322
  )
346
323
  return [
347
324
  d.date()
@@ -355,7 +332,9 @@ def generate_calender_date_range(
355
332
  if end and not start:
356
333
  tmp_range = date_range(end=end, periods=trading_days * 365 // 252, freq="D")
357
334
  calendar = holiday_calendar(
358
- startyear=tmp_range[0].year, endyear=end.year, countries=countries
335
+ startyear=tmp_range[0].year,
336
+ endyear=end.year,
337
+ countries=countries,
359
338
  )
360
339
  return [
361
340
  d.date()
@@ -368,21 +347,22 @@ def generate_calender_date_range(
368
347
 
369
348
  raise ValueError(
370
349
  "Provide one of start or end date, but not both. "
371
- "Date range is inferred from number of trading days."
350
+ "Date range is inferred from number of trading days.",
372
351
  )
373
352
 
374
353
 
375
354
  def align_dataframe_to_local_cdays(
376
- data: DataFrame, countries: CountriesType = "SE"
355
+ data: DataFrame,
356
+ countries: CountriesType = "SE",
377
357
  ) -> DataFrame:
378
- """Changes the index of the associated Pandas DataFrame .tsdf to align with
379
- local calendar business days
358
+ """
359
+ Align the index .tsdf with local calendar business days.
380
360
 
381
361
  Parameters
382
362
  ----------
383
363
  data: pandas.DataFrame
384
364
  The timeseries data
385
- countries: list[str] | str, default: "SE"
365
+ countries: CountriesType, default: "SE"
386
366
  (List of) country code(s) according to ISO 3166-1 alpha-2
387
367
 
388
368
  Returns
@@ -393,7 +373,9 @@ def align_dataframe_to_local_cdays(
393
373
  startyear = data.index[0].year
394
374
  endyear = data.index[-1].year
395
375
  calendar = holiday_calendar(
396
- startyear=startyear, endyear=endyear, countries=countries
376
+ startyear=startyear,
377
+ endyear=endyear,
378
+ countries=countries,
397
379
  )
398
380
 
399
381
  d_range = [
@@ -415,9 +397,10 @@ def do_resample_to_business_period_ends(
415
397
  countries: CountriesType,
416
398
  convention: LiteralPandasResampleConvention,
417
399
  ) -> DatetimeIndex:
418
- """Resamples timeseries frequency to the business calendar
419
- month end dates of each period while leaving any stubs
420
- in place. Stubs will be aligned to the shortest stub
400
+ """
401
+ Resample timeseries frequency to business calendar month end dates.
402
+
403
+ Stubs left in place. Stubs will be aligned to the shortest stub.
421
404
 
422
405
  Parameters
423
406
  ----------
@@ -440,12 +423,11 @@ def do_resample_to_business_period_ends(
440
423
  Pandas.DatetimeIndex
441
424
  A date range aligned to business period ends
442
425
  """
443
-
444
426
  head = head.to_frame().T
445
427
  tail = tail.to_frame().T
446
428
  data.index = DatetimeIndex(data.index)
447
429
  data = data.resample(rule=freq, convention=convention).last()
448
- data.drop(index=data.index[-1], inplace=True)
430
+ data = data.drop(index=data.index[-1])
449
431
  data.index = [d.date() for d in DatetimeIndex(data.index)]
450
432
 
451
433
  if head.index[0] not in data.index:
@@ -454,7 +436,7 @@ def do_resample_to_business_period_ends(
454
436
  if tail.index[0] not in data.index:
455
437
  data = concat([data, tail])
456
438
 
457
- data.sort_index(inplace=True)
439
+ data = data.sort_index()
458
440
 
459
441
  dates = DatetimeIndex(
460
442
  [data.index[0]]
@@ -470,7 +452,7 @@ def do_resample_to_business_period_ends(
470
452
  )
471
453
  for d in data.index[1:-1]
472
454
  ]
473
- + [data.index[-1]]
455
+ + [data.index[-1]],
474
456
  )
475
457
  dates = dates.drop_duplicates()
476
458
  return dates
@@ -482,7 +464,8 @@ def get_calc_range(
482
464
  from_dt: Optional[dt.date] = None,
483
465
  to_dt: Optional[dt.date] = None,
484
466
  ) -> tuple[dt.date, dt.date]:
485
- """Creates user defined date range
467
+ """
468
+ Create user defined date range.
486
469
 
487
470
  Parameters
488
471
  ----------