openseries 1.5.2__tar.gz → 1.5.4__tar.gz

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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openseries
3
- Version: 1.5.2
3
+ Version: 1.5.4
4
4
  Summary: Tools for analyzing financial timeseries.
5
5
  Home-page: https://github.com/CaptorAB/openseries
6
6
  License: BSD-3-Clause
@@ -25,7 +25,7 @@ Requires-Dist: numpy (>=1.23.2,<=2.0.0)
25
25
  Requires-Dist: openpyxl (>=3.1.2,<4.0.0)
26
26
  Requires-Dist: pandas (>=2.1.2,<3.0.0)
27
27
  Requires-Dist: plotly (>=5.18.0,<6.0.0)
28
- Requires-Dist: pyarrow (>=14.0.2,<16.0.0)
28
+ Requires-Dist: pyarrow (>=14.0.2,<17.0.0)
29
29
  Requires-Dist: pydantic (>=2.5.2,<3.0.0)
30
30
  Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
31
31
  Requires-Dist: requests (>=2.20.0,<3.0.0)
@@ -53,7 +53,7 @@ width="81" height="100" align="left" float="right"/><br/>
53
53
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://beta.ruff.rs/docs/)
54
54
  [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
55
55
 
56
- `openseries` is a project with tools to analyse financial timeseries of a single
56
+ This is a project with tools to analyze financial timeseries of a single
57
57
  asset or a group of assets. It is solely made for daily or less frequent data.
58
58
 
59
59
  <span style="font-size:2em;">[CHANGELOG](https://github.com/CaptorAB/openseries/blob/master/CHANGELOG.md)</span>
@@ -289,6 +289,7 @@ make lint
289
289
  | `rolling_info_ratio` | `OpenFrame` | Returns a pandas.DataFrame with the rolling [information ratio](https://www.investopedia.com/terms/i/informationratio.asp) between two series. |
290
290
  | `rolling_beta` | `OpenFrame` | Returns a pandas.DataFrame with the rolling [Beta](https://www.investopedia.com/terms/b/beta.asp) of an asset relative a market. |
291
291
  | `rolling_corr` | `OpenFrame` | Calculates and adds a series of rolling [correlations](https://www.investopedia.com/terms/c/correlation.asp) between two other series. |
292
+ | `correl_matrix` | `OpenFrame` | Returns a `pandas.DataFrame` with a correlation matrix. |
292
293
  | `ewma_risk` | `OpenFrame` | Returns a `pandas.DataFrame` with volatility and correlation based on [Exponentially Weighted Moving Average](https://www.investopedia.com/articles/07/ewma.asp). |
293
294
 
294
295
  ### Methods that apply to both the [OpenTimeSeries](https://github.com/CaptorAB/openseries/blob/master/openseries/series.py) and the [OpenFrame](https://github.com/CaptorAB/openseries/blob/master/openseries/frame.py) class.
@@ -328,6 +329,7 @@ make lint
328
329
  | `downside_deviation` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Downside deviation](https://www.investopedia.com/terms/d/downside-deviation.asp) is the volatility of all negative return observations. Minimum Accepted Return (MAR) set to zero. |
329
330
  | `ret_vol_ratio` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Ratio of arithmetic mean return and annualized volatility. It is the [Sharpe Ratio](https://www.investopedia.com/terms/s/sharperatio.asp) with the riskfree rate set to zero. |
330
331
  | `sortino_ratio` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | The [Sortino Ratio](https://www.investopedia.com/terms/s/sortinoratio.asp) is the arithmetic mean return divided by the downside deviation. This attribute assumes that the riskfree rate and the MAR are both zero. |
332
+ | `omega_ratio` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | The [Omega Ratio](https://en.wikipedia.org/wiki/Omega_ratio) compares returns above a certain target level (MAR) to the total downside risk below MAR. |
331
333
  | `var_down` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Downside 95% [Value At Risk](https://www.investopedia.com/terms/v/var.asp), "VaR". The equivalent of percentile.inc([...], 1-level) over returns in MS Excel. For other confidence levels use the corresponding method. |
332
334
  | `cvar_down` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Downside 95% [Conditional Value At Risk](https://www.investopedia.com/terms/c/conditional_value_at_risk.asp), "CVaR". For other confidence levels use the corresponding method. |
333
335
  | `worst` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Most negative percentage change of a single observation. |
@@ -339,7 +341,6 @@ make lint
339
341
  | `skew` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Skew](https://www.investopedia.com/terms/s/skewness.asp) of the return distribution. |
340
342
  | `kurtosis` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Kurtosis](https://www.investopedia.com/terms/k/kurtosis.asp) of the return distribution. |
341
343
  | `z_score` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Z-score](https://www.investopedia.com/terms/z/zscore.asp) as (last return - mean return) / standard deviation of returns. |
342
- | `correl_matrix` | `pandas.DataFrame` | `OpenFrame` | A correlation matrix. |
343
344
 
344
345
  ### Methods below are identical to the Numerical Properties above.
345
346
 
@@ -355,6 +356,7 @@ properties for subset periods._
355
356
  | `downside_deviation_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Downside deviation](https://www.investopedia.com/terms/d/downside-deviation.asp) is the volatility of all negative return observations. MAR and riskfree rate can be set. |
356
357
  | `ret_vol_ratio_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Ratio of arithmetic mean return and annualized volatility. It is the [Sharpe Ratio](https://www.investopedia.com/terms/s/sharperatio.asp) with the riskfree rate set to zero. A riskfree rate can be set as a float or a series chosen for the frame function. |
357
358
  | `sortino_ratio_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | The [Sortino Ratio](https://www.investopedia.com/terms/s/sortinoratio.asp) is the arithmetic mean return divided by the downside deviation. A riskfree rate can be set as a float or a series chosen for the frame function. MAR is set to zero. |
359
+ | `omega_ratio_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | The [Omega Ratio](https://en.wikipedia.org/wiki/Omega_ratio) compares returns above a certain target level (MAR) to the total downside risk below MAR. |
358
360
  | `var_down_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Downside 95% [Value At Risk](https://www.investopedia.com/terms/v/var.asp), "VaR". The equivalent of percentile.inc([...], 1-level) over returns in MS Excel. Default is 95% confidence level. |
359
361
  | `cvar_down_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Downside 95% [Conditional Value At Risk](https://www.investopedia.com/terms/c/conditional_value_at_risk.asp), "CVaR". Default is 95% confidence level. |
360
362
  | `worst_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Most negative percentage change for a given number of observations (default=1). |
@@ -17,7 +17,7 @@ width="81" height="100" align="left" float="right"/><br/>
17
17
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://beta.ruff.rs/docs/)
18
18
  [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
19
19
 
20
- `openseries` is a project with tools to analyse financial timeseries of a single
20
+ This is a project with tools to analyze financial timeseries of a single
21
21
  asset or a group of assets. It is solely made for daily or less frequent data.
22
22
 
23
23
  <span style="font-size:2em;">[CHANGELOG](https://github.com/CaptorAB/openseries/blob/master/CHANGELOG.md)</span>
@@ -253,6 +253,7 @@ make lint
253
253
  | `rolling_info_ratio` | `OpenFrame` | Returns a pandas.DataFrame with the rolling [information ratio](https://www.investopedia.com/terms/i/informationratio.asp) between two series. |
254
254
  | `rolling_beta` | `OpenFrame` | Returns a pandas.DataFrame with the rolling [Beta](https://www.investopedia.com/terms/b/beta.asp) of an asset relative a market. |
255
255
  | `rolling_corr` | `OpenFrame` | Calculates and adds a series of rolling [correlations](https://www.investopedia.com/terms/c/correlation.asp) between two other series. |
256
+ | `correl_matrix` | `OpenFrame` | Returns a `pandas.DataFrame` with a correlation matrix. |
256
257
  | `ewma_risk` | `OpenFrame` | Returns a `pandas.DataFrame` with volatility and correlation based on [Exponentially Weighted Moving Average](https://www.investopedia.com/articles/07/ewma.asp). |
257
258
 
258
259
  ### Methods that apply to both the [OpenTimeSeries](https://github.com/CaptorAB/openseries/blob/master/openseries/series.py) and the [OpenFrame](https://github.com/CaptorAB/openseries/blob/master/openseries/frame.py) class.
@@ -292,6 +293,7 @@ make lint
292
293
  | `downside_deviation` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Downside deviation](https://www.investopedia.com/terms/d/downside-deviation.asp) is the volatility of all negative return observations. Minimum Accepted Return (MAR) set to zero. |
293
294
  | `ret_vol_ratio` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Ratio of arithmetic mean return and annualized volatility. It is the [Sharpe Ratio](https://www.investopedia.com/terms/s/sharperatio.asp) with the riskfree rate set to zero. |
294
295
  | `sortino_ratio` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | The [Sortino Ratio](https://www.investopedia.com/terms/s/sortinoratio.asp) is the arithmetic mean return divided by the downside deviation. This attribute assumes that the riskfree rate and the MAR are both zero. |
296
+ | `omega_ratio` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | The [Omega Ratio](https://en.wikipedia.org/wiki/Omega_ratio) compares returns above a certain target level (MAR) to the total downside risk below MAR. |
295
297
  | `var_down` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Downside 95% [Value At Risk](https://www.investopedia.com/terms/v/var.asp), "VaR". The equivalent of percentile.inc([...], 1-level) over returns in MS Excel. For other confidence levels use the corresponding method. |
296
298
  | `cvar_down` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Downside 95% [Conditional Value At Risk](https://www.investopedia.com/terms/c/conditional_value_at_risk.asp), "CVaR". For other confidence levels use the corresponding method. |
297
299
  | `worst` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Most negative percentage change of a single observation. |
@@ -303,7 +305,6 @@ make lint
303
305
  | `skew` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Skew](https://www.investopedia.com/terms/s/skewness.asp) of the return distribution. |
304
306
  | `kurtosis` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Kurtosis](https://www.investopedia.com/terms/k/kurtosis.asp) of the return distribution. |
305
307
  | `z_score` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Z-score](https://www.investopedia.com/terms/z/zscore.asp) as (last return - mean return) / standard deviation of returns. |
306
- | `correl_matrix` | `pandas.DataFrame` | `OpenFrame` | A correlation matrix. |
307
308
 
308
309
  ### Methods below are identical to the Numerical Properties above.
309
310
 
@@ -319,6 +320,7 @@ properties for subset periods._
319
320
  | `downside_deviation_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Downside deviation](https://www.investopedia.com/terms/d/downside-deviation.asp) is the volatility of all negative return observations. MAR and riskfree rate can be set. |
320
321
  | `ret_vol_ratio_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Ratio of arithmetic mean return and annualized volatility. It is the [Sharpe Ratio](https://www.investopedia.com/terms/s/sharperatio.asp) with the riskfree rate set to zero. A riskfree rate can be set as a float or a series chosen for the frame function. |
321
322
  | `sortino_ratio_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | The [Sortino Ratio](https://www.investopedia.com/terms/s/sortinoratio.asp) is the arithmetic mean return divided by the downside deviation. A riskfree rate can be set as a float or a series chosen for the frame function. MAR is set to zero. |
323
+ | `omega_ratio_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | The [Omega Ratio](https://en.wikipedia.org/wiki/Omega_ratio) compares returns above a certain target level (MAR) to the total downside risk below MAR. |
322
324
  | `var_down_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Downside 95% [Value At Risk](https://www.investopedia.com/terms/v/var.asp), "VaR". The equivalent of percentile.inc([...], 1-level) over returns in MS Excel. Default is 95% confidence level. |
323
325
  | `cvar_down_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Downside 95% [Conditional Value At Risk](https://www.investopedia.com/terms/c/conditional_value_at_risk.asp), "CVaR". Default is 95% confidence level. |
324
326
  | `worst_func` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | Most negative percentage change for a given number of observations (default=1). |
@@ -1,4 +1,5 @@
1
1
  """Defining the _CommonModel class."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import datetime as dt
@@ -162,7 +163,7 @@ class _CommonModel(BaseModel):
162
163
  return Series(
163
164
  data=mddc,
164
165
  index=self.tsdf.columns,
165
- name="Max Drawdown in cal yr",
166
+ name="Max drawdown in cal yr",
166
167
  dtype="float64",
167
168
  )
168
169
 
@@ -274,6 +275,20 @@ class _CommonModel(BaseModel):
274
275
  min_accepted_return=minimum_accepted_return,
275
276
  )
276
277
 
278
+ @property
279
+ def omega_ratio(self: Self) -> Union[float, Series[float]]:
280
+ """
281
+ https://en.wikipedia.org/wiki/Omega_ratio.
282
+
283
+ Returns
284
+ -------
285
+ Union[float, Pandas.Series[float]]
286
+ Omega ratio calculation
287
+
288
+ """
289
+ minimum_accepted_return: float = 0.0
290
+ return self.omega_ratio_func(min_accepted_return=minimum_accepted_return)
291
+
277
292
  @property
278
293
  def z_score(self: Self) -> Union[float, Series[float]]:
279
294
  """
@@ -1678,7 +1693,7 @@ class _CommonModel(BaseModel):
1678
1693
  return Series(
1679
1694
  data=result,
1680
1695
  index=self.tsdf.columns,
1681
- name="Max Drawdown",
1696
+ name="Max drawdown",
1682
1697
  dtype="float64",
1683
1698
  )
1684
1699
 
@@ -1734,7 +1749,7 @@ class _CommonModel(BaseModel):
1734
1749
  return Series(
1735
1750
  data=share,
1736
1751
  index=self.tsdf.columns,
1737
- name="Positive Share",
1752
+ name="Positive share",
1738
1753
  dtype="float64",
1739
1754
  )
1740
1755
 
@@ -1868,6 +1883,60 @@ class _CommonModel(BaseModel):
1868
1883
  dtype="float64",
1869
1884
  )
1870
1885
 
1886
+ def omega_ratio_func(
1887
+ self: Self,
1888
+ min_accepted_return: float = 0.0,
1889
+ months_from_last: Optional[int] = None,
1890
+ from_date: Optional[dt.date] = None,
1891
+ to_date: Optional[dt.date] = None,
1892
+ ) -> Union[float, Series[float]]:
1893
+ """
1894
+ Omega Ratio.
1895
+
1896
+ The Omega Ratio compares returns above a certain target level
1897
+ (often referred to as the “minimum acceptable return” or “MAR”)
1898
+ to the total downside risk below that same threshold.
1899
+ https://en.wikipedia.org/wiki/Omega_ratio.
1900
+
1901
+ Parameters
1902
+ ----------
1903
+ min_accepted_return : float, optional
1904
+ The annualized Minimum Accepted Return (MAR)
1905
+ months_from_last : int, optional
1906
+ number of months offset as positive integer. Overrides use of from_date
1907
+ and to_date
1908
+ from_date : datetime.date, optional
1909
+ Specific from date
1910
+ to_date : datetime.date, optional
1911
+ Specific to date
1912
+
1913
+ Returns
1914
+ -------
1915
+ Union[float, Pandas.Series[float]]
1916
+ Omega ratio calculation
1917
+
1918
+ """
1919
+ earlier, later = self.calc_range(
1920
+ months_offset=months_from_last,
1921
+ from_dt=from_date,
1922
+ to_dt=to_date,
1923
+ )
1924
+ retdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change(
1925
+ fill_method=cast(str, None),
1926
+ )
1927
+ pos = retdf[retdf > min_accepted_return].sub(min_accepted_return).sum()
1928
+ neg = retdf[retdf < min_accepted_return].sub(min_accepted_return).sum()
1929
+ ratio = pos / -neg
1930
+
1931
+ if self.tsdf.shape[1] == 1:
1932
+ return float(cast(float64, ratio.iloc[0]))
1933
+ return Series(
1934
+ data=ratio,
1935
+ index=self.tsdf.columns,
1936
+ name="Omega ratio",
1937
+ dtype="float64",
1938
+ )
1939
+
1871
1940
  def value_ret_func(
1872
1941
  self: Self,
1873
1942
  months_from_last: Optional[int] = None,
@@ -1,4 +1,5 @@
1
1
  """Various risk related functions."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from math import ceil
@@ -1,4 +1,5 @@
1
1
  """Date related utilities."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import datetime as dt
@@ -1934,8 +1934,9 @@ def efficient_frontier( # noqa: C901
1934
1934
  limit_small = 0.0001
1935
1935
  line_df = line_df.mask(line_df.abs() < limit_small, 0.0)
1936
1936
  line_df["text"] = line_df.apply(
1937
- lambda c: "<br>".join(
1938
- [f"{c[nm]:.1%} - {nm}" for nm in eframe.columns_lvl_zero],
1937
+ lambda c: "<br><br>Weights:<br>"
1938
+ + "<br>".join(
1939
+ [f"{c[nm]:.1%} {nm}" for nm in eframe.columns_lvl_zero],
1939
1940
  ),
1940
1941
  axis="columns",
1941
1942
  )
@@ -2038,9 +2039,9 @@ def prepare_plot_data(
2038
2039
  The data prepared with mean returns, volatility and weights
2039
2040
 
2040
2041
  """
2041
- txt = "<br>".join(
2042
+ txt = "<br><br>Weights:<br>" + "<br>".join(
2042
2043
  [
2043
- f"{wgt:.1%} - {nm}"
2044
+ f"{wgt:.1%} {nm}"
2044
2045
  for wgt, nm in zip(
2045
2046
  cast(list[float], assets.weights),
2046
2047
  assets.columns_lvl_zero,
@@ -2048,12 +2049,10 @@ def prepare_plot_data(
2048
2049
  ],
2049
2050
  )
2050
2051
 
2051
- opt_text = "<br>".join(
2052
- [
2053
- f"{wgt:.1%} - {nm}"
2054
- for wgt, nm in zip(optimized[3:], assets.columns_lvl_zero)
2055
- ],
2056
- )
2052
+ opt_text_list = [
2053
+ f"{wgt:.1%} {nm}" for wgt, nm in zip(optimized[3:], assets.columns_lvl_zero)
2054
+ ]
2055
+ opt_text = "<br><br>Weights:<br>" + "<br>".join(opt_text_list)
2057
2056
  vol: Series[float] = assets.vol
2058
2057
  plotframe = DataFrame(
2059
2058
  data=[
@@ -2166,7 +2165,10 @@ def sharpeplot( # noqa: C901
2166
2165
  x=line_frame.loc[:, "stdev"],
2167
2166
  y=line_frame.loc[:, "ret"],
2168
2167
  text=line_frame.loc[:, "text"],
2169
- hovertemplate="%{text}<br>Return %{y}<br>Vol %{x}",
2168
+ xhoverformat=".2%",
2169
+ yhoverformat=".2%",
2170
+ hovertemplate="Return %{y}<br>Vol %{x}%{text}",
2171
+ hoverlabel_align="right",
2170
2172
  line={"width": 2.5, "dash": "solid"},
2171
2173
  mode="lines",
2172
2174
  name="Efficient frontier",
@@ -2182,11 +2184,12 @@ def sharpeplot( # noqa: C901
2182
2184
  risk.extend([point_frame.loc["stdev", col]])
2183
2185
  figure.add_scatter(
2184
2186
  x=[point_frame.loc["stdev", col]],
2185
- xhoverformat=".2%",
2186
2187
  y=[point_frame.loc["ret", col]],
2188
+ xhoverformat=".2%",
2187
2189
  yhoverformat=".2%",
2188
2190
  hovertext=[point_frame.loc["text", col]],
2189
- hoverinfo="x+y+text+name",
2191
+ hovertemplate=("Return %{y}<br>Vol %{x}%{hovertext}"),
2192
+ hoverlabel_align="right",
2190
2193
  marker={"size": 20, "color": clr},
2191
2194
  mode=point_frame_mode,
2192
2195
  name=col,
@@ -1,4 +1,5 @@
1
1
  """Function to load plotly layout and configuration from local json file."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from json import load
@@ -6,6 +7,7 @@ from logging import warning
6
7
  from pathlib import Path
7
8
 
8
9
  import requests
10
+ from requests.exceptions import ConnectionError
9
11
 
10
12
  from openseries.types import CaptorLogoType, PlotlyLayoutType
11
13
 
@@ -31,7 +33,7 @@ def _check_remote_file_existence(url: str) -> bool:
31
33
  response = requests.head(url, timeout=30)
32
34
  if response.status_code != ok_code:
33
35
  return False
34
- except requests.exceptions.ConnectionError:
36
+ except ConnectionError:
35
37
  return False
36
38
  return True
37
39
 
@@ -58,9 +60,9 @@ def load_plotly_dict(
58
60
  layoutfile = project_root.joinpath("openseries").joinpath("plotly_layouts.json")
59
61
  logofile = project_root.joinpath("openseries").joinpath("plotly_captor_logo.json")
60
62
 
61
- with Path.open(layoutfile, encoding="utf-8") as layout_file:
63
+ with Path.open(layoutfile, mode="r", encoding="utf-8") as layout_file:
62
64
  fig = load(layout_file)
63
- with Path.open(logofile, encoding="utf-8") as logo_file:
65
+ with Path.open(logofile, mode="r", encoding="utf-8") as logo_file:
64
66
  logo = load(logo_file)
65
67
 
66
68
  if _check_remote_file_existence(url=logo["source"]) is False:
@@ -1,4 +1,5 @@
1
1
  """Defining the OpenTimeSeries class."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import datetime as dt
@@ -1,4 +1,5 @@
1
1
  """Defining the ReturnSimulation class."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import datetime as dt
@@ -76,8 +77,6 @@ class ReturnSimulation(BaseModel):
76
77
  This is the average jump size
77
78
  seed: int, optional
78
79
  Seed for random process initiation
79
- randomizer: numpy.random.Generator, optional
80
- Random process generator
81
80
 
82
81
  """
83
82
 
@@ -91,7 +90,6 @@ class ReturnSimulation(BaseModel):
91
90
  jumps_sigma: NonNegativeFloat = 0.0
92
91
  jumps_mu: float = 0.0
93
92
  seed: Optional[int] = None
94
- randomizer: Optional[Generator] = None
95
93
 
96
94
  model_config = ConfigDict(
97
95
  arbitrary_types_allowed=True,
@@ -159,6 +157,7 @@ class ReturnSimulation(BaseModel):
159
157
  trading_days: PositiveInt,
160
158
  seed: int,
161
159
  trading_days_in_year: DaysInYearType = 252,
160
+ randomizer: Optional[Generator] = None,
162
161
  ) -> ReturnSimulation:
163
162
  """
164
163
  Create a Normal distribution simulation.
@@ -178,6 +177,8 @@ class ReturnSimulation(BaseModel):
178
177
  trading_days_in_year: DaysInYearType,
179
178
  default: 252
180
179
  Number of trading days used to annualize
180
+ randomizer: numpy.random.Generator, optional
181
+ Random process generator
181
182
 
182
183
  Returns
183
184
  -------
@@ -185,9 +186,10 @@ class ReturnSimulation(BaseModel):
185
186
  Normal distribution simulation
186
187
 
187
188
  """
188
- cls.randomizer = random_generator(seed=seed)
189
+ if not randomizer:
190
+ randomizer = random_generator(seed=seed)
189
191
 
190
- returns = cls.randomizer.normal(
192
+ returns = randomizer.normal(
191
193
  loc=mean_annual_return / trading_days_in_year,
192
194
  scale=mean_annual_vol / sqrt(trading_days_in_year),
193
195
  size=(number_of_sims, trading_days),
@@ -201,7 +203,6 @@ class ReturnSimulation(BaseModel):
201
203
  mean_annual_vol=mean_annual_vol,
202
204
  dframe=DataFrame(data=returns, dtype="float64"),
203
205
  seed=seed,
204
- randomizer=cls.randomizer,
205
206
  )
206
207
 
207
208
  @classmethod
@@ -213,6 +214,7 @@ class ReturnSimulation(BaseModel):
213
214
  trading_days: PositiveInt,
214
215
  seed: int,
215
216
  trading_days_in_year: DaysInYearType = 252,
217
+ randomizer: Optional[Generator] = None,
216
218
  ) -> ReturnSimulation:
217
219
  """
218
220
  Create a Lognormal distribution simulation.
@@ -232,6 +234,8 @@ class ReturnSimulation(BaseModel):
232
234
  trading_days_in_year: DaysInYearType,
233
235
  default: 252
234
236
  Number of trading days used to annualize
237
+ randomizer: numpy.random.Generator, optional
238
+ Random process generator
235
239
 
236
240
  Returns
237
241
  -------
@@ -239,10 +243,11 @@ class ReturnSimulation(BaseModel):
239
243
  Lognormal distribution simulation
240
244
 
241
245
  """
242
- cls.randomizer = random_generator(seed=seed)
246
+ if not randomizer:
247
+ randomizer = random_generator(seed=seed)
243
248
 
244
249
  returns = (
245
- cls.randomizer.lognormal(
250
+ randomizer.lognormal(
246
251
  mean=mean_annual_return / trading_days_in_year,
247
252
  sigma=mean_annual_vol / sqrt(trading_days_in_year),
248
253
  size=(number_of_sims, trading_days),
@@ -258,7 +263,6 @@ class ReturnSimulation(BaseModel):
258
263
  mean_annual_vol=mean_annual_vol,
259
264
  dframe=DataFrame(data=returns, dtype="float64"),
260
265
  seed=seed,
261
- randomizer=cls.randomizer,
262
266
  )
263
267
 
264
268
  @classmethod
@@ -270,6 +274,7 @@ class ReturnSimulation(BaseModel):
270
274
  trading_days: PositiveInt,
271
275
  seed: int,
272
276
  trading_days_in_year: DaysInYearType = 252,
277
+ randomizer: Optional[Generator] = None,
273
278
  ) -> ReturnSimulation:
274
279
  """
275
280
  Create a Geometric Brownian Motion simulation.
@@ -288,6 +293,8 @@ class ReturnSimulation(BaseModel):
288
293
  Seed for random process initiation
289
294
  trading_days_in_year: DaysInYearType, default: 252
290
295
  Number of trading days used to annualize
296
+ randomizer: numpy.random.Generator, optional
297
+ Random process generator
291
298
 
292
299
  Returns
293
300
  -------
@@ -295,14 +302,15 @@ class ReturnSimulation(BaseModel):
295
302
  Geometric Brownian Motion simulation
296
303
 
297
304
  """
298
- cls.randomizer = random_generator(seed=seed)
305
+ if not randomizer:
306
+ randomizer = random_generator(seed=seed)
299
307
 
300
308
  drift = (mean_annual_return - 0.5 * mean_annual_vol**2.0) * (
301
309
  1.0 / trading_days_in_year
302
310
  )
303
311
 
304
312
  normal_mean = 0.0
305
- wiener = cls.randomizer.normal(
313
+ wiener = randomizer.normal(
306
314
  loc=normal_mean,
307
315
  scale=sqrt(1.0 / trading_days_in_year) * mean_annual_vol,
308
316
  size=(number_of_sims, trading_days),
@@ -318,7 +326,6 @@ class ReturnSimulation(BaseModel):
318
326
  mean_annual_vol=mean_annual_vol,
319
327
  dframe=DataFrame(data=returns, dtype="float64"),
320
328
  seed=seed,
321
- randomizer=cls.randomizer,
322
329
  )
323
330
 
324
331
  @classmethod
@@ -333,6 +340,7 @@ class ReturnSimulation(BaseModel):
333
340
  jumps_sigma: NonNegativeFloat = 0.0,
334
341
  jumps_mu: float = 0.0,
335
342
  trading_days_in_year: DaysInYearType = 252,
343
+ randomizer: Optional[Generator] = None,
336
344
  ) -> ReturnSimulation:
337
345
  """
338
346
  Create a Merton Jump-Diffusion model simulation.
@@ -357,6 +365,8 @@ class ReturnSimulation(BaseModel):
357
365
  This is the average jump size
358
366
  trading_days_in_year: DaysInYearType, default: 252
359
367
  Number of trading days used to annualize
368
+ randomizer: numpy.random.Generator, optional
369
+ Random process generator
360
370
 
361
371
  Returns
362
372
  -------
@@ -364,21 +374,22 @@ class ReturnSimulation(BaseModel):
364
374
  Merton Jump-Diffusion model simulation
365
375
 
366
376
  """
367
- cls.randomizer = random_generator(seed=seed)
377
+ if not randomizer:
378
+ randomizer = random_generator(seed=seed)
368
379
 
369
380
  normal_mean = 0.0
370
- wiener = cls.randomizer.normal(
381
+ wiener = randomizer.normal(
371
382
  loc=normal_mean,
372
383
  scale=sqrt(1.0 / trading_days_in_year) * mean_annual_vol,
373
384
  size=(number_of_sims, trading_days),
374
385
  )
375
386
 
376
387
  poisson_jumps = multiply(
377
- cls.randomizer.poisson(
388
+ randomizer.poisson(
378
389
  lam=jumps_lamda * (1.0 / trading_days_in_year),
379
390
  size=(number_of_sims, trading_days),
380
391
  ),
381
- cls.randomizer.normal(
392
+ randomizer.normal(
382
393
  loc=jumps_mu,
383
394
  scale=jumps_sigma,
384
395
  size=(number_of_sims, trading_days),
@@ -406,7 +417,6 @@ class ReturnSimulation(BaseModel):
406
417
  jumps_mu=jumps_mu,
407
418
  dframe=DataFrame(data=returns, dtype="float64"),
408
419
  seed=seed,
409
- randomizer=cls.randomizer,
410
420
  )
411
421
 
412
422
  def to_dataframe(
@@ -220,6 +220,7 @@ class OpenTimeSeriesPropertiesList(list[str]):
220
220
  "downside_deviation",
221
221
  "ret_vol_ratio",
222
222
  "sortino_ratio",
223
+ "omega_ratio",
223
224
  "z_score",
224
225
  "skew",
225
226
  "kurtosis",
@@ -276,6 +277,7 @@ class OpenFramePropertiesList(list[str]):
276
277
  "downside_deviation",
277
278
  "ret_vol_ratio",
278
279
  "sortino_ratio",
280
+ "omega_ratio",
279
281
  "z_score",
280
282
  "skew",
281
283
  "kurtosis",
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "openseries"
3
- version = "1.5.2"
3
+ version = "1.5.4"
4
4
  description = "Tools for analyzing financial timeseries."
5
5
  authors = ["Martin Karrin <martin.karrin@captor.se>"]
6
6
  repository = "https://github.com/CaptorAB/openseries"
@@ -39,7 +39,7 @@ numpy = ">=1.23.2,<=2.0.0"
39
39
  openpyxl = ">=3.1.2,<4.0.0"
40
40
  pandas = ">=2.1.2,<3.0.0"
41
41
  plotly = ">=5.18.0,<6.0.0"
42
- pyarrow = ">=14.0.2,<16.0.0"
42
+ pyarrow = ">=14.0.2,<17.0.0"
43
43
  pydantic = ">=2.5.2,<3.0.0"
44
44
  python-dateutil = ">=2.8.2,<3.0.0"
45
45
  requests = ">=2.20.0,<3.0.0"
@@ -47,14 +47,14 @@ scipy = ">=1.11.4,<2.0.0"
47
47
  statsmodels = ">=0.14.0,<1.0.0"
48
48
 
49
49
  [tool.poetry.group.dev.dependencies]
50
- coverage = "^7.4.4"
51
- coverage-badge = "^1.1.0"
52
- mypy = "^1.9.0"
50
+ coverage = "^7.5.1"
51
+ coverage-badge = "^1.1.1"
52
+ mypy = "^1.10.0"
53
53
  pandas-stubs = "^2.2.1.240316"
54
54
  pre-commit = "^3.7.0"
55
- pytest = "^8.1.1"
56
- ruff = "^0.3.7"
57
- types-openpyxl = "^3.1.0.20240408"
55
+ pytest = "^8.2.0"
56
+ ruff = "^0.4.3"
57
+ types-openpyxl = "^3.1.0.20240428"
58
58
  types-python-dateutil = "^2.9.0.20240316"
59
59
  types-requests = "^2.31.0.20240406"
60
60
 
File without changes