openseries 1.5.6__tar.gz → 1.6.0__tar.gz
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-1.5.6 → openseries-1.6.0}/PKG-INFO +3 -3
- openseries-1.6.0/openseries/__init__.py +44 -0
- {openseries-1.5.6 → openseries-1.6.0}/openseries/_common_model.py +6 -5
- {openseries-1.5.6 → openseries-1.6.0}/openseries/_risk.py +0 -61
- {openseries-1.5.6 → openseries-1.6.0}/openseries/datefixer.py +21 -10
- {openseries-1.5.6 → openseries-1.6.0}/openseries/frame.py +21 -589
- {openseries-1.5.6 → openseries-1.6.0}/openseries/load_plotly.py +2 -0
- openseries-1.6.0/openseries/portfoliotools.py +613 -0
- {openseries-1.5.6 → openseries-1.6.0}/openseries/series.py +6 -9
- {openseries-1.5.6 → openseries-1.6.0}/openseries/simulation.py +7 -5
- {openseries-1.5.6 → openseries-1.6.0}/openseries/types.py +20 -0
- {openseries-1.5.6 → openseries-1.6.0}/pyproject.toml +17 -11
- openseries-1.5.6/openseries/__init__.py +0 -1
- {openseries-1.5.6 → openseries-1.6.0}/LICENSE.md +0 -0
- {openseries-1.5.6 → openseries-1.6.0}/README.md +0 -0
- {openseries-1.5.6 → openseries-1.6.0}/openseries/plotly_captor_logo.json +0 -0
- {openseries-1.5.6 → openseries-1.6.0}/openseries/plotly_layouts.json +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: openseries
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.6.0
|
4
4
|
Summary: Tools for analyzing financial timeseries.
|
5
5
|
Home-page: https://github.com/CaptorAB/openseries
|
6
6
|
License: BSD-3-Clause
|
@@ -20,8 +20,8 @@ Classifier: Programming Language :: Python :: 3.10
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
22
22
|
Classifier: Topic :: Office/Business :: Financial :: Investment
|
23
|
-
Requires-Dist: holidays (>=0.30,<0
|
24
|
-
Requires-Dist: numpy (>=1.23.2,<=
|
23
|
+
Requires-Dist: holidays (>=0.30,<1.0)
|
24
|
+
Requires-Dist: numpy (>=1.23.2,<=3.0.0)
|
25
25
|
Requires-Dist: openpyxl (>=3.1.2,<4.0.0)
|
26
26
|
Requires-Dist: pandas (>=2.1.2,<3.0.0)
|
27
27
|
Requires-Dist: plotly (>=5.18.0,<6.0.0)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
"""openseries.openseries.__init__.py."""
|
2
|
+
|
3
|
+
from .datefixer import (
|
4
|
+
date_fix,
|
5
|
+
date_offset_foll,
|
6
|
+
do_resample_to_business_period_ends,
|
7
|
+
generate_calendar_date_range,
|
8
|
+
get_previous_business_day_before_today,
|
9
|
+
holiday_calendar,
|
10
|
+
offset_business_days,
|
11
|
+
)
|
12
|
+
from .frame import OpenFrame
|
13
|
+
from .load_plotly import load_plotly_dict
|
14
|
+
from .portfoliotools import (
|
15
|
+
constrain_optimized_portfolios,
|
16
|
+
efficient_frontier,
|
17
|
+
prepare_plot_data,
|
18
|
+
sharpeplot,
|
19
|
+
simulate_portfolios,
|
20
|
+
)
|
21
|
+
from .series import OpenTimeSeries, timeseries_chain
|
22
|
+
from .simulation import ReturnSimulation
|
23
|
+
from .types import ValueType
|
24
|
+
|
25
|
+
__all__ = [
|
26
|
+
"OpenFrame",
|
27
|
+
"constrain_optimized_portfolios",
|
28
|
+
"efficient_frontier",
|
29
|
+
"prepare_plot_data",
|
30
|
+
"sharpeplot",
|
31
|
+
"simulate_portfolios",
|
32
|
+
"date_fix",
|
33
|
+
"date_offset_foll",
|
34
|
+
"do_resample_to_business_period_ends",
|
35
|
+
"generate_calendar_date_range",
|
36
|
+
"get_previous_business_day_before_today",
|
37
|
+
"holiday_calendar",
|
38
|
+
"offset_business_days",
|
39
|
+
"load_plotly_dict",
|
40
|
+
"OpenTimeSeries",
|
41
|
+
"timeseries_chain",
|
42
|
+
"ReturnSimulation",
|
43
|
+
"ValueType",
|
44
|
+
]
|
@@ -9,9 +9,10 @@ from math import ceil
|
|
9
9
|
from pathlib import Path
|
10
10
|
from secrets import choice
|
11
11
|
from string import ascii_letters
|
12
|
-
from typing import Any, Optional, Union, cast
|
12
|
+
from typing import Any, Optional, SupportsFloat, Union, cast
|
13
13
|
|
14
14
|
from numpy import float64, inf, isnan, log, maximum, sqrt
|
15
|
+
from numpy.typing import NDArray
|
15
16
|
from openpyxl.utils.dataframe import dataframe_to_rows
|
16
17
|
from openpyxl.workbook.workbook import Workbook
|
17
18
|
from openpyxl.worksheet.worksheet import Worksheet
|
@@ -1132,7 +1133,7 @@ class _CommonModel(BaseModel):
|
|
1132
1133
|
)
|
1133
1134
|
|
1134
1135
|
if self.tsdf.shape[1] == 1:
|
1135
|
-
return float(result.iloc[0])
|
1136
|
+
return float(cast(SupportsFloat, result.iloc[0]))
|
1136
1137
|
return Series(
|
1137
1138
|
data=result,
|
1138
1139
|
index=self.tsdf.columns,
|
@@ -1361,7 +1362,7 @@ class _CommonModel(BaseModel):
|
|
1361
1362
|
label = f"Imp vol from VaR {level:.0%}"
|
1362
1363
|
|
1363
1364
|
if self.tsdf.shape[1] == 1:
|
1364
|
-
return float(result.iloc[0])
|
1365
|
+
return float(cast(SupportsFloat, result.iloc[0]))
|
1365
1366
|
return Series(
|
1366
1367
|
data=result,
|
1367
1368
|
index=self.tsdf.columns,
|
@@ -1593,7 +1594,7 @@ class _CommonModel(BaseModel):
|
|
1593
1594
|
from_dt=from_date,
|
1594
1595
|
to_dt=to_date,
|
1595
1596
|
)
|
1596
|
-
result = skew(
|
1597
|
+
result: NDArray[float64] = skew(
|
1597
1598
|
a=self.tsdf.loc[cast(int, earlier) : cast(int, later)]
|
1598
1599
|
.pct_change(fill_method=cast(str, None))
|
1599
1600
|
.to_numpy(),
|
@@ -1642,7 +1643,7 @@ class _CommonModel(BaseModel):
|
|
1642
1643
|
from_dt=from_date,
|
1643
1644
|
to_dt=to_date,
|
1644
1645
|
)
|
1645
|
-
result = kurtosis(
|
1646
|
+
result: NDArray[float64] = kurtosis(
|
1646
1647
|
self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change(
|
1647
1648
|
fill_method=cast(str, None),
|
1648
1649
|
),
|
@@ -6,19 +6,11 @@ from math import ceil
|
|
6
6
|
from typing import Union, cast
|
7
7
|
|
8
8
|
from numpy import (
|
9
|
-
divide,
|
10
|
-
float64,
|
11
|
-
isinf,
|
12
9
|
mean,
|
13
|
-
nan,
|
14
10
|
nan_to_num,
|
15
11
|
quantile,
|
16
12
|
sort,
|
17
|
-
sqrt,
|
18
|
-
square,
|
19
|
-
std,
|
20
13
|
)
|
21
|
-
from numpy.typing import NDArray
|
22
14
|
from pandas import DataFrame, Series
|
23
15
|
|
24
16
|
from openseries.types import LiteralQuantileInterp
|
@@ -87,56 +79,3 @@ def _var_down_calc(
|
|
87
79
|
clean = nan_to_num(data)
|
88
80
|
ret = clean[1:] / clean[:-1] - 1
|
89
81
|
return cast(float, quantile(ret, 1 - level, method=interpolation))
|
90
|
-
|
91
|
-
|
92
|
-
def _ewma_calc(
|
93
|
-
reeturn: float,
|
94
|
-
prev_ewma: float,
|
95
|
-
time_factor: float,
|
96
|
-
lmbda: float = 0.94,
|
97
|
-
) -> float:
|
98
|
-
"""
|
99
|
-
Calculate Exponentially Weighted Moving Average volatility.
|
100
|
-
|
101
|
-
Parameters
|
102
|
-
----------
|
103
|
-
reeturn : float
|
104
|
-
Return value
|
105
|
-
prev_ewma : float
|
106
|
-
Previous EWMA volatility value
|
107
|
-
time_factor : float
|
108
|
-
Scaling factor to annualize
|
109
|
-
lmbda: float, default: 0.94
|
110
|
-
Scaling factor to determine weighting.
|
111
|
-
|
112
|
-
Returns
|
113
|
-
-------
|
114
|
-
float
|
115
|
-
EWMA volatility value
|
116
|
-
|
117
|
-
"""
|
118
|
-
return cast(
|
119
|
-
float,
|
120
|
-
sqrt(square(reeturn) * time_factor * (1 - lmbda) + square(prev_ewma) * lmbda),
|
121
|
-
)
|
122
|
-
|
123
|
-
|
124
|
-
def _calc_inv_vol_weights(returns: DataFrame) -> NDArray[float64]:
|
125
|
-
"""
|
126
|
-
Calculate weights proportional to inverse volatility.
|
127
|
-
|
128
|
-
Parameters
|
129
|
-
----------
|
130
|
-
returns: pandas.DataFrame
|
131
|
-
returns data
|
132
|
-
|
133
|
-
Returns
|
134
|
-
-------
|
135
|
-
NDArray[float64]
|
136
|
-
Calculated weights
|
137
|
-
|
138
|
-
"""
|
139
|
-
vol = divide(1.0, std(returns, axis=0, ddof=1))
|
140
|
-
vol[isinf(vol)] = nan
|
141
|
-
volsum = vol.sum()
|
142
|
-
return cast(NDArray[float64], divide(vol, volsum))
|
@@ -30,6 +30,16 @@ from openseries.types import (
|
|
30
30
|
LiteralBizDayFreq,
|
31
31
|
)
|
32
32
|
|
33
|
+
__all__ = [
|
34
|
+
"date_fix",
|
35
|
+
"date_offset_foll",
|
36
|
+
"do_resample_to_business_period_ends",
|
37
|
+
"generate_calendar_date_range",
|
38
|
+
"get_previous_business_day_before_today",
|
39
|
+
"holiday_calendar",
|
40
|
+
"offset_business_days",
|
41
|
+
]
|
42
|
+
|
33
43
|
|
34
44
|
def holiday_calendar(
|
35
45
|
startyear: int,
|
@@ -62,7 +72,7 @@ def holiday_calendar(
|
|
62
72
|
endyear += 1
|
63
73
|
if startyear == endyear:
|
64
74
|
endyear += 1
|
65
|
-
years = list(range(
|
75
|
+
years = list(range(startyear, endyear))
|
66
76
|
|
67
77
|
if isinstance(countries, str) and countries in list_supported_countries():
|
68
78
|
staging = country_holidays(country=countries, years=years)
|
@@ -116,15 +126,11 @@ def date_fix(
|
|
116
126
|
if isinstance(fixerdate, datetime64):
|
117
127
|
return (
|
118
128
|
dt.datetime.strptime(str(fixerdate)[:10], "%Y-%m-%d")
|
119
|
-
.
|
129
|
+
.astimezone()
|
120
130
|
.date()
|
121
131
|
)
|
122
132
|
if isinstance(fixerdate, str):
|
123
|
-
return (
|
124
|
-
dt.datetime.strptime(fixerdate, "%Y-%m-%d")
|
125
|
-
.replace(tzinfo=dt.timezone.utc)
|
126
|
-
.date()
|
127
|
-
)
|
133
|
+
return dt.datetime.strptime(fixerdate, "%Y-%m-%d").astimezone().date()
|
128
134
|
msg = f"Unknown date format {fixerdate!s} of type {type(fixerdate)!s} encountered"
|
129
135
|
raise TypeError(
|
130
136
|
msg,
|
@@ -212,7 +218,7 @@ def get_previous_business_day_before_today(
|
|
212
218
|
|
213
219
|
"""
|
214
220
|
if today is None:
|
215
|
-
today = dt.datetime.now(
|
221
|
+
today = dt.datetime.now().astimezone().date()
|
216
222
|
|
217
223
|
return date_offset_foll(
|
218
224
|
today - dt.timedelta(days=1),
|
@@ -241,8 +247,8 @@ def offset_business_days(
|
|
241
247
|
ddate: datetime.date
|
242
248
|
A starting date that does not have to be a business day
|
243
249
|
days: int
|
244
|
-
The number of business days to offset from the business day that is
|
245
|
-
|
250
|
+
The number of business days to offset from the business day that is given
|
251
|
+
If days is set as anything other than an integer its value is set to zero
|
246
252
|
countries: CountriesType, default: "SE"
|
247
253
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
248
254
|
custom_holidays: HolidayType, optional
|
@@ -255,6 +261,11 @@ def offset_business_days(
|
|
255
261
|
The new offset business day
|
256
262
|
|
257
263
|
"""
|
264
|
+
try:
|
265
|
+
days = int(days)
|
266
|
+
except TypeError:
|
267
|
+
days = 0
|
268
|
+
|
258
269
|
if days <= 0:
|
259
270
|
scaledtoyeardays = int((days * 372 / 250) // 1) - 365
|
260
271
|
ndate = ddate + dt.timedelta(days=scaledtoyeardays)
|