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.
@@ -1,5 +1,6 @@
1
1
  """Defining the _CommonModel class."""
2
2
 
3
+ # mypy: disable-error-code="no-any-return"
3
4
  from __future__ import annotations
4
5
 
5
6
  import datetime as dt
@@ -13,11 +14,32 @@ from typing import TYPE_CHECKING, Any, SupportsFloat, cast
13
14
 
14
15
  from numpy import float64, inf, isnan, log, maximum, sqrt
15
16
 
16
- if TYPE_CHECKING:
17
- from numpy.typing import NDArray # pragma: no cover
17
+ from .owntypes import (
18
+ DateAlignmentError,
19
+ InitialValueZeroError,
20
+ NumberOfItemsAndLabelsNotSameError,
21
+ Self,
22
+ )
23
+
24
+ if TYPE_CHECKING: # pragma: no cover
25
+ from numpy.typing import NDArray
26
+ from openpyxl.worksheet.worksheet import Worksheet
27
+
28
+ from .owntypes import (
29
+ CountriesType,
30
+ DaysInYearType,
31
+ LiteralBarPlotMode,
32
+ LiteralJsonOutput,
33
+ LiteralLinePlotMode,
34
+ LiteralNanMethod,
35
+ LiteralPandasReindexMethod,
36
+ LiteralPlotlyJSlib,
37
+ LiteralPlotlyOutput,
38
+ LiteralQuantileInterp,
39
+ ValueType,
40
+ )
18
41
  from openpyxl.utils.dataframe import dataframe_to_rows
19
42
  from openpyxl.workbook.workbook import Workbook
