openseries 1.9.3__tar.gz → 1.9.5__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.
- {openseries-1.9.3 → openseries-1.9.5}/PKG-INFO +1 -1
- {openseries-1.9.3 → openseries-1.9.5}/openseries/_common_model.py +56 -28
- {openseries-1.9.3 → openseries-1.9.5}/openseries/datefixer.py +4 -4
- {openseries-1.9.3 → openseries-1.9.5}/openseries/frame.py +183 -165
- {openseries-1.9.3 → openseries-1.9.5}/openseries/owntypes.py +4 -0
- {openseries-1.9.3 → openseries-1.9.5}/openseries/plotly_layouts.json +1 -1
- {openseries-1.9.3 → openseries-1.9.5}/openseries/portfoliotools.py +9 -10
- {openseries-1.9.3 → openseries-1.9.5}/openseries/report.py +61 -56
- {openseries-1.9.3 → openseries-1.9.5}/openseries/series.py +37 -23
- {openseries-1.9.3 → openseries-1.9.5}/openseries/simulation.py +9 -6
- {openseries-1.9.3 → openseries-1.9.5}/pyproject.toml +7 -9
- {openseries-1.9.3 → openseries-1.9.5}/LICENSE.md +0 -0
- {openseries-1.9.3 → openseries-1.9.5}/README.md +0 -0
- {openseries-1.9.3 → openseries-1.9.5}/openseries/__init__.py +0 -0
- {openseries-1.9.3 → openseries-1.9.5}/openseries/_risk.py +0 -0
- {openseries-1.9.3 → openseries-1.9.5}/openseries/load_plotly.py +0 -0
- {openseries-1.9.3 → openseries-1.9.5}/openseries/plotly_captor_logo.json +0 -0
@@ -7,7 +7,6 @@ https://github.com/CaptorAB/openseries/blob/master/LICENSE.md
|
|
7
7
|
SPDX-License-Identifier: BSD-3-Clause
|
8
8
|
"""
|
9
9
|
|
10
|
-
# mypy: disable-error-code="no-any-return"
|
11
10
|
from __future__ import annotations
|
12
11
|
|
13
12
|
import datetime as dt
|
@@ -25,7 +24,9 @@ from .owntypes import (
|
|
25
24
|
DateAlignmentError,
|
26
25
|
InitialValueZeroError,
|
27
26
|
NumberOfItemsAndLabelsNotSameError,
|
27
|
+
ResampleDataLossError,
|
28
28
|
Self,
|
29
|
+
ValueType,
|
29
30
|
)
|
30
31
|
|
31
32
|
if TYPE_CHECKING: # pragma: no cover
|
@@ -47,7 +48,6 @@ if TYPE_CHECKING: # pragma: no cover
|
|
47
48
|
LiteralPlotlyJSlib,
|
48
49
|
LiteralPlotlyOutput,
|
49
50
|
LiteralQuantileInterp,
|
50
|
-
ValueType,
|
51
51
|
)
|
52
52
|
from openpyxl.utils.dataframe import dataframe_to_rows
|
53
53
|
from openpyxl.workbook.workbook import Workbook
|
@@ -66,7 +66,7 @@ from plotly.graph_objs import Figure # type: ignore[import-untyped]
|
|
66
66
|
from plotly.io import to_html # type: ignore[import-untyped]
|
67
67
|
from plotly.offline import plot # type: ignore[import-untyped]
|
68
68
|
from pydantic import BaseModel, ConfigDict, DirectoryPath, ValidationError
|
69
|
-
from scipy.stats import (
|
69
|
+
from scipy.stats import (
|
70
70
|
kurtosis,
|
71
71
|
norm,
|
72
72
|
skew,
|
@@ -432,7 +432,17 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
432
432
|
|
433
433
|
wmdf = wmdf.reindex(index=[deyt.date() for deyt in dates], method=method)
|
434
434
|
wmdf.index = DatetimeIndex(wmdf.index)
|
435
|
-
|
435
|
+
|
436
|
+
vtypes = [x == ValueType.RTRN for x in wmdf.columns.get_level_values(1)]
|
437
|
+
if any(vtypes):
|
438
|
+
msg = (
|
439
|
+
"Do not run worst_month on return series. The operation will "
|
440
|
+
"pick the last data point in the sparser series. It will not sum "
|
441
|
+
"returns and therefore data will be lost and result will be wrong."
|
442
|
+
)
|
443
|
+
raise ResampleDataLossError(msg)
|
444
|
+
|
445
|
+
result = wmdf.ffill().pct_change().min()
|
436
446
|
|
437
447
|
if self.tsdf.shape[1] == 1:
|
438
448
|
return float(result.iloc[0])
|
@@ -1305,6 +1315,7 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1305
1315
|
|
1306
1316
|
result = (
|
1307
1317
|
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1318
|
+
.ffill()
|
1308
1319
|
.pct_change()
|
1309
1320
|
.mean()
|
1310
1321
|
* time_factor
|
@@ -1366,7 +1377,7 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1366
1377
|
time_factor = how_many / fraction
|
1367
1378
|
|
1368
1379
|
data = self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1369
|
-
result = data.pct_change().std().mul(sqrt(time_factor))
|
1380
|
+
result = data.ffill().pct_change().std().mul(sqrt(time_factor))
|
1370
1381
|
|
1371
1382
|
if self.tsdf.shape[1] == 1:
|
1372
1383
|
return float(cast("SupportsFloat", result.iloc[0]))
|
@@ -1564,21 +1575,24 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1564
1575
|
if drift_adjust:
|
1565
1576
|
imp_vol = (-sqrt(time_factor) / norm.ppf(level)) * (
|
1566
1577
|
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1578
|
+
.ffill()
|
1567
1579
|
.pct_change()
|
1568
1580
|
.quantile(1 - level, interpolation=interpolation)
|
1569
1581
|
- self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1582
|
+
.ffill()
|
1570
1583
|
.pct_change()
|
1571
1584
|
.sum()
|
1572
1585
|
/ len(
|
1573
|
-
self.tsdf.loc[
|
1574
|
-
|
1575
|
-
|
1586
|
+
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1587
|
+
.ffill()
|
1588
|
+
.pct_change(),
|
1576
1589
|
)
|
1577
1590
|
)
|
1578
1591
|
else:
|
1579
1592
|
imp_vol = (
|
1580
1593
|
-sqrt(time_factor)
|
1581
1594
|
* self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1595
|
+
.ffill()
|
1582
1596
|
.pct_change()
|
1583
1597
|
.quantile(1 - level, interpolation=interpolation)
|
1584
1598
|
/ norm.ppf(level)
|
@@ -1643,15 +1657,16 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1643
1657
|
deep=True
|
1644
1658
|
)
|
1645
1659
|
result = [
|
1646
|
-
cvar_df.loc[:, x] # type: ignore[call-overload
|
1660
|
+
cvar_df.loc[:, x] # type: ignore[call-overload]
|
1661
|
+
.ffill()
|
1647
1662
|
.pct_change()
|
1648
1663
|
.sort_values()
|
1649
1664
|
.iloc[
|
1650
1665
|
: ceil(
|
1651
|
-
(
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1666
|
+
cast(
|
1667
|
+
"int",
|
1668
|
+
(1 - level) * cvar_df.loc[:, x].ffill().pct_change().count(),
|
1669
|
+
)
|
1655
1670
|
),
|
1656
1671
|
]
|
1657
1672
|
.mean()
|
@@ -1718,6 +1733,7 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1718
1733
|
|
1719
1734
|
how_many = (
|
1720
1735
|
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1736
|
+
.ffill()
|
1721
1737
|
.pct_change()
|
1722
1738
|
.count(numeric_only=True)
|
1723
1739
|
)
|
@@ -1734,6 +1750,7 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1734
1750
|
per_period_mar = min_accepted_return / time_factor
|
1735
1751
|
diff = (
|
1736
1752
|
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1753
|
+
.ffill()
|
1737
1754
|
.pct_change()
|
1738
1755
|
.sub(per_period_mar)
|
1739
1756
|
)
|
@@ -1788,12 +1805,7 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1788
1805
|
)
|
1789
1806
|
fraction = (later - earlier).days / 365.25
|
1790
1807
|
|
1791
|
-
any_below_zero = any(
|
1792
|
-
self.tsdf.loc[[earlier, later]] # type: ignore[index]
|
1793
|
-
.lt(0.0)
|
1794
|
-
.any()
|
1795
|
-
.to_numpy()
|
1796
|
-
)
|
1808
|
+
any_below_zero = any(self.tsdf.loc[[earlier, later]].lt(0.0).any().to_numpy())
|
1797
1809
|
if zero in self.tsdf.loc[earlier].to_numpy() or any_below_zero:
|
1798
1810
|
msg = (
|
1799
1811
|
"Geometric return cannot be calculated due to "
|
@@ -1845,6 +1857,7 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1845
1857
|
)
|
1846
1858
|
result: NDArray[float64] = skew(
|
1847
1859
|
a=self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1860
|
+
.ffill()
|
1848
1861
|
.pct_change()
|
1849
1862
|
.to_numpy(),
|
1850
1863
|
bias=True,
|
@@ -1892,7 +1905,11 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1892
1905
|
to_dt=to_date,
|
1893
1906
|
)
|
1894
1907
|
result: NDArray[float64] = kurtosis(
|
1895
|
-
a=(
|
1908
|
+
a=(
|
1909
|
+
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1910
|
+
.ffill()
|
1911
|
+
.pct_change()
|
1912
|
+
),
|
1896
1913
|
fisher=True,
|
1897
1914
|
bias=True,
|
1898
1915
|
nan_policy="omit",
|
@@ -1988,16 +2005,18 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1988
2005
|
)
|
1989
2006
|
pos = (
|
1990
2007
|
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
2008
|
+
.ffill()
|
1991
2009
|
.pct_change()[1:][
|
1992
|
-
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
1993
|
-
|
1994
|
-
]
|
2010
|
+
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
2011
|
+
.ffill()
|
2012
|
+
.pct_change()[1:]
|
1995
2013
|
> zero
|
1996
2014
|
]
|
1997
2015
|
.count()
|
1998
2016
|
)
|
1999
2017
|
tot = (
|
2000
2018
|
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
2019
|
+
.ffill()
|
2001
2020
|
.pct_change()
|
2002
2021
|
.count()
|
2003
2022
|
)
|
@@ -2184,7 +2203,11 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
2184
2203
|
from_dt=from_date,
|
2185
2204
|
to_dt=to_date,
|
2186
2205
|
)
|
2187
|
-
retdf =
|
2206
|
+
retdf = (
|
2207
|
+
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
2208
|
+
.ffill()
|
2209
|
+
.pct_change()
|
2210
|
+
)
|
2188
2211
|
pos = retdf[retdf > min_accepted_return].sub(min_accepted_return).sum()
|
2189
2212
|
neg = retdf[retdf < min_accepted_return].sub(min_accepted_return).sum()
|
2190
2213
|
ratio = pos / -neg
|
@@ -2272,7 +2295,7 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
2272
2295
|
period = "-".join([str(year), str(month).zfill(2)])
|
2273
2296
|
vrdf = self.tsdf.copy()
|
2274
2297
|
vrdf.index = DatetimeIndex(vrdf.index)
|
2275
|
-
resultdf = DataFrame(vrdf.pct_change())
|
2298
|
+
resultdf = DataFrame(vrdf.ffill().pct_change())
|
2276
2299
|
result = resultdf.loc[period] + 1
|
2277
2300
|
cal_period = result.cumprod(axis="index").iloc[-1] - 1
|
2278
2301
|
if self.tsdf.shape[1] == 1:
|
@@ -2324,6 +2347,7 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
2324
2347
|
)
|
2325
2348
|
result = (
|
2326
2349
|
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
2350
|
+
.ffill()
|
2327
2351
|
.pct_change()
|
2328
2352
|
.quantile(1 - level, interpolation=interpolation)
|
2329
2353
|
)
|
@@ -2372,6 +2396,7 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
2372
2396
|
)
|
2373
2397
|
result = (
|
2374
2398
|
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
2399
|
+
.ffill()
|
2375
2400
|
.pct_change()
|
2376
2401
|
.rolling(observations, min_periods=observations)
|
2377
2402
|
.sum()
|
@@ -2418,9 +2443,11 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
2418
2443
|
from_dt=from_date,
|
2419
2444
|
to_dt=to_date,
|
2420
2445
|
)
|
2421
|
-
zscframe =
|
2422
|
-
cast("int", earlier) : cast("int", later)
|
2423
|
-
|
2446
|
+
zscframe = (
|
2447
|
+
self.tsdf.loc[cast("int", earlier) : cast("int", later)]
|
2448
|
+
.ffill()
|
2449
|
+
.pct_change()
|
2450
|
+
)
|
2424
2451
|
result = (zscframe.iloc[-1] - zscframe.mean()) / zscframe.std()
|
2425
2452
|
|
2426
2453
|
if self.tsdf.shape[1] == 1:
|
@@ -2489,6 +2516,7 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
2489
2516
|
ret_label = cast("tuple[str]", self.tsdf.iloc[:, column].name)[0]
|
2490
2517
|
retseries = (
|
2491
2518
|
Series(self.tsdf.iloc[:, column])
|
2519
|
+
.ffill()
|
2492
2520
|
.pct_change()
|
2493
2521
|
.rolling(observations, min_periods=observations)
|
2494
2522
|
.sum()
|
@@ -12,7 +12,7 @@ from __future__ import annotations
|
|
12
12
|
import datetime as dt
|
13
13
|
from typing import TYPE_CHECKING, cast
|
14
14
|
|
15
|
-
import exchange_calendars as exchcal
|
15
|
+
import exchange_calendars as exchcal # type: ignore[import-untyped]
|
16
16
|
from dateutil.relativedelta import relativedelta
|
17
17
|
from holidays import (
|
18
18
|
country_holidays,
|
@@ -163,7 +163,7 @@ def holiday_calendar(
|
|
163
163
|
custom_list = (
|
164
164
|
[custom_holidays]
|
165
165
|
if isinstance(custom_holidays, str)
|
166
|
-
else list(custom_holidays)
|
166
|
+
else list(custom_holidays)
|
167
167
|
)
|
168
168
|
hols.extend([date_fix(fixerdate=ddate) for ddate in custom_list])
|
169
169
|
|
@@ -255,7 +255,7 @@ def date_offset_foll(
|
|
255
255
|
while not is_busday(dates=new_date, busdaycal=calendar):
|
256
256
|
new_date += day_delta
|
257
257
|
|
258
|
-
return new_date
|
258
|
+
return new_date
|
259
259
|
|
260
260
|
|
261
261
|
def get_previous_business_day_before_today(
|
@@ -505,7 +505,7 @@ def _do_resample_to_business_period_ends(
|
|
505
505
|
dates = DatetimeIndex(
|
506
506
|
[copydata.index[0]]
|
507
507
|
+ [
|
508
|
-
date_offset_foll(
|
508
|
+
date_offset_foll(
|
509
509
|
raw_date=dt.date(d.year, d.month, 1)
|
510
510
|
+ relativedelta(months=1)
|
511
511
|
- dt.timedelta(days=1),
|