openseries 1.9.6__py3-none-any.whl → 2.0.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.
- openseries/__init__.py +1 -8
- openseries/_common_model.py +375 -224
- openseries/_risk.py +3 -10
- openseries/datefixer.py +9 -16
- openseries/frame.py +100 -117
- openseries/load_plotly.py +3 -10
- openseries/owntypes.py +3 -10
- openseries/portfoliotools.py +19 -41
- openseries/py.typed +0 -0
- openseries/report.py +6 -12
- openseries/series.py +36 -54
- openseries/simulation.py +104 -39
- openseries-2.0.0.dist-info/METADATA +126 -0
- openseries-2.0.0.dist-info/RECORD +18 -0
- {openseries-1.9.6.dist-info → openseries-2.0.0.dist-info}/WHEEL +1 -1
- openseries-1.9.6.dist-info/METADATA +0 -369
- openseries-1.9.6.dist-info/RECORD +0 -17
- {openseries-1.9.6.dist-info → openseries-2.0.0.dist-info}/licenses/LICENSE.md +0 -0
openseries/portfoliotools.py
CHANGED
@@ -1,11 +1,4 @@
|
|
1
|
-
"""Defining the portfolio tools for the OpenFrame class.
|
2
|
-
|
3
|
-
Copyright (c) Captor Fund Management AB. This file is part of the openseries project.
|
4
|
-
|
5
|
-
Licensed under the BSD 3-Clause License. You may obtain a copy of the License at:
|
6
|
-
https://github.com/CaptorAB/openseries/blob/master/LICENSE.md
|
7
|
-
SPDX-License-Identifier: BSD-3-Clause
|
8
|
-
"""
|
1
|
+
"""Defining the portfolio tools for the OpenFrame class."""
|
9
2
|
|
10
3
|
from __future__ import annotations
|
11
4
|
|
@@ -16,13 +9,13 @@ from typing import TYPE_CHECKING, cast
|
|
16
9
|
from numpy import (
|
17
10
|
append,
|
18
11
|
array,
|
12
|
+
einsum,
|
19
13
|
float64,
|
20
14
|
inf,
|
21
15
|
isnan,
|
22
16
|
linspace,
|
23
17
|
nan,
|
24
18
|
sqrt,
|
25
|
-
zeros,
|
26
19
|
)
|
27
20
|
from numpy import (
|
28
21
|
sum as npsum,
|
@@ -48,12 +41,9 @@ from .owntypes import (
|
|
48
41
|
ValueType,
|
49
42
|
)
|
50
43
|
from .series import OpenTimeSeries
|
51
|
-
|
52
|
-
# noinspection PyProtectedMember
|
53
44
|
from .simulation import _random_generator
|
54
45
|
|
55
46
|
if TYPE_CHECKING: # pragma: no cover
|
56
|
-
# noinspection PyUnresolvedReferences
|
57
47
|
from collections.abc import Callable
|
58
48
|
|
59
49
|
from numpy.typing import NDArray
|
@@ -87,7 +77,7 @@ def simulate_portfolios(
|
|
87
77
|
The seed for the random process
|
88
78
|
|
89
79
|
Returns:
|
90
|
-
|
80
|
+
--------
|
91
81
|
pandas.DataFrame
|
92
82
|
The resulting data
|
93
83
|
|
@@ -106,25 +96,16 @@ def simulate_portfolios(
|
|
106
96
|
|
107
97
|
log_ret.columns = log_ret.columns.droplevel(level=1)
|
108
98
|
|
109
|
-
|
110
|
-
|
111
|
-
all_weights = zeros((num_ports, simframe.item_count))
|
112
|
-
ret_arr = zeros(num_ports)
|
113
|
-
vol_arr = zeros(num_ports)
|
114
|
-
sharpe_arr = zeros(num_ports)
|
99
|
+
cov_matrix = log_ret.cov() * simframe.periods_in_a_year
|
100
|
+
mean_returns = log_ret.mean() * simframe.periods_in_a_year
|
115
101
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
all_weights[x, :] = weights
|
120
|
-
|
121
|
-
vol_arr[x] = sqrt(
|
122
|
-
weights.T @ (log_ret.cov() * simframe.periods_in_a_year @ weights),
|
123
|
-
)
|
124
|
-
|
125
|
-
ret_arr[x] = npsum(log_ret.mean() * weights * simframe.periods_in_a_year)
|
102
|
+
randomizer = _random_generator(seed=seed)
|
103
|
+
all_weights = randomizer.random((num_ports, simframe.item_count))
|
104
|
+
all_weights = all_weights / all_weights.sum(axis=1, keepdims=True)
|
126
105
|
|
127
|
-
|
106
|
+
ret_arr = all_weights @ mean_returns
|
107
|
+
vol_arr = sqrt(einsum("ij,jk,ik->i", all_weights, cov_matrix, all_weights))
|
108
|
+
sharpe_arr = ret_arr / vol_arr
|
128
109
|
|
129
110
|
simdf = concat(
|
130
111
|
[
|
@@ -137,7 +118,6 @@ def simulate_portfolios(
|
|
137
118
|
return simdf.dropna()
|
138
119
|
|
139
120
|
|
140
|
-
# noinspection PyUnusedLocal
|
141
121
|
def efficient_frontier(
|
142
122
|
eframe: OpenFrame,
|
143
123
|
num_ports: int = 5000,
|
@@ -168,7 +148,7 @@ def efficient_frontier(
|
|
168
148
|
cutting the frontier to exclude multiple points with almost the same risk
|
169
149
|
|
170
150
|
Returns:
|
171
|
-
|
151
|
+
--------
|
172
152
|
tuple[DataFrame, DataFrame, NDArray[float]]
|
173
153
|
The efficient frontier data, simulation data and optimal portfolio
|
174
154
|
|
@@ -230,7 +210,7 @@ def efficient_frontier(
|
|
230
210
|
_get_ret_vol_sr(
|
231
211
|
lg_ret=log_ret,
|
232
212
|
weights=weights,
|
233
|
-
per_in_yr=
|
213
|
+
per_in_yr=copi.periods_in_a_year,
|
234
214
|
)[2]
|
235
215
|
* -1,
|
236
216
|
)
|
@@ -243,7 +223,7 @@ def efficient_frontier(
|
|
243
223
|
_get_ret_vol_sr(
|
244
224
|
lg_ret=log_ret,
|
245
225
|
weights=weights,
|
246
|
-
per_in_yr=
|
226
|
+
per_in_yr=copi.periods_in_a_year,
|
247
227
|
)[1],
|
248
228
|
)
|
249
229
|
|
@@ -263,7 +243,7 @@ def efficient_frontier(
|
|
263
243
|
optimal = _get_ret_vol_sr(
|
264
244
|
lg_ret=log_ret,
|
265
245
|
weights=opt_results.x,
|
266
|
-
per_in_yr=
|
246
|
+
per_in_yr=copi.periods_in_a_year,
|
267
247
|
)
|
268
248
|
|
269
249
|
frontier_y = linspace(start=frontier_min, stop=frontier_max, num=frontier_points)
|
@@ -280,7 +260,7 @@ def efficient_frontier(
|
|
280
260
|
"fun": lambda w, poss_return=possible_return: _diff_return(
|
281
261
|
lg_ret=log_ret,
|
282
262
|
weights=w,
|
283
|
-
per_in_yr=
|
263
|
+
per_in_yr=copi.periods_in_a_year,
|
284
264
|
poss_return=poss_return,
|
285
265
|
),
|
286
266
|
},
|
@@ -355,7 +335,7 @@ def constrain_optimized_portfolios(
|
|
355
335
|
The method passed into the scipy.minimize function
|
356
336
|
|
357
337
|
Returns:
|
358
|
-
|
338
|
+
--------
|
359
339
|
tuple[OpenFrame, OpenTimeSeries, OpenFrame, OpenTimeSeries]
|
360
340
|
The constrained optimal portfolio data
|
361
341
|
|
@@ -375,7 +355,6 @@ def constrain_optimized_portfolios(
|
|
375
355
|
)
|
376
356
|
|
377
357
|
condition_least_ret = front_frame.ret > serie.arithmetic_ret
|
378
|
-
# noinspection PyArgumentList
|
379
358
|
least_ret_frame = front_frame[condition_least_ret].sort_values(by="stdev")
|
380
359
|
least_ret_port: Series[float] = least_ret_frame.iloc[0]
|
381
360
|
least_ret_port_name = f"Minimize vol & target return of {portfolioname}"
|
@@ -386,7 +365,6 @@ def constrain_optimized_portfolios(
|
|
386
365
|
resleast = OpenTimeSeries.from_df(lr_frame.make_portfolio(least_ret_port_name))
|
387
366
|
|
388
367
|
condition_most_vol = front_frame.stdev < serie.vol
|
389
|
-
# noinspection PyArgumentList
|
390
368
|
most_vol_frame = front_frame[condition_most_vol].sort_values(
|
391
369
|
by="ret",
|
392
370
|
ascending=False,
|
@@ -419,7 +397,7 @@ def prepare_plot_data(
|
|
419
397
|
Data optimized with the efficient_frontier method
|
420
398
|
|
421
399
|
Returns:
|
422
|
-
|
400
|
+
--------
|
423
401
|
DataFrame
|
424
402
|
The data prepared with mean returns, volatility and weights
|
425
403
|
|
@@ -503,7 +481,7 @@ def sharpeplot(
|
|
503
481
|
Determines whether to open a browser window with the plot
|
504
482
|
|
505
483
|
Returns:
|
506
|
-
|
484
|
+
--------
|
507
485
|
Figure
|
508
486
|
The scatter plot with simulated and optimized results
|
509
487
|
|
openseries/py.typed
ADDED
File without changes
|
openseries/report.py
CHANGED
@@ -1,11 +1,4 @@
|
|
1
|
-
"""Functions related to HTML reports.
|
2
|
-
|
3
|
-
Copyright (c) Captor Fund Management AB. This file is part of the openseries project.
|
4
|
-
|
5
|
-
Licensed under the BSD 3-Clause License. You may obtain a copy of the License at:
|
6
|
-
https://github.com/CaptorAB/openseries/blob/master/LICENSE.md
|
7
|
-
SPDX-License-Identifier: BSD-3-Clause
|
8
|
-
"""
|
1
|
+
"""Functions related to HTML reports."""
|
9
2
|
|
10
3
|
from __future__ import annotations
|
11
4
|
|
@@ -61,7 +54,7 @@ def calendar_period_returns(
|
|
61
54
|
Whether to set new appropriate labels
|
62
55
|
|
63
56
|
Returns:
|
64
|
-
|
57
|
+
--------
|
65
58
|
pandas.DataFrame
|
66
59
|
The resulting data
|
67
60
|
|
@@ -122,7 +115,7 @@ def report_html(
|
|
122
115
|
Determines whether to vertically align the legend's labels
|
123
116
|
|
124
117
|
Returns:
|
125
|
-
|
118
|
+
--------
|
126
119
|
tuple[plotly.go.Figure, str]
|
127
120
|
Plotly Figure and a div section or a html filename with location
|
128
121
|
|
@@ -264,7 +257,6 @@ def report_html(
|
|
264
257
|
"{:.2f}",
|
265
258
|
]
|
266
259
|
|
267
|
-
# noinspection PyTypeChecker
|
268
260
|
rpt_df = copied.all_properties(
|
269
261
|
properties=cast("list[LiteralFrameProps]", properties),
|
270
262
|
)
|
@@ -353,7 +345,9 @@ def report_html(
|
|
353
345
|
|
354
346
|
for item, f in zip(rpt_df.index, formats, strict=False):
|
355
347
|
rpt_df.loc[item] = rpt_df.loc[item].apply(
|
356
|
-
lambda x, fmt=f:
|
348
|
+
lambda x, fmt=f: str(x)
|
349
|
+
if (isinstance(x, str) or x is None)
|
350
|
+
else fmt.format(x),
|
357
351
|
)
|
358
352
|
|
359
353
|
rpt_df.index = Index(labels_init)
|
openseries/series.py
CHANGED
@@ -1,11 +1,4 @@
|
|
1
|
-
"""
|
2
|
-
|
3
|
-
Copyright (c) Captor Fund Management AB. This file is part of the openseries project.
|
4
|
-
|
5
|
-
Licensed under the BSD 3-Clause License. You may obtain a copy of the License at:
|
6
|
-
https://github.com/CaptorAB/openseries/blob/master/LICENSE.md
|
7
|
-
SPDX-License-Identifier: BSD-3-Clause
|
8
|
-
"""
|
1
|
+
"""The OpenTimeSeries class."""
|
9
2
|
|
10
3
|
from __future__ import annotations
|
11
4
|
|
@@ -15,7 +8,6 @@ from typing import TYPE_CHECKING, Any, TypeVar, cast
|
|
15
8
|
|
16
9
|
if TYPE_CHECKING: # pragma: no cover
|
17
10
|
import datetime as dt
|
18
|
-
from collections.abc import Callable
|
19
11
|
|
20
12
|
from numpy.typing import NDArray
|
21
13
|
from pandas import Timestamp
|
@@ -41,7 +33,7 @@ from pandas import (
|
|
41
33
|
)
|
42
34
|
from pydantic import field_validator, model_validator
|
43
35
|
|
44
|
-
from ._common_model import _CommonModel
|
36
|
+
from ._common_model import _calculate_time_factor, _CommonModel
|
45
37
|
from .datefixer import _do_resample_to_business_period_ends, date_fix
|
46
38
|
from .owntypes import (
|
47
39
|
Countries,
|
@@ -63,9 +55,6 @@ from .owntypes import (
|
|
63
55
|
ValueType,
|
64
56
|
)
|
65
57
|
|
66
|
-
FieldValidator = cast("Callable[..., Callable[..., Any]]", field_validator)
|
67
|
-
ModelValidator = cast("Callable[..., Callable[..., Any]]", model_validator)
|
68
|
-
|
69
58
|
logger = getLogger(__name__)
|
70
59
|
|
71
60
|
__all__ = ["OpenTimeSeries", "timeseries_chain"]
|
@@ -73,7 +62,6 @@ __all__ = ["OpenTimeSeries", "timeseries_chain"]
|
|
73
62
|
TypeOpenTimeSeries = TypeVar("TypeOpenTimeSeries", bound="OpenTimeSeries")
|
74
63
|
|
75
64
|
|
76
|
-
# noinspection PyUnresolvedReferences,PyNestedDecorators
|
77
65
|
class OpenTimeSeries(_CommonModel[float]):
|
78
66
|
"""OpenTimeSeries objects are at the core of the openseries package.
|
79
67
|
|
@@ -130,21 +118,21 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
130
118
|
isin: str | None = None
|
131
119
|
label: str | None = None
|
132
120
|
|
133
|
-
@
|
121
|
+
@field_validator("domestic", mode="before")
|
134
122
|
@classmethod
|
135
123
|
def _validate_domestic(cls, value: CurrencyStringType) -> CurrencyStringType:
|
136
124
|
"""Pydantic validator to ensure domestic field is validated."""
|
137
125
|
_ = Currency(ccy=value)
|
138
126
|
return value
|
139
127
|
|
140
|
-
@
|
128
|
+
@field_validator("countries", mode="before")
|
141
129
|
@classmethod
|
142
130
|
def _validate_countries(cls, value: CountriesType) -> CountriesType:
|
143
131
|
"""Pydantic validator to ensure countries field is validated."""
|
144
132
|
_ = Countries(countryinput=value)
|
145
133
|
return value
|
146
134
|
|
147
|
-
@
|
135
|
+
@field_validator("markets", mode="before")
|
148
136
|
@classmethod
|
149
137
|
def _validate_markets(
|
150
138
|
cls,
|
@@ -164,7 +152,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
164
152
|
raise MarketsNotStringNorListStrError(item_msg)
|
165
153
|
raise MarketsNotStringNorListStrError(msg)
|
166
154
|
|
167
|
-
@
|
155
|
+
@model_validator(mode="after")
|
168
156
|
def _dates_and_values_validate(self: Self) -> Self:
|
169
157
|
"""Pydantic validator to ensure dates and values are validated."""
|
170
158
|
values_list_length = len(self.values)
|
@@ -220,7 +208,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
220
208
|
Boolean flag indicating if timeseries is in local currency
|
221
209
|
|
222
210
|
Returns:
|
223
|
-
|
211
|
+
--------
|
224
212
|
OpenTimeSeries
|
225
213
|
An OpenTimeSeries object
|
226
214
|
|
@@ -270,7 +258,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
270
258
|
Boolean flag indicating if timeseries is in local currency
|
271
259
|
|
272
260
|
Returns:
|
273
|
-
|
261
|
+
--------
|
274
262
|
OpenTimeSeries
|
275
263
|
An OpenTimeSeries object
|
276
264
|
|
@@ -282,7 +270,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
282
270
|
label, _ = dframe.name
|
283
271
|
else:
|
284
272
|
label = dframe.name
|
285
|
-
values =
|
273
|
+
values = dframe.to_numpy().tolist()
|
286
274
|
elif isinstance(dframe, DataFrame):
|
287
275
|
values = dframe.iloc[:, column_nmbr].to_list()
|
288
276
|
if isinstance(dframe.columns, MultiIndex):
|
@@ -301,12 +289,11 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
301
289
|
msg = f"valuetype missing. Adding: {valuetype.value}"
|
302
290
|
logger.warning(msg=msg)
|
303
291
|
else:
|
304
|
-
valuetype =
|
305
|
-
|
306
|
-
|
307
|
-
)
|
292
|
+
valuetype = dframe.columns.get_level_values(1).to_numpy()[
|
293
|
+
column_nmbr
|
294
|
+
]
|
308
295
|
else:
|
309
|
-
label =
|
296
|
+
label = dframe.columns.to_numpy()[column_nmbr]
|
310
297
|
else:
|
311
298
|
raise TypeError(msg)
|
312
299
|
|
@@ -370,7 +357,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
370
357
|
Boolean flag indicating if timeseries is in local currency
|
371
358
|
|
372
359
|
Returns:
|
373
|
-
|
360
|
+
--------
|
374
361
|
OpenTimeSeries
|
375
362
|
An OpenTimeSeries object
|
376
363
|
|
@@ -409,7 +396,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
409
396
|
"""Create copy of OpenTimeSeries object.
|
410
397
|
|
411
398
|
Returns:
|
412
|
-
|
399
|
+
--------
|
413
400
|
OpenTimeSeries
|
414
401
|
An OpenTimeSeries object
|
415
402
|
|
@@ -420,7 +407,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
420
407
|
"""Populate .tsdf Pandas DataFrame from the .dates and .values lists.
|
421
408
|
|
422
409
|
Returns:
|
423
|
-
|
410
|
+
--------
|
424
411
|
OpenTimeSeries
|
425
412
|
An OpenTimeSeries object
|
426
413
|
|
@@ -447,7 +434,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
447
434
|
The properties to calculate. Defaults to calculating all available.
|
448
435
|
|
449
436
|
Returns:
|
450
|
-
|
437
|
+
--------
|
451
438
|
pandas.DataFrame
|
452
439
|
Properties of the OpenTimeSeries
|
453
440
|
|
@@ -467,12 +454,11 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
467
454
|
"""Convert series of values into series of returns.
|
468
455
|
|
469
456
|
Returns:
|
470
|
-
|
457
|
+
--------
|
471
458
|
OpenTimeSeries
|
472
459
|
The returns of the values in the series
|
473
460
|
|
474
461
|
"""
|
475
|
-
# noinspection PyCallingNonCallable
|
476
462
|
returns = self.tsdf.ffill().pct_change()
|
477
463
|
returns.iloc[0] = 0
|
478
464
|
self.valuetype = ValueType.RTRN
|
@@ -493,7 +479,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
493
479
|
is calculated
|
494
480
|
|
495
481
|
Returns:
|
496
|
-
|
482
|
+
--------
|
497
483
|
OpenTimeSeries
|
498
484
|
An OpenTimeSeries object
|
499
485
|
|
@@ -513,7 +499,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
513
499
|
"""Convert series of returns into cumulative series of values.
|
514
500
|
|
515
501
|
Returns:
|
516
|
-
|
502
|
+
--------
|
517
503
|
OpenTimeSeries
|
518
504
|
An OpenTimeSeries object
|
519
505
|
|
@@ -548,7 +534,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
548
534
|
Convenience divider for when the 1-day rate is not scaled correctly
|
549
535
|
|
550
536
|
Returns:
|
551
|
-
|
537
|
+
--------
|
552
538
|
OpenTimeSeries
|
553
539
|
An OpenTimeSeries object
|
554
540
|
|
@@ -589,7 +575,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
589
575
|
The date offset string that sets the resampled frequency
|
590
576
|
|
591
577
|
Returns:
|
592
|
-
|
578
|
+
--------
|
593
579
|
OpenTimeSeries
|
594
580
|
An OpenTimeSeries object
|
595
581
|
|
@@ -619,7 +605,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
619
605
|
Controls the method used to align values across columns
|
620
606
|
|
621
607
|
Returns:
|
622
|
-
|
608
|
+
--------
|
623
609
|
OpenTimeSeries
|
624
610
|
An OpenTimeSeries object
|
625
611
|
|
@@ -674,7 +660,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
674
660
|
Allows locking the periods-in-a-year to simplify test cases and comparisons
|
675
661
|
|
676
662
|
Returns:
|
677
|
-
|
663
|
+
--------
|
678
664
|
Pandas.Series[float]
|
679
665
|
Series EWMA volatility
|
680
666
|
|
@@ -684,16 +670,14 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
684
670
|
from_dt=from_date,
|
685
671
|
to_dt=to_date,
|
686
672
|
)
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
fraction = (later - earlier).days / 365.25
|
696
|
-
time_factor = how_many / fraction
|
673
|
+
time_factor = _calculate_time_factor(
|
674
|
+
data=self.tsdf.loc[
|
675
|
+
cast("Timestamp", earlier) : cast("Timestamp", later)
|
676
|
+
].iloc[:, 0],
|
677
|
+
earlier=earlier,
|
678
|
+
later=later,
|
679
|
+
periods_in_a_year_fixed=periods_in_a_year_fixed,
|
680
|
+
)
|
697
681
|
|
698
682
|
data = self.tsdf.loc[
|
699
683
|
cast("Timestamp", earlier) : cast("Timestamp", later)
|
@@ -741,7 +725,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
741
725
|
assumed number of days in a calendar year
|
742
726
|
|
743
727
|
Returns:
|
744
|
-
|
728
|
+
--------
|
745
729
|
OpenTimeSeries
|
746
730
|
An OpenTimeSeries object
|
747
731
|
|
@@ -757,7 +741,6 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
757
741
|
ra_df = ra_df.dropna()
|
758
742
|
|
759
743
|
prev = self.first_idx
|
760
|
-
# noinspection PyTypeChecker
|
761
744
|
dates: list[dt.date] = [prev]
|
762
745
|
|
763
746
|
for idx, row in ra_df.iterrows():
|
@@ -803,7 +786,7 @@ class OpenTimeSeries(_CommonModel[float]):
|
|
803
786
|
If True the level one label is deleted
|
804
787
|
|
805
788
|
Returns:
|
806
|
-
|
789
|
+
--------
|
807
790
|
OpenTimeSeries
|
808
791
|
An OpenTimeSeries object
|
809
792
|
|
@@ -845,7 +828,7 @@ def timeseries_chain(
|
|
845
828
|
Fee to apply to earlier series
|
846
829
|
|
847
830
|
Returns:
|
848
|
-
|
831
|
+
--------
|
849
832
|
TypeOpenTimeSeries
|
850
833
|
An OpenTimeSeries object or a subclass thereof
|
851
834
|
|
@@ -878,7 +861,6 @@ def timeseries_chain(
|
|
878
861
|
|
879
862
|
dates.extend([x.strftime("%Y-%m-%d") for x in new.tsdf.index])
|
880
863
|
|
881
|
-
# noinspection PyTypeChecker
|
882
864
|
return back.__class__(
|
883
865
|
timeseries_id=new.timeseries_id,
|
884
866
|
instrument_id=new.instrument_id,
|
@@ -907,7 +889,7 @@ def _check_if_none(item: Any) -> bool: # noqa: ANN401
|
|
907
889
|
variable to be checked
|
908
890
|
|
909
891
|
Returns:
|
910
|
-
|
892
|
+
--------
|
911
893
|
bool
|
912
894
|
Answer to whether the variable is None or equivalent
|
913
895
|
|