openseries 1.2.2__py3-none-any.whl → 1.2.4__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/load_plotly.py CHANGED
@@ -1,32 +1,22 @@
1
- """
2
- Function to load plotly layout and configuration from local json file
3
- """
1
+ """Function to load plotly layout and configuration from local json file."""
2
+ from __future__ import annotations
3
+
4
4
  from json import load
5
5
  from os.path import abspath, dirname, join
6
- from typing import Union
6
+
7
+ from openseries.types import PlotlyLayoutType
7
8
 
8
9
 
9
10
  def load_plotly_dict(
10
11
  responsive: bool = True,
11
- ) -> tuple[
12
- dict[
13
- str,
14
- Union[
15
- str,
16
- int,
17
- float,
18
- bool,
19
- list[str],
20
- dict[str, Union[str, int, float, bool, list[str]]],
21
- ],
22
- ],
23
- dict[str, Union[str, float]],
24
- ]:
25
- """Function to load the plotly defaults
12
+ ) -> PlotlyLayoutType:
13
+ """
14
+ Load Plotly defaults.
26
15
 
27
16
  Parameters
28
17
  ----------
29
18
  responsive : bool
19
+ Flag whether to load as responsive
30
20
 
31
21
  Returns
32
22
  -------
@@ -36,9 +26,9 @@ def load_plotly_dict(
36
26
  layoutfile = join(abspath(project_root), "openseries", "plotly_layouts.json")
37
27
  logofile = join(abspath(project_root), "openseries", "plotly_captor_logo.json")
38
28
 
39
- with open(layoutfile, "r", encoding="utf-8") as layout_file:
29
+ with open(layoutfile, encoding="utf-8") as layout_file:
40
30
  fig = load(layout_file)
41
- with open(logofile, "r", encoding="utf-8") as logo_file:
31
+ with open(logofile, encoding="utf-8") as logo_file:
42
32
  logo = load(logo_file)
43
33
 
44
34
  fig["config"].update({"responsive": responsive})
openseries/risk.py CHANGED
@@ -8,7 +8,8 @@ from __future__ import annotations
8
8
 
9
9
  import datetime as dt
10
10
  from math import ceil
11
- from typing import cast, Union
11
+ from typing import Union, cast
12
+
12
13
  from numpy import (
13
14
  Inf,
14
15
  isnan,
@@ -26,13 +27,17 @@ from openseries.types import LiteralQuantileInterp
26
27
 
27
28
 
28
29
  def cvar_down_calc(
29
- data: Union[DataFrame, Series, list[float]], level: float = 0.95
30
+ data: Union[DataFrame, Series, list[float]],
31
+ level: float = 0.95,
30
32
  ) -> float:
31
- """https://www.investopedia.com/terms/c/conditional_value_at_risk.asp
33
+ """
34
+ Calculate downside Conditional Value at Risk (CVaR).
35
+
36
+ https://www.investopedia.com/terms/c/conditional_value_at_risk.asp.
32
37
 
33
38
  Parameters
34
39
  ----------
35
- data: DataFrame | Series | list[float]
40
+ data: Union[DataFrame, Series, list[float]]
36
41
  The data to perform the calculation over
37
42
  level: float, default: 0.95
38
43
  The sought CVaR level
@@ -42,7 +47,6 @@ def cvar_down_calc(
42
47
  float
43
48
  Downside Conditional Value At Risk "CVaR"
44
49
  """
45
-
46
50
  if isinstance(data, DataFrame):
47
51
  clean = nan_to_num(data.iloc[:, 0])
48
52
  else:
@@ -57,13 +61,15 @@ def var_down_calc(
57
61
  level: float = 0.95,
58
62
  interpolation: LiteralQuantileInterp = "lower",
59
63
  ) -> float:
60
- """Downside Value At Risk, "VaR". The equivalent of
61
- percentile.inc([...], 1-level) over returns in MS Excel \n
62
- https://www.investopedia.com/terms/v/var.asp
64
+ """
65
+ Calculate downside Value At Risk (VaR).
66
+
67
+ The equivalent of percentile.inc([...], 1-level) over returns in MS Excel
68
+ https://www.investopedia.com/terms/v/var.asp.
63
69
 
64
70
  Parameters
65
71
  ----------
66
- data: DataFrame | Series | list[float]
72
+ data: Union[DataFrame, Series, list[float]]
67
73
  The data to perform the calculation over
68
74
  level: float, default: 0.95
69
75
  The sought VaR level
@@ -75,7 +81,6 @@ def var_down_calc(
75
81
  float
76
82
  Downside Value At Risk
77
83
  """
78
-
79
84
  if isinstance(data, DataFrame):
80
85
  clean = nan_to_num(data.iloc[:, 0])
81
86
  else:
@@ -85,7 +90,10 @@ def var_down_calc(
85
90
 
86
91
 
87
92
  def drawdown_series(prices: Union[DataFrame, Series]) -> Union[DataFrame, Series]:
88
- """Calculates https://www.investopedia.com/terms/d/drawdown.asp
93
+ """
94
+ Convert series into a maximum drawdown series.
95
+
96
+ Calculates https://www.investopedia.com/terms/d/drawdown.asp
89
97
  This returns a series representing a drawdown. When the price is at all-time
90
98
  highs, the drawdown is 0. However, when prices are below high watermarks,
91
99
  the drawdown series = current / hwm - 1 The max drawdown can be obtained by
@@ -94,12 +102,12 @@ def drawdown_series(prices: Union[DataFrame, Series]) -> Union[DataFrame, Series
94
102
 
95
103
  Parameters
96
104
  ----------
97
- prices: DataFrame | Series
105
+ prices: Union[DataFrame, Series]
98
106
  A timeserie of dates and values
99
107
 
100
108
  Returns
101
109
  -------
102
- DataFrame | Series
110
+ Union[DataFrame, Series]
103
111
  A drawdown timeserie
104
112
  """
105
113
  drawdown = prices.copy()
@@ -111,11 +119,12 @@ def drawdown_series(prices: Union[DataFrame, Series]) -> Union[DataFrame, Series
111
119
 
112
120
 
113
121
  def drawdown_details(prices: Union[DataFrame, Series], min_periods: int = 1) -> Series:
114
- """Details of the maximum drawdown
122
+ """
123
+ Details of the maximum drawdown.
115
124
 
116
125
  Parameters
117
126
  ----------
118
- prices: DataFrame | Series
127
+ prices: Union[DataFrame, Series]
119
128
  A timeserie of dates and values
120
129
  min_periods: int, default: 1
121
130
  Smallest number of observations to use to find the maximum drawdown
@@ -129,19 +138,28 @@ def drawdown_details(prices: Union[DataFrame, Series], min_periods: int = 1) ->
129
138
  Days from start to bottom
130
139
  Average fall per day
131
140
  """
132
-
133
141
  mdd_date = (
134
- (prices / prices.expanding(min_periods=min_periods).max()).idxmin().values[0]
142
+ (prices / prices.expanding(min_periods=min_periods).max())
143
+ .idxmin()
144
+ .to_numpy()[0]
145
+ )
146
+ mdate = (
147
+ dt.datetime.strptime(str(mdd_date)[:10], "%Y-%m-%d")
148
+ .replace(tzinfo=dt.timezone.utc)
149
+ .date()
135
150
  )
136
- mdate = dt.datetime.strptime(str(mdd_date)[:10], "%Y-%m-%d").date()
137
151
  maxdown = (
138
152
  (prices / prices.expanding(min_periods=min_periods).max()).min() - 1
139
153
  ).iloc[0]
140
154
  ddata = prices.copy()
141
155
  drwdwn = drawdown_series(ddata).loc[: cast(int, mdate)]
142
- drwdwn.sort_index(ascending=False, inplace=True)
143
- sdate = drwdwn[drwdwn == 0.0].idxmax().values[0]
144
- sdate = dt.datetime.strptime(str(sdate)[:10], "%Y-%m-%d").date()
156
+ drwdwn = drwdwn.sort_index(ascending=False)
157
+ sdate = drwdwn[drwdwn == 0.0].idxmax().to_numpy()[0]
158
+ sdate = (
159
+ dt.datetime.strptime(str(sdate)[:10], "%Y-%m-%d")
160
+ .replace(tzinfo=dt.timezone.utc)
161
+ .date()
162
+ )
145
163
  duration = (mdate - sdate).days
146
164
  ret_per_day = maxdown / duration
147
165
 
@@ -159,9 +177,13 @@ def drawdown_details(prices: Union[DataFrame, Series], min_periods: int = 1) ->
159
177
 
160
178
 
161
179
  def ewma_calc(
162
- reeturn: float, prev_ewma: float, time_factor: float, lmbda: float = 0.94
180
+ reeturn: float,
181
+ prev_ewma: float,
182
+ time_factor: float,
183
+ lmbda: float = 0.94,
163
184
  ) -> float:
164
- """Helper function for EWMA calculation
185
+ """
186
+ Calculate Exponentially Weighted Moving Average volatility.
165
187
 
166
188
  Parameters
167
189
  ----------