openseries 1.5.2__py3-none-any.whl → 1.5.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openseries/_common_model.py +1 -0
- openseries/_risk.py +1 -0
- openseries/datefixer.py +1 -0
- openseries/frame.py +16 -13
- openseries/load_plotly.py +1 -0
- openseries/series.py +1 -0
- openseries/simulation.py +27 -17
- {openseries-1.5.2.dist-info → openseries-1.5.3.dist-info}/METADATA +3 -3
- openseries-1.5.3.dist-info/RECORD +15 -0
- openseries-1.5.2.dist-info/RECORD +0 -15
- {openseries-1.5.2.dist-info → openseries-1.5.3.dist-info}/LICENSE.md +0 -0
- {openseries-1.5.2.dist-info → openseries-1.5.3.dist-info}/WHEEL +0 -0
openseries/_common_model.py
CHANGED
openseries/_risk.py
CHANGED
openseries/datefixer.py
CHANGED
openseries/frame.py
CHANGED
@@ -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>"
|
1938
|
-
|
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%}
|
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
|
-
|
2052
|
-
[
|
2053
|
-
|
2054
|
-
|
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
|
-
|
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
|
-
|
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,
|
openseries/load_plotly.py
CHANGED
openseries/series.py
CHANGED
openseries/simulation.py
CHANGED
@@ -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
|
-
|
189
|
+
if not randomizer:
|
190
|
+
randomizer = random_generator(seed=seed)
|
189
191
|
|
190
|
-
returns =
|
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
|
-
|
246
|
+
if not randomizer:
|
247
|
+
randomizer = random_generator(seed=seed)
|
243
248
|
|
244
249
|
returns = (
|
245
|
-
|
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
|
-
|
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 =
|
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
|
-
|
377
|
+
if not randomizer:
|
378
|
+
randomizer = random_generator(seed=seed)
|
368
379
|
|
369
380
|
normal_mean = 0.0
|
370
|
-
wiener =
|
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
|
-
|
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
|
-
|
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
|
Metadata-Version: 2.1
|
2
2
|
Name: openseries
|
3
|
-
Version: 1.5.
|
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,<
|
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
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
openseries/__init__.py,sha256=W429Ojwa-wPgHV5PDAOQOBOAzPOR4wrVHRwdZQjRKcQ,41
|
2
|
+
openseries/_common_model.py,sha256=whzIHppEGjXqv2C5ZBg7c-QTCC6dHWBjNKX40WV_c6g,72469
|
3
|
+
openseries/_risk.py,sha256=4ckiA-0-uuoUOsuc_uElUA_2rLS_U3xJyiva2BX4W1s,3300
|
4
|
+
openseries/datefixer.py,sha256=ZOSPp4kLkMEsZv50GQaSo2vAEDVaXEr9iX3wTO7ZdB4,12378
|
5
|
+
openseries/frame.py,sha256=SH1sPUFoJS71o2uVJBLoDl3rSC8AP7wQZw3baMD9T4U,74019
|
6
|
+
openseries/load_plotly.py,sha256=V99KNaG3OAYsQYKRmpp2-WHgLAQNxKcgrDXpXZ7K8io,1808
|
7
|
+
openseries/plotly_captor_logo.json,sha256=F5nhMzEyxKywtjvQqMTKgKRCJQYMDIiBgDSxdte8Clo,178
|
8
|
+
openseries/plotly_layouts.json,sha256=ahx8-dL4_RPzvHtBOX0SiL0AH7xQJzNRSDhGrSmU-Og,1429
|
9
|
+
openseries/series.py,sha256=pIpiD3v8z7SyIppBatbsPuqOWCf3qI_us2145JnPUkQ,28313
|
10
|
+
openseries/simulation.py,sha256=VYxc-e5VSyC55DdfACpQen-necYbhso-6RMyOhYX-5k,13905
|
11
|
+
openseries/types.py,sha256=qOKWorkGsv22Q0b2RvMW8IS2pkO3PJlbhQfAviRYNts,7597
|
12
|
+
openseries-1.5.3.dist-info/LICENSE.md,sha256=cPUabMxJ6-ziqzqS6aLGkR-ilIOKe_s3Qtyp0ioTmo0,1521
|
13
|
+
openseries-1.5.3.dist-info/METADATA,sha256=3OfZAp-QudbPL2HwXiVCMz-zVAMkSVLV2n4APtzZSLg,43579
|
14
|
+
openseries-1.5.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
15
|
+
openseries-1.5.3.dist-info/RECORD,,
|
@@ -1,15 +0,0 @@
|
|
1
|
-
openseries/__init__.py,sha256=W429Ojwa-wPgHV5PDAOQOBOAzPOR4wrVHRwdZQjRKcQ,41
|
2
|
-
openseries/_common_model.py,sha256=PGJDMI305RrxThXaBXSdKrMOvUozjjNprhWilKcp-G0,72468
|
3
|
-
openseries/_risk.py,sha256=JnwAklqs2G3YIp9KTNKbumqs4VgfFIk-eFs0dZ-_jCY,3299
|
4
|
-
openseries/datefixer.py,sha256=3DQz_nUbUVBHCxM5wdqbAQUfdG-9xN3S5yqEX4F_NpI,12377
|
5
|
-
openseries/frame.py,sha256=en_GL0_l9OHsZk6AmYin5Pip7geLyTeLZNAZqIR4xcI,73772
|
6
|
-
openseries/load_plotly.py,sha256=kIjvJ2H1sIXWsjd-mZclLvj7ebh-4Hdb1dwB2gR9b-Y,1807
|
7
|
-
openseries/plotly_captor_logo.json,sha256=F5nhMzEyxKywtjvQqMTKgKRCJQYMDIiBgDSxdte8Clo,178
|
8
|
-
openseries/plotly_layouts.json,sha256=ahx8-dL4_RPzvHtBOX0SiL0AH7xQJzNRSDhGrSmU-Og,1429
|
9
|
-
openseries/series.py,sha256=jHbJkzFUeGokUJZ1Md4Hx1keSwpNMjPq1r_FDCputIE,28312
|
10
|
-
openseries/simulation.py,sha256=y9A_kNuGoIqVlFsQzxA39hBi9Nt9X1yN2VYjShYNhtE,13549
|
11
|
-
openseries/types.py,sha256=qOKWorkGsv22Q0b2RvMW8IS2pkO3PJlbhQfAviRYNts,7597
|
12
|
-
openseries-1.5.2.dist-info/LICENSE.md,sha256=cPUabMxJ6-ziqzqS6aLGkR-ilIOKe_s3Qtyp0ioTmo0,1521
|
13
|
-
openseries-1.5.2.dist-info/METADATA,sha256=5siFdLZ275Ry94LNRRA4VlD--ye9-uafpxC6HjLorjY,43661
|
14
|
-
openseries-1.5.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
15
|
-
openseries-1.5.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|