openseries 1.8.0__py3-none-any.whl → 1.8.2__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 +2 -1
- openseries/_common_model.py +180 -155
- openseries/_risk.py +4 -4
- openseries/datefixer.py +20 -14
- openseries/frame.py +124 -109
- openseries/load_plotly.py +6 -4
- openseries/owntypes.py +66 -5
- openseries/plotly_layouts.json +6 -6
- openseries/portfoliotools.py +33 -29
- openseries/series.py +69 -79
- openseries/simulation.py +15 -14
- openseries-1.8.2.dist-info/LICENSE.md +27 -0
- {openseries-1.8.0.dist-info → openseries-1.8.2.dist-info}/METADATA +44 -16
- openseries-1.8.2.dist-info/RECORD +16 -0
- {openseries-1.8.0.dist-info → openseries-1.8.2.dist-info}/WHEEL +1 -1
- openseries-1.8.0.dist-info/LICENSE.md +0 -27
- openseries-1.8.0.dist-info/RECORD +0 -16
openseries/frame.py
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
"""Defining the OpenFrame class."""
|
2
2
|
|
3
|
-
# mypy: disable-error-code="index,assignment,arg-type"
|
3
|
+
# mypy: disable-error-code="index,assignment,arg-type,no-any-return"
|
4
4
|
from __future__ import annotations
|
5
5
|
|
6
6
|
from copy import deepcopy
|
7
7
|
from functools import reduce
|
8
|
-
from logging import
|
8
|
+
from logging import getLogger
|
9
9
|
from typing import TYPE_CHECKING, cast
|
10
10
|
|
11
|
-
if TYPE_CHECKING:
|
12
|
-
import datetime as dt
|
11
|
+
if TYPE_CHECKING: # pragma: no cover
|
12
|
+
import datetime as dt
|
13
13
|
|
14
|
-
|
14
|
+
from statsmodels.regression.linear_model import ( # type: ignore[import-untyped]
|
15
|
+
OLSResults,
|
16
|
+
)
|
17
|
+
|
18
|
+
import statsmodels.api as sm # type: ignore[import-untyped]
|
15
19
|
from numpy import (
|
16
20
|
array,
|
17
21
|
cov,
|
@@ -27,7 +31,6 @@ from pandas import (
|
|
27
31
|
DataFrame,
|
28
32
|
DatetimeIndex,
|
29
33
|
Index,
|
30
|
-
Int64Dtype,
|
31
34
|
MultiIndex,
|
32
35
|
Series,
|
33
36
|
concat,
|
@@ -35,17 +38,12 @@ from pandas import (
|
|
35
38
|
)
|
36
39
|
from pydantic import field_validator
|
37
40
|
|
38
|
-
# noinspection PyProtectedMember
|
39
|
-
from statsmodels.regression.linear_model import ( # type: ignore[import-untyped,unused-ignore]
|
40
|
-
OLSResults,
|
41
|
-
)
|
42
|
-
from typing_extensions import Self
|
43
|
-
|
44
41
|
from ._common_model import _CommonModel
|
45
42
|
from .datefixer import _do_resample_to_business_period_ends
|
46
43
|
from .owntypes import (
|
47
44
|
CountriesType,
|
48
45
|
DaysInYearType,
|
46
|
+
LabelsNotUniqueError,
|
49
47
|
LiteralBizDayFreq,
|
50
48
|
LiteralCaptureRatio,
|
51
49
|
LiteralFrameProps,
|
@@ -55,11 +53,18 @@ from .owntypes import (
|
|
55
53
|
LiteralPandasReindexMethod,
|
56
54
|
LiteralPortfolioWeightings,
|
57
55
|
LiteralTrunc,
|
56
|
+
MergingResultedInEmptyError,
|
57
|
+
MixedValuetypesError,
|
58
|
+
NoWeightsError,
|
58
59
|
OpenFramePropertiesList,
|
60
|
+
RatioInputError,
|
61
|
+
Self,
|
59
62
|
ValueType,
|
60
63
|
)
|
61
64
|
from .series import OpenTimeSeries
|
62
65
|
|
66
|
+
logger = getLogger(__name__)
|
67
|
+
|
63
68
|
__all__ = ["OpenFrame"]
|
64
69
|
|
65
70
|
|
@@ -76,7 +81,7 @@ class OpenFrame(_CommonModel):
|
|
76
81
|
weights: list[float], optional
|
77
82
|
List of weights in float format.
|
78
83
|
|
79
|
-
Returns
|
84
|
+
Returns:
|
80
85
|
-------
|
81
86
|
OpenFrame
|
82
87
|
Object of the class OpenFrame
|
@@ -89,7 +94,7 @@ class OpenFrame(_CommonModel):
|
|
89
94
|
|
90
95
|
# noinspection PyMethodParameters
|
91
96
|
@field_validator("constituents") # type: ignore[misc]
|
92
|
-
def _check_labels_unique(
|
97
|
+
def _check_labels_unique( # type: ignore[misc]
|
93
98
|
cls: OpenFrame, # noqa: N805
|
94
99
|
tseries: list[OpenTimeSeries],
|
95
100
|
) -> list[OpenTimeSeries]:
|
@@ -97,7 +102,7 @@ class OpenFrame(_CommonModel):
|
|
97
102
|
labls = [x.label for x in tseries]
|
98
103
|
if len(set(labls)) != len(labls):
|
99
104
|
msg = "TimeSeries names/labels must be unique"
|
100
|
-
raise
|
105
|
+
raise LabelsNotUniqueError(msg)
|
101
106
|
return tseries
|
102
107
|
|
103
108
|
def __init__(
|
@@ -116,7 +121,7 @@ class OpenFrame(_CommonModel):
|
|
116
121
|
weights: list[float], optional
|
117
122
|
List of weights in float format.
|
118
123
|
|
119
|
-
Returns
|
124
|
+
Returns:
|
120
125
|
-------
|
121
126
|
OpenFrame
|
122
127
|
Object of the class OpenFrame
|
@@ -139,12 +144,12 @@ class OpenFrame(_CommonModel):
|
|
139
144
|
[x.tsdf for x in self.constituents],
|
140
145
|
)
|
141
146
|
else:
|
142
|
-
warning("OpenFrame() was passed an empty list.")
|
147
|
+
logger.warning("OpenFrame() was passed an empty list.")
|
143
148
|
|
144
149
|
def from_deepcopy(self: Self) -> Self:
|
145
150
|
"""Create copy of the OpenFrame object.
|
146
151
|
|
147
|
-
Returns
|
152
|
+
Returns:
|
148
153
|
-------
|
149
154
|
OpenFrame
|
150
155
|
An OpenFrame object
|
@@ -163,7 +168,7 @@ class OpenFrame(_CommonModel):
|
|
163
168
|
how: LiteralHowMerge, default: "outer"
|
164
169
|
The Pandas merge method.
|
165
170
|
|
166
|
-
Returns
|
171
|
+
Returns:
|
167
172
|
-------
|
168
173
|
OpenFrame
|
169
174
|
An OpenFrame object
|
@@ -189,7 +194,7 @@ class OpenFrame(_CommonModel):
|
|
189
194
|
"Merging OpenTimeSeries DataFrames with "
|
190
195
|
f"argument how={how} produced an empty DataFrame."
|
191
196
|
)
|
192
|
-
raise
|
197
|
+
raise MergingResultedInEmptyError(msg)
|
193
198
|
|
194
199
|
if how == "inner":
|
195
200
|
for xerie in self.constituents:
|
@@ -207,7 +212,7 @@ class OpenFrame(_CommonModel):
|
|
207
212
|
properties: list[LiteralFrameProps], optional
|
208
213
|
The properties to calculate. Defaults to calculating all available.
|
209
214
|
|
210
|
-
Returns
|
215
|
+
Returns:
|
211
216
|
-------
|
212
217
|
pandas.DataFrame
|
213
218
|
Properties of the contituent OpenTimeSeries
|
@@ -220,30 +225,29 @@ class OpenFrame(_CommonModel):
|
|
220
225
|
prop_list = [
|
221
226
|
getattr(self, x) for x in OpenFramePropertiesList.allowed_strings
|
222
227
|
]
|
223
|
-
return cast(DataFrame, concat(prop_list, axis="columns").T)
|
228
|
+
return cast("DataFrame", concat(prop_list, axis="columns").T)
|
224
229
|
|
225
230
|
@property
|
226
231
|
def lengths_of_items(self: Self) -> Series[int]:
|
227
232
|
"""Number of observations of all constituents.
|
228
233
|
|
229
|
-
Returns
|
234
|
+
Returns:
|
230
235
|
-------
|
231
236
|
Pandas.Series[int]
|
232
237
|
Number of observations of all constituents
|
233
238
|
|
234
239
|
"""
|
235
240
|
return Series(
|
236
|
-
data=[
|
241
|
+
data=[self.tsdf.loc[:, d].count() for d in self.tsdf],
|
237
242
|
index=self.tsdf.columns,
|
238
243
|
name="observations",
|
239
|
-
|
240
|
-
)
|
244
|
+
).astype(int)
|
241
245
|
|
242
246
|
@property
|
243
247
|
def item_count(self: Self) -> int:
|
244
248
|
"""Number of constituents.
|
245
249
|
|
246
|
-
Returns
|
250
|
+
Returns:
|
247
251
|
-------
|
248
252
|
int
|
249
253
|
Number of constituents
|
@@ -255,7 +259,7 @@ class OpenFrame(_CommonModel):
|
|
255
259
|
def columns_lvl_zero(self: Self) -> list[str]:
|
256
260
|
"""Level 0 values of the MultiIndex columns in the .tsdf DataFrame.
|
257
261
|
|
258
|
-
Returns
|
262
|
+
Returns:
|
259
263
|
-------
|
260
264
|
list[str]
|
261
265
|
Level 0 values of the MultiIndex columns in the .tsdf DataFrame
|
@@ -267,7 +271,7 @@ class OpenFrame(_CommonModel):
|
|
267
271
|
def columns_lvl_one(self: Self) -> list[ValueType]:
|
268
272
|
"""Level 1 values of the MultiIndex columns in the .tsdf DataFrame.
|
269
273
|
|
270
|
-
Returns
|
274
|
+
Returns:
|
271
275
|
-------
|
272
276
|
list[ValueType]
|
273
277
|
Level 1 values of the MultiIndex columns in the .tsdf DataFrame
|
@@ -279,7 +283,7 @@ class OpenFrame(_CommonModel):
|
|
279
283
|
def first_indices(self: Self) -> Series[dt.date]:
|
280
284
|
"""The first dates in the timeseries of all constituents.
|
281
285
|
|
282
|
-
Returns
|
286
|
+
Returns:
|
283
287
|
-------
|
284
288
|
Pandas.Series[dt.date]
|
285
289
|
The first dates in the timeseries of all constituents
|
@@ -296,7 +300,7 @@ class OpenFrame(_CommonModel):
|
|
296
300
|
def last_indices(self: Self) -> Series[dt.date]:
|
297
301
|
"""The last dates in the timeseries of all constituents.
|
298
302
|
|
299
|
-
Returns
|
303
|
+
Returns:
|
300
304
|
-------
|
301
305
|
Pandas.Series[dt.date]
|
302
306
|
The last dates in the timeseries of all constituents
|
@@ -313,7 +317,7 @@ class OpenFrame(_CommonModel):
|
|
313
317
|
def span_of_days_all(self: Self) -> Series[int]:
|
314
318
|
"""Number of days from the first date to the last for all items in the frame.
|
315
319
|
|
316
|
-
Returns
|
320
|
+
Returns:
|
317
321
|
-------
|
318
322
|
Pandas.Series[int]
|
319
323
|
Number of days from the first date to the last for all
|
@@ -324,13 +328,12 @@ class OpenFrame(_CommonModel):
|
|
324
328
|
data=[c.span_of_days for c in self.constituents],
|
325
329
|
index=self.tsdf.columns,
|
326
330
|
name="span of days",
|
327
|
-
|
328
|
-
)
|
331
|
+
).astype(int)
|
329
332
|
|
330
333
|
def value_to_ret(self: Self) -> Self:
|
331
334
|
"""Convert series of values into series of returns.
|
332
335
|
|
333
|
-
Returns
|
336
|
+
Returns:
|
334
337
|
-------
|
335
338
|
OpenFrame
|
336
339
|
The returns of the values in the series
|
@@ -353,7 +356,7 @@ class OpenFrame(_CommonModel):
|
|
353
356
|
The number of periods between observations over which difference
|
354
357
|
is calculated
|
355
358
|
|
356
|
-
Returns
|
359
|
+
Returns:
|
357
360
|
-------
|
358
361
|
OpenFrame
|
359
362
|
An OpenFrame object
|
@@ -369,7 +372,7 @@ class OpenFrame(_CommonModel):
|
|
369
372
|
def to_cumret(self: Self) -> Self:
|
370
373
|
"""Convert series of returns into cumulative series of values.
|
371
374
|
|
372
|
-
Returns
|
375
|
+
Returns:
|
373
376
|
-------
|
374
377
|
OpenFrame
|
375
378
|
An OpenFrame object
|
@@ -384,7 +387,7 @@ class OpenFrame(_CommonModel):
|
|
384
387
|
returns.iloc[0] = 0
|
385
388
|
else:
|
386
389
|
msg = "Mix of series types will give inconsistent results"
|
387
|
-
raise
|
390
|
+
raise MixedValuetypesError(msg)
|
388
391
|
|
389
392
|
returns = returns.add(1.0)
|
390
393
|
self.tsdf = returns.cumprod(axis=0) / returns.iloc[0]
|
@@ -405,7 +408,7 @@ class OpenFrame(_CommonModel):
|
|
405
408
|
freq: LiteralBizDayFreq | str, default "BME"
|
406
409
|
The date offset string that sets the resampled frequency
|
407
410
|
|
408
|
-
Returns
|
411
|
+
Returns:
|
409
412
|
-------
|
410
413
|
OpenFrame
|
411
414
|
An OpenFrame object
|
@@ -443,7 +446,7 @@ class OpenFrame(_CommonModel):
|
|
443
446
|
method: LiteralPandasReindexMethod, default: nearest
|
444
447
|
Controls the method used to align values across columns
|
445
448
|
|
446
|
-
Returns
|
449
|
+
Returns:
|
447
450
|
-------
|
448
451
|
OpenFrame
|
449
452
|
An OpenFrame object
|
@@ -511,7 +514,7 @@ class OpenFrame(_CommonModel):
|
|
511
514
|
Allows locking the periods-in-a-year to simplify test cases and
|
512
515
|
comparisons
|
513
516
|
|
514
|
-
Returns
|
517
|
+
Returns:
|
515
518
|
-------
|
516
519
|
Pandas.DataFrame
|
517
520
|
Series volatilities and correlation
|
@@ -521,23 +524,25 @@ class OpenFrame(_CommonModel):
|
|
521
524
|
if periods_in_a_year_fixed is None:
|
522
525
|
fraction = (later - earlier).days / 365.25
|
523
526
|
how_many = (
|
524
|
-
self.tsdf.loc[cast(int, earlier) : cast(int, later)]
|
527
|
+
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
528
|
+
.count()
|
529
|
+
.iloc[0]
|
525
530
|
)
|
526
531
|
time_factor = how_many / fraction
|
527
532
|
else:
|
528
533
|
time_factor = periods_in_a_year_fixed
|
529
534
|
|
530
535
|
corr_label = (
|
531
|
-
cast(tuple[str, str], self.tsdf.iloc[:, first_column].name)[0]
|
536
|
+
cast("tuple[str, str]", self.tsdf.iloc[:, first_column].name)[0]
|
532
537
|
+ "_VS_"
|
533
|
-
+ cast(tuple[str, str], self.tsdf.iloc[:, second_column].name)[0]
|
538
|
+
+ cast("tuple[str, str]", self.tsdf.iloc[:, second_column].name)[0]
|
534
539
|
)
|
535
540
|
cols = [
|
536
|
-
cast(tuple[str, str], self.tsdf.iloc[:, first_column].name)[0],
|
537
|
-
cast(tuple[str, str], self.tsdf.iloc[:, second_column].name)[0],
|
541
|
+
cast("tuple[str, str]", self.tsdf.iloc[:, first_column].name)[0],
|
542
|
+
cast("tuple[str, str]", self.tsdf.iloc[:, second_column].name)[0],
|
538
543
|
]
|
539
544
|
|
540
|
-
data = self.tsdf.loc[cast(int, earlier) : cast(int, later)].copy()
|
545
|
+
data = self.tsdf.loc[cast("int", earlier) : cast("int", later)].copy()
|
541
546
|
|
542
547
|
for rtn in cols:
|
543
548
|
data[rtn, ValueType.RTRN] = (
|
@@ -597,7 +602,7 @@ class OpenFrame(_CommonModel):
|
|
597
602
|
def correl_matrix(self: Self) -> DataFrame:
|
598
603
|
"""Correlation matrix.
|
599
604
|
|
600
|
-
Returns
|
605
|
+
Returns:
|
601
606
|
-------
|
602
607
|
Pandas.DataFrame
|
603
608
|
Correlation matrix
|
@@ -627,7 +632,7 @@ class OpenFrame(_CommonModel):
|
|
627
632
|
new_series: OpenTimeSeries
|
628
633
|
The timeseries to add
|
629
634
|
|
630
|
-
Returns
|
635
|
+
Returns:
|
631
636
|
-------
|
632
637
|
OpenFrame
|
633
638
|
An OpenFrame object
|
@@ -645,7 +650,7 @@ class OpenFrame(_CommonModel):
|
|
645
650
|
lvl_zero_item: str
|
646
651
|
The .tsdf column level 0 value of the timeseries to delete
|
647
652
|
|
648
|
-
Returns
|
653
|
+
Returns:
|
649
654
|
-------
|
650
655
|
OpenFrame
|
651
656
|
An OpenFrame object
|
@@ -684,7 +689,7 @@ class OpenFrame(_CommonModel):
|
|
684
689
|
Determines where dataframe is truncated also when start_cut
|
685
690
|
or end_cut is None.
|
686
691
|
|
687
|
-
Returns
|
692
|
+
Returns:
|
688
693
|
-------
|
689
694
|
OpenFrame
|
690
695
|
An OpenFrame object
|
@@ -709,14 +714,14 @@ class OpenFrame(_CommonModel):
|
|
709
714
|
f"not truncated to same start dates.\n"
|
710
715
|
f"{self.tsdf.head()}"
|
711
716
|
)
|
712
|
-
warning(msg=msg)
|
717
|
+
logger.warning(msg=msg)
|
713
718
|
if len(set(self.last_indices)) != 1:
|
714
719
|
msg = (
|
715
720
|
f"One or more constituents still "
|
716
721
|
f"not truncated to same end dates.\n"
|
717
722
|
f"{self.tsdf.tail()}"
|
718
723
|
)
|
719
|
-
warning(msg=msg)
|
724
|
+
logger.warning(msg=msg)
|
720
725
|
return self
|
721
726
|
|
722
727
|
def relative(
|
@@ -740,9 +745,9 @@ class OpenFrame(_CommonModel):
|
|
740
745
|
|
741
746
|
"""
|
742
747
|
rel_label = (
|
743
|
-
cast(tuple[str, str], self.tsdf.iloc[:, long_column].name)[0]
|
748
|
+
cast("tuple[str, str]", self.tsdf.iloc[:, long_column].name)[0]
|
744
749
|
+ "_over_"
|
745
|
-
+ cast(tuple[str, str], self.tsdf.iloc[:, short_column].name)[0]
|
750
|
+
+ cast("tuple[str, str]", self.tsdf.iloc[:, short_column].name)[0]
|
746
751
|
)
|
747
752
|
if base_zero:
|
748
753
|
self.tsdf[rel_label, ValueType.RELRTRN] = (
|
@@ -785,7 +790,7 @@ class OpenFrame(_CommonModel):
|
|
785
790
|
Allows locking the periods-in-a-year to simplify test cases and
|
786
791
|
comparisons
|
787
792
|
|
788
|
-
Returns
|
793
|
+
Returns:
|
789
794
|
-------
|
790
795
|
Pandas.Series[float]
|
791
796
|
Tracking Errors
|
@@ -796,17 +801,17 @@ class OpenFrame(_CommonModel):
|
|
796
801
|
|
797
802
|
msg = "base_column should be a tuple[str, ValueType] or an integer."
|
798
803
|
if isinstance(base_column, tuple):
|
799
|
-
shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
|
804
|
+
shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
|
800
805
|
:,
|
801
806
|
base_column,
|
802
807
|
]
|
803
808
|
short_item = base_column
|
804
809
|
short_label = cast(
|
805
|
-
tuple[str, ValueType],
|
810
|
+
"tuple[str, ValueType]",
|
806
811
|
self.tsdf.loc[:, base_column].name,
|
807
812
|
)[0]
|
808
813
|
elif isinstance(base_column, int):
|
809
|
-
shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].iloc[
|
814
|
+
shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].iloc[
|
810
815
|
:,
|
811
816
|
base_column,
|
812
817
|
]
|
@@ -814,7 +819,9 @@ class OpenFrame(_CommonModel):
|
|
814
819
|
:,
|
815
820
|
base_column,
|
816
821
|
].name
|
817
|
-
short_label = cast(tuple[str, str], self.tsdf.iloc[:, base_column].name)[
|
822
|
+
short_label = cast("tuple[str, str]", self.tsdf.iloc[:, base_column].name)[
|
823
|
+
0
|
824
|
+
]
|
818
825
|
else:
|
819
826
|
raise TypeError(msg)
|
820
827
|
|
@@ -828,7 +835,7 @@ class OpenFrame(_CommonModel):
|
|
828
835
|
if item == short_item:
|
829
836
|
terrors.append(0.0)
|
830
837
|
else:
|
831
|
-
longdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
|
838
|
+
longdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
|
832
839
|
:,
|
833
840
|
item,
|
834
841
|
]
|
@@ -875,7 +882,7 @@ class OpenFrame(_CommonModel):
|
|
875
882
|
Allows locking the periods-in-a-year to simplify test cases and
|
876
883
|
comparisons
|
877
884
|
|
878
|
-
Returns
|
885
|
+
Returns:
|
879
886
|
-------
|
880
887
|
Pandas.Series[float]
|
881
888
|
Information Ratios
|
@@ -886,17 +893,17 @@ class OpenFrame(_CommonModel):
|
|
886
893
|
|
887
894
|
msg = "base_column should be a tuple[str, ValueType] or an integer."
|
888
895
|
if isinstance(base_column, tuple):
|
889
|
-
shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
|
896
|
+
shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
|
890
897
|
:,
|
891
898
|
base_column,
|
892
899
|
]
|
893
900
|
short_item = base_column
|
894
901
|
short_label = cast(
|
895
|
-
tuple[str, str],
|
902
|
+
"tuple[str, str]",
|
896
903
|
self.tsdf.loc[:, base_column].name,
|
897
904
|
)[0]
|
898
905
|
elif isinstance(base_column, int):
|
899
|
-
shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].iloc[
|
906
|
+
shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].iloc[
|
900
907
|
:,
|
901
908
|
base_column,
|
902
909
|
]
|
@@ -904,7 +911,9 @@ class OpenFrame(_CommonModel):
|
|
904
911
|
:,
|
905
912
|
base_column,
|
906
913
|
].name
|
907
|
-
short_label = cast(tuple[str, str], self.tsdf.iloc[:, base_column].name)[
|
914
|
+
short_label = cast("tuple[str, str]", self.tsdf.iloc[:, base_column].name)[
|
915
|
+
0
|
916
|
+
]
|
908
917
|
else:
|
909
918
|
raise TypeError(msg)
|
910
919
|
|
@@ -918,7 +927,7 @@ class OpenFrame(_CommonModel):
|
|
918
927
|
if item == short_item:
|
919
928
|
ratios.append(0.0)
|
920
929
|
else:
|
921
|
-
longdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
|
930
|
+
longdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
|
922
931
|
:,
|
923
932
|
item,
|
924
933
|
]
|
@@ -938,7 +947,7 @@ class OpenFrame(_CommonModel):
|
|
938
947
|
dtype="float64",
|
939
948
|
)
|
940
949
|
|
941
|
-
def capture_ratio_func(
|
950
|
+
def capture_ratio_func(
|
942
951
|
self: Self,
|
943
952
|
ratio: LiteralCaptureRatio,
|
944
953
|
base_column: tuple[str, ValueType] | int = -1,
|
@@ -975,7 +984,7 @@ class OpenFrame(_CommonModel):
|
|
975
984
|
Allows locking the periods-in-a-year to simplify test cases and
|
976
985
|
comparisons
|
977
986
|
|
978
|
-
Returns
|
987
|
+
Returns:
|
979
988
|
-------
|
980
989
|
Pandas.Series[float]
|
981
990
|
Capture Ratios
|
@@ -987,17 +996,17 @@ class OpenFrame(_CommonModel):
|
|
987
996
|
|
988
997
|
msg = "base_column should be a tuple[str, ValueType] or an integer."
|
989
998
|
if isinstance(base_column, tuple):
|
990
|
-
shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
|
999
|
+
shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
|
991
1000
|
:,
|
992
1001
|
base_column,
|
993
1002
|
]
|
994
1003
|
short_item = base_column
|
995
1004
|
short_label = cast(
|
996
|
-
tuple[str, str],
|
1005
|
+
"tuple[str, str]",
|
997
1006
|
self.tsdf.loc[:, base_column].name,
|
998
1007
|
)[0]
|
999
1008
|
elif isinstance(base_column, int):
|
1000
|
-
shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].iloc[
|
1009
|
+
shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].iloc[
|
1001
1010
|
:,
|
1002
1011
|
base_column,
|
1003
1012
|
]
|
@@ -1005,7 +1014,9 @@ class OpenFrame(_CommonModel):
|
|
1005
1014
|
:,
|
1006
1015
|
base_column,
|
1007
1016
|
].name
|
1008
|
-
short_label = cast(tuple[str, str], self.tsdf.iloc[:, base_column].name)[
|
1017
|
+
short_label = cast("tuple[str, str]", self.tsdf.iloc[:, base_column].name)[
|
1018
|
+
0
|
1019
|
+
]
|
1009
1020
|
else:
|
1010
1021
|
raise TypeError(msg)
|
1011
1022
|
|
@@ -1019,7 +1030,7 @@ class OpenFrame(_CommonModel):
|
|
1019
1030
|
if item == short_item:
|
1020
1031
|
ratios.append(0.0)
|
1021
1032
|
else:
|
1022
|
-
longdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
|
1033
|
+
longdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
|
1023
1034
|
:,
|
1024
1035
|
item,
|
1025
1036
|
]
|
@@ -1119,7 +1130,7 @@ class OpenFrame(_CommonModel):
|
|
1119
1130
|
(up_rtrn / up_idx_return) / (down_return / down_idx_return),
|
1120
1131
|
)
|
1121
1132
|
else:
|
1122
|
-
raise
|
1133
|
+
raise RatioInputError(msg)
|
1123
1134
|
|
1124
1135
|
if ratio == "up":
|
1125
1136
|
resultname = f"Up Capture Ratios vs {short_label}"
|
@@ -1155,7 +1166,7 @@ class OpenFrame(_CommonModel):
|
|
1155
1166
|
dlta_degr_freedms: int, default: 1
|
1156
1167
|
Variance bias factor taking the value 0 or 1.
|
1157
1168
|
|
1158
|
-
Returns
|
1169
|
+
Returns:
|
1159
1170
|
-------
|
1160
1171
|
float
|
1161
1172
|
Beta as Co-variance of x & y divided by Variance of x
|
@@ -1188,7 +1199,7 @@ class OpenFrame(_CommonModel):
|
|
1188
1199
|
)
|
1189
1200
|
elif isinstance(asset, int):
|
1190
1201
|
y_value = log(
|
1191
|
-
self.tsdf.iloc[:, asset] / cast(float, self.tsdf.iloc[0, asset]),
|
1202
|
+
self.tsdf.iloc[:, asset] / cast("float", self.tsdf.iloc[0, asset]),
|
1192
1203
|
)
|
1193
1204
|
else:
|
1194
1205
|
raise TypeError(msg)
|
@@ -1200,7 +1211,8 @@ class OpenFrame(_CommonModel):
|
|
1200
1211
|
)
|
1201
1212
|
elif isinstance(market, int):
|
1202
1213
|
x_value = log(
|
1203
|
-
self.tsdf.iloc[:, market]
|
1214
|
+
self.tsdf.iloc[:, market]
|
1215
|
+
/ cast("float", self.tsdf.iloc[0, market]),
|
1204
1216
|
)
|
1205
1217
|
else:
|
1206
1218
|
raise TypeError(msg)
|
@@ -1238,7 +1250,7 @@ class OpenFrame(_CommonModel):
|
|
1238
1250
|
fitted_series: bool, default: True
|
1239
1251
|
If True the fit is added as a new column in the .tsdf Pandas.DataFrame
|
1240
1252
|
|
1241
|
-
Returns
|
1253
|
+
Returns:
|
1242
1254
|
-------
|
1243
1255
|
OLSResults
|
1244
1256
|
The Statsmodels regression output
|
@@ -1248,12 +1260,12 @@ class OpenFrame(_CommonModel):
|
|
1248
1260
|
if isinstance(y_column, tuple):
|
1249
1261
|
y_value = self.tsdf.loc[:, y_column]
|
1250
1262
|
y_label = cast(
|
1251
|
-
tuple[str, str],
|
1263
|
+
"tuple[str, str]",
|
1252
1264
|
self.tsdf.loc[:, y_column].name,
|
1253
1265
|
)[0]
|
1254
1266
|
elif isinstance(y_column, int):
|
1255
1267
|
y_value = self.tsdf.iloc[:, y_column]
|
1256
|
-
y_label = cast(tuple[str, str], self.tsdf.iloc[:, y_column].name)[0]
|
1268
|
+
y_label = cast("tuple[str, str]", self.tsdf.iloc[:, y_column].name)[0]
|
1257
1269
|
else:
|
1258
1270
|
raise TypeError(msg)
|
1259
1271
|
|
@@ -1261,12 +1273,12 @@ class OpenFrame(_CommonModel):
|
|
1261
1273
|
if isinstance(x_column, tuple):
|
1262
1274
|
x_value = self.tsdf.loc[:, x_column]
|
1263
1275
|
x_label = cast(
|
1264
|
-
tuple[str, str],
|
1276
|
+
"tuple[str, str]",
|
1265
1277
|
self.tsdf.loc[:, x_column].name,
|
1266
1278
|
)[0]
|
1267
1279
|
elif isinstance(x_column, int):
|
1268
1280
|
x_value = self.tsdf.iloc[:, x_column]
|
1269
|
-
x_label = cast(tuple[str, str], self.tsdf.iloc[:, x_column].name)[0]
|
1281
|
+
x_label = cast("tuple[str, str]", self.tsdf.iloc[:, x_column].name)[0]
|
1270
1282
|
else:
|
1271
1283
|
raise TypeError(msg)
|
1272
1284
|
|
@@ -1274,9 +1286,9 @@ class OpenFrame(_CommonModel):
|
|
1274
1286
|
if fitted_series:
|
1275
1287
|
self.tsdf[y_label, x_label] = results.predict(x_value)
|
1276
1288
|
|
1277
|
-
return cast(OLSResults, results)
|
1289
|
+
return cast("OLSResults", results)
|
1278
1290
|
|
1279
|
-
def jensen_alpha(
|
1291
|
+
def jensen_alpha(
|
1280
1292
|
self: Self,
|
1281
1293
|
asset: tuple[str, ValueType] | int,
|
1282
1294
|
market: tuple[str, ValueType] | int,
|
@@ -1303,7 +1315,7 @@ class OpenFrame(_CommonModel):
|
|
1303
1315
|
dlta_degr_freedms: int, default: 1
|
1304
1316
|
Variance bias factor taking the value 0 or 1.
|
1305
1317
|
|
1306
|
-
Returns
|
1318
|
+
Returns:
|
1307
1319
|
-------
|
1308
1320
|
float
|
1309
1321
|
Jensen's alpha
|
@@ -1330,17 +1342,17 @@ class OpenFrame(_CommonModel):
|
|
1330
1342
|
)
|
1331
1343
|
elif isinstance(asset, int):
|
1332
1344
|
asset_log = log(
|
1333
|
-
self.tsdf.iloc[:, asset] / cast(float, self.tsdf.iloc[0, asset]),
|
1345
|
+
self.tsdf.iloc[:, asset] / cast("float", self.tsdf.iloc[0, asset]),
|
1334
1346
|
)
|
1335
1347
|
if self.yearfrac > full_year:
|
1336
1348
|
asset_cagr = (
|
1337
|
-
cast(float, self.tsdf.iloc[-1, asset])
|
1338
|
-
/ cast(float, self.tsdf.iloc[0, asset])
|
1349
|
+
cast("float", self.tsdf.iloc[-1, asset])
|
1350
|
+
/ cast("float", self.tsdf.iloc[0, asset])
|
1339
1351
|
) ** (1 / self.yearfrac) - 1
|
1340
1352
|
else:
|
1341
1353
|
asset_cagr = (
|
1342
|
-
cast(float, self.tsdf.iloc[-1, asset])
|
1343
|
-
/ cast(float, self.tsdf.iloc[0, asset])
|
1354
|
+
cast("float", self.tsdf.iloc[-1, asset])
|
1355
|
+
/ cast("float", self.tsdf.iloc[0, asset])
|
1344
1356
|
- 1
|
1345
1357
|
)
|
1346
1358
|
else:
|
@@ -1364,17 +1376,18 @@ class OpenFrame(_CommonModel):
|
|
1364
1376
|
)
|
1365
1377
|
elif isinstance(market, int):
|
1366
1378
|
market_log = log(
|
1367
|
-
self.tsdf.iloc[:, market]
|
1379
|
+
self.tsdf.iloc[:, market]
|
1380
|
+
/ cast("float", self.tsdf.iloc[0, market]),
|
1368
1381
|
)
|
1369
1382
|
if self.yearfrac > full_year:
|
1370
1383
|
market_cagr = (
|
1371
|
-
cast(float, self.tsdf.iloc[-1, market])
|
1372
|
-
/ cast(float, self.tsdf.iloc[0, market])
|
1384
|
+
cast("float", self.tsdf.iloc[-1, market])
|
1385
|
+
/ cast("float", self.tsdf.iloc[0, market])
|
1373
1386
|
) ** (1 / self.yearfrac) - 1
|
1374
1387
|
else:
|
1375
1388
|
market_cagr = (
|
1376
|
-
cast(float, self.tsdf.iloc[-1, market])
|
1377
|
-
/ cast(float, self.tsdf.iloc[0, market])
|
1389
|
+
cast("float", self.tsdf.iloc[-1, market])
|
1390
|
+
/ cast("float", self.tsdf.iloc[0, market])
|
1378
1391
|
- 1
|
1379
1392
|
)
|
1380
1393
|
else:
|
@@ -1401,7 +1414,7 @@ class OpenFrame(_CommonModel):
|
|
1401
1414
|
raise TypeError(msg)
|
1402
1415
|
else:
|
1403
1416
|
msg = "Mix of series types will give inconsistent results"
|
1404
|
-
raise
|
1417
|
+
raise MixedValuetypesError(msg)
|
1405
1418
|
|
1406
1419
|
covariance = cov(asset_log, market_log, ddof=dlta_degr_freedms)
|
1407
1420
|
beta = covariance[0, 1] / covariance[1, 1]
|
@@ -1422,7 +1435,7 @@ class OpenFrame(_CommonModel):
|
|
1422
1435
|
weight_strat: LiteralPortfolioWeightings, optional
|
1423
1436
|
weight calculation strategies
|
1424
1437
|
|
1425
|
-
Returns
|
1438
|
+
Returns:
|
1426
1439
|
-------
|
1427
1440
|
Pandas.DataFrame
|
1428
1441
|
A basket timeseries
|
@@ -1433,7 +1446,7 @@ class OpenFrame(_CommonModel):
|
|
1433
1446
|
"OpenFrame weights property must be provided "
|
1434
1447
|
"to run the make_portfolio method."
|
1435
1448
|
)
|
1436
|
-
raise
|
1449
|
+
raise NoWeightsError(msg)
|
1437
1450
|
|
1438
1451
|
vtypes = [x == ValueType.RTRN for x in self.tsdf.columns.get_level_values(1)]
|
1439
1452
|
if not any(vtypes):
|
@@ -1443,7 +1456,7 @@ class OpenFrame(_CommonModel):
|
|
1443
1456
|
returns = self.tsdf.copy()
|
1444
1457
|
else:
|
1445
1458
|
msg = "Mix of series types will give inconsistent results"
|
1446
|
-
raise
|
1459
|
+
raise MixedValuetypesError(msg)
|
1447
1460
|
|
1448
1461
|
msg = "Weight strategy not implemented"
|
1449
1462
|
if weight_strat:
|
@@ -1487,18 +1500,18 @@ class OpenFrame(_CommonModel):
|
|
1487
1500
|
periods_in_a_year_fixed : DaysInYearType, optional
|
1488
1501
|
Allows locking the periods-in-a-year to simplify test cases and comparisons
|
1489
1502
|
|
1490
|
-
Returns
|
1503
|
+
Returns:
|
1491
1504
|
-------
|
1492
1505
|
Pandas.DataFrame
|
1493
1506
|
Rolling Information Ratios
|
1494
1507
|
|
1495
1508
|
"""
|
1496
1509
|
long_label = cast(
|
1497
|
-
tuple[str, str],
|
1510
|
+
"tuple[str, str]",
|
1498
1511
|
self.tsdf.iloc[:, long_column].name,
|
1499
1512
|
)[0]
|
1500
1513
|
short_label = cast(
|
1501
|
-
tuple[str, str],
|
1514
|
+
"tuple[str, str]",
|
1502
1515
|
self.tsdf.iloc[:, short_column].name,
|
1503
1516
|
)[0]
|
1504
1517
|
ratio_label = f"{long_label} / {short_label}"
|
@@ -1553,14 +1566,16 @@ class OpenFrame(_CommonModel):
|
|
1553
1566
|
dlta_degr_freedms: int, default: 1
|
1554
1567
|
Variance bias factor taking the value 0 or 1.
|
1555
1568
|
|
1556
|
-
Returns
|
1569
|
+
Returns:
|
1557
1570
|
-------
|
1558
1571
|
Pandas.DataFrame
|
1559
1572
|
Rolling Betas
|
1560
1573
|
|
1561
1574
|
"""
|
1562
|
-
market_label = cast(tuple[str, str], self.tsdf.iloc[:, market_column].name)[
|
1563
|
-
|
1575
|
+
market_label = cast("tuple[str, str]", self.tsdf.iloc[:, market_column].name)[
|
1576
|
+
0
|
1577
|
+
]
|
1578
|
+
asset_label = cast("tuple[str, str]", self.tsdf.iloc[:, asset_column].name)[0]
|
1564
1579
|
beta_label = f"{asset_label} / {market_label}"
|
1565
1580
|
|
1566
1581
|
rolling = (
|
@@ -1611,16 +1626,16 @@ class OpenFrame(_CommonModel):
|
|
1611
1626
|
observations: int, default: 21
|
1612
1627
|
The length of the rolling window to use is set as number of observations
|
1613
1628
|
|
1614
|
-
Returns
|
1629
|
+
Returns:
|
1615
1630
|
-------
|
1616
1631
|
Pandas.DataFrame
|
1617
1632
|
Rolling Correlations
|
1618
1633
|
|
1619
1634
|
"""
|
1620
1635
|
corr_label = (
|
1621
|
-
cast(tuple[str, str], self.tsdf.iloc[:, first_column].name)[0]
|
1636
|
+
cast("tuple[str, str]", self.tsdf.iloc[:, first_column].name)[0]
|
1622
1637
|
+ "_VS_"
|
1623
|
-
+ cast(tuple[str, str], self.tsdf.iloc[:, second_column].name)[0]
|
1638
|
+
+ cast("tuple[str, str]", self.tsdf.iloc[:, second_column].name)[0]
|
1624
1639
|
)
|
1625
1640
|
first_series = (
|
1626
1641
|
self.tsdf.iloc[:, first_column]
|