openseries 1.8.1__py3-none-any.whl → 1.8.3__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 +250 -77
- openseries/_risk.py +2 -2
- openseries/datefixer.py +10 -10
- openseries/frame.py +38 -41
- openseries/load_plotly.py +2 -2
- openseries/owntypes.py +11 -2
- openseries/plotly_layouts.json +4 -4
- openseries/portfoliotools.py +14 -13
- openseries/series.py +29 -27
- openseries/simulation.py +10 -10
- {openseries-1.8.1.dist-info → openseries-1.8.3.dist-info}/METADATA +7 -6
- openseries-1.8.3.dist-info/RECORD +16 -0
- {openseries-1.8.1.dist-info → openseries-1.8.3.dist-info}/WHEEL +1 -1
- openseries-1.8.1.dist-info/RECORD +0 -16
- {openseries-1.8.1.dist-info → openseries-1.8.3.dist-info}/LICENSE.md +0 -0
openseries/_common_model.py
CHANGED
@@ -33,6 +33,10 @@ if TYPE_CHECKING: # pragma: no cover
|
|
33
33
|
LiteralLinePlotMode,
|
34
34
|
LiteralNanMethod,
|
35
35
|
LiteralPandasReindexMethod,
|
36
|
+
LiteralPlotlyHistogramBarMode,
|
37
|
+
LiteralPlotlyHistogramCurveType,
|
38
|
+
LiteralPlotlyHistogramHistNorm,
|
39
|
+
LiteralPlotlyHistogramPlotType,
|
36
40
|
LiteralPlotlyJSlib,
|
37
41
|
LiteralPlotlyOutput,
|
38
42
|
LiteralQuantileInterp,
|
@@ -50,11 +54,12 @@ from pandas import (
|
|
50
54
|
to_datetime,
|
51
55
|
)
|
52
56
|
from pandas.tseries.offsets import CustomBusinessDay
|
53
|
-
from plotly.
|
54
|
-
from plotly.
|
55
|
-
from plotly.
|
57
|
+
from plotly.figure_factory import create_distplot # type: ignore[import-untyped]
|
58
|
+
from plotly.graph_objs import Figure # type: ignore[import-untyped]
|
59
|
+
from plotly.io import to_html # type: ignore[import-untyped]
|
60
|
+
from plotly.offline import plot # type: ignore[import-untyped]
|
56
61
|
from pydantic import BaseModel, ConfigDict, DirectoryPath
|
57
|
-
from scipy.stats import ( # type: ignore[import-untyped
|
62
|
+
from scipy.stats import ( # type: ignore[import-untyped]
|
58
63
|
kurtosis,
|
59
64
|
norm,
|
60
65
|
skew,
|
@@ -73,7 +78,7 @@ from .load_plotly import load_plotly_dict
|
|
73
78
|
|
74
79
|
|
75
80
|
# noinspection PyTypeChecker
|
76
|
-
class _CommonModel(BaseModel):
|
81
|
+
class _CommonModel(BaseModel): # type: ignore[misc]
|
77
82
|
"""Declare _CommonModel."""
|
78
83
|
|
79
84
|
tsdf: DataFrame = DataFrame(dtype="float64")
|
@@ -88,7 +93,7 @@ class _CommonModel(BaseModel):
|
|
88
93
|
def length(self: Self) -> int:
|
89
94
|
"""Number of observations.
|
90
95
|
|
91
|
-
Returns
|
96
|
+
Returns:
|
92
97
|
-------
|
93
98
|
int
|
94
99
|
Number of observations
|
@@ -100,7 +105,7 @@ class _CommonModel(BaseModel):
|
|
100
105
|
def first_idx(self: Self) -> dt.date:
|
101
106
|
"""The first date in the timeseries.
|
102
107
|
|
103
|
-
Returns
|
108
|
+
Returns:
|
104
109
|
-------
|
105
110
|
datetime.date
|
106
111
|
The first date in the timeseries
|
@@ -112,7 +117,7 @@ class _CommonModel(BaseModel):
|
|
112
117
|
def last_idx(self: Self) -> dt.date:
|
113
118
|
"""The last date in the timeseries.
|
114
119
|
|
115
|
-
Returns
|
120
|
+
Returns:
|
116
121
|
-------
|
117
122
|
datetime.date
|
118
123
|
The last date in the timeseries
|
@@ -124,7 +129,7 @@ class _CommonModel(BaseModel):
|
|
124
129
|
def span_of_days(self: Self) -> int:
|
125
130
|
"""Number of days from the first date to the last.
|
126
131
|
|
127
|
-
Returns
|
132
|
+
Returns:
|
128
133
|
-------
|
129
134
|
int
|
130
135
|
Number of days from the first date to the last
|
@@ -136,7 +141,7 @@ class _CommonModel(BaseModel):
|
|
136
141
|
def yearfrac(self: Self) -> float:
|
137
142
|
"""Length of series expressed in years assuming all years have 365.25 days.
|
138
143
|
|
139
|
-
Returns
|
144
|
+
Returns:
|
140
145
|
-------
|
141
146
|
float
|
142
147
|
Length of the timeseries expressed in years assuming all years
|
@@ -149,7 +154,7 @@ class _CommonModel(BaseModel):
|
|
149
154
|
def periods_in_a_year(self: Self) -> float:
|
150
155
|
"""The average number of observations per year.
|
151
156
|
|
152
|
-
Returns
|
157
|
+
Returns:
|
153
158
|
-------
|
154
159
|
float
|
155
160
|
The average number of observations per year
|
@@ -161,7 +166,7 @@ class _CommonModel(BaseModel):
|
|
161
166
|
def max_drawdown_cal_year(self: Self) -> float | Series[float]:
|
162
167
|
"""https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp.
|
163
168
|
|
164
|
-
Returns
|
169
|
+
Returns:
|
165
170
|
-------
|
166
171
|
float | Pandas.Series[float]
|
167
172
|
Maximum drawdown in a single calendar year.
|
@@ -189,7 +194,7 @@ class _CommonModel(BaseModel):
|
|
189
194
|
def geo_ret(self: Self) -> float | Series[float]:
|
190
195
|
"""https://www.investopedia.com/terms/c/cagr.asp.
|
191
196
|
|
192
|
-
Returns
|
197
|
+
Returns:
|
193
198
|
-------
|
194
199
|
float | Pandas.Series[float]
|
195
200
|
Compounded Annual Growth Rate (CAGR)
|
@@ -201,7 +206,7 @@ class _CommonModel(BaseModel):
|
|
201
206
|
def arithmetic_ret(self: Self) -> float | Series[float]:
|
202
207
|
"""https://www.investopedia.com/terms/a/arithmeticmean.asp.
|
203
208
|
|
204
|
-
Returns
|
209
|
+
Returns:
|
205
210
|
-------
|
206
211
|
float | Pandas.Series[float]
|
207
212
|
Annualized arithmetic mean of returns
|
@@ -213,7 +218,7 @@ class _CommonModel(BaseModel):
|
|
213
218
|
def value_ret(self: Self) -> float | Series[float]:
|
214
219
|
"""Simple return.
|
215
220
|
|
216
|
-
Returns
|
221
|
+
Returns:
|
217
222
|
-------
|
218
223
|
float | Pandas.Series[float]
|
219
224
|
Simple return
|
@@ -228,7 +233,7 @@ class _CommonModel(BaseModel):
|
|
228
233
|
Based on Pandas .std() which is the equivalent of stdev.s([...]) in MS Excel.
|
229
234
|
https://www.investopedia.com/terms/v/volatility.asp.
|
230
235
|
|
231
|
-
Returns
|
236
|
+
Returns:
|
232
237
|
-------
|
233
238
|
float | Pandas.Series[float]
|
234
239
|
Annualized volatility
|
@@ -244,7 +249,7 @@ class _CommonModel(BaseModel):
|
|
244
249
|
of zero. It is used to calculate the Sortino Ratio.
|
245
250
|
https://www.investopedia.com/terms/d/downside-deviation.asp.
|
246
251
|
|
247
|
-
Returns
|
252
|
+
Returns:
|
248
253
|
-------
|
249
254
|
float | Pandas.Series[float]
|
250
255
|
Downside deviation
|
@@ -257,7 +262,7 @@ class _CommonModel(BaseModel):
|
|
257
262
|
def ret_vol_ratio(self: Self) -> float | Series[float]:
|
258
263
|
"""Ratio of annualized arithmetic mean of returns and annualized volatility.
|
259
264
|
|
260
|
-
Returns
|
265
|
+
Returns:
|
261
266
|
-------
|
262
267
|
float | Pandas.Series[float]
|
263
268
|
Ratio of the annualized arithmetic mean of returns and annualized
|
@@ -271,7 +276,7 @@ class _CommonModel(BaseModel):
|
|
271
276
|
def sortino_ratio(self: Self) -> float | Series[float]:
|
272
277
|
"""https://www.investopedia.com/terms/s/sortinoratio.asp.
|
273
278
|
|
274
|
-
Returns
|
279
|
+
Returns:
|
275
280
|
-------
|
276
281
|
float | Pandas.Series[float]
|
277
282
|
Sortino ratio calculated as the annualized arithmetic mean of returns
|
@@ -290,7 +295,7 @@ class _CommonModel(BaseModel):
|
|
290
295
|
def omega_ratio(self: Self) -> float | Series[float]:
|
291
296
|
"""https://en.wikipedia.org/wiki/Omega_ratio.
|
292
297
|
|
293
|
-
Returns
|
298
|
+
Returns:
|
294
299
|
-------
|
295
300
|
float | Pandas.Series[float]
|
296
301
|
Omega ratio calculation
|
@@ -303,7 +308,7 @@ class _CommonModel(BaseModel):
|
|
303
308
|
def z_score(self: Self) -> float | Series[float]:
|
304
309
|
"""https://www.investopedia.com/terms/z/zscore.asp.
|
305
310
|
|
306
|
-
Returns
|
311
|
+
Returns:
|
307
312
|
-------
|
308
313
|
float | Pandas.Series[float]
|
309
314
|
Z-score as (last return - mean return) / standard deviation of returns.
|
@@ -315,7 +320,7 @@ class _CommonModel(BaseModel):
|
|
315
320
|
def max_drawdown(self: Self) -> float | Series[float]:
|
316
321
|
"""https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp.
|
317
322
|
|
318
|
-
Returns
|
323
|
+
Returns:
|
319
324
|
-------
|
320
325
|
float | Pandas.Series[float]
|
321
326
|
Maximum drawdown without any limit on date range
|
@@ -329,7 +334,7 @@ class _CommonModel(BaseModel):
|
|
329
334
|
|
330
335
|
https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp.
|
331
336
|
|
332
|
-
Returns
|
337
|
+
Returns:
|
333
338
|
-------
|
334
339
|
datetime.date | pandas.Series[dt.date]
|
335
340
|
Date when the maximum drawdown occurred
|
@@ -352,7 +357,7 @@ class _CommonModel(BaseModel):
|
|
352
357
|
def worst(self: Self) -> float | Series[float]:
|
353
358
|
"""Most negative percentage change.
|
354
359
|
|
355
|
-
Returns
|
360
|
+
Returns:
|
356
361
|
-------
|
357
362
|
float | Pandas.Series[float]
|
358
363
|
Most negative percentage change
|
@@ -365,7 +370,7 @@ class _CommonModel(BaseModel):
|
|
365
370
|
def worst_month(self: Self) -> float | Series[float]:
|
366
371
|
"""Most negative month.
|
367
372
|
|
368
|
-
Returns
|
373
|
+
Returns:
|
369
374
|
-------
|
370
375
|
Pandas.Series[float]
|
371
376
|
Most negative month
|
@@ -396,7 +401,7 @@ class _CommonModel(BaseModel):
|
|
396
401
|
def positive_share(self: Self) -> float | Series[float]:
|
397
402
|
"""The share of percentage changes that are greater than zero.
|
398
403
|
|
399
|
-
Returns
|
404
|
+
Returns:
|
400
405
|
-------
|
401
406
|
float | Pandas.Series[float]
|
402
407
|
The share of percentage changes that are greater than zero
|
@@ -408,7 +413,7 @@ class _CommonModel(BaseModel):
|
|
408
413
|
def skew(self: Self) -> float | Series[float]:
|
409
414
|
"""https://www.investopedia.com/terms/s/skewness.asp.
|
410
415
|
|
411
|
-
Returns
|
416
|
+
Returns:
|
412
417
|
-------
|
413
418
|
float | Pandas.Series[float]
|
414
419
|
Skew of the return distribution
|
@@ -420,7 +425,7 @@ class _CommonModel(BaseModel):
|
|
420
425
|
def kurtosis(self: Self) -> float | Series[float]:
|
421
426
|
"""https://www.investopedia.com/terms/k/kurtosis.asp.
|
422
427
|
|
423
|
-
Returns
|
428
|
+
Returns:
|
424
429
|
-------
|
425
430
|
float | Pandas.Series[float]
|
426
431
|
Kurtosis of the return distribution
|
@@ -432,7 +437,7 @@ class _CommonModel(BaseModel):
|
|
432
437
|
def cvar_down(self: Self) -> float | Series[float]:
|
433
438
|
"""https://www.investopedia.com/terms/c/conditional_value_at_risk.asp.
|
434
439
|
|
435
|
-
Returns
|
440
|
+
Returns:
|
436
441
|
-------
|
437
442
|
float | Pandas.Series[float]
|
438
443
|
Downside 95% Conditional Value At Risk "CVaR"
|
@@ -448,7 +453,7 @@ class _CommonModel(BaseModel):
|
|
448
453
|
The equivalent of percentile.inc([...], 1-level) over returns in MS Excel.
|
449
454
|
https://www.investopedia.com/terms/v/var.asp.
|
450
455
|
|
451
|
-
Returns
|
456
|
+
Returns:
|
452
457
|
-------
|
453
458
|
float | Pandas.Series[float]
|
454
459
|
Downside 95% Value At Risk (VaR)
|
@@ -464,7 +469,7 @@ class _CommonModel(BaseModel):
|
|
464
469
|
|
465
470
|
Assumes that returns are normally distributed.
|
466
471
|
|
467
|
-
Returns
|
472
|
+
Returns:
|
468
473
|
-------
|
469
474
|
float | Pandas.Series[float]
|
470
475
|
Implied annualized volatility from the Downside 95% VaR using the
|
@@ -493,7 +498,7 @@ class _CommonModel(BaseModel):
|
|
493
498
|
to_dt: datetime.date, optional
|
494
499
|
Specific from date
|
495
500
|
|
496
|
-
Returns
|
501
|
+
Returns:
|
497
502
|
-------
|
498
503
|
tuple[datetime.date, datetime.date]
|
499
504
|
Start and end date of the chosen date range
|
@@ -543,14 +548,14 @@ class _CommonModel(BaseModel):
|
|
543
548
|
countries: CountriesType, default: "SE"
|
544
549
|
(List of) country code(s) according to ISO 3166-1 alpha-2
|
545
550
|
|
546
|
-
Returns
|
551
|
+
Returns:
|
547
552
|
-------
|
548
553
|
OpenFrame
|
549
554
|
An OpenFrame object
|
550
555
|
|
551
556
|
"""
|
552
|
-
startyear = to_datetime(self.tsdf.index[0]).year
|
553
|
-
endyear = to_datetime(self.tsdf.index[-1]).year
|
557
|
+
startyear = cast("int", to_datetime(self.tsdf.index[0]).year)
|
558
|
+
endyear = cast("int", to_datetime(self.tsdf.index[-1]).year)
|
554
559
|
calendar = holiday_calendar(
|
555
560
|
startyear=startyear,
|
556
561
|
endyear=endyear,
|
@@ -574,7 +579,7 @@ class _CommonModel(BaseModel):
|
|
574
579
|
|
575
580
|
Equivalent to LN(value[t] / value[t=0]) in Excel.
|
576
581
|
|
577
|
-
Returns
|
582
|
+
Returns:
|
578
583
|
-------
|
579
584
|
self
|
580
585
|
An object of the same class
|
@@ -595,7 +600,7 @@ class _CommonModel(BaseModel):
|
|
595
600
|
method: LiteralNanMethod, default: "fill"
|
596
601
|
Method used to handle NaN. Either fill with last known or drop
|
597
602
|
|
598
|
-
Returns
|
603
|
+
Returns:
|
599
604
|
-------
|
600
605
|
self
|
601
606
|
An object of the same class
|
@@ -615,7 +620,7 @@ class _CommonModel(BaseModel):
|
|
615
620
|
method: LiteralNanMethod, default: "fill"
|
616
621
|
Method used to handle NaN. Either fill with zero or drop
|
617
622
|
|
618
|
-
Returns
|
623
|
+
Returns:
|
619
624
|
-------
|
620
625
|
self
|
621
626
|
An object of the same class
|
@@ -630,7 +635,7 @@ class _CommonModel(BaseModel):
|
|
630
635
|
def to_drawdown_series(self: Self) -> Self:
|
631
636
|
"""Convert timeseries into a drawdown series.
|
632
637
|
|
633
|
-
Returns
|
638
|
+
Returns:
|
634
639
|
-------
|
635
640
|
self
|
636
641
|
An object of the same class
|
@@ -660,7 +665,7 @@ class _CommonModel(BaseModel):
|
|
660
665
|
directory: DirectoryPath, optional
|
661
666
|
File folder location
|
662
667
|
|
663
|
-
Returns
|
668
|
+
Returns:
|
664
669
|
-------
|
665
670
|
list[dict[str, str | bool | ValueType | list[str] | list[float]]]
|
666
671
|
A list of dictionaries with the data of the series
|
@@ -678,7 +683,7 @@ class _CommonModel(BaseModel):
|
|
678
683
|
output = []
|
679
684
|
if "label" in data:
|
680
685
|
if what_output == "tsdf":
|
681
|
-
values = self.tsdf.iloc[:, 0].tolist()
|
686
|
+
values = Series(self.tsdf.iloc[:, 0]).tolist()
|
682
687
|
else:
|
683
688
|
values = list(cast("list[float]", data.get("values")))
|
684
689
|
for item in cleaner_list:
|
@@ -727,7 +732,7 @@ class _CommonModel(BaseModel):
|
|
727
732
|
overwrite: bool, default: True
|
728
733
|
Flag whether to overwrite an existing file
|
729
734
|
|
730
|
-
Returns
|
735
|
+
Returns:
|
731
736
|
-------
|
732
737
|
str
|
733
738
|
The Excel file path
|
@@ -802,7 +807,7 @@ class _CommonModel(BaseModel):
|
|
802
807
|
add_logo: bool, default: True
|
803
808
|
If True a Captor logo is added to the plot
|
804
809
|
|
805
|
-
Returns
|
810
|
+
Returns:
|
806
811
|
-------
|
807
812
|
tuple[plotly.go.Figure, str]
|
808
813
|
Plotly Figure and a div section or a html filename with location
|
@@ -917,7 +922,7 @@ class _CommonModel(BaseModel):
|
|
917
922
|
show_last: bool, default: False
|
918
923
|
If True the last self.tsdf point is highlighted as red dot with a label
|
919
924
|
|
920
|
-
Returns
|
925
|
+
Returns:
|
921
926
|
-------
|
922
927
|
tuple[plotly.go.Figure, str]
|
923
928
|
Plotly Figure and a div section or a html filename with location
|
@@ -966,7 +971,7 @@ class _CommonModel(BaseModel):
|
|
966
971
|
|
967
972
|
for item in range(self.tsdf.shape[1]):
|
968
973
|
figure.add_scatter(
|
969
|
-
x=[self.tsdf.iloc[:, item].index[-1]],
|
974
|
+
x=[Series(self.tsdf.iloc[:, item]).index[-1]],
|
970
975
|
y=[self.tsdf.iloc[-1, item]],
|
971
976
|
mode="markers + text",
|
972
977
|
marker={"color": "red", "size": 12},
|
@@ -1005,6 +1010,174 @@ class _CommonModel(BaseModel):
|
|
1005
1010
|
|
1006
1011
|
return figure, string_output
|
1007
1012
|
|
1013
|
+
def plot_histogram(
|
1014
|
+
self: Self,
|
1015
|
+
plot_type: LiteralPlotlyHistogramPlotType = "bars",
|
1016
|
+
histnorm: LiteralPlotlyHistogramHistNorm = "percent",
|
1017
|
+
barmode: LiteralPlotlyHistogramBarMode = "overlay",
|
1018
|
+
xbins_size: float | None = None,
|
1019
|
+
opacity: float = 0.75,
|
1020
|
+
bargap: float = 0.0,
|
1021
|
+
bargroupgap: float = 0.0,
|
1022
|
+
curve_type: LiteralPlotlyHistogramCurveType = "kde",
|
1023
|
+
x_fmt: str | None = None,
|
1024
|
+
y_fmt: str | None = None,
|
1025
|
+
filename: str | None = None,
|
1026
|
+
directory: DirectoryPath | None = None,
|
1027
|
+
labels: list[str] | None = None,
|
1028
|
+
output_type: LiteralPlotlyOutput = "file",
|
1029
|
+
include_plotlyjs: LiteralPlotlyJSlib = "cdn",
|
1030
|
+
*,
|
1031
|
+
cumulative: bool = False,
|
1032
|
+
show_rug: bool = False,
|
1033
|
+
auto_open: bool = True,
|
1034
|
+
add_logo: bool = True,
|
1035
|
+
) -> tuple[Figure, str]:
|
1036
|
+
"""Create a Plotly Histogram Figure.
|
1037
|
+
|
1038
|
+
Parameters
|
1039
|
+
----------
|
1040
|
+
plot_type: LiteralPlotlyHistogramPlotType, default: bars
|
1041
|
+
Type of plot
|
1042
|
+
histnorm: LiteralPlotlyHistogramHistNorm, default: percent
|
1043
|
+
Sets the normalization mode
|
1044
|
+
barmode: LiteralPlotlyHistogramBarMode, default: overlay
|
1045
|
+
Specifies how bar traces are displayed relative to one another
|
1046
|
+
xbins_size: float, optional
|
1047
|
+
Explicitly sets the width of each bin along the x-axis in data units
|
1048
|
+
opacity: float, default: 0.75
|
1049
|
+
Sets the trace opacity, must be between 0 (fully transparent) and 1
|
1050
|
+
bargap: float, default: 0.0
|
1051
|
+
Sets the gap between bars of adjacent location coordinates
|
1052
|
+
bargroupgap: float, default: 0.0
|
1053
|
+
Sets the gap between bar “groups” at the same location coordinate
|
1054
|
+
curve_type: LiteralPlotlyHistogramCurveType, default: kde
|
1055
|
+
Specifies the type of distribution curve to overlay on the histogram
|
1056
|
+
y_fmt: str, optional
|
1057
|
+
None, '%', '.1%' depending on number of decimals to show on the y-axis
|
1058
|
+
x_fmt: str, optional
|
1059
|
+
None, '%', '.1%' depending on number of decimals to show on the x-axis
|
1060
|
+
filename: str, optional
|
1061
|
+
Name of the Plotly html file
|
1062
|
+
directory: DirectoryPath, optional
|
1063
|
+
Directory where Plotly html file is saved
|
1064
|
+
labels: list[str], optional
|
1065
|
+
A list of labels to manually override using the names of
|
1066
|
+
the input self.tsdf
|
1067
|
+
output_type: LiteralPlotlyOutput, default: "file"
|
1068
|
+
Determines output type
|
1069
|
+
include_plotlyjs: LiteralPlotlyJSlib, default: "cdn"
|
1070
|
+
Determines how the plotly.js library is included in the output
|
1071
|
+
cumulative: bool, default: False
|
1072
|
+
Determines whether to compute a cumulative histogram
|
1073
|
+
show_rug: bool, default: False
|
1074
|
+
Determines whether to draw a rug plot alongside the distribution
|
1075
|
+
auto_open: bool, default: True
|
1076
|
+
Determines whether to open a browser window with the plot
|
1077
|
+
add_logo: bool, default: True
|
1078
|
+
If True a Captor logo is added to the plot
|
1079
|
+
|
1080
|
+
Returns:
|
1081
|
+
-------
|
1082
|
+
tuple[plotly.go.Figure, str]
|
1083
|
+
Plotly Figure and a div section or a html filename with location
|
1084
|
+
|
1085
|
+
"""
|
1086
|
+
if labels:
|
1087
|
+
if len(labels) != self.tsdf.shape[1]:
|
1088
|
+
msg = "Must provide same number of labels as items in frame."
|
1089
|
+
raise NumberOfItemsAndLabelsNotSameError(msg)
|
1090
|
+
else:
|
1091
|
+
labels = list(self.tsdf.columns.get_level_values(0))
|
1092
|
+
|
1093
|
+
if directory:
|
1094
|
+
dirpath = Path(directory).resolve()
|
1095
|
+
elif Path.home().joinpath("Documents").exists():
|
1096
|
+
dirpath = Path.home().joinpath("Documents")
|
1097
|
+
else:
|
1098
|
+
dirpath = Path(stack()[1].filename).parent
|
1099
|
+
|
1100
|
+
if not filename:
|
1101
|
+
filename = "".join(choice(ascii_letters) for _ in range(6)) + ".html"
|
1102
|
+
plotfile = dirpath.joinpath(filename)
|
1103
|
+
|
1104
|
+
fig_dict, logo = load_plotly_dict()
|
1105
|
+
|
1106
|
+
hovertemplate = f"Count: %{{y:{y_fmt}}}" if y_fmt else "Count: %{y}"
|
1107
|
+
|
1108
|
+
if x_fmt:
|
1109
|
+
hovertemplate += f"<br>%{{x:{x_fmt}}}"
|
1110
|
+
else:
|
1111
|
+
hovertemplate += "<br>%{x}"
|
1112
|
+
|
1113
|
+
msg = "plot_type must be 'bars' or 'lines'."
|
1114
|
+
if plot_type == "bars":
|
1115
|
+
figure = Figure(fig_dict)
|
1116
|
+
for item in range(self.tsdf.shape[1]):
|
1117
|
+
figure.add_histogram(
|
1118
|
+
x=self.tsdf.iloc[:, item],
|
1119
|
+
cumulative={"enabled": cumulative},
|
1120
|
+
histnorm=histnorm,
|
1121
|
+
name=labels[item],
|
1122
|
+
xbins={"size": xbins_size},
|
1123
|
+
opacity=opacity,
|
1124
|
+
hovertemplate=hovertemplate,
|
1125
|
+
)
|
1126
|
+
figure.update_layout(
|
1127
|
+
barmode=barmode,
|
1128
|
+
bargap=bargap,
|
1129
|
+
bargroupgap=bargroupgap,
|
1130
|
+
)
|
1131
|
+
elif plot_type == "lines":
|
1132
|
+
hist_data = [
|
1133
|
+
cast("Series[float]", self.tsdf.loc[:, ds]).dropna().tolist()
|
1134
|
+
for ds in self.tsdf
|
1135
|
+
]
|
1136
|
+
figure = create_distplot(
|
1137
|
+
hist_data=hist_data,
|
1138
|
+
curve_type=curve_type,
|
1139
|
+
group_labels=labels,
|
1140
|
+
show_hist=False,
|
1141
|
+
show_rug=show_rug,
|
1142
|
+
histnorm=histnorm,
|
1143
|
+
)
|
1144
|
+
figure.update_layout(dict1=fig_dict["layout"])
|
1145
|
+
else:
|
1146
|
+
raise TypeError(msg)
|
1147
|
+
|
1148
|
+
figure.update_layout(xaxis={"tickformat": x_fmt}, yaxis={"tickformat": y_fmt})
|
1149
|
+
|
1150
|
+
figure.update_xaxes(zeroline=True, zerolinewidth=2, zerolinecolor="lightgrey")
|
1151
|
+
figure.update_yaxes(zeroline=True, zerolinewidth=2, zerolinecolor="lightgrey")
|
1152
|
+
|
1153
|
+
if add_logo:
|
1154
|
+
figure.add_layout_image(logo)
|
1155
|
+
|
1156
|
+
if output_type == "file":
|
1157
|
+
plot(
|
1158
|
+
figure_or_data=figure,
|
1159
|
+
filename=str(plotfile),
|
1160
|
+
auto_open=auto_open,
|
1161
|
+
auto_play=False,
|
1162
|
+
link_text="",
|
1163
|
+
include_plotlyjs=cast("bool", include_plotlyjs),
|
1164
|
+
config=fig_dict["config"],
|
1165
|
+
output_type=output_type,
|
1166
|
+
)
|
1167
|
+
string_output = str(plotfile)
|
1168
|
+
else:
|
1169
|
+
div_id = filename.rsplit(".", 1)[0]
|
1170
|
+
string_output = to_html(
|
1171
|
+
fig=figure,
|
1172
|
+
config=fig_dict["config"],
|
1173
|
+
auto_play=False,
|
1174
|
+
include_plotlyjs=cast("bool", include_plotlyjs),
|
1175
|
+
full_html=False,
|
1176
|
+
div_id=div_id,
|
1177
|
+
)
|
1178
|
+
|
1179
|
+
return figure, string_output
|
1180
|
+
|
1008
1181
|
def arithmetic_ret_func(
|
1009
1182
|
self: Self,
|
1010
1183
|
months_from_last: int | None = None,
|
@@ -1027,7 +1200,7 @@ class _CommonModel(BaseModel):
|
|
1027
1200
|
Allows locking the periods-in-a-year to simplify test cases and
|
1028
1201
|
comparisons
|
1029
1202
|
|
1030
|
-
Returns
|
1203
|
+
Returns:
|
1031
1204
|
-------
|
1032
1205
|
float | Pandas.Series[float]
|
1033
1206
|
Annualized arithmetic mean of returns
|
@@ -1046,7 +1219,7 @@ class _CommonModel(BaseModel):
|
|
1046
1219
|
cast("int", earlier) : cast("int", later),
|
1047
1220
|
self.tsdf.columns.to_numpy()[0],
|
1048
1221
|
].count()
|
1049
|
-
time_factor = how_many / fraction
|
1222
|
+
time_factor = cast("int", how_many) / fraction
|
1050
1223
|
|
1051
1224
|
result = (
|
1052
1225
|
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
@@ -1089,7 +1262,7 @@ class _CommonModel(BaseModel):
|
|
1089
1262
|
periods_in_a_year_fixed : DaysInYearType, optional
|
1090
1263
|
Allows locking the periods-in-a-year to simplify test cases and comparisons
|
1091
1264
|
|
1092
|
-
Returns
|
1265
|
+
Returns:
|
1093
1266
|
-------
|
1094
1267
|
float | Pandas.Series[float]
|
1095
1268
|
Annualized volatility
|
@@ -1158,7 +1331,7 @@ class _CommonModel(BaseModel):
|
|
1158
1331
|
drift_adjust: bool, default: False
|
1159
1332
|
An adjustment to remove the bias implied by the average return
|
1160
1333
|
|
1161
|
-
Returns
|
1334
|
+
Returns:
|
1162
1335
|
-------
|
1163
1336
|
float | Pandas.Series[float]
|
1164
1337
|
Implied annualized volatility from the Downside VaR using the
|
@@ -1219,7 +1392,7 @@ class _CommonModel(BaseModel):
|
|
1219
1392
|
drift_adjust: bool, default: False
|
1220
1393
|
An adjustment to remove the bias implied by the average return
|
1221
1394
|
|
1222
|
-
Returns
|
1395
|
+
Returns:
|
1223
1396
|
-------
|
1224
1397
|
float | Pandas.Series[float]
|
1225
1398
|
A position weight multiplier from the ratio between a VaR implied
|
@@ -1285,7 +1458,7 @@ class _CommonModel(BaseModel):
|
|
1285
1458
|
drift_adjust: bool, default: False
|
1286
1459
|
An adjustment to remove the bias implied by the average return
|
1287
1460
|
|
1288
|
-
Returns
|
1461
|
+
Returns:
|
1289
1462
|
-------
|
1290
1463
|
float | Pandas.Series[float]
|
1291
1464
|
Target volatility if target_vol is provided otherwise the VaR
|
@@ -1377,7 +1550,7 @@ class _CommonModel(BaseModel):
|
|
1377
1550
|
to_date : datetime.date, optional
|
1378
1551
|
Specific to date
|
1379
1552
|
|
1380
|
-
Returns
|
1553
|
+
Returns:
|
1381
1554
|
-------
|
1382
1555
|
float | Pandas.Series[float]
|
1383
1556
|
Downside Conditional Value At Risk "CVaR"
|
@@ -1392,14 +1565,14 @@ class _CommonModel(BaseModel):
|
|
1392
1565
|
deep=True
|
1393
1566
|
)
|
1394
1567
|
result = [
|
1395
|
-
cvar_df.loc[:, x] # type: ignore[call-overload,index
|
1568
|
+
cvar_df.loc[:, x] # type: ignore[call-overload,index]
|
1396
1569
|
.ffill()
|
1397
1570
|
.pct_change()
|
1398
1571
|
.sort_values()
|
1399
1572
|
.iloc[
|
1400
1573
|
: ceil(
|
1401
1574
|
(1 - level)
|
1402
|
-
* cvar_df.loc[:, x] # type: ignore[index
|
1575
|
+
* cvar_df.loc[:, x] # type: ignore[index]
|
1403
1576
|
.ffill()
|
1404
1577
|
.pct_change()
|
1405
1578
|
.count(),
|
@@ -1446,7 +1619,7 @@ class _CommonModel(BaseModel):
|
|
1446
1619
|
Allows locking the periods-in-a-year to simplify test cases and
|
1447
1620
|
comparisons
|
1448
1621
|
|
1449
|
-
Returns
|
1622
|
+
Returns:
|
1450
1623
|
-------
|
1451
1624
|
float | Pandas.Series[float]
|
1452
1625
|
Downside deviation
|
@@ -1514,7 +1687,7 @@ class _CommonModel(BaseModel):
|
|
1514
1687
|
to_date : datetime.date, optional
|
1515
1688
|
Specific to date
|
1516
1689
|
|
1517
|
-
Returns
|
1690
|
+
Returns:
|
1518
1691
|
-------
|
1519
1692
|
float | Pandas.Series[float]
|
1520
1693
|
Compounded Annual Growth Rate (CAGR)
|
@@ -1529,7 +1702,7 @@ class _CommonModel(BaseModel):
|
|
1529
1702
|
fraction = (later - earlier).days / 365.25
|
1530
1703
|
|
1531
1704
|
any_below_zero = any(
|
1532
|
-
self.tsdf.loc[[earlier, later]] # type: ignore[index
|
1705
|
+
self.tsdf.loc[[earlier, later]] # type: ignore[index]
|
1533
1706
|
.lt(0.0)
|
1534
1707
|
.any()
|
1535
1708
|
.to_numpy()
|
@@ -1572,7 +1745,7 @@ class _CommonModel(BaseModel):
|
|
1572
1745
|
to_date : datetime.date, optional
|
1573
1746
|
Specific to date
|
1574
1747
|
|
1575
|
-
Returns
|
1748
|
+
Returns:
|
1576
1749
|
-------
|
1577
1750
|
float | Pandas.Series[float]
|
1578
1751
|
Skew of the return distribution
|
@@ -1621,7 +1794,7 @@ class _CommonModel(BaseModel):
|
|
1621
1794
|
to_date : datetime.date, optional
|
1622
1795
|
Specific to date
|
1623
1796
|
|
1624
|
-
Returns
|
1797
|
+
Returns:
|
1625
1798
|
-------
|
1626
1799
|
float | Pandas.Series[float]
|
1627
1800
|
Kurtosis of the return distribution
|
@@ -1675,7 +1848,7 @@ class _CommonModel(BaseModel):
|
|
1675
1848
|
min_periods: int, default: 1
|
1676
1849
|
Smallest number of observations to use to find the maximum drawdown
|
1677
1850
|
|
1678
|
-
Returns
|
1851
|
+
Returns:
|
1679
1852
|
-------
|
1680
1853
|
float | Pandas.Series[float]
|
1681
1854
|
Maximum drawdown without any limit on date range
|
@@ -1719,7 +1892,7 @@ class _CommonModel(BaseModel):
|
|
1719
1892
|
to_date : datetime.date, optional
|
1720
1893
|
Specific to date
|
1721
1894
|
|
1722
|
-
Returns
|
1895
|
+
Returns:
|
1723
1896
|
-------
|
1724
1897
|
float | Pandas.Series[float]
|
1725
1898
|
Calculate share of percentage changes that are greater than zero
|
@@ -1789,7 +1962,7 @@ class _CommonModel(BaseModel):
|
|
1789
1962
|
Allows locking the periods-in-a-year to simplify test cases and
|
1790
1963
|
comparisons
|
1791
1964
|
|
1792
|
-
Returns
|
1965
|
+
Returns:
|
1793
1966
|
-------
|
1794
1967
|
float | Pandas.Series[float]
|
1795
1968
|
Ratio of the annualized arithmetic mean of returns and annualized
|
@@ -1854,7 +2027,7 @@ class _CommonModel(BaseModel):
|
|
1854
2027
|
Allows locking the periods-in-a-year to simplify test cases and
|
1855
2028
|
comparisons
|
1856
2029
|
|
1857
|
-
Returns
|
2030
|
+
Returns:
|
1858
2031
|
-------
|
1859
2032
|
float | Pandas.Series[float]
|
1860
2033
|
Sortino ratio calculated as ( return - riskfree return ) /
|
@@ -1912,7 +2085,7 @@ class _CommonModel(BaseModel):
|
|
1912
2085
|
to_date : datetime.date, optional
|
1913
2086
|
Specific to date
|
1914
2087
|
|
1915
|
-
Returns
|
2088
|
+
Returns:
|
1916
2089
|
-------
|
1917
2090
|
float | Pandas.Series[float]
|
1918
2091
|
Omega ratio calculation
|
@@ -1959,7 +2132,7 @@ class _CommonModel(BaseModel):
|
|
1959
2132
|
to_date : datetime.date, optional
|
1960
2133
|
Specific to date
|
1961
2134
|
|
1962
|
-
Returns
|
2135
|
+
Returns:
|
1963
2136
|
-------
|
1964
2137
|
float | Pandas.Series[float]
|
1965
2138
|
Calculate simple return
|
@@ -2003,7 +2176,7 @@ class _CommonModel(BaseModel):
|
|
2003
2176
|
month : int, optional
|
2004
2177
|
Calendar month of the period to calculate.
|
2005
2178
|
|
2006
|
-
Returns
|
2179
|
+
Returns:
|
2007
2180
|
-------
|
2008
2181
|
float | Pandas.Series[float]
|
2009
2182
|
Calculate simple return for a specific calendar period
|
@@ -2054,7 +2227,7 @@ class _CommonModel(BaseModel):
|
|
2054
2227
|
interpolation: LiteralQuantileInterp, default: "lower"
|
2055
2228
|
Type of interpolation in Pandas.DataFrame.quantile() function.
|
2056
2229
|
|
2057
|
-
Returns
|
2230
|
+
Returns:
|
2058
2231
|
-------
|
2059
2232
|
float | Pandas.Series[float]
|
2060
2233
|
Downside Value At Risk
|
@@ -2102,7 +2275,7 @@ class _CommonModel(BaseModel):
|
|
2102
2275
|
to_date : datetime.date, optional
|
2103
2276
|
Specific to date
|
2104
2277
|
|
2105
|
-
Returns
|
2278
|
+
Returns:
|
2106
2279
|
-------
|
2107
2280
|
float | Pandas.Series[float]
|
2108
2281
|
Most negative percentage change over a rolling number of observations
|
@@ -2152,7 +2325,7 @@ class _CommonModel(BaseModel):
|
|
2152
2325
|
to_date : datetime.date, optional
|
2153
2326
|
Specific to date
|
2154
2327
|
|
2155
|
-
Returns
|
2328
|
+
Returns:
|
2156
2329
|
-------
|
2157
2330
|
float | Pandas.Series[float]
|
2158
2331
|
Z-score as (last return - mean return) / standard deviation of returns
|
@@ -2196,7 +2369,7 @@ class _CommonModel(BaseModel):
|
|
2196
2369
|
observations: int, default: 252
|
2197
2370
|
Number of observations in the overlapping window.
|
2198
2371
|
|
2199
|
-
Returns
|
2372
|
+
Returns:
|
2200
2373
|
-------
|
2201
2374
|
Pandas.DataFrame
|
2202
2375
|
Calculate rolling annualized downside CVaR
|
@@ -2204,7 +2377,7 @@ class _CommonModel(BaseModel):
|
|
2204
2377
|
"""
|
2205
2378
|
cvar_label = cast("tuple[str]", self.tsdf.iloc[:, column].name)[0]
|
2206
2379
|
cvarseries = (
|
2207
|
-
self.tsdf.iloc[:, column]
|
2380
|
+
Series(self.tsdf.iloc[:, column])
|
2208
2381
|
.rolling(observations, min_periods=observations)
|
2209
2382
|
.apply(lambda x: _cvar_down_calc(x, level=level))
|
2210
2383
|
)
|
@@ -2227,7 +2400,7 @@ class _CommonModel(BaseModel):
|
|
2227
2400
|
observations: int, default: 21
|
2228
2401
|
Number of observations in the overlapping window.
|
2229
2402
|
|
2230
|
-
Returns
|
2403
|
+
Returns:
|
2231
2404
|
-------
|
2232
2405
|
Pandas.DataFrame
|
2233
2406
|
Calculate rolling returns
|
@@ -2235,7 +2408,7 @@ class _CommonModel(BaseModel):
|
|
2235
2408
|
"""
|
2236
2409
|
ret_label = cast("tuple[str]", self.tsdf.iloc[:, column].name)[0]
|
2237
2410
|
retseries = (
|
2238
|
-
self.tsdf.iloc[:, column]
|
2411
|
+
Series(self.tsdf.iloc[:, column])
|
2239
2412
|
.ffill()
|
2240
2413
|
.pct_change()
|
2241
2414
|
.rolling(observations, min_periods=observations)
|
@@ -2266,7 +2439,7 @@ class _CommonModel(BaseModel):
|
|
2266
2439
|
interpolation: LiteralQuantileInterp, default: "lower"
|
2267
2440
|
Type of interpolation in Pandas.DataFrame.quantile() function.
|
2268
2441
|
|
2269
|
-
Returns
|
2442
|
+
Returns:
|
2270
2443
|
-------
|
2271
2444
|
Pandas.DataFrame
|
2272
2445
|
Calculate rolling annualized downside Value At Risk "VaR"
|
@@ -2274,7 +2447,7 @@ class _CommonModel(BaseModel):
|
|
2274
2447
|
"""
|
2275
2448
|
var_label = cast("tuple[str]", self.tsdf.iloc[:, column].name)[0]
|
2276
2449
|
varseries = (
|
2277
|
-
self.tsdf.iloc[:, column]
|
2450
|
+
Series(self.tsdf.iloc[:, column])
|
2278
2451
|
.rolling(observations, min_periods=observations)
|
2279
2452
|
.apply(
|
2280
2453
|
lambda x: _var_down_calc(x, level=level, interpolation=interpolation),
|
@@ -2303,7 +2476,7 @@ class _CommonModel(BaseModel):
|
|
2303
2476
|
Allows locking the periods-in-a-year to simplify test cases and
|
2304
2477
|
comparisons
|
2305
2478
|
|
2306
|
-
Returns
|
2479
|
+
Returns:
|
2307
2480
|
-------
|
2308
2481
|
Pandas.DataFrame
|
2309
2482
|
Calculate rolling annualised volatilities
|
@@ -2314,7 +2487,7 @@ class _CommonModel(BaseModel):
|
|
2314
2487
|
else:
|
2315
2488
|
time_factor = self.periods_in_a_year
|
2316
2489
|
vol_label = cast("tuple[str, ValueType]", self.tsdf.iloc[:, column].name)[0]
|
2317
|
-
dframe = self.tsdf.iloc[:, column].ffill().pct_change()
|
2490
|
+
dframe = Series(self.tsdf.iloc[:, column]).ffill().pct_change()
|
2318
2491
|
volseries = dframe.rolling(
|
2319
2492
|
observations,
|
2320
2493
|
min_periods=observations,
|