openseries 1.5.2__tar.gz → 1.5.3__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.3
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)
@@ -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.
@@ -339,7 +340,6 @@ make lint
339
340
  | `skew` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Skew](https://www.investopedia.com/terms/s/skewness.asp) of the return distribution. |
340
341
  | `kurtosis` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Kurtosis](https://www.investopedia.com/terms/k/kurtosis.asp) of the return distribution. |
341
342
  | `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
343
 
344
344
  ### Methods below are identical to the Numerical Properties above.
345
345
 
@@ -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.
@@ -303,7 +304,6 @@ make lint
303
304
  | `skew` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Skew](https://www.investopedia.com/terms/s/skewness.asp) of the return distribution. |
304
305
  | `kurtosis` | `float`, `pandas.Series` | `OpenTimeSeries`, `OpenFrame` | [Kurtosis](https://www.investopedia.com/terms/k/kurtosis.asp) of the return distribution. |
305
306
  | `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
307
 
308
308
  ### Methods below are identical to the Numerical Properties above.
309
309
 
@@ -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
@@ -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
@@ -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(
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "openseries"
3
- version = "1.5.2"
3
+ version = "1.5.3"
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.0"
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.2"
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