20
- from openpyxl.worksheet.worksheet import Worksheet
21
43
  from pandas import (
22
44
  DataFrame,
23
45
  DatetimeIndex,
@@ -28,16 +50,15 @@ from pandas import (
28
50
  to_datetime,
29
51
  )
30
52
  from pandas.tseries.offsets import CustomBusinessDay
31
- from plotly.graph_objs import Figure # type: ignore[import-untyped,unused-ignore]
32
- from plotly.io import to_html # type: ignore[import-untyped,unused-ignore]
33
- from plotly.offline import plot # type: ignore[import-untyped,unused-ignore]
53
+ from plotly.graph_objs import Figure # type: ignore[import-untyped]
54
+ from plotly.io import to_html # type: ignore[import-untyped]
55
+ from plotly.offline import plot # type: ignore[import-untyped]
34
56
  from pydantic import BaseModel, ConfigDict, DirectoryPath
35
- from scipy.stats import ( # type: ignore[import-untyped,unused-ignore]
57
+ from scipy.stats import ( # type: ignore[import-untyped]
36
58
  kurtosis,
37
59
  norm,
38
60
  skew,
39
61
  )
40
- from typing_extensions import Self
41
62
 
42
63
  from ._risk import (
43
64
  _cvar_down_calc,
@@ -49,23 +70,10 @@ from .datefixer import (
49
70
  holiday_calendar,
50
71
  )
51
72
  from .load_plotly import load_plotly_dict
52
- from .owntypes import (
53
- CountriesType,
54
- DaysInYearType,
55
- LiteralBarPlotMode,
56
- LiteralJsonOutput,
57
- LiteralLinePlotMode,
58
- LiteralNanMethod,
59
- LiteralPandasReindexMethod,
60
- LiteralPlotlyJSlib,
61
- LiteralPlotlyOutput,
62
- LiteralQuantileInterp,
63
- ValueType,
64
- )
65
73
 
66
74
 
67
75
  # noinspection PyTypeChecker
68
- class _CommonModel(BaseModel):
76
+ class _CommonModel(BaseModel): # type: ignore[misc]
69
77
  """Declare _CommonModel."""
70
78
 
71
79
  tsdf: DataFrame = DataFrame(dtype="float64")
@@ -80,7 +88,7 @@ class _CommonModel(BaseModel):
80
88
  def length(self: Self) -> int:
81
89
  """Number of observations.
82
90
 
83
- Returns
91
+ Returns:
84
92
  -------
85
93
  int
86
94
  Number of observations
@@ -92,31 +100,31 @@ class _CommonModel(BaseModel):
92
100
  def first_idx(self: Self) -> dt.date:
93
101
  """The first date in the timeseries.
94
102
 
95
- Returns
103
+ Returns:
96
104
  -------
97
105
  datetime.date
98
106
  The first date in the timeseries
99
107
 
100
108
  """
101
- return cast(dt.date, self.tsdf.index[0])
109
+ return cast("dt.date", self.tsdf.index[0])
102
110
 
103
111
  @property
104
112
  def last_idx(self: Self) -> dt.date:
105
113
  """The last date in the timeseries.
106
114
 
107
- Returns
115
+ Returns:
108
116
  -------
109
117
  datetime.date
110
118
  The last date in the timeseries
111
119
 
112
120
  """
113
- return cast(dt.date, self.tsdf.index[-1])
121
+ return cast("dt.date", self.tsdf.index[-1])
114
122
 
115
123
  @property
116
124
  def span_of_days(self: Self) -> int:
117
125
  """Number of days from the first date to the last.
118
126
 
119
- Returns
127
+ Returns:
120
128
  -------
121
129
  int
122
130
  Number of days from the first date to the last
@@ -128,7 +136,7 @@ class _CommonModel(BaseModel):
128
136
  def yearfrac(self: Self) -> float:
129
137
  """Length of series expressed in years assuming all years have 365.25 days.
130
138
 
131
- Returns
139
+ Returns:
132
140
  -------
133
141
  float
134
142
  Length of the timeseries expressed in years assuming all years
@@ -141,7 +149,7 @@ class _CommonModel(BaseModel):
141
149
  def periods_in_a_year(self: Self) -> float:
142
150
  """The average number of observations per year.
143
151
 
144
- Returns
152
+ Returns:
145
153
  -------
146
154
  float
147
155
  The average number of observations per year
@@ -153,7 +161,7 @@ class _CommonModel(BaseModel):
153
161
  def max_drawdown_cal_year(self: Self) -> float | Series[float]:
154
162
  """https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp.
155
163
 
156
- Returns
164
+ Returns:
157
165
  -------
158
166
  float | Pandas.Series[float]
159
167
  Maximum drawdown in a single calendar year.
@@ -181,7 +189,7 @@ class _CommonModel(BaseModel):
181
189
  def geo_ret(self: Self) -> float | Series[float]:
182
190
  """https://www.investopedia.com/terms/c/cagr.asp.
183
191
 
184
- Returns
192
+ Returns:
185
193
  -------
186
194
  float | Pandas.Series[float]
187
195
  Compounded Annual Growth Rate (CAGR)
@@ -193,7 +201,7 @@ class _CommonModel(BaseModel):
193
201
  def arithmetic_ret(self: Self) -> float | Series[float]:
194
202
  """https://www.investopedia.com/terms/a/arithmeticmean.asp.
195
203
 
196
- Returns
204
+ Returns:
197
205
  -------
198
206
  float | Pandas.Series[float]
199
207
  Annualized arithmetic mean of returns
@@ -205,7 +213,7 @@ class _CommonModel(BaseModel):
205
213
  def value_ret(self: Self) -> float | Series[float]:
206
214
  """Simple return.
207
215
 
208
- Returns
216
+ Returns:
209
217
  -------
210
218
  float | Pandas.Series[float]
211
219
  Simple return
@@ -220,7 +228,7 @@ class _CommonModel(BaseModel):
220
228
  Based on Pandas .std() which is the equivalent of stdev.s([...]) in MS Excel.
221
229
  https://www.investopedia.com/terms/v/volatility.asp.
222
230
 
223
- Returns
231
+ Returns:
224
232
  -------
225
233
  float | Pandas.Series[float]
226
234
  Annualized volatility
@@ -236,7 +244,7 @@ class _CommonModel(BaseModel):
236
244
  of zero. It is used to calculate the Sortino Ratio.
237
245
  https://www.investopedia.com/terms/d/downside-deviation.asp.
238
246
 
239
- Returns
247
+ Returns:
240
248
  -------
241
249
  float | Pandas.Series[float]
242
250
  Downside deviation
@@ -249,7 +257,7 @@ class _CommonModel(BaseModel):
249
257
  def ret_vol_ratio(self: Self) -> float | Series[float]:
250
258
  """Ratio of annualized arithmetic mean of returns and annualized volatility.
251
259
 
252
- Returns
260
+ Returns:
253
261
  -------
254
262
  float | Pandas.Series[float]
255
263
  Ratio of the annualized arithmetic mean of returns and annualized
@@ -263,7 +271,7 @@ class _CommonModel(BaseModel):
263
271
  def sortino_ratio(self: Self) -> float | Series[float]:
264
272
  """https://www.investopedia.com/terms/s/sortinoratio.asp.
265
273
 
266
- Returns
274
+ Returns:
267
275
  -------
268
276
  float | Pandas.Series[float]
269
277
  Sortino ratio calculated as the annualized arithmetic mean of returns
@@ -282,7 +290,7 @@ class _CommonModel(BaseModel):
282
290
  def omega_ratio(self: Self) -> float | Series[float]:
283
291
  """https://en.wikipedia.org/wiki/Omega_ratio.
284
292
 
285
- Returns
293
+ Returns:
286
294
  -------
287
295
  float | Pandas.Series[float]
288
296
  Omega ratio calculation
@@ -295,7 +303,7 @@ class _CommonModel(BaseModel):
295
303
  def z_score(self: Self) -> float | Series[float]:
296
304
  """https://www.investopedia.com/terms/z/zscore.asp.
297
305
 
298
- Returns
306
+ Returns:
299
307
  -------
300
308
  float | Pandas.Series[float]
301
309
  Z-score as (last return - mean return) / standard deviation of returns.
@@ -307,7 +315,7 @@ class _CommonModel(BaseModel):
307
315
  def max_drawdown(self: Self) -> float | Series[float]:
308
316
  """https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp.
309
317
 
310
- Returns
318
+ Returns:
311
319
  -------
312
320
  float | Pandas.Series[float]
313
321
  Maximum drawdown without any limit on date range
@@ -321,7 +329,7 @@ class _CommonModel(BaseModel):
321
329
 
322
330
  https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp.
323
331
 
324
- Returns
332
+ Returns:
325
333
  -------
326
334
  datetime.date | pandas.Series[dt.date]
327
335
  Date when the maximum drawdown occurred
@@ -344,7 +352,7 @@ class _CommonModel(BaseModel):
344
352
  def worst(self: Self) -> float | Series[float]:
345
353
  """Most negative percentage change.
346
354
 
347
- Returns
355
+ Returns:
348
356
  -------
349
357
  float | Pandas.Series[float]
350
358
  Most negative percentage change
@@ -357,7 +365,7 @@ class _CommonModel(BaseModel):
357
365
  def worst_month(self: Self) -> float | Series[float]:
358
366
  """Most negative month.
359
367
 
360
- Returns
368
+ Returns:
361
369
  -------
362
370
  Pandas.Series[float]
363
371
  Most negative month
@@ -388,7 +396,7 @@ class _CommonModel(BaseModel):
388
396
  def positive_share(self: Self) -> float | Series[float]:
389
397
  """The share of percentage changes that are greater than zero.
390
398
 
391
- Returns
399
+ Returns:
392
400
  -------
393
401
  float | Pandas.Series[float]
394
402
  The share of percentage changes that are greater than zero
@@ -400,7 +408,7 @@ class _CommonModel(BaseModel):
400
408
  def skew(self: Self) -> float | Series[float]:
401
409
  """https://www.investopedia.com/terms/s/skewness.asp.
402
410
 
403
- Returns
411
+ Returns:
404
412
  -------
405
413
  float | Pandas.Series[float]
406
414
  Skew of the return distribution
@@ -412,7 +420,7 @@ class _CommonModel(BaseModel):
412
420
  def kurtosis(self: Self) -> float | Series[float]:
413
421
  """https://www.investopedia.com/terms/k/kurtosis.asp.
414
422
 
415
- Returns
423
+ Returns:
416
424
  -------
417
425
  float | Pandas.Series[float]
418
426
  Kurtosis of the return distribution
@@ -424,7 +432,7 @@ class _CommonModel(BaseModel):
424
432
  def cvar_down(self: Self) -> float | Series[float]:
425
433
  """https://www.investopedia.com/terms/c/conditional_value_at_risk.asp.
426
434
 
427
- Returns
435
+ Returns:
428
436
  -------
429
437
  float | Pandas.Series[float]
430
438
  Downside 95% Conditional Value At Risk "CVaR"
@@ -440,7 +448,7 @@ class _CommonModel(BaseModel):
440
448
  The equivalent of percentile.inc([...], 1-level) over returns in MS Excel.
441
449
  https://www.investopedia.com/terms/v/var.asp.
442
450
 
443
- Returns
451
+ Returns:
444
452
  -------
445
453
  float | Pandas.Series[float]
446
454
  Downside 95% Value At Risk (VaR)
@@ -456,7 +464,7 @@ class _CommonModel(BaseModel):
456
464
 
457
465
  Assumes that returns are normally distributed.
458
466
 
459
- Returns
467
+ Returns:
460
468
  -------
461
469
  float | Pandas.Series[float]
462
470
  Implied annualized volatility from the Downside 95% VaR using the
@@ -485,7 +493,7 @@ class _CommonModel(BaseModel):
485
493
  to_dt: datetime.date, optional
486
494
  Specific from date
487
495
 
488
- Returns
496
+ Returns:
489
497
  -------
490
498
  tuple[datetime.date, datetime.date]
491
499
  Start and end date of the chosen date range
@@ -504,18 +512,18 @@ class _CommonModel(BaseModel):
504
512
  "Argument months_offset implies start"
505
513
  "date before first date in series."
506
514
  )
507
- raise ValueError(msg)
515
+ raise DateAlignmentError(msg)
508
516
  later = self.last_idx
509
517
  else:
510
518
  if from_dt is not None:
511
519
  if from_dt < self.first_idx:
512
520
  msg = "Given from_dt date < series start"
513
- raise ValueError(msg)
521
+ raise DateAlignmentError(msg)
514
522
  earlier = from_dt
515
523
  if to_dt is not None:
516
524
  if to_dt > self.last_idx:
517
525
  msg = "Given to_dt date > series end"
518
- raise ValueError(msg)
526
+ raise DateAlignmentError(msg)
519
527
  later = to_dt
520
528
  while earlier not in self.tsdf.index:
521
529
  earlier -= dt.timedelta(days=1)
@@ -535,14 +543,14 @@ class _CommonModel(BaseModel):
535
543
  countries: CountriesType, default: "SE"
536
544
  (List of) country code(s) according to ISO 3166-1 alpha-2
537
545
 
538
- Returns
546
+ Returns:
539
547
  -------
540
548
  OpenFrame
541
549
  An OpenFrame object
542
550
 
543
551
  """
544
- startyear = to_datetime(self.tsdf.index[0]).year
545
- endyear = to_datetime(self.tsdf.index[-1]).year
552
+ startyear = cast("int", to_datetime(self.tsdf.index[0]).year)
553
+ endyear = cast("int", to_datetime(self.tsdf.index[-1]).year)
546
554
  calendar = holiday_calendar(
547
555
  startyear=startyear,
548
556
  endyear=endyear,
@@ -552,8 +560,8 @@ class _CommonModel(BaseModel):
552
560
  d_range = [
553
561
  d.date()
554
562
  for d in date_range(
555
- start=cast(dt.date, self.tsdf.first_valid_index()),
556
- end=cast(dt.date, self.tsdf.last_valid_index()),
563
+ start=cast("dt.date", self.tsdf.first_valid_index()),
564
+ end=cast("dt.date", self.tsdf.last_valid_index()),
557
565
  freq=CustomBusinessDay(calendar=calendar),
558
566
  )
559
567
  ]
@@ -566,7 +574,7 @@ class _CommonModel(BaseModel):
566
574
 
567
575
  Equivalent to LN(value[t] / value[t=0]) in Excel.
568
576
 
569
- Returns
577
+ Returns:
570
578
  -------
571
579
  self
572
580
  An object of the same class
@@ -587,7 +595,7 @@ class _CommonModel(BaseModel):
587
595
  method: LiteralNanMethod, default: "fill"
588
596
  Method used to handle NaN. Either fill with last known or drop
589
597
 
590
- Returns
598
+ Returns:
591
599
  -------
592
600
  self
593
601
  An object of the same class
@@ -607,7 +615,7 @@ class _CommonModel(BaseModel):
607
615
  method: LiteralNanMethod, default: "fill"
608
616
  Method used to handle NaN. Either fill with zero or drop
609
617
 
610
- Returns
618
+ Returns:
611
619
  -------
612
620
  self
613
621
  An object of the same class
@@ -622,7 +630,7 @@ class _CommonModel(BaseModel):
622
630
  def to_drawdown_series(self: Self) -> Self:
623
631
  """Convert timeseries into a drawdown series.
624
632
 
625
- Returns
633
+ Returns:
626
634
  -------
627
635
  self
628
636
  An object of the same class
@@ -652,7 +660,7 @@ class _CommonModel(BaseModel):
652
660
  directory: DirectoryPath, optional
653
661
  File folder location
654
662
 
655
- Returns
663
+ Returns:
656
664
  -------
657
665
  list[dict[str, str | bool | ValueType | list[str] | list[float]]]
658
666
  A list of dictionaries with the data of the series
@@ -670,17 +678,17 @@ class _CommonModel(BaseModel):
670
678
  output = []
671
679
  if "label" in data:
672
680
  if what_output == "tsdf":
673
- values = self.tsdf.iloc[:, 0].tolist()
681
+ values = Series(self.tsdf.iloc[:, 0]).tolist()
674
682
  else:
675
- values = list(cast(list[float], data.get("values")))
683
+ values = list(cast("list[float]", data.get("values")))
676
684
  for item in cleaner_list:
677
685
  data.pop(item)
678
- valuetype = cast(ValueType, data.get("valuetype")).value
686
+ valuetype = cast("ValueType", data.get("valuetype")).value
679
687
  data.update({"valuetype": valuetype})
680
688
  data.update({"values": values})
681
689
  output.append(dict(data))
682
690
  else:
683
- for serie in cast(list[Any], data.get("constituents")):
691
+ for serie in cast("list[Any]", data.get("constituents")):
684
692
  if what_output == "tsdf":
685
693
  values = serie.tsdf.iloc[:, 0].tolist()
686
694
  else:
@@ -688,7 +696,7 @@ class _CommonModel(BaseModel):
688
696
  itemdata = dict(serie.__dict__)
689
697
  for item in cleaner_list:
690
698
  itemdata.pop(item)
691
- valuetype = cast(ValueType, itemdata["valuetype"]).value
699
+ valuetype = cast("ValueType", itemdata["valuetype"]).value
692
700
  itemdata.update({"valuetype": valuetype})
693
701
  itemdata.update({"values": values})
694
702
  output.append(dict(itemdata))
@@ -719,7 +727,7 @@ class _CommonModel(BaseModel):
719
727
  overwrite: bool, default: True
720
728
  Flag whether to overwrite an existing file
721
729
 
722
- Returns
730
+ Returns:
723
731
  -------
724
732
  str
725
733
  The Excel file path
@@ -742,10 +750,10 @@ class _CommonModel(BaseModel):
742
750
  wrksheet = wrkbook.active
743
751
 
744
752
  if sheet_title:
745
- cast(Worksheet, wrksheet).title = sheet_title
753
+ cast("Worksheet", wrksheet).title = sheet_title
746
754
 
747
755
  for row in dataframe_to_rows(df=self.tsdf, index=True, header=True):
748
- cast(Worksheet, wrksheet).append(row)
756
+ cast("Worksheet", wrksheet).append(row)
749
757
 
750
758
  if not overwrite and Path(sheetfile).exists():
751
759
  msg = f"{sheetfile!s} already exists."
@@ -794,7 +802,7 @@ class _CommonModel(BaseModel):
794
802
  add_logo: bool, default: True
795
803
  If True a Captor logo is added to the plot
796
804
 
797
- Returns
805
+ Returns:
798
806
  -------
799
807
  tuple[plotly.go.Figure, str]
800
808
  Plotly Figure and a div section or a html filename with location
@@ -803,7 +811,7 @@ class _CommonModel(BaseModel):
803
811
  if labels:
804
812
  if len(labels) != self.tsdf.shape[1]:
805
813
  msg = "Must provide same number of labels as items in frame."
806
- raise ValueError(msg)
814
+ raise NumberOfItemsAndLabelsNotSameError(msg)
807
815
  else:
808
816
  labels = list(self.tsdf.columns.get_level_values(0))
809
817
 
@@ -849,7 +857,7 @@ class _CommonModel(BaseModel):
849
857
  auto_open=auto_open,
850
858
  auto_play=False,
851
859
  link_text="",
852
- include_plotlyjs=cast(bool, include_plotlyjs),
860
+ include_plotlyjs=cast("bool", include_plotlyjs),
853
861
  config=fig["config"],
854
862
  output_type=output_type,
855
863
  )
@@ -860,14 +868,14 @@ class _CommonModel(BaseModel):
860
868
  fig=figure,
861
869
  config=fig["config"],
862
870
  auto_play=False,
863
- include_plotlyjs=cast(bool, include_plotlyjs),
871
+ include_plotlyjs=cast("bool", include_plotlyjs),
864
872
  full_html=False,
865
873
  div_id=div_id,
866
874
  )
867
875
 
868
876
  return figure, string_output
869
877
 
870
- def plot_series( # noqa: C901
878
+ def plot_series(
871
879
  self: Self,
872
880
  mode: LiteralLinePlotMode = "lines",
873
881
  tick_fmt: str | None = None,
@@ -909,7 +917,7 @@ class _CommonModel(BaseModel):
909
917
  show_last: bool, default: False
910
918
  If True the last self.tsdf point is highlighted as red dot with a label
911
919
 
912
- Returns
920
+ Returns:
913
921
  -------
914
922
  tuple[plotly.go.Figure, str]
915
923
  Plotly Figure and a div section or a html filename with location
@@ -918,7 +926,7 @@ class _CommonModel(BaseModel):
918
926
  if labels:
919
927
  if len(labels) != self.tsdf.shape[1]:
920
928
  msg = "Must provide same number of labels as items in frame."
921
- raise ValueError(msg)
929
+ raise NumberOfItemsAndLabelsNotSameError(msg)
922
930
  else:
923
931
  labels = list(self.tsdf.columns.get_level_values(0))
924
932
 
@@ -958,7 +966,7 @@ class _CommonModel(BaseModel):
958
966
 
959
967
  for item in range(self.tsdf.shape[1]):
960
968
  figure.add_scatter(
961
- x=[self.tsdf.iloc[:, item].index[-1]],
969
+ x=[Series(self.tsdf.iloc[:, item]).index[-1]],
962
970
  y=[self.tsdf.iloc[-1, item]],
963
971
  mode="markers + text",
964
972
  marker={"color": "red", "size": 12},
@@ -979,7 +987,7 @@ class _CommonModel(BaseModel):
979
987
  auto_open=auto_open,
980
988
  auto_play=False,
981
989
  link_text="",
982
- include_plotlyjs=cast(bool, include_plotlyjs),
990
+ include_plotlyjs=cast("bool", include_plotlyjs),
983
991
  config=fig["config"],
984
992
  output_type=output_type,
985
993
  )
@@ -990,7 +998,7 @@ class _CommonModel(BaseModel):
990
998
  fig=figure,
991
999
  config=fig["config"],
992
1000
  auto_play=False,
993
- include_plotlyjs=cast(bool, include_plotlyjs),
1001
+ include_plotlyjs=cast("bool", include_plotlyjs),
994
1002
  full_html=False,
995
1003
  div_id=div_id,
996
1004
  )
@@ -1019,7 +1027,7 @@ class _CommonModel(BaseModel):
1019
1027
  Allows locking the periods-in-a-year to simplify test cases and
1020
1028
  comparisons
1021
1029
 
1022
- Returns
1030
+ Returns:
1023
1031
  -------
1024
1032
  float | Pandas.Series[float]
1025
1033
  Annualized arithmetic mean of returns
@@ -1035,13 +1043,13 @@ class _CommonModel(BaseModel):
1035
1043
  else:
1036
1044
  fraction = (later - earlier).days / 365.25
1037
1045
  how_many = self.tsdf.loc[
1038
- cast(int, earlier) : cast(int, later),
1046
+ cast("int", earlier) : cast("int", later),
1039
1047
  self.tsdf.columns.to_numpy()[0],
1040
1048
  ].count()
1041
- time_factor = how_many / fraction
1049
+ time_factor = cast("int", how_many) / fraction
1042
1050
 
1043
1051
  result = (
1044
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1052
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1045
1053
  .ffill()
1046
1054
  .pct_change()
1047
1055
  .mean()
@@ -1081,7 +1089,7 @@ class _CommonModel(BaseModel):
1081
1089
  periods_in_a_year_fixed : DaysInYearType, optional
1082
1090
  Allows locking the periods-in-a-year to simplify test cases and comparisons
1083
1091
 
1084
- Returns
1092
+ Returns:
1085
1093
  -------
1086
1094
  float | Pandas.Series[float]
1087
1095
  Annualized volatility
@@ -1097,15 +1105,17 @@ class _CommonModel(BaseModel):
1097
1105
  else:
1098
1106
  fraction = (later - earlier).days / 365.25
1099
1107
  how_many = (
1100
- self.tsdf.loc[cast(int, earlier) : cast(int, later)].count().iloc[0]
1108
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1109
+ .count()
1110
+ .iloc[0]
1101
1111
  )
1102
1112
  time_factor = how_many / fraction
1103
1113
 
1104
- data = self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1114
+ data = self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1105
1115
  result = data.ffill().pct_change().std().mul(sqrt(time_factor))
1106
1116
 
1107
1117
  if self.tsdf.shape[1] == 1:
1108
- return float(cast(SupportsFloat, result.iloc[0]))
1118
+ return float(cast("SupportsFloat", result.iloc[0]))
1109
1119
  return Series(
1110
1120
  data=result,
1111
1121
  index=self.tsdf.columns,
@@ -1148,7 +1158,7 @@ class _CommonModel(BaseModel):
1148
1158
  drift_adjust: bool, default: False
1149
1159
  An adjustment to remove the bias implied by the average return
1150
1160
 
1151
- Returns
1161
+ Returns:
1152
1162
  -------
1153
1163
  float | Pandas.Series[float]
1154
1164
  Implied annualized volatility from the Downside VaR using the
@@ -1209,7 +1219,7 @@ class _CommonModel(BaseModel):
1209
1219
  drift_adjust: bool, default: False
1210
1220
  An adjustment to remove the bias implied by the average return
1211
1221
 
1212
- Returns
1222
+ Returns:
1213
1223
  -------
1214
1224
  float | Pandas.Series[float]
1215
1225
  A position weight multiplier from the ratio between a VaR implied
@@ -1275,7 +1285,7 @@ class _CommonModel(BaseModel):
1275
1285
  drift_adjust: bool, default: False
1276
1286
  An adjustment to remove the bias implied by the average return
1277
1287
 
1278
- Returns
1288
+ Returns:
1279
1289
  -------
1280
1290
  float | Pandas.Series[float]
1281
1291
  Target volatility if target_vol is provided otherwise the VaR
@@ -1292,21 +1302,23 @@ class _CommonModel(BaseModel):
1292
1302
  else:
1293
1303
  fraction = (later - earlier).days / 365.25
1294
1304
  how_many = (
1295
- self.tsdf.loc[cast(int, earlier) : cast(int, later)].count().iloc[0]
1305
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1306
+ .count()
1307
+ .iloc[0]
1296
1308
  )
1297
1309
  time_factor = how_many / fraction
1298
1310
  if drift_adjust:
1299
1311
  imp_vol = (-sqrt(time_factor) / norm.ppf(level)) * (
1300
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1312
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1301
1313
  .ffill()
1302
1314
  .pct_change()
1303
1315
  .quantile(1 - level, interpolation=interpolation)
1304
- - self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1316
+ - self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1305
1317
  .ffill()
1306
1318
  .pct_change()
1307
1319
  .sum()
1308
1320
  / len(
1309
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1321
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1310
1322
  .ffill()
1311
1323
  .pct_change(),
1312
1324
  )
@@ -1314,7 +1326,7 @@ class _CommonModel(BaseModel):
1314
1326
  else:
1315
1327
  imp_vol = (
1316
1328
  -sqrt(time_factor)
1317
- * self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1329
+ * self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1318
1330
  .ffill()
1319
1331
  .pct_change()
1320
1332
  .quantile(1 - level, interpolation=interpolation)
@@ -1334,7 +1346,7 @@ class _CommonModel(BaseModel):
1334
1346
  label = f"Imp vol from VaR {level:.0%}"
1335
1347
 
1336
1348
  if self.tsdf.shape[1] == 1:
1337
- return float(cast(SupportsFloat, result.iloc[0]))
1349
+ return float(cast("SupportsFloat", result.iloc[0]))
1338
1350
  return Series(
1339
1351
  data=result,
1340
1352
  index=self.tsdf.columns,
@@ -1365,7 +1377,7 @@ class _CommonModel(BaseModel):
1365
1377
  to_date : datetime.date, optional
1366
1378
  Specific to date
1367
1379
 
1368
- Returns
1380
+ Returns:
1369
1381
  -------
1370
1382
  float | Pandas.Series[float]
1371
1383
  Downside Conditional Value At Risk "CVaR"
@@ -1376,22 +1388,22 @@ class _CommonModel(BaseModel):
1376
1388
  from_dt=from_date,
1377
1389
  to_dt=to_date,
1378
1390
  )
1379
- cvar_df = self.tsdf.loc[cast(int, earlier) : cast(int, later)].copy(deep=True)
1391
+ cvar_df = self.tsdf.loc[cast("int", earlier) : cast("int", later)].copy(
1392
+ deep=True
1393
+ )
1380
1394
  result = [
1381
1395
  cvar_df.loc[:, x] # type: ignore[call-overload,index]
1382
1396
  .ffill()
1383
1397
  .pct_change()
1384
1398
  .sort_values()
1385
1399
  .iloc[
1386
- : int(
1387
- ceil(
1388
- (1 - level)
1389
- * cvar_df.loc[:, x] # type: ignore[index]
1390
- .ffill()
1391
- .pct_change()
1392
- .count(),
1393
- ),
1394
- )
1400
+ : ceil(
1401
+ (1 - level)
1402
+ * cvar_df.loc[:, x] # type: ignore[index]
1403
+ .ffill()
1404
+ .pct_change()
1405
+ .count(),
1406
+ ),
1395
1407
  ]
1396
1408
  .mean()
1397
1409
  for x in self.tsdf
@@ -1434,7 +1446,7 @@ class _CommonModel(BaseModel):
1434
1446
  Allows locking the periods-in-a-year to simplify test cases and
1435
1447
  comparisons
1436
1448
 
1437
- Returns
1449
+ Returns:
1438
1450
  -------
1439
1451
  float | Pandas.Series[float]
1440
1452
  Downside deviation
@@ -1447,7 +1459,7 @@ class _CommonModel(BaseModel):
1447
1459
  to_dt=to_date,
1448
1460
  )
1449
1461
  how_many = (
1450
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1462
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1451
1463
  .ffill()
1452
1464
  .pct_change()
1453
1465
  .count(numeric_only=True)
@@ -1463,7 +1475,7 @@ class _CommonModel(BaseModel):
1463
1475
  time_factor = how_many.div(fraction)
1464
1476
 
1465
1477
  dddf = (
1466
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1478
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1467
1479
  .ffill()
1468
1480
  .pct_change()
1469
1481
  .sub(min_accepted_return / time_factor)
@@ -1502,7 +1514,7 @@ class _CommonModel(BaseModel):
1502
1514
  to_date : datetime.date, optional
1503
1515
  Specific to date
1504
1516
 
1505
- Returns
1517
+ Returns:
1506
1518
  -------
1507
1519
  float | Pandas.Series[float]
1508
1520
  Compounded Annual Growth Rate (CAGR)
@@ -1516,13 +1528,18 @@ class _CommonModel(BaseModel):
1516
1528
  )
1517
1529
  fraction = (later - earlier).days / 365.25
1518
1530
 
1519
- any_below_zero = any(self.tsdf.loc[[earlier, later]].lt(0.0).any().to_numpy())
1531
+ any_below_zero = any(
1532
+ self.tsdf.loc[[earlier, later]] # type: ignore[index]
1533
+ .lt(0.0)
1534
+ .any()
1535
+ .to_numpy()
1536
+ )
1520
1537
  if zero in self.tsdf.loc[earlier].to_numpy() or any_below_zero:
1521
1538
  msg = (
1522
1539
  "Geometric return cannot be calculated due to "
1523
1540
  "an initial value being zero or a negative value."
1524
1541
  )
1525
- raise ValueError(msg)
1542
+ raise InitialValueZeroError(msg)
1526
1543
 
1527
1544
  result = (self.tsdf.loc[later] / self.tsdf.loc[earlier]) ** (1 / fraction) - 1
1528
1545
 
@@ -1555,7 +1572,7 @@ class _CommonModel(BaseModel):
1555
1572
  to_date : datetime.date, optional
1556
1573
  Specific to date
1557
1574
 
1558
- Returns
1575
+ Returns:
1559
1576
  -------
1560
1577
  float | Pandas.Series[float]
1561
1578
  Skew of the return distribution
@@ -1567,7 +1584,7 @@ class _CommonModel(BaseModel):
1567
1584
  to_dt=to_date,
1568
1585
  )
1569
1586
  result: NDArray[float64] = skew(
1570
- a=self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1587
+ a=self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1571
1588
  .ffill()
1572
1589
  .pct_change()
1573
1590
  .to_numpy(),
@@ -1604,7 +1621,7 @@ class _CommonModel(BaseModel):
1604
1621
  to_date : datetime.date, optional
1605
1622
  Specific to date
1606
1623
 
1607
- Returns
1624
+ Returns:
1608
1625
  -------
1609
1626
  float | Pandas.Series[float]
1610
1627
  Kurtosis of the return distribution
@@ -1616,7 +1633,11 @@ class _CommonModel(BaseModel):
1616
1633
  to_dt=to_date,
1617
1634
  )
1618
1635
  result: NDArray[float64] = kurtosis(
1619
- self.tsdf.loc[cast(int, earlier) : cast(int, later)].ffill().pct_change(),
1636
+ a=(
1637
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1638
+ .ffill()
1639
+ .pct_change()
1640
+ ),
1620
1641
  fisher=True,
1621
1642
  bias=True,
1622
1643
  nan_policy="omit",
@@ -1654,7 +1675,7 @@ class _CommonModel(BaseModel):
1654
1675
  min_periods: int, default: 1
1655
1676
  Smallest number of observations to use to find the maximum drawdown
1656
1677
 
1657
- Returns
1678
+ Returns:
1658
1679
  -------
1659
1680
  float | Pandas.Series[float]
1660
1681
  Maximum drawdown without any limit on date range
@@ -1666,8 +1687,8 @@ class _CommonModel(BaseModel):
1666
1687
  to_dt=to_date,
1667
1688
  )
1668
1689
  result = (
1669
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1670
- / self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1690
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1691
+ / self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1671
1692
  .expanding(min_periods=min_periods)
1672
1693
  .max()
1673
1694
  ).min() - 1
@@ -1698,7 +1719,7 @@ class _CommonModel(BaseModel):
1698
1719
  to_date : datetime.date, optional
1699
1720
  Specific to date
1700
1721
 
1701
- Returns
1722
+ Returns:
1702
1723
  -------
1703
1724
  float | Pandas.Series[float]
1704
1725
  Calculate share of percentage changes that are greater than zero
@@ -1711,10 +1732,10 @@ class _CommonModel(BaseModel):
1711
1732
  to_dt=to_date,
1712
1733
  )
1713
1734
  pos = (
1714
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1735
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1715
1736
  .ffill()
1716
1737
  .pct_change()[1:][
1717
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1738
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1718
1739
  .ffill()
1719
1740
  .pct_change()[1:]
1720
1741
  > zero
@@ -1722,7 +1743,7 @@ class _CommonModel(BaseModel):
1722
1743
  .count()
1723
1744
  )
1724
1745
  tot = (
1725
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1746
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1726
1747
  .ffill()
1727
1748
  .pct_change()
1728
1749
  .count()
@@ -1768,7 +1789,7 @@ class _CommonModel(BaseModel):
1768
1789
  Allows locking the periods-in-a-year to simplify test cases and
1769
1790
  comparisons
1770
1791
 
1771
- Returns
1792
+ Returns:
1772
1793
  -------
1773
1794
  float | Pandas.Series[float]
1774
1795
  Ratio of the annualized arithmetic mean of returns and annualized
@@ -1791,7 +1812,7 @@ class _CommonModel(BaseModel):
1791
1812
  )
1792
1813
 
1793
1814
  if self.tsdf.shape[1] == 1:
1794
- return float(cast(float64, ratio.iloc[0]))
1815
+ return float(cast("float64", ratio.iloc[0]))
1795
1816
  return Series(
1796
1817
  data=ratio,
1797
1818
  index=self.tsdf.columns,
@@ -1833,7 +1854,7 @@ class _CommonModel(BaseModel):
1833
1854
  Allows locking the periods-in-a-year to simplify test cases and
1834
1855
  comparisons
1835
1856
 
1836
- Returns
1857
+ Returns:
1837
1858
  -------
1838
1859
  float | Pandas.Series[float]
1839
1860
  Sortino ratio calculated as ( return - riskfree return ) /
@@ -1857,7 +1878,7 @@ class _CommonModel(BaseModel):
1857
1878
  )
1858
1879
 
1859
1880
  if self.tsdf.shape[1] == 1:
1860
- return float(cast(float64, ratio.iloc[0]))
1881
+ return float(cast("float64", ratio.iloc[0]))
1861
1882
  return Series(
1862
1883
  data=ratio,
1863
1884
  index=self.tsdf.columns,
@@ -1891,7 +1912,7 @@ class _CommonModel(BaseModel):
1891
1912
  to_date : datetime.date, optional
1892
1913
  Specific to date
1893
1914
 
1894
- Returns
1915
+ Returns:
1895
1916
  -------
1896
1917
  float | Pandas.Series[float]
1897
1918
  Omega ratio calculation
@@ -1903,14 +1924,16 @@ class _CommonModel(BaseModel):
1903
1924
  to_dt=to_date,
1904
1925
  )
1905
1926
  retdf = (
1906
- self.tsdf.loc[cast(int, earlier) : cast(int, later)].ffill().pct_change()
1927
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
1928
+ .ffill()
1929
+ .pct_change()
1907
1930
  )
1908
1931
  pos = retdf[retdf > min_accepted_return].sub(min_accepted_return).sum()
1909
1932
  neg = retdf[retdf < min_accepted_return].sub(min_accepted_return).sum()
1910
1933
  ratio = pos / -neg
1911
1934
 
1912
1935
  if self.tsdf.shape[1] == 1:
1913
- return float(cast(float64, ratio.iloc[0]))
1936
+ return float(cast("float64", ratio.iloc[0]))
1914
1937
  return Series(
1915
1938
  data=ratio,
1916
1939
  index=self.tsdf.columns,
@@ -1936,7 +1959,7 @@ class _CommonModel(BaseModel):
1936
1959
  to_date : datetime.date, optional
1937
1960
  Specific to date
1938
1961
 
1939
- Returns
1962
+ Returns:
1940
1963
  -------
1941
1964
  float | Pandas.Series[float]
1942
1965
  Calculate simple return
@@ -1953,7 +1976,7 @@ class _CommonModel(BaseModel):
1953
1976
  "Simple return cannot be calculated due to "
1954
1977
  f"an initial value being zero. ({self.tsdf.head(3)})"
1955
1978
  )
1956
- raise ValueError(msg)
1979
+ raise InitialValueZeroError(msg)
1957
1980
 
1958
1981
  result = self.tsdf.loc[later] / self.tsdf.loc[earlier] - 1
1959
1982
 
@@ -1980,7 +2003,7 @@ class _CommonModel(BaseModel):
1980
2003
  month : int, optional
1981
2004
  Calendar month of the period to calculate.
1982
2005
 
1983
- Returns
2006
+ Returns:
1984
2007
  -------
1985
2008
  float | Pandas.Series[float]
1986
2009
  Calculate simple return for a specific calendar period
@@ -2031,7 +2054,7 @@ class _CommonModel(BaseModel):
2031
2054
  interpolation: LiteralQuantileInterp, default: "lower"
2032
2055
  Type of interpolation in Pandas.DataFrame.quantile() function.
2033
2056
 
2034
- Returns
2057
+ Returns:
2035
2058
  -------
2036
2059
  float | Pandas.Series[float]
2037
2060
  Downside Value At Risk
@@ -2043,7 +2066,7 @@ class _CommonModel(BaseModel):
2043
2066
  to_dt=to_date,
2044
2067
  )
2045
2068
  result = (
2046
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
2069
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
2047
2070
  .ffill()
2048
2071
  .pct_change()
2049
2072
  .quantile(1 - level, interpolation=interpolation)
@@ -2079,7 +2102,7 @@ class _CommonModel(BaseModel):
2079
2102
  to_date : datetime.date, optional
2080
2103
  Specific to date
2081
2104
 
2082
- Returns
2105
+ Returns:
2083
2106
  -------
2084
2107
  float | Pandas.Series[float]
2085
2108
  Most negative percentage change over a rolling number of observations
@@ -2092,7 +2115,7 @@ class _CommonModel(BaseModel):
2092
2115
  to_dt=to_date,
2093
2116
  )
2094
2117
  result = (
2095
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
2118
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
2096
2119
  .ffill()
2097
2120
  .pct_change()
2098
2121
  .rolling(observations, min_periods=observations)
@@ -2129,7 +2152,7 @@ class _CommonModel(BaseModel):
2129
2152
  to_date : datetime.date, optional
2130
2153
  Specific to date
2131
2154
 
2132
- Returns
2155
+ Returns:
2133
2156
  -------
2134
2157
  float | Pandas.Series[float]
2135
2158
  Z-score as (last return - mean return) / standard deviation of returns
@@ -2141,7 +2164,9 @@ class _CommonModel(BaseModel):
2141
2164
  to_dt=to_date,
2142
2165
  )
2143
2166
  zscframe = (
2144
- self.tsdf.loc[cast(int, earlier) : cast(int, later)].ffill().pct_change()
2167
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
2168
+ .ffill()
2169
+ .pct_change()
2145
2170
  )
2146
2171
  result = (zscframe.iloc[-1] - zscframe.mean()) / zscframe.std()
2147
2172
 
@@ -2171,15 +2196,15 @@ class _CommonModel(BaseModel):
2171
2196
  observations: int, default: 252
2172
2197
  Number of observations in the overlapping window.
2173
2198
 
2174
- Returns
2199
+ Returns:
2175
2200
  -------
2176
2201
  Pandas.DataFrame
2177
2202
  Calculate rolling annualized downside CVaR
2178
2203
 
2179
2204
  """
2180
- cvar_label = cast(tuple[str], self.tsdf.iloc[:, column].name)[0]
2205
+ cvar_label = cast("tuple[str]", self.tsdf.iloc[:, column].name)[0]
2181
2206
  cvarseries = (
2182
- self.tsdf.iloc[:, column]
2207
+ Series(self.tsdf.iloc[:, column])
2183
2208
  .rolling(observations, min_periods=observations)
2184
2209
  .apply(lambda x: _cvar_down_calc(x, level=level))
2185
2210
  )
@@ -2202,15 +2227,15 @@ class _CommonModel(BaseModel):
2202
2227
  observations: int, default: 21
2203
2228
  Number of observations in the overlapping window.
2204
2229
 
2205
- Returns
2230
+ Returns:
2206
2231
  -------
2207
2232
  Pandas.DataFrame
2208
2233
  Calculate rolling returns
2209
2234
 
2210
2235
  """
2211
- ret_label = cast(tuple[str], self.tsdf.iloc[:, column].name)[0]
2236
+ ret_label = cast("tuple[str]", self.tsdf.iloc[:, column].name)[0]
2212
2237
  retseries = (
2213
- self.tsdf.iloc[:, column]
2238
+ Series(self.tsdf.iloc[:, column])
2214
2239
  .ffill()
2215
2240
  .pct_change()
2216
2241
  .rolling(observations, min_periods=observations)
@@ -2241,15 +2266,15 @@ class _CommonModel(BaseModel):
2241
2266
  interpolation: LiteralQuantileInterp, default: "lower"
2242
2267
  Type of interpolation in Pandas.DataFrame.quantile() function.
2243
2268
 
2244
- Returns
2269
+ Returns:
2245
2270
  -------
2246
2271
  Pandas.DataFrame
2247
2272
  Calculate rolling annualized downside Value At Risk "VaR"
2248
2273
 
2249
2274
  """
2250
- var_label = cast(tuple[str], self.tsdf.iloc[:, column].name)[0]
2275
+ var_label = cast("tuple[str]", self.tsdf.iloc[:, column].name)[0]
2251
2276
  varseries = (
2252
- self.tsdf.iloc[:, column]
2277
+ Series(self.tsdf.iloc[:, column])
2253
2278
  .rolling(observations, min_periods=observations)
2254
2279
  .apply(
2255
2280
  lambda x: _var_down_calc(x, level=level, interpolation=interpolation),
@@ -2278,7 +2303,7 @@ class _CommonModel(BaseModel):
2278
2303
  Allows locking the periods-in-a-year to simplify test cases and
2279
2304
  comparisons
2280
2305
 
2281
- Returns
2306
+ Returns:
2282
2307
  -------
2283
2308
  Pandas.DataFrame
2284
2309
  Calculate rolling annualised volatilities
@@ -2288,8 +2313,8 @@ class _CommonModel(BaseModel):
2288
2313
  time_factor = float(periods_in_a_year_fixed)
2289
2314
  else:
2290
2315
  time_factor = self.periods_in_a_year
2291
- vol_label = cast(tuple[str, ValueType], self.tsdf.iloc[:, column].name)[0]
2292
- dframe = self.tsdf.iloc[:, column].ffill().pct_change()
2316
+ vol_label = cast("tuple[str, ValueType]", self.tsdf.iloc[:, column].name)[0]
2317
+ dframe = Series(self.tsdf.iloc[:, column]).ffill().pct_change()
2293
2318
  volseries = dframe.rolling(
2294
2319
  observations,
2295
2320
  min_periods=observations,