openseries 1.6.0__py3-none-any.whl → 1.7.1__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/__init__.py +0 -2
- openseries/_common_model.py +261 -324
- openseries/_risk.py +9 -10
- openseries/datefixer.py +42 -63
- openseries/frame.py +125 -161
- openseries/load_plotly.py +8 -8
- openseries/portfoliotools.py +39 -35
- openseries/series.py +65 -93
- openseries/simulation.py +39 -50
- openseries/types.py +17 -24
- {openseries-1.6.0.dist-info → openseries-1.7.1.dist-info}/LICENSE.md +1 -1
- {openseries-1.6.0.dist-info → openseries-1.7.1.dist-info}/METADATA +4 -5
- openseries-1.7.1.dist-info/RECORD +16 -0
- openseries-1.6.0.dist-info/RECORD +0 -16
- {openseries-1.6.0.dist-info → openseries-1.7.1.dist-info}/WHEEL +0 -0
openseries/load_plotly.py
CHANGED
@@ -5,18 +5,19 @@ from __future__ import annotations
|
|
5
5
|
from json import load
|
6
6
|
from logging import warning
|
7
7
|
from pathlib import Path
|
8
|
+
from typing import TYPE_CHECKING
|
8
9
|
|
9
10
|
import requests
|
10
11
|
from requests.exceptions import ConnectionError
|
11
12
|
|
12
|
-
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from .types import CaptorLogoType, PlotlyLayoutType # pragma: no cover
|
13
15
|
|
14
16
|
__all__ = ["load_plotly_dict"]
|
15
17
|
|
16
18
|
|
17
19
|
def _check_remote_file_existence(url: str) -> bool:
|
18
|
-
"""
|
19
|
-
Check if remote file exists.
|
20
|
+
"""Check if remote file exists.
|
20
21
|
|
21
22
|
Parameters
|
22
23
|
----------
|
@@ -44,8 +45,7 @@ def load_plotly_dict(
|
|
44
45
|
*,
|
45
46
|
responsive: bool = True,
|
46
47
|
) -> tuple[PlotlyLayoutType, CaptorLogoType]:
|
47
|
-
"""
|
48
|
-
Load Plotly defaults.
|
48
|
+
"""Load Plotly defaults.
|
49
49
|
|
50
50
|
Parameters
|
51
51
|
----------
|
@@ -58,13 +58,13 @@ def load_plotly_dict(
|
|
58
58
|
A dictionary with the Plotly config and layout template
|
59
59
|
|
60
60
|
"""
|
61
|
-
project_root = Path(__file__).
|
61
|
+
project_root = Path(__file__).parent.parent
|
62
62
|
layoutfile = project_root.joinpath("openseries").joinpath("plotly_layouts.json")
|
63
63
|
logofile = project_root.joinpath("openseries").joinpath("plotly_captor_logo.json")
|
64
64
|
|
65
|
-
with
|
65
|
+
with layoutfile.open(mode="r", encoding="utf-8") as layout_file:
|
66
66
|
fig = load(layout_file)
|
67
|
-
with
|
67
|
+
with logofile.open(mode="r", encoding="utf-8") as logo_file:
|
68
68
|
logo = load(logo_file)
|
69
69
|
|
70
70
|
if _check_remote_file_existence(url=logo["source"]) is False:
|
openseries/portfoliotools.py
CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
5
5
|
|
6
6
|
from inspect import stack
|
7
7
|
from pathlib import Path
|
8
|
-
from typing import
|
8
|
+
from typing import TYPE_CHECKING, Callable, cast
|
9
9
|
|
10
10
|
from numpy import (
|
11
11
|
append,
|
@@ -13,6 +13,7 @@ from numpy import (
|
|
13
13
|
dot,
|
14
14
|
float64,
|
15
15
|
inf,
|
16
|
+
isnan,
|
16
17
|
linspace,
|
17
18
|
nan,
|
18
19
|
sqrt,
|
@@ -30,15 +31,14 @@ from pandas import (
|
|
30
31
|
from plotly.graph_objs import Figure # type: ignore[import-untyped,unused-ignore]
|
31
32
|
from plotly.io import to_html # type: ignore[import-untyped,unused-ignore]
|
32
33
|
from plotly.offline import plot # type: ignore[import-untyped,unused-ignore]
|
33
|
-
from pydantic import DirectoryPath
|
34
34
|
from scipy.optimize import minimize # type: ignore[import-untyped,unused-ignore]
|
35
35
|
|
36
|
+
from .load_plotly import load_plotly_dict
|
37
|
+
from .series import OpenTimeSeries
|
38
|
+
|
36
39
|
# noinspection PyProtectedMember
|
37
|
-
from
|
38
|
-
from
|
39
|
-
from openseries.series import OpenTimeSeries
|
40
|
-
from openseries.simulation import _random_generator
|
41
|
-
from openseries.types import (
|
40
|
+
from .simulation import _random_generator
|
41
|
+
from .types import (
|
42
42
|
LiteralLinePlotMode,
|
43
43
|
LiteralMinimizeMethods,
|
44
44
|
LiteralPlotlyJSlib,
|
@@ -46,6 +46,11 @@ from openseries.types import (
|
|
46
46
|
ValueType,
|
47
47
|
)
|
48
48
|
|
49
|
+
if TYPE_CHECKING: # pragma: no cover
|
50
|
+
from pydantic import DirectoryPath
|
51
|
+
|
52
|
+
from .frame import OpenFrame
|
53
|
+
|
49
54
|
__all__ = [
|
50
55
|
"constrain_optimized_portfolios",
|
51
56
|
"efficient_frontier",
|
@@ -60,8 +65,7 @@ def simulate_portfolios(
|
|
60
65
|
num_ports: int,
|
61
66
|
seed: int,
|
62
67
|
) -> DataFrame:
|
63
|
-
"""
|
64
|
-
Generate random weights for simulated portfolios.
|
68
|
+
"""Generate random weights for simulated portfolios.
|
65
69
|
|
66
70
|
Parameters
|
67
71
|
----------
|
@@ -130,14 +134,13 @@ def efficient_frontier( # noqa: C901
|
|
130
134
|
eframe: OpenFrame,
|
131
135
|
num_ports: int = 5000,
|
132
136
|
seed: int = 71,
|
133
|
-
bounds:
|
137
|
+
bounds: tuple[tuple[float]] | None = None,
|
134
138
|
frontier_points: int = 200,
|
135
139
|
minimize_method: LiteralMinimizeMethods = "SLSQP",
|
136
140
|
*,
|
137
141
|
tweak: bool = True,
|
138
142
|
) -> tuple[DataFrame, DataFrame, NDArray[float64]]:
|
139
|
-
"""
|
140
|
-
Identify an efficient frontier.
|
143
|
+
"""Identify an efficient frontier.
|
141
144
|
|
142
145
|
Parameters
|
143
146
|
----------
|
@@ -180,10 +183,11 @@ def efficient_frontier( # noqa: C901
|
|
180
183
|
simulated = simulate_portfolios(simframe=copi, num_ports=num_ports, seed=seed)
|
181
184
|
|
182
185
|
frontier_min = simulated.loc[simulated["stdev"].idxmin()]["ret"]
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
186
|
+
|
187
|
+
arithmetic_means = array(log_ret.mean() * copi.periods_in_a_year)
|
188
|
+
cleaned_arithmetic_means = arithmetic_means[~isnan(arithmetic_means)]
|
189
|
+
|
190
|
+
frontier_max = cleaned_arithmetic_means.max()
|
187
191
|
|
188
192
|
def _check_sum(weights: NDArray[float64]) -> float64:
|
189
193
|
return cast(float64, npsum(weights) - 1)
|
@@ -258,7 +262,7 @@ def efficient_frontier( # noqa: C901
|
|
258
262
|
|
259
263
|
for possible_return in frontier_y:
|
260
264
|
cons = cast(
|
261
|
-
dict[str,
|
265
|
+
dict[str, str | Callable[[float, NDArray[float64]], float64]],
|
262
266
|
(
|
263
267
|
{"type": "eq", "fun": _check_sum},
|
264
268
|
{
|
@@ -284,7 +288,6 @@ def efficient_frontier( # noqa: C901
|
|
284
288
|
frontier_x.append(result["fun"])
|
285
289
|
frontier_weights.append(result["x"])
|
286
290
|
|
287
|
-
# noinspection PyUnreachableCode
|
288
291
|
line_df = concat(
|
289
292
|
[
|
290
293
|
DataFrame(data=frontier_weights, columns=eframe.columns_lvl_zero),
|
@@ -319,11 +322,10 @@ def constrain_optimized_portfolios(
|
|
319
322
|
portfolioname: str = "Current Portfolio",
|
320
323
|
simulations: int = 10000,
|
321
324
|
curve_points: int = 200,
|
322
|
-
bounds:
|
325
|
+
bounds: tuple[tuple[float]] | None = None,
|
323
326
|
minimize_method: LiteralMinimizeMethods = "SLSQP",
|
324
327
|
) -> tuple[OpenFrame, OpenTimeSeries, OpenFrame, OpenTimeSeries]:
|
325
|
-
"""
|
326
|
-
Constrain optimized portfolios to those that improve on the current one.
|
328
|
+
"""Constrain optimized portfolios to those that improve on the current one.
|
327
329
|
|
328
330
|
Parameters
|
329
331
|
----------
|
@@ -391,8 +393,7 @@ def prepare_plot_data(
|
|
391
393
|
current: OpenTimeSeries,
|
392
394
|
optimized: NDArray[float64],
|
393
395
|
) -> DataFrame:
|
394
|
-
"""
|
395
|
-
Prepare date to be used as point_frame in the sharpeplot function.
|
396
|
+
"""Prepare date to be used as point_frame in the sharpeplot function.
|
396
397
|
|
397
398
|
Parameters
|
398
399
|
----------
|
@@ -443,13 +444,13 @@ def prepare_plot_data(
|
|
443
444
|
|
444
445
|
|
445
446
|
def sharpeplot( # noqa: C901
|
446
|
-
sim_frame:
|
447
|
-
line_frame:
|
448
|
-
point_frame:
|
447
|
+
sim_frame: DataFrame | None = None,
|
448
|
+
line_frame: DataFrame | None = None,
|
449
|
+
point_frame: DataFrame | None = None,
|
449
450
|
point_frame_mode: LiteralLinePlotMode = "markers",
|
450
|
-
filename:
|
451
|
-
directory:
|
452
|
-
titletext:
|
451
|
+
filename: str | None = None,
|
452
|
+
directory: DirectoryPath | None = None,
|
453
|
+
titletext: str | None = None,
|
453
454
|
output_type: LiteralPlotlyOutput = "file",
|
454
455
|
include_plotlyjs: LiteralPlotlyJSlib = "cdn",
|
455
456
|
*,
|
@@ -457,8 +458,7 @@ def sharpeplot( # noqa: C901
|
|
457
458
|
add_logo: bool = True,
|
458
459
|
auto_open: bool = True,
|
459
460
|
) -> tuple[Figure, str]:
|
460
|
-
"""
|
461
|
-
Create scatter plot coloured by Sharpe Ratio.
|
461
|
+
"""Create scatter plot coloured by Sharpe Ratio.
|
462
462
|
|
463
463
|
Parameters
|
464
464
|
----------
|
@@ -510,6 +510,10 @@ def sharpeplot( # noqa: C901
|
|
510
510
|
fig, logo = load_plotly_dict()
|
511
511
|
figure = Figure(fig)
|
512
512
|
|
513
|
+
if sim_frame is None and line_frame is None and point_frame is None:
|
514
|
+
msg = "One of sim_frame, line_frame or point_frame must be provided."
|
515
|
+
raise ValueError(msg)
|
516
|
+
|
513
517
|
if sim_frame is not None:
|
514
518
|
returns.extend(list(sim_frame.loc[:, "ret"]))
|
515
519
|
risk.extend(list(sim_frame.loc[:, "stdev"]))
|
@@ -544,11 +548,11 @@ def sharpeplot( # noqa: C901
|
|
544
548
|
name="Efficient frontier",
|
545
549
|
)
|
546
550
|
|
547
|
-
colorway = fig["layout"].get("colorway")[ # type: ignore[union-attr]
|
548
|
-
: len(cast(DataFrame, point_frame).columns)
|
549
|
-
]
|
550
|
-
|
551
551
|
if point_frame is not None:
|
552
|
+
colorway = cast(
|
553
|
+
dict[str, str | int | float | bool | list[str]],
|
554
|
+
fig["layout"],
|
555
|
+
).get("colorway")[: len(point_frame.columns)]
|
552
556
|
for col, clr in zip(point_frame.columns, colorway):
|
553
557
|
returns.extend([point_frame.loc["ret", col]])
|
554
558
|
risk.extend([point_frame.loc["stdev", col]])
|
openseries/series.py
CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
5
5
|
import datetime as dt
|
6
6
|
from copy import deepcopy
|
7
7
|
from logging import warning
|
8
|
-
from typing import Any,
|
8
|
+
from typing import Any, TypeVar, cast
|
9
9
|
|
10
10
|
from numpy import (
|
11
11
|
append,
|
@@ -25,12 +25,12 @@ from pandas import (
|
|
25
25
|
Series,
|
26
26
|
date_range,
|
27
27
|
)
|
28
|
-
from pydantic import model_validator
|
28
|
+
from pydantic import field_validator, model_validator
|
29
29
|
from typing_extensions import Self
|
30
30
|
|
31
|
-
from
|
32
|
-
from
|
33
|
-
from
|
31
|
+
from ._common_model import _CommonModel
|
32
|
+
from .datefixer import _do_resample_to_business_period_ends, date_fix
|
33
|
+
from .types import (
|
34
34
|
Countries,
|
35
35
|
CountriesType,
|
36
36
|
Currency,
|
@@ -53,9 +53,7 @@ TypeOpenTimeSeries = TypeVar("TypeOpenTimeSeries", bound="OpenTimeSeries")
|
|
53
53
|
|
54
54
|
# noinspection PyUnresolvedReferences
|
55
55
|
class OpenTimeSeries(_CommonModel):
|
56
|
-
|
57
|
-
"""
|
58
|
-
OpenTimeSeries objects are at the core of the openseries package.
|
56
|
+
"""OpenTimeSeries objects are at the core of the openseries package.
|
59
57
|
|
60
58
|
The intended use is to allow analyses of financial timeseries.
|
61
59
|
It is only intended for daily or less frequent data samples.
|
@@ -104,8 +102,22 @@ class OpenTimeSeries(_CommonModel):
|
|
104
102
|
currency: CurrencyStringType
|
105
103
|
domestic: CurrencyStringType = "SEK"
|
106
104
|
countries: CountriesType = "SE"
|
107
|
-
isin:
|
108
|
-
label:
|
105
|
+
isin: str | None = None
|
106
|
+
label: str | None = None
|
107
|
+
|
108
|
+
@field_validator("domestic", mode="before")
|
109
|
+
@classmethod
|
110
|
+
def validate_domestic(cls, value: CurrencyStringType) -> CurrencyStringType:
|
111
|
+
"""Pydantic validator to ensure domestic field is validated."""
|
112
|
+
_ = Currency(ccy=value)
|
113
|
+
return value
|
114
|
+
|
115
|
+
@field_validator("countries", mode="before")
|
116
|
+
@classmethod
|
117
|
+
def validate_countries(cls, value: CountriesType) -> CountriesType:
|
118
|
+
"""Pydantic validator to ensure countries field is validated."""
|
119
|
+
_ = Countries(countryinput=value)
|
120
|
+
return value
|
109
121
|
|
110
122
|
@model_validator(mode="after") # type: ignore[misc,unused-ignore]
|
111
123
|
def dates_and_values_validate(self: Self) -> Self:
|
@@ -128,29 +140,6 @@ class OpenTimeSeries(_CommonModel):
|
|
128
140
|
raise ValueError(msg)
|
129
141
|
return self
|
130
142
|
|
131
|
-
@classmethod
|
132
|
-
def setup_class(
|
133
|
-
cls: type[OpenTimeSeries],
|
134
|
-
domestic_ccy: CurrencyStringType = "SEK",
|
135
|
-
countries: CountriesType = "SE",
|
136
|
-
) -> None:
|
137
|
-
"""
|
138
|
-
Set the domestic currency and calendar of the user.
|
139
|
-
|
140
|
-
Parameters
|
141
|
-
----------
|
142
|
-
domestic_ccy : CurrencyStringType, default: "SEK"
|
143
|
-
Currency code according to ISO 4217
|
144
|
-
countries: CountriesType, default: "SE"
|
145
|
-
(List of) country code(s) according to ISO 3166-1 alpha-2
|
146
|
-
|
147
|
-
"""
|
148
|
-
_ = Currency(ccy=domestic_ccy)
|
149
|
-
_ = Countries(countryinput=countries)
|
150
|
-
|
151
|
-
cls.domestic = domestic_ccy
|
152
|
-
cls.countries = countries
|
153
|
-
|
154
143
|
@classmethod
|
155
144
|
def from_arrays(
|
156
145
|
cls: type[OpenTimeSeries],
|
@@ -160,13 +149,12 @@ class OpenTimeSeries(_CommonModel):
|
|
160
149
|
valuetype: ValueType = ValueType.PRICE,
|
161
150
|
timeseries_id: DatabaseIdStringType = "",
|
162
151
|
instrument_id: DatabaseIdStringType = "",
|
163
|
-
isin:
|
152
|
+
isin: str | None = None,
|
164
153
|
baseccy: CurrencyStringType = "SEK",
|
165
154
|
*,
|
166
155
|
local_ccy: bool = True,
|
167
156
|
) -> OpenTimeSeries:
|
168
|
-
"""
|
169
|
-
Create series from a Pandas DataFrame or Series.
|
157
|
+
"""Create series from a Pandas DataFrame or Series.
|
170
158
|
|
171
159
|
Parameters
|
172
160
|
----------
|
@@ -217,19 +205,18 @@ class OpenTimeSeries(_CommonModel):
|
|
217
205
|
@classmethod
|
218
206
|
def from_df(
|
219
207
|
cls: type[OpenTimeSeries],
|
220
|
-
dframe:
|
208
|
+
dframe: DataFrame | Series[float],
|
221
209
|
column_nmbr: int = 0,
|
222
210
|
valuetype: ValueType = ValueType.PRICE,
|
223
211
|
baseccy: CurrencyStringType = "SEK",
|
224
212
|
*,
|
225
213
|
local_ccy: bool = True,
|
226
214
|
) -> OpenTimeSeries:
|
227
|
-
"""
|
228
|
-
Create series from a Pandas DataFrame or Series.
|
215
|
+
"""Create series from a Pandas DataFrame or Series.
|
229
216
|
|
230
217
|
Parameters
|
231
218
|
----------
|
232
|
-
dframe:
|
219
|
+
dframe: DataFrame | Series[float]
|
233
220
|
Pandas DataFrame or Series
|
234
221
|
column_nmbr : int, default: 0
|
235
222
|
Using iloc[:, column_nmbr] to pick column
|
@@ -252,8 +239,8 @@ class OpenTimeSeries(_CommonModel):
|
|
252
239
|
else:
|
253
240
|
label = dframe.name
|
254
241
|
values = dframe.to_numpy().tolist()
|
255
|
-
|
256
|
-
values =
|
242
|
+
elif isinstance(dframe, DataFrame):
|
243
|
+
values = dframe.iloc[:, column_nmbr].to_list()
|
257
244
|
if isinstance(dframe.columns, MultiIndex):
|
258
245
|
if _check_if_none(
|
259
246
|
dframe.columns.get_level_values(0).to_numpy()[column_nmbr],
|
@@ -275,6 +262,10 @@ class OpenTimeSeries(_CommonModel):
|
|
275
262
|
]
|
276
263
|
else:
|
277
264
|
label = cast(MultiIndex, dframe.columns).to_numpy()[column_nmbr]
|
265
|
+
else:
|
266
|
+
msg = "Argument dframe must be pandas Series or DataFrame."
|
267
|
+
raise TypeError(msg)
|
268
|
+
|
278
269
|
dates = [date_fix(d).strftime("%Y-%m-%d") for d in dframe.index]
|
279
270
|
|
280
271
|
return cls(
|
@@ -299,17 +290,16 @@ class OpenTimeSeries(_CommonModel):
|
|
299
290
|
def from_fixed_rate(
|
300
291
|
cls: type[OpenTimeSeries],
|
301
292
|
rate: float,
|
302
|
-
d_range:
|
303
|
-
days:
|
304
|
-
end_dt:
|
293
|
+
d_range: DatetimeIndex | None = None,
|
294
|
+
days: int | None = None,
|
295
|
+
end_dt: dt.date | None = None,
|
305
296
|
label: str = "Series",
|
306
297
|
valuetype: ValueType = ValueType.PRICE,
|
307
298
|
baseccy: CurrencyStringType = "SEK",
|
308
299
|
*,
|
309
300
|
local_ccy: bool = True,
|
310
301
|
) -> OpenTimeSeries:
|
311
|
-
"""
|
312
|
-
Create series from values accruing with a given fixed rate return.
|
302
|
+
"""Create series from values accruing with a given fixed rate return.
|
313
303
|
|
314
304
|
Providing a date_range of type Pandas DatetimeIndex takes priority over
|
315
305
|
providing a combination of days and an end date.
|
@@ -380,8 +370,7 @@ class OpenTimeSeries(_CommonModel):
|
|
380
370
|
)
|
381
371
|
|
382
372
|
def from_deepcopy(self: Self) -> Self:
|
383
|
-
"""
|
384
|
-
Create copy of OpenTimeSeries object.
|
373
|
+
"""Create copy of OpenTimeSeries object.
|
385
374
|
|
386
375
|
Returns
|
387
376
|
-------
|
@@ -392,8 +381,7 @@ class OpenTimeSeries(_CommonModel):
|
|
392
381
|
return deepcopy(self)
|
393
382
|
|
394
383
|
def pandas_df(self: Self) -> Self:
|
395
|
-
"""
|
396
|
-
Populate .tsdf Pandas DataFrame from the .dates and .values lists.
|
384
|
+
"""Populate .tsdf Pandas DataFrame from the .dates and .values lists.
|
397
385
|
|
398
386
|
Returns
|
399
387
|
-------
|
@@ -413,10 +401,9 @@ class OpenTimeSeries(_CommonModel):
|
|
413
401
|
|
414
402
|
def all_properties(
|
415
403
|
self: Self,
|
416
|
-
properties:
|
404
|
+
properties: list[LiteralSeriesProps] | None = None,
|
417
405
|
) -> DataFrame:
|
418
|
-
"""
|
419
|
-
Calculate chosen properties.
|
406
|
+
"""Calculate chosen properties.
|
420
407
|
|
421
408
|
Parameters
|
422
409
|
----------
|
@@ -441,8 +428,7 @@ class OpenTimeSeries(_CommonModel):
|
|
441
428
|
return pdf
|
442
429
|
|
443
430
|
def value_to_ret(self: Self) -> Self:
|
444
|
-
"""
|
445
|
-
Convert series of values into series of returns.
|
431
|
+
"""Convert series of values into series of returns.
|
446
432
|
|
447
433
|
Returns
|
448
434
|
-------
|
@@ -450,7 +436,7 @@ class OpenTimeSeries(_CommonModel):
|
|
450
436
|
The returns of the values in the series
|
451
437
|
|
452
438
|
"""
|
453
|
-
self.tsdf = self.tsdf.pct_change(fill_method=
|
439
|
+
self.tsdf = self.tsdf.pct_change(fill_method=None) # type: ignore[arg-type]
|
454
440
|
self.tsdf.iloc[0] = 0
|
455
441
|
self.valuetype = ValueType.RTRN
|
456
442
|
self.tsdf.columns = MultiIndex.from_arrays(
|
@@ -462,8 +448,7 @@ class OpenTimeSeries(_CommonModel):
|
|
462
448
|
return self
|
463
449
|
|
464
450
|
def value_to_diff(self: Self, periods: int = 1) -> Self:
|
465
|
-
"""
|
466
|
-
Convert series of values to series of their period differences.
|
451
|
+
"""Convert series of values to series of their period differences.
|
467
452
|
|
468
453
|
Parameters
|
469
454
|
----------
|
@@ -489,8 +474,7 @@ class OpenTimeSeries(_CommonModel):
|
|
489
474
|
return self
|
490
475
|
|
491
476
|
def to_cumret(self: Self) -> Self:
|
492
|
-
"""
|
493
|
-
Convert series of returns into cumulative series of values.
|
477
|
+
"""Convert series of returns into cumulative series of values.
|
494
478
|
|
495
479
|
Returns
|
496
480
|
-------
|
@@ -520,8 +504,7 @@ class OpenTimeSeries(_CommonModel):
|
|
520
504
|
days_in_year: int = 365,
|
521
505
|
divider: float = 1.0,
|
522
506
|
) -> Self:
|
523
|
-
"""
|
524
|
-
Convert series of 1-day rates into series of cumulative values.
|
507
|
+
"""Convert series of 1-day rates into series of cumulative values.
|
525
508
|
|
526
509
|
Parameters
|
527
510
|
----------
|
@@ -555,14 +538,13 @@ class OpenTimeSeries(_CommonModel):
|
|
555
538
|
|
556
539
|
def resample(
|
557
540
|
self: Self,
|
558
|
-
freq:
|
541
|
+
freq: LiteralBizDayFreq | str = "BME",
|
559
542
|
) -> Self:
|
560
|
-
"""
|
561
|
-
Resamples the timeseries frequency.
|
543
|
+
"""Resamples the timeseries frequency.
|
562
544
|
|
563
545
|
Parameters
|
564
546
|
----------
|
565
|
-
freq:
|
547
|
+
freq: LiteralBizDayFreq | str, default "BME"
|
566
548
|
The date offset string that sets the resampled frequency
|
567
549
|
|
568
550
|
Returns
|
@@ -581,8 +563,7 @@ class OpenTimeSeries(_CommonModel):
|
|
581
563
|
freq: LiteralBizDayFreq = "BME",
|
582
564
|
method: LiteralPandasReindexMethod = "nearest",
|
583
565
|
) -> Self:
|
584
|
-
"""
|
585
|
-
Resamples timeseries frequency to the business calendar month end dates.
|
566
|
+
"""Resamples timeseries frequency to the business calendar month end dates.
|
586
567
|
|
587
568
|
Stubs left in place. Stubs will be aligned to the shortest stub.
|
588
569
|
|
@@ -599,12 +580,8 @@ class OpenTimeSeries(_CommonModel):
|
|
599
580
|
An OpenTimeSeries object
|
600
581
|
|
601
582
|
"""
|
602
|
-
|
603
|
-
tail = self.tsdf.iloc[-1].copy()
|
604
|
-
dates = do_resample_to_business_period_ends(
|
583
|
+
dates = _do_resample_to_business_period_ends(
|
605
584
|
data=self.tsdf,
|
606
|
-
head=head,
|
607
|
-
tail=tail,
|
608
585
|
freq=freq,
|
609
586
|
countries=self.countries,
|
610
587
|
)
|
@@ -616,13 +593,12 @@ class OpenTimeSeries(_CommonModel):
|
|
616
593
|
lmbda: float = 0.94,
|
617
594
|
day_chunk: int = 11,
|
618
595
|
dlta_degr_freedms: int = 0,
|
619
|
-
months_from_last:
|
620
|
-
from_date:
|
621
|
-
to_date:
|
622
|
-
periods_in_a_year_fixed:
|
596
|
+
months_from_last: int | None = None,
|
597
|
+
from_date: dt.date | None = None,
|
598
|
+
to_date: dt.date | None = None,
|
599
|
+
periods_in_a_year_fixed: DaysInYearType | None = None,
|
623
600
|
) -> Series[float]:
|
624
|
-
"""
|
625
|
-
Exponentially Weighted Moving Average Model for Volatility.
|
601
|
+
"""Exponentially Weighted Moving Average Model for Volatility.
|
626
602
|
|
627
603
|
https://www.investopedia.com/articles/07/ewma.asp.
|
628
604
|
|
@@ -694,8 +670,7 @@ class OpenTimeSeries(_CommonModel):
|
|
694
670
|
adjustment: float,
|
695
671
|
days_in_year: int = 365,
|
696
672
|
) -> Self:
|
697
|
-
"""
|
698
|
-
Add or subtract a fee from the timeseries return.
|
673
|
+
"""Add or subtract a fee from the timeseries return.
|
699
674
|
|
700
675
|
Parameters
|
701
676
|
----------
|
@@ -721,7 +696,7 @@ class OpenTimeSeries(_CommonModel):
|
|
721
696
|
returns_input = True
|
722
697
|
else:
|
723
698
|
values = [cast(float, self.tsdf.iloc[0, 0])]
|
724
|
-
ra_df = self.tsdf.pct_change(fill_method=
|
699
|
+
ra_df = self.tsdf.pct_change(fill_method=None) # type: ignore[arg-type]
|
725
700
|
returns_input = False
|
726
701
|
ra_df = ra_df.dropna()
|
727
702
|
|
@@ -754,13 +729,12 @@ class OpenTimeSeries(_CommonModel):
|
|
754
729
|
|
755
730
|
def set_new_label(
|
756
731
|
self: Self,
|
757
|
-
lvl_zero:
|
758
|
-
lvl_one:
|
732
|
+
lvl_zero: str | None = None,
|
733
|
+
lvl_one: ValueType | None = None,
|
759
734
|
*,
|
760
735
|
delete_lvl_one: bool = False,
|
761
736
|
) -> Self:
|
762
|
-
"""
|
763
|
-
Set the column labels of the .tsdf Pandas Dataframe.
|
737
|
+
"""Set the column labels of the .tsdf Pandas Dataframe.
|
764
738
|
|
765
739
|
Parameters
|
766
740
|
----------
|
@@ -799,9 +773,8 @@ def timeseries_chain(
|
|
799
773
|
front: TypeOpenTimeSeries,
|
800
774
|
back: TypeOpenTimeSeries,
|
801
775
|
old_fee: float = 0.0,
|
802
|
-
) ->
|
803
|
-
"""
|
804
|
-
Chain two timeseries together.
|
776
|
+
) -> TypeOpenTimeSeries | OpenTimeSeries:
|
777
|
+
"""Chain two timeseries together.
|
805
778
|
|
806
779
|
The function assumes that the two series have at least one date in common.
|
807
780
|
|
@@ -816,7 +789,7 @@ def timeseries_chain(
|
|
816
789
|
|
817
790
|
Returns
|
818
791
|
-------
|
819
|
-
|
792
|
+
TypeOpenTimeSeries | OpenTimeSeries
|
820
793
|
An OpenTimeSeries object or a subclass thereof
|
821
794
|
|
822
795
|
"""
|
@@ -887,8 +860,7 @@ def timeseries_chain(
|
|
887
860
|
|
888
861
|
|
889
862
|
def _check_if_none(item: Any) -> bool: # noqa: ANN401
|
890
|
-
"""
|
891
|
-
Check if a variable is None or equivalent.
|
863
|
+
"""Check if a variable is None or equivalent.
|
892
864
|
|
893
865
|
Parameters
|
894
866
|
----------
|