openseries 1.9.3__py3-none-any.whl → 1.9.5__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.
@@ -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 ( # type: ignore[import-untyped]
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
- result = wmdf.pct_change().min()
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
- cast("int", earlier) : cast("int", later)
1575
- ].pct_change(),
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,index]
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
- (1 - level)
1652
- * cvar_df.loc[:, x] # type: ignore[index]
1653
- .pct_change()
1654
- .count(),
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=(self.tsdf.loc[cast("int", earlier) : cast("int", later)].pct_change()),
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)].pct_change()[
1993
- 1:
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 = self.tsdf.loc[cast("int", earlier) : cast("int", later)].pct_change()
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 = self.tsdf.loc[
2422
- cast("int", earlier) : cast("int", later)
2423
- ].pct_change()
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()
openseries/datefixer.py CHANGED
@@ -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) # type: ignore[arg-type]
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 # type: ignore[no-any-return]
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( # type: ignore[misc]
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),