openseries 1.8.3__py3-none-any.whl → 1.8.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/_common_model.py +47 -4
- openseries/datefixer.py +120 -32
- openseries/frame.py +2 -6
- openseries/owntypes.py +4 -10
- openseries/series.py +24 -0
- openseries/simulation.py +4 -0
- {openseries-1.8.3.dist-info → openseries-1.8.4.dist-info}/METADATA +34 -34
- openseries-1.8.4.dist-info/RECORD +16 -0
- openseries-1.8.3.dist-info/RECORD +0 -16
- {openseries-1.8.3.dist-info → openseries-1.8.4.dist-info}/LICENSE.md +0 -0
- {openseries-1.8.3.dist-info → openseries-1.8.4.dist-info}/WHEEL +0 -0
openseries/_common_model.py
CHANGED
@@ -58,7 +58,7 @@ from plotly.figure_factory import create_distplot # type: ignore[import-untyped
|
|
58
58
|
from plotly.graph_objs import Figure # type: ignore[import-untyped]
|
59
59
|
from plotly.io import to_html # type: ignore[import-untyped]
|
60
60
|
from plotly.offline import plot # type: ignore[import-untyped]
|
61
|
-
from pydantic import BaseModel, ConfigDict, DirectoryPath
|
61
|
+
from pydantic import BaseModel, ConfigDict, DirectoryPath, ValidationError
|
62
62
|
from scipy.stats import ( # type: ignore[import-untyped]
|
63
63
|
kurtosis,
|
64
64
|
norm,
|
@@ -377,13 +377,23 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
377
377
|
|
378
378
|
"""
|
379
379
|
method: LiteralPandasReindexMethod = "nearest"
|
380
|
-
|
380
|
+
|
381
|
+
try:
|
382
|
+
countries = self.countries
|
383
|
+
markets = self.markets
|
384
|
+
except AttributeError:
|
385
|
+
countries = self.constituents[0].countries
|
386
|
+
markets = self.constituents[0].markets
|
387
|
+
|
381
388
|
wmdf = self.tsdf.copy()
|
389
|
+
|
382
390
|
dates = _do_resample_to_business_period_ends(
|
383
391
|
data=wmdf,
|
384
392
|
freq="BME",
|
385
393
|
countries=countries,
|
394
|
+
markets=markets,
|
386
395
|
)
|
396
|
+
|
387
397
|
wmdf = wmdf.reindex(index=[deyt.date() for deyt in dates], method=method)
|
388
398
|
wmdf.index = DatetimeIndex(wmdf.index)
|
389
399
|
result = wmdf.ffill().pct_change().min()
|
@@ -539,14 +549,20 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
539
549
|
|
540
550
|
def align_index_to_local_cdays(
|
541
551
|
self: Self,
|
542
|
-
countries: CountriesType =
|
552
|
+
countries: CountriesType | None = None,
|
553
|
+
markets: list[str] | str | None = None,
|
554
|
+
custom_holidays: list[str] | str | None = None,
|
543
555
|
) -> Self:
|
544
556
|
"""Align the index of .tsdf with local calendar business days.
|
545
557
|
|
546
558
|
Parameters
|
547
559
|
----------
|
548
|
-
countries: CountriesType,
|
560
|
+
countries: CountriesType, optional
|
549
561
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
562
|
+
markets: list[str] | str, optional
|
563
|
+
(List of) markets code(s) according to pandas-market-calendars
|
564
|
+
custom_holidays: list[str] | str, optional
|
565
|
+
Argument where missing holidays can be added
|
550
566
|
|
551
567
|
Returns:
|
552
568
|
-------
|
@@ -556,10 +572,37 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
556
572
|
"""
|
557
573
|
startyear = cast("int", to_datetime(self.tsdf.index[0]).year)
|
558
574
|
endyear = cast("int", to_datetime(self.tsdf.index[-1]).year)
|
575
|
+
|
576
|
+
if countries:
|
577
|
+
try:
|
578
|
+
self.countries = countries
|
579
|
+
except ValidationError:
|
580
|
+
for serie in self.constituents:
|
581
|
+
serie.countries = countries
|
582
|
+
else:
|
583
|
+
try:
|
584
|
+
countries = self.countries
|
585
|
+
except AttributeError:
|
586
|
+
countries = self.constituents[0].countries
|
587
|
+
|
588
|
+
if markets:
|
589
|
+
try:
|
590
|
+
self.markets = markets
|
591
|
+
except ValidationError:
|
592
|
+
for serie in self.constituents:
|
593
|
+
serie.markets = markets
|
594
|
+
else:
|
595
|
+
try:
|
596
|
+
markets = self.markets
|
597
|
+
except AttributeError:
|
598
|
+
markets = self.constituents[0].markets
|
599
|
+
|
559
600
|
calendar = holiday_calendar(
|
560
601
|
startyear=startyear,
|
561
602
|
endyear=endyear,
|
562
603
|
countries=countries,
|
604
|
+
markets=markets,
|
605
|
+
custom_holidays=custom_holidays,
|
563
606
|
)
|
564
607
|
|
565
608
|
d_range = [
|
openseries/datefixer.py
CHANGED
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
5
5
|
import datetime as dt
|
6
6
|
from typing import TYPE_CHECKING, cast
|
7
7
|
|
8
|
+
import pandas_market_calendars as mcal
|
8
9
|
from dateutil.relativedelta import relativedelta
|
9
10
|
from holidays import (
|
10
11
|
country_holidays,
|
@@ -24,6 +25,7 @@ from pandas.tseries.offsets import CustomBusinessDay
|
|
24
25
|
from .owntypes import (
|
25
26
|
BothStartAndEndError,
|
26
27
|
CountriesNotStringNorListStrError,
|
28
|
+
MarketsNotStringNorListStrError,
|
27
29
|
TradingDaysNotAboveZeroError,
|
28
30
|
)
|
29
31
|
|
@@ -31,7 +33,6 @@ if TYPE_CHECKING:
|
|
31
33
|
from .owntypes import ( # pragma: no cover
|
32
34
|
CountriesType,
|
33
35
|
DateType,
|
34
|
-
HolidayType,
|
35
36
|
LiteralBizDayFreq,
|
36
37
|
)
|
37
38
|
|
@@ -45,11 +46,59 @@ __all__ = [
|
|
45
46
|
]
|
46
47
|
|
47
48
|
|
49
|
+
def market_holidays(
|
50
|
+
startyear: int,
|
51
|
+
endyear: int,
|
52
|
+
markets: str | list[str],
|
53
|
+
) -> list[str]:
|
54
|
+
"""Return a dict of holiday dates mapping to list of markets closed.
|
55
|
+
|
56
|
+
Parameters
|
57
|
+
----------
|
58
|
+
startyear: int
|
59
|
+
First year (inclusive) to consider.
|
60
|
+
endyear: int
|
61
|
+
Last year (inclusive) to consider.
|
62
|
+
markets: str | list[str]
|
63
|
+
String or list of market codes supported by pandas_market_calendars.
|
64
|
+
|
65
|
+
Returns:
|
66
|
+
-------
|
67
|
+
list[str]
|
68
|
+
list of holiday dates.
|
69
|
+
"""
|
70
|
+
market_list = [markets] if isinstance(markets, str) else list(markets)
|
71
|
+
|
72
|
+
supported = mcal.get_calendar_names()
|
73
|
+
|
74
|
+
if not all(m in supported for m in market_list):
|
75
|
+
msg = (
|
76
|
+
"Argument markets must be a string market code or a list of market "
|
77
|
+
"codes supported by pandas_market_calendars."
|
78
|
+
)
|
79
|
+
raise MarketsNotStringNorListStrError(msg)
|
80
|
+
|
81
|
+
holidays: list[str] = []
|
82
|
+
for m in market_list:
|
83
|
+
cal = mcal.get_calendar(m)
|
84
|
+
# noinspection PyUnresolvedReferences
|
85
|
+
cal_hols = cal.holidays().calendar.holidays
|
86
|
+
my_hols: list[str] = [
|
87
|
+
str(date)
|
88
|
+
for date in cal_hols
|
89
|
+
if (startyear <= int(str(date)[:4]) <= endyear)
|
90
|
+
]
|
91
|
+
holidays.extend(my_hols)
|
92
|
+
|
93
|
+
return list(set(holidays))
|
94
|
+
|
95
|
+
|
48
96
|
def holiday_calendar(
|
49
97
|
startyear: int,
|
50
98
|
endyear: int,
|
51
99
|
countries: CountriesType = "SE",
|
52
|
-
|
100
|
+
markets: list[str] | str | None = None,
|
101
|
+
custom_holidays: list[str] | str | None = None,
|
53
102
|
) -> busdaycalendar:
|
54
103
|
"""Generate a business calendar.
|
55
104
|
|
@@ -61,9 +110,10 @@ def holiday_calendar(
|
|
61
110
|
Last year in date range generated
|
62
111
|
countries: CountriesType, default: "SE"
|
63
112
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
64
|
-
|
65
|
-
|
66
|
-
|
113
|
+
markets: list[str] | str, optional
|
114
|
+
(List of) markets code(s) according to pandas-market-calendars
|
115
|
+
custom_holidays: list[str] | str, optional
|
116
|
+
Argument where missing holidays can be added
|
67
117
|
|
68
118
|
Returns:
|
69
119
|
-------
|
@@ -79,20 +129,16 @@ def holiday_calendar(
|
|
79
129
|
|
80
130
|
if isinstance(countries, str) and countries in list_supported_countries():
|
81
131
|
staging = country_holidays(country=countries, years=years)
|
82
|
-
|
83
|
-
|
84
|
-
hols = array(sorted(staging.keys()), dtype="datetime64[D]")
|
85
|
-
elif isinstance(countries, list) and all(
|
132
|
+
hols = list(staging.keys())
|
133
|
+
elif isinstance(countries, (list, set)) and all(
|
86
134
|
country in list_supported_countries() for country in countries
|
87
135
|
):
|
88
136
|
country: str
|
89
137
|
countryholidays: list[dt.date | str] = []
|
90
|
-
for
|
138
|
+
for country in countries:
|
91
139
|
staging = country_holidays(country=country, years=years)
|
92
|
-
if i == 0 and custom_holidays is not None:
|
93
|
-
staging.update(custom_holidays)
|
94
140
|
countryholidays += list(staging)
|
95
|
-
hols =
|
141
|
+
hols = list(countryholidays)
|
96
142
|
else:
|
97
143
|
msg = (
|
98
144
|
"Argument countries must be a string country code or "
|
@@ -100,7 +146,22 @@ def holiday_calendar(
|
|
100
146
|
)
|
101
147
|
raise CountriesNotStringNorListStrError(msg)
|
102
148
|
|
103
|
-
|
149
|
+
if markets:
|
150
|
+
market_hols = market_holidays(
|
151
|
+
startyear=startyear, endyear=endyear, markets=markets
|
152
|
+
)
|
153
|
+
dt_mkt_hols = [date_fix(fixerdate=ddate) for ddate in market_hols]
|
154
|
+
hols.extend(dt_mkt_hols)
|
155
|
+
|
156
|
+
if custom_holidays:
|
157
|
+
custom_list = (
|
158
|
+
[custom_holidays]
|
159
|
+
if isinstance(custom_holidays, str)
|
160
|
+
else list(custom_holidays) # type: ignore[arg-type]
|
161
|
+
)
|
162
|
+
hols.extend([date_fix(fixerdate=ddate) for ddate in custom_list])
|
163
|
+
|
164
|
+
return busdaycalendar(holidays=array(sorted(set(hols)), dtype="datetime64[D]"))
|
104
165
|
|
105
166
|
|
106
167
|
def date_fix(
|
@@ -137,7 +198,8 @@ def date_offset_foll(
|
|
137
198
|
raw_date: DateType,
|
138
199
|
months_offset: int = 12,
|
139
200
|
countries: CountriesType = "SE",
|
140
|
-
|
201
|
+
markets: list[str] | str | None = None,
|
202
|
+
custom_holidays: list[str] | str | None = None,
|
141
203
|
*,
|
142
204
|
adjust: bool = False,
|
143
205
|
following: bool = True,
|
@@ -152,9 +214,10 @@ def date_offset_foll(
|
|
152
214
|
Number of months as integer
|
153
215
|
countries: CountriesType, default: "SE"
|
154
216
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
155
|
-
|
156
|
-
|
157
|
-
|
217
|
+
markets: list[str] | str, optional
|
218
|
+
(List of) markets code(s) according to pandas-market-calendars
|
219
|
+
custom_holidays: list[str] | str, optional
|
220
|
+
Argument where missing holidays can be added
|
158
221
|
adjust: bool, default: False
|
159
222
|
Determines if offset should adjust for business days
|
160
223
|
following: bool, default: True
|
@@ -180,6 +243,7 @@ def date_offset_foll(
|
|
180
243
|
startyear=startyear,
|
181
244
|
endyear=endyear,
|
182
245
|
countries=countries,
|
246
|
+
markets=markets,
|
183
247
|
custom_holidays=custom_holidays,
|
184
248
|
)
|
185
249
|
while not is_busday(dates=new_date, busdaycal=calendar):
|
@@ -191,7 +255,8 @@ def date_offset_foll(
|
|
191
255
|
def get_previous_business_day_before_today(
|
192
256
|
today: dt.date | None = None,
|
193
257
|
countries: CountriesType = "SE",
|
194
|
-
|
258
|
+
markets: list[str] | str | None = None,
|
259
|
+
custom_holidays: list[str] | str | None = None,
|
195
260
|
) -> dt.date:
|
196
261
|
"""Bump date backwards to find the previous business day.
|
197
262
|
|
@@ -201,9 +266,10 @@ def get_previous_business_day_before_today(
|
|
201
266
|
Manual input of the day from where the previous business day is found
|
202
267
|
countries: CountriesType, default: "SE"
|
203
268
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
204
|
-
|
205
|
-
|
206
|
-
|
269
|
+
markets: list[str] | str, optional
|
270
|
+
(List of) markets code(s) according to pandas-market-calendars
|
271
|
+
custom_holidays: list[str] | str, optional
|
272
|
+
Argument where missing holidays can be added
|
207
273
|
|
208
274
|
Returns:
|
209
275
|
-------
|
@@ -215,10 +281,11 @@ def get_previous_business_day_before_today(
|
|
215
281
|
today = dt.datetime.now().astimezone().date()
|
216
282
|
|
217
283
|
return date_offset_foll(
|
218
|
-
today - dt.timedelta(days=1),
|
284
|
+
raw_date=today - dt.timedelta(days=1),
|
285
|
+
months_offset=0,
|
219
286
|
countries=countries,
|
287
|
+
markets=markets,
|
220
288
|
custom_holidays=custom_holidays,
|
221
|
-
months_offset=0,
|
222
289
|
adjust=True,
|
223
290
|
following=False,
|
224
291
|
)
|
@@ -228,7 +295,8 @@ def offset_business_days(
|
|
228
295
|
ddate: dt.date,
|
229
296
|
days: int,
|
230
297
|
countries: CountriesType = "SE",
|
231
|
-
|
298
|
+
markets: list[str] | str | None = None,
|
299
|
+
custom_holidays: list[str] | str | None = None,
|
232
300
|
) -> dt.date:
|
233
301
|
"""Bump date by business days.
|
234
302
|
|
@@ -244,9 +312,10 @@ def offset_business_days(
|
|
244
312
|
If days is set as anything other than an integer its value is set to zero
|
245
313
|
countries: CountriesType, default: "SE"
|
246
314
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
247
|
-
|
248
|
-
|
249
|
-
|
315
|
+
markets: list[str] | str, optional
|
316
|
+
(List of) markets code(s) according to pandas-market-calendars
|
317
|
+
custom_holidays: list[str] | str, optional
|
318
|
+
Argument where missing holidays can be added
|
250
319
|
|
251
320
|
Returns:
|
252
321
|
-------
|
@@ -266,6 +335,7 @@ def offset_business_days(
|
|
266
335
|
startyear=ndate.year,
|
267
336
|
endyear=ddate.year,
|
268
337
|
countries=countries,
|
338
|
+
markets=markets,
|
269
339
|
custom_holidays=custom_holidays,
|
270
340
|
)
|
271
341
|
local_bdays: list[dt.date] = [
|
@@ -283,6 +353,7 @@ def offset_business_days(
|
|
283
353
|
startyear=ddate.year,
|
284
354
|
endyear=ndate.year,
|
285
355
|
countries=countries,
|
356
|
+
markets=markets,
|
286
357
|
custom_holidays=custom_holidays,
|
287
358
|
)
|
288
359
|
local_bdays = [
|
@@ -310,6 +381,8 @@ def generate_calendar_date_range(
|
|
310
381
|
start: dt.date | None = None,
|
311
382
|
end: dt.date | None = None,
|
312
383
|
countries: CountriesType = "SE",
|
384
|
+
markets: list[str] | str | None = None,
|
385
|
+
custom_holidays: list[str] | str | None = None,
|
313
386
|
) -> list[dt.date]:
|
314
387
|
"""Generate a list of business day calendar dates.
|
315
388
|
|
@@ -323,6 +396,10 @@ def generate_calendar_date_range(
|
|
323
396
|
Date when the range ends
|
324
397
|
countries: CountriesType, default: "SE"
|
325
398
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
399
|
+
markets: list[str] | str, optional
|
400
|
+
(List of) markets code(s) according to pandas-market-calendars
|
401
|
+
custom_holidays: list[str] | str, optional
|
402
|
+
Argument where missing holidays can be added
|
326
403
|
|
327
404
|
Returns:
|
328
405
|
-------
|
@@ -344,6 +421,8 @@ def generate_calendar_date_range(
|
|
344
421
|
startyear=start.year,
|
345
422
|
endyear=date_fix(tmp_range.tolist()[-1]).year,
|
346
423
|
countries=countries,
|
424
|
+
markets=markets,
|
425
|
+
custom_holidays=custom_holidays,
|
347
426
|
)
|
348
427
|
return [
|
349
428
|
d.date()
|
@@ -360,6 +439,8 @@ def generate_calendar_date_range(
|
|
360
439
|
startyear=date_fix(tmp_range.tolist()[0]).year,
|
361
440
|
endyear=end.year,
|
362
441
|
countries=countries,
|
442
|
+
markets=markets,
|
443
|
+
custom_holidays=custom_holidays,
|
363
444
|
)
|
364
445
|
return [
|
365
446
|
d.date()
|
@@ -381,6 +462,8 @@ def _do_resample_to_business_period_ends(
|
|
381
462
|
data: DataFrame,
|
382
463
|
freq: LiteralBizDayFreq,
|
383
464
|
countries: CountriesType,
|
465
|
+
markets: list[str] | str | None = None,
|
466
|
+
custom_holidays: list[str] | str | None = None,
|
384
467
|
) -> DatetimeIndex:
|
385
468
|
"""Resample timeseries frequency to business calendar month end dates.
|
386
469
|
|
@@ -394,7 +477,10 @@ def _do_resample_to_business_period_ends(
|
|
394
477
|
The date offset string that sets the resampled frequency
|
395
478
|
countries: CountriesType
|
396
479
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
397
|
-
|
480
|
+
markets: list[str] | str, optional
|
481
|
+
(List of) markets code(s) according to pandas-market-calendars
|
482
|
+
custom_holidays: list[str] | str, optional
|
483
|
+
Argument where missing holidays can be added
|
398
484
|
|
399
485
|
Returns:
|
400
486
|
-------
|
@@ -413,12 +499,14 @@ def _do_resample_to_business_period_ends(
|
|
413
499
|
dates = DatetimeIndex(
|
414
500
|
[copydata.index[0]]
|
415
501
|
+ [
|
416
|
-
date_offset_foll(
|
417
|
-
dt.date(d.year, d.month, 1)
|
502
|
+
date_offset_foll( # type: ignore[misc]
|
503
|
+
raw_date=dt.date(d.year, d.month, 1)
|
418
504
|
+ relativedelta(months=1)
|
419
505
|
- dt.timedelta(days=1),
|
420
|
-
countries=countries,
|
421
506
|
months_offset=0,
|
507
|
+
countries=countries,
|
508
|
+
markets=markets,
|
509
|
+
custom_holidays=custom_holidays,
|
422
510
|
adjust=True,
|
423
511
|
following=False,
|
424
512
|
)
|
openseries/frame.py
CHANGED
@@ -41,7 +41,6 @@ from pydantic import field_validator
|
|
41
41
|
from ._common_model import _CommonModel
|
42
42
|
from .datefixer import _do_resample_to_business_period_ends
|
43
43
|
from .owntypes import (
|
44
|
-
CountriesType,
|
45
44
|
DaysInYearType,
|
46
45
|
LabelsNotUniqueError,
|
47
46
|
LiteralBizDayFreq,
|
@@ -429,7 +428,6 @@ class OpenFrame(_CommonModel):
|
|
429
428
|
def resample_to_business_period_ends(
|
430
429
|
self: Self,
|
431
430
|
freq: LiteralBizDayFreq = "BME",
|
432
|
-
countries: CountriesType = "SE",
|
433
431
|
method: LiteralPandasReindexMethod = "nearest",
|
434
432
|
) -> Self:
|
435
433
|
"""Resamples timeseries frequency to the business calendar month end dates.
|
@@ -440,9 +438,6 @@ class OpenFrame(_CommonModel):
|
|
440
438
|
----------
|
441
439
|
freq: LiteralBizDayFreq, default "BME"
|
442
440
|
The date offset string that sets the resampled frequency
|
443
|
-
countries: CountriesType, default: "SE"
|
444
|
-
(List of) country code(s) according to ISO 3166-1 alpha-2
|
445
|
-
to create a business day calendar used for date adjustments
|
446
441
|
method: LiteralPandasReindexMethod, default: nearest
|
447
442
|
Controls the method used to align values across columns
|
448
443
|
|
@@ -456,7 +451,8 @@ class OpenFrame(_CommonModel):
|
|
456
451
|
dates = _do_resample_to_business_period_ends(
|
457
452
|
data=xerie.tsdf,
|
458
453
|
freq=freq,
|
459
|
-
countries=countries,
|
454
|
+
countries=xerie.countries,
|
455
|
+
markets=xerie.markets,
|
460
456
|
)
|
461
457
|
xerie.tsdf = xerie.tsdf.reindex(
|
462
458
|
[deyt.date() for deyt in dates],
|
openseries/owntypes.py
CHANGED
@@ -86,16 +86,6 @@ DaysInYearType = Annotated[int, Field(strict=True, ge=1, le=366)]
|
|
86
86
|
|
87
87
|
DateType = str | dt.date | dt.datetime | datetime64 | Timestamp
|
88
88
|
|
89
|
-
HolidayType = (
|
90
|
-
dict[dt.date | dt.datetime | str | float | int, str]
|
91
|
-
| list[dt.date | dt.datetime | str | float | int]
|
92
|
-
| dt.date
|
93
|
-
| dt.datetime
|
94
|
-
| str
|
95
|
-
| float
|
96
|
-
| int
|
97
|
-
)
|
98
|
-
|
99
89
|
PlotlyLayoutType = dict[
|
100
90
|
str,
|
101
91
|
str
|
@@ -356,6 +346,10 @@ class CountriesNotStringNorListStrError(Exception):
|
|
356
346
|
"""Raised when countries argument is not provided in correct format."""
|
357
347
|
|
358
348
|
|
349
|
+
class MarketsNotStringNorListStrError(Exception):
|
350
|
+
"""Raised when markets argument is not provided in correct format."""
|
351
|
+
|
352
|
+
|
359
353
|
class TradingDaysNotAboveZeroError(Exception):
|
360
354
|
"""Raised when trading days argument is not above zero."""
|
361
355
|
|
openseries/series.py
CHANGED
@@ -45,6 +45,7 @@ from .owntypes import (
|
|
45
45
|
LiteralBizDayFreq,
|
46
46
|
LiteralPandasReindexMethod,
|
47
47
|
LiteralSeriesProps,
|
48
|
+
MarketsNotStringNorListStrError,
|
48
49
|
OpenTimeSeriesPropertiesList,
|
49
50
|
Self,
|
50
51
|
ValueListType,
|
@@ -91,6 +92,8 @@ class OpenTimeSeries(_CommonModel):
|
|
91
92
|
ISO 4217 currency code of the user's home currency
|
92
93
|
countries: CountriesType, default: "SE"
|
93
94
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
95
|
+
markets: list[str] | str, optional
|
96
|
+
(List of) markets code(s) according to pandas-market-calendars
|
94
97
|
isin : str, optional
|
95
98
|
ISO 6166 identifier code of the associated instrument
|
96
99
|
label : str, optional
|
@@ -109,6 +112,7 @@ class OpenTimeSeries(_CommonModel):
|
|
109
112
|
currency: CurrencyStringType
|
110
113
|
domestic: CurrencyStringType = "SEK"
|
111
114
|
countries: CountriesType = "SE"
|
115
|
+
markets: list[str] | str | None = None # type: ignore[assignment]
|
112
116
|
isin: str | None = None
|
113
117
|
label: str | None = None
|
114
118
|
|
@@ -126,6 +130,25 @@ class OpenTimeSeries(_CommonModel):
|
|
126
130
|
_ = Countries(countryinput=value)
|
127
131
|
return value
|
128
132
|
|
133
|
+
@field_validator("markets", mode="before") # type: ignore[misc]
|
134
|
+
@classmethod
|
135
|
+
def _validate_markets(
|
136
|
+
cls, value: list[str] | str | None
|
137
|
+
) -> list[str] | str | None:
|
138
|
+
"""Pydantic validator to ensure markets field is validated."""
|
139
|
+
msg = (
|
140
|
+
"'markets' must be a string or list of strings, "
|
141
|
+
f"got {type(value).__name__!r}"
|
142
|
+
)
|
143
|
+
if value is None or isinstance(value, str):
|
144
|
+
return value
|
145
|
+
if isinstance(value, list):
|
146
|
+
if all(isinstance(item, str) for item in value) and len(value) != 0:
|
147
|
+
return value
|
148
|
+
item_msg = "All items in 'markets' must be strings."
|
149
|
+
raise MarketsNotStringNorListStrError(item_msg)
|
150
|
+
raise MarketsNotStringNorListStrError(msg)
|
151
|
+
|
129
152
|
@model_validator(mode="after") # type: ignore[misc,unused-ignore]
|
130
153
|
def _dates_and_values_validate(self: Self) -> Self:
|
131
154
|
"""Pydantic validator to ensure dates and values are validated."""
|
@@ -587,6 +610,7 @@ class OpenTimeSeries(_CommonModel):
|
|
587
610
|
data=self.tsdf,
|
588
611
|
freq=freq,
|
589
612
|
countries=self.countries,
|
613
|
+
markets=self.markets,
|
590
614
|
)
|
591
615
|
self.tsdf = self.tsdf.reindex([deyt.date() for deyt in dates], method=method)
|
592
616
|
return self
|
openseries/simulation.py
CHANGED
@@ -417,6 +417,7 @@ class ReturnSimulation(BaseModel): # type: ignore[misc]
|
|
417
417
|
start: dt.date | None = None,
|
418
418
|
end: dt.date | None = None,
|
419
419
|
countries: CountriesType = "SE",
|
420
|
+
markets: list[str] | str | None = None,
|
420
421
|
) -> DataFrame:
|
421
422
|
"""Create a pandas.DataFrame from simulation(s).
|
422
423
|
|
@@ -430,6 +431,8 @@ class ReturnSimulation(BaseModel): # type: ignore[misc]
|
|
430
431
|
Date when the simulation ends
|
431
432
|
countries: CountriesType, default: "SE"
|
432
433
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
434
|
+
markets: list[str] | str, optional
|
435
|
+
(List of) markets code(s) according to pandas-market-calendars
|
433
436
|
|
434
437
|
Returns:
|
435
438
|
-------
|
@@ -442,6 +445,7 @@ class ReturnSimulation(BaseModel): # type: ignore[misc]
|
|
442
445
|
start=start,
|
443
446
|
end=end,
|
444
447
|
countries=countries,
|
448
|
+
markets=markets,
|
445
449
|
)
|
446
450
|
|
447
451
|
if self.number_of_sims == 1:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: openseries
|
3
|
-
Version: 1.8.
|
3
|
+
Version: 1.8.4
|
4
4
|
Summary: Tools for analyzing financial timeseries.
|
5
5
|
License: # BSD 3-Clause License
|
6
6
|
|
@@ -50,6 +50,7 @@ Requires-Dist: holidays (>=0.30,<1.0)
|
|
50
50
|
Requires-Dist: numpy (>=1.23.2,<3.0.0)
|
51
51
|
Requires-Dist: openpyxl (>=3.1.2,<5.0.0)
|
52
52
|
Requires-Dist: pandas (>=2.1.2,<3.0.0)
|
53
|
+
Requires-Dist: pandas-market-calendars (>=5.1.0,<7.0.0)
|
53
54
|
Requires-Dist: plotly (>=5.18.0,<7.0.0)
|
54
55
|
Requires-Dist: pyarrow (>=14.0.2,<21.0.0)
|
55
56
|
Requires-Dist: pydantic (>=2.5.2,<3.0.0)
|
@@ -83,7 +84,6 @@ Description-Content-Type: text/markdown
|
|
83
84
|
This is a project with tools to analyze financial timeseries of a single
|
84
85
|
asset or a group of assets. It is solely made for daily or less frequent data.
|
85
86
|
|
86
|
-
|
87
87
|
## Basic Usage
|
88
88
|
|
89
89
|
To install:
|
@@ -119,6 +119,7 @@ _,_=series.plot_series()
|
|
119
119
|
```
|
120
120
|
|
121
121
|
### Sample output using the OpenFrame.all_properties() method:
|
122
|
+
|
122
123
|
```
|
123
124
|
Scilla Global Equity C (simulation+fund) Global Low Volatility index, SEK
|
124
125
|
ValueType.PRICE ValueType.PRICE
|
@@ -156,7 +157,6 @@ The OpenTimeSeries and OpenFrame classes are both subclasses of
|
|
156
157
|
the [Pydantic BaseModel](https://docs.pydantic.dev/usage/models/). Please refer to its documentation for information
|
157
158
|
on any attributes or methods inherited from this model.
|
158
159
|
|
159
|
-
|
160
160
|
### Windows Powershell
|
161
161
|
|
162
162
|
```powershell
|
@@ -202,7 +202,6 @@ make lint
|
|
202
202
|
|
203
203
|
```
|
204
204
|
|
205
|
-
|
206
205
|
## Table of Contents
|
207
206
|
|
208
207
|
- [Basic Usage](#basic-usage)
|
@@ -239,34 +238,35 @@ make lint
|
|
239
238
|
|
240
239
|
### Non-numerical or "helper" properties that apply only to the [OpenTimeSeries](https://github.com/CaptorAB/openseries/blob/master/openseries/series.py) class.
|
241
240
|
|
242
|
-
| Property | type
|
243
|
-
|
244
|
-
| `timeseries_id` | `str`
|
245
|
-
| `instrument_id` | `str`
|
246
|
-
| `dates` | `list[str]`
|
247
|
-
| `values` | `list[float]`
|
248
|
-
| `currency` | `str`
|
249
|
-
| `domestic` | `str`
|
250
|
-
| `local_ccy` | `bool`
|
251
|
-
| `name` | `str`
|
252
|
-
| `isin` | `str`
|
253
|
-
| `label` | `str`
|
254
|
-
| `countries` | `list` or `str` | `OpenTimeSeries` | (List of) country code(s) according to ISO 3166-1 alpha-2 used to generate business days.
|
255
|
-
| `
|
241
|
+
| Property | type | Applies to | Description |
|
242
|
+
|:----------------|:---------------------|:-----------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
243
|
+
| `timeseries_id` | `str` | `OpenTimeSeries` | Placeholder for database identifier for the timeseries. Can be left as empty string. |
|
244
|
+
| `instrument_id` | `str` | `OpenTimeSeries` | Placeholder for database identifier for the instrument associated with the timeseries. Can be left as empty string. |
|
245
|
+
| `dates` | `list[str]` | `OpenTimeSeries` | Dates of the timeseries. Not edited by any method to allow reversion to original. |
|
246
|
+
| `values` | `list[float]` | `OpenTimeSeries` | Values of the timeseries. Not edited by any method to allow reversion to original. |
|
247
|
+
| `currency` | `str` | `OpenTimeSeries` | Currency of the timeseries. Only used if conversion/hedging methods are added. |
|
248
|
+
| `domestic` | `str` | `OpenTimeSeries` | Domestic currency of the user / investor. Only used if conversion/hedging methods are added. |
|
249
|
+
| `local_ccy` | `bool` | `OpenTimeSeries` | Indicates if series should be in its local currency or the domestic currency of the user. Only used if conversion/hedging methods are added. |
|
250
|
+
| `name` | `str` | `OpenTimeSeries` | An identifier field. |
|
251
|
+
| `isin` | `str` | `OpenTimeSeries` | ISIN code of the associated instrument. If any. |
|
252
|
+
| `label` | `str` | `OpenTimeSeries` | Field used in outputs. Derived from name as default. |
|
253
|
+
| `countries` | `list[str]` or `str` | `OpenTimeSeries` | (List of) country code(s) according to ISO 3166-1 alpha-2 used in the [holidays](https://github.com/vacanza/holidays/) package to generate business days. |
|
254
|
+
| `markets` | `list[str]` or `str` | `OpenTimeSeries` | (List of) markets code(s) according to market code(s) input for the [pandas-market-calendars](https://pandas-market-calendars.readthedocs.io/en/latest/) package. |
|
255
|
+
| `valuetype` | `ValueType` | `OpenTimeSeries` | Field identifies the type of values in the series. ValueType is an Enum. |
|
256
256
|
|
257
257
|
### Non-numerical or "helper" properties that apply only to the [OpenFrame](https://github.com/CaptorAB/openseries/blob/master/openseries/frame.py) class.
|
258
258
|
|
259
|
-
| Property | type
|
260
|
-
|
261
|
-
| `constituents` | `list[OpenTimeSeries]`
|
262
|
-
| `columns_lvl_zero` | `list`
|
263
|
-
| `columns_lvl_one` | `list`
|
264
|
-
| `item_count` | `int`
|
265
|
-
| `weights` | `list[float]`
|
266
|
-
| `first_indices` | `pandas.Series`
|
267
|
-
| `last_indices` | `pandas.Series`
|
268
|
-
| `lengths_of_items` | `pandas.Series`
|
269
|
-
| `span_of_days_all` | `pandas.Series`
|
259
|
+
| Property | type | Applies to | Description |
|
260
|
+
|:-------------------|:---------------------------------|:------------|:-------------------------------------------------------------------------|
|
261
|
+
| `constituents` | `list[OpenTimeSeries]` | `OpenFrame` | A list of the OpenTimeSeries that make up an OpenFrame. |
|
262
|
+
| `columns_lvl_zero` | `list[str]` | `OpenFrame` | A list of the level zero column names in the OpenFrame pandas.DataFrame. |
|
263
|
+
| `columns_lvl_one` | `list[ValueType]` or `list[str]` | `OpenFrame` | A list of the level one column names in the OpenFrame pandas.DataFrame. |
|
264
|
+
| `item_count` | `int` | `OpenFrame` | Number of columns in the OpenFrame pandas.DataFrame. |
|
265
|
+
| `weights` | `list[float]` | `OpenFrame` | Weights used in the method `make_portfolio`. |
|
266
|
+
| `first_indices` | `pandas.Series[dt.date]` | `OpenFrame` | First dates of all the series in the OpenFrame. |
|
267
|
+
| `last_indices` | `pandas.Series[dt.date]` | `OpenFrame` | Last dates of all the series in the OpenFrame. |
|
268
|
+
| `lengths_of_items` | `pandas.Series[int]` | `OpenFrame` | Number of items in each of the series in the OpenFrame. |
|
269
|
+
| `span_of_days_all` | `pandas.Series[int]` | `OpenFrame` | Number of days from the first to the last in each of the series. |
|
270
270
|
|
271
271
|
### Non-numerical or "helper" properties that apply to both the [OpenTimeSeries](https://github.com/CaptorAB/openseries/blob/master/openseries/series.py) and the [OpenFrame](https://github.com/CaptorAB/openseries/blob/master/openseries/frame.py) class.
|
272
272
|
|
@@ -290,7 +290,7 @@ make lint
|
|
290
290
|
| `running_adjustment` | `OpenTimeSeries` | Adjusts the series performance with a `float` factor. |
|
291
291
|
| `ewma_vol_func` | `OpenTimeSeries` | Returns a `pandas.Series` with volatility based on [Exponentially Weighted Moving Average](https://www.investopedia.com/articles/07/ewma.asp). |
|
292
292
|
| `from_1d_rate_to_cumret` | `OpenTimeSeries` | Converts a series of 1-day rates into a cumulative valueseries. |
|
293
|
-
|
293
|
+
|
|
294
294
|
|
295
295
|
### Methods that apply only to the [OpenFrame](https://github.com/CaptorAB/openseries/blob/master/openseries/frame.py) class.
|
296
296
|
|
@@ -328,11 +328,11 @@ make lint
|
|
328
328
|
| `to_xlsx` | `OpenTimeSeries`, `OpenFrame` | Method to save the data in the .tsdf DataFrame to an Excel file. |
|
329
329
|
| `value_to_ret` | `OpenTimeSeries`, `OpenFrame` | Converts a value series into a percentage return series. |
|
330
330
|
| `value_to_diff` | `OpenTimeSeries`, `OpenFrame` | Converts a value series into a series of differences. |
|
331
|
-
| `value_to_log` | `OpenTimeSeries`, `OpenFrame` | Converts a value series into a
|
331
|
+
| `value_to_log` | `OpenTimeSeries`, `OpenFrame` | Converts a value series into a cumulative log return series, useful for plotting growth relative to the starting point. |
|
332
332
|
| `value_ret_calendar_period` | `OpenTimeSeries`, `OpenFrame` | Returns the series simple return for a specific calendar period. |
|
333
|
-
| `plot_series` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Scatter](https://plotly.com/python/line-and-scatter/) plot of the serie(s) in a browser window.
|
334
|
-
| `plot_bars` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Bar](https://plotly.com/python/bar-charts/) plot of the serie(s) in a browser window.
|
335
|
-
| `plot_histogram` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Histogram](https://plotly.com/python/histograms/) plot of the serie(s) in a browser window.
|
333
|
+
| `plot_series` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Scatter](https://plotly.com/python/line-and-scatter/) plot of the serie(s) in a browser window. |
|
334
|
+
| `plot_bars` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Bar](https://plotly.com/python/bar-charts/) plot of the serie(s) in a browser window. |
|
335
|
+
| `plot_histogram` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Histogram](https://plotly.com/python/histograms/) plot of the serie(s) in a browser window. |
|
336
336
|
| `to_drawdown_series` | `OpenTimeSeries`, `OpenFrame` | Converts the series into drawdown series. |
|
337
337
|
| `rolling_return` | `OpenTimeSeries`, `OpenFrame` | Returns a pandas.DataFrame with rolling returns. |
|
338
338
|
| `rolling_vol` | `OpenTimeSeries`, `OpenFrame` | Returns a pandas.DataFrame with rolling volatilities. |
|
@@ -0,0 +1,16 @@
|
|
1
|
+
openseries/__init__.py,sha256=dKw_wEfgrCwwV1IRljesrtxjE9AVFwTyhE8k4CFIck8,1053
|
2
|
+
openseries/_common_model.py,sha256=oW-jM8CCAS4E7R-E7hPHDUkMJ5i2sc1DRGxvRB498SA,82789
|
3
|
+
openseries/_risk.py,sha256=lZzoP5yjq9vHtKhYe7kU3-iG8rADcu00bkT9kIgsi_E,2086
|
4
|
+
openseries/datefixer.py,sha256=7ugMIEKf5lhifd1FlC_aHlHt_Fwvof3aRSLaHM2fvR8,15525
|
5
|
+
openseries/frame.py,sha256=H3gAIKTDy5aStKTjBtudsJX15knH0t-8NfdD26JVu3Y,55654
|
6
|
+
openseries/load_plotly.py,sha256=VGDdS8ojPQK7AQr-dGi9IfShi5O0EfjM8kUQrJhG_Zw,2000
|
7
|
+
openseries/owntypes.py,sha256=j5KUbHqJTKyZPaQOurojtf7RoPgk5TC8A1KEtMO6EN0,9341
|
8
|
+
openseries/plotly_captor_logo.json,sha256=F5nhMzEyxKywtjvQqMTKgKRCJQYMDIiBgDSxdte8Clo,178
|
9
|
+
openseries/plotly_layouts.json,sha256=9tKAeittrjwJWhBMV8SnCDAWdhgbVnUqXcN6P_J_bos,1433
|
10
|
+
openseries/portfoliotools.py,sha256=6bgz64-B6qJVrHAE-pLp8JJCmJkO_JAExHL5G3AwPWE,19295
|
11
|
+
openseries/series.py,sha256=xYU4c1MA02AaFko5q72JoVDEKs60zDmxkfG4P_uCpOY,28265
|
12
|
+
openseries/simulation.py,sha256=LhKfA32uskMNr90V9r1_uYKC7FZgWRcdLzw2--D1qUM,14131
|
13
|
+
openseries-1.8.4.dist-info/LICENSE.md,sha256=wNupG-KLsG0aTncb_SMNDh1ExtrKXlpxSJ6RC-g-SWs,1516
|
14
|
+
openseries-1.8.4.dist-info/METADATA,sha256=D6rlANyAmZ72EonbQTMyjE10ctd1MuJqQ3nVXbqNO3A,46550
|
15
|
+
openseries-1.8.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
16
|
+
openseries-1.8.4.dist-info/RECORD,,
|
@@ -1,16 +0,0 @@
|
|
1
|
-
openseries/__init__.py,sha256=dKw_wEfgrCwwV1IRljesrtxjE9AVFwTyhE8k4CFIck8,1053
|
2
|
-
openseries/_common_model.py,sha256=eaa981RlF5fUt9t53P2Uk2_aTL1wCG7hd160Wz3cKnI,81385
|
3
|
-
openseries/_risk.py,sha256=lZzoP5yjq9vHtKhYe7kU3-iG8rADcu00bkT9kIgsi_E,2086
|
4
|
-
openseries/datefixer.py,sha256=5S4Ib9CRHfsVPKsDKcIW7x8G86zxkMJz2jZ_Sig2Asw,12535
|
5
|
-
openseries/frame.py,sha256=lqRwNNs82TCbl1rjVpy48VpndmIgRztIBNitjaHy2Mo,55859
|
6
|
-
openseries/load_plotly.py,sha256=VGDdS8ojPQK7AQr-dGi9IfShi5O0EfjM8kUQrJhG_Zw,2000
|
7
|
-
openseries/owntypes.py,sha256=jVPhPTYhr_lLZd8RYTzWFmhN8Gmmj09TzvIPyDu7vbc,9409
|
8
|
-
openseries/plotly_captor_logo.json,sha256=F5nhMzEyxKywtjvQqMTKgKRCJQYMDIiBgDSxdte8Clo,178
|
9
|
-
openseries/plotly_layouts.json,sha256=9tKAeittrjwJWhBMV8SnCDAWdhgbVnUqXcN6P_J_bos,1433
|
10
|
-
openseries/portfoliotools.py,sha256=6bgz64-B6qJVrHAE-pLp8JJCmJkO_JAExHL5G3AwPWE,19295
|
11
|
-
openseries/series.py,sha256=7le45RBcBqF9exDzfXfWbY8iiqgl70UUHNOWORvHAuQ,27221
|
12
|
-
openseries/simulation.py,sha256=WHmPU2sNl03YvaL7-Ots7i0I_ZZ9j0hBSCnDYWSypK0,13936
|
13
|
-
openseries-1.8.3.dist-info/LICENSE.md,sha256=wNupG-KLsG0aTncb_SMNDh1ExtrKXlpxSJ6RC-g-SWs,1516
|
14
|
-
openseries-1.8.3.dist-info/METADATA,sha256=MkJAQEjImJUKh8UZ9w3e1_tyzh-kyGHdXmUGiOjZKVU,45877
|
15
|
-
openseries-1.8.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
16
|
-
openseries-1.8.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|