openseries 2.0.0__py3-none-any.whl → 2.0.1__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/__init__.py CHANGED
@@ -1,5 +1,7 @@
1
1
  """openseries.openseries.__init__.py."""
2
2
 
3
+ __version__ = "2.0.1"
4
+
3
5
  from .datefixer import (
4
6
  date_fix,
5
7
  date_offset_foll,
@@ -760,6 +760,7 @@ class _CommonModel(BaseModel, Generic[SeriesOrFloat_co]):
760
760
 
761
761
  Parameters
762
762
  ----------
763
+
763
764
  countries: CountriesType, optional
764
765
  (List of) country code(s) according to ISO 3166-1 alpha-2
765
766
  markets: list[str] | str, optional
@@ -767,7 +768,7 @@ class _CommonModel(BaseModel, Generic[SeriesOrFloat_co]):
767
768
  custom_holidays: list[str] | str, optional
768
769
  Argument where missing holidays can be added
769
770
  method: LiteralPandasReindexMethod, default: "nearest"
770
-
771
+ Method for reindexing when aligning to business days
771
772
 
772
773
  Returns:
773
774
  --------
@@ -2694,3 +2695,70 @@ class _CommonModel(BaseModel, Generic[SeriesOrFloat_co]):
2694
2695
  )
2695
2696
 
2696
2697
  return DataFrame(voldf)
2698
+
2699
+ def outliers(
2700
+ self: Self,
2701
+ threshold: float = 3.0,
2702
+ months_from_last: int | None = None,
2703
+ from_date: dt.date | None = None,
2704
+ to_date: dt.date | None = None,
2705
+ ) -> Series | DataFrame:
2706
+ """Detect outliers using z-score analysis.
2707
+
2708
+ Identifies data points where the absolute z-score exceeds the threshold.
2709
+ For OpenTimeSeries, returns a pandas Series with dates and outlier values.
2710
+ For OpenFrame, returns a pandas DataFrame with dates and outlier values
2711
+ for each column.
2712
+
2713
+ Parameters
2714
+ ----------
2715
+ threshold: float, default: 3.0
2716
+ Z-score threshold for outlier detection. Values with absolute
2717
+ z-score > threshold are considered outliers.
2718
+ months_from_last : int, optional
2719
+ Number of months offset as positive integer. Overrides use of from_date
2720
+ and to_date
2721
+ from_date : datetime.date, optional
2722
+ Specific from date
2723
+ to_date : datetime.date, optional
2724
+ Specific to date
2725
+
2726
+ Returns:
2727
+ --------
2728
+ pandas.Series | pandas.DataFrame
2729
+ For OpenTimeSeries: Series with dates as index and outlier values.
2730
+ For OpenFrame: DataFrame with dates as index and outlier values for
2731
+ each column.
2732
+ Returns empty Series/DataFrame if no outliers found.
2733
+
2734
+ """
2735
+ earlier, later = self.calc_range(
2736
+ months_offset=months_from_last,
2737
+ from_dt=from_date,
2738
+ to_dt=to_date,
2739
+ )
2740
+
2741
+ # Get the data for the specified date range
2742
+ data = self.tsdf.loc[cast("Timestamp", earlier) : cast("Timestamp", later)]
2743
+
2744
+ # Calculate z-scores for each column
2745
+ z_scores = (data - data.mean()) / data.std()
2746
+
2747
+ # Find outliers where |z-score| > threshold
2748
+ outliers_mask = z_scores.abs() > threshold
2749
+
2750
+ if self.tsdf.shape[1] == 1:
2751
+ # OpenTimeSeries case - return Series
2752
+ outlier_values = data[outliers_mask].iloc[:, 0].dropna()
2753
+ return Series(
2754
+ data=outlier_values.values,
2755
+ index=outlier_values.index,
2756
+ name="Outliers",
2757
+ dtype="float64",
2758
+ )
2759
+ # OpenFrame case - return DataFrame
2760
+ outlier_df = data[outliers_mask].dropna(how="all")
2761
+ return DataFrame(
2762
+ data=outlier_df,
2763
+ dtype="float64",
2764
+ )
openseries/frame.py CHANGED
@@ -60,14 +60,19 @@ from .owntypes import (
60
60
  LiteralPandasReindexMethod,
61
61
  LiteralPortfolioWeightings,
62
62
  LiteralTrunc,
63
+ MaxDiversificationNaNError,
64
+ MaxDiversificationNegativeWeightsError,
63
65
  MergingResultedInEmptyError,
64
66
  MixedValuetypesError,
67
+ MultipleCurrenciesError,
65
68
  NoWeightsError,
66
69
  OpenFramePropertiesList,
70
+ PortfolioItemsNotWithinFrameError,
67
71
  RatioInputError,
68
72
  ResampleDataLossError,
69
73
  Self,
70
74
  ValueType,
75
+ WeightsNotProvidedError,
71
76
  )
72
77
  from .series import OpenTimeSeries
73
78
 
@@ -1434,12 +1439,49 @@ class OpenFrame(_CommonModel[SeriesFloat]):
1434
1439
  corr_matrix = corrcoef(returns.T)
1435
1440
  corr_matrix[isinf(corr_matrix)] = nan
1436
1441
  corr_matrix[isnan(corr_matrix)] = nan
1442
+
1443
+ msga = (
1444
+ "max_div weight strategy failed: "
1445
+ "correlation matrix contains NaN values"
1446
+ )
1447
+ if isnan(corr_matrix).any():
1448
+ raise MaxDiversificationNaNError(msga)
1449
+
1437
1450
  try:
1438
1451
  inv_corr_sum = linalg.inv(corr_matrix).sum(axis=1)
1452
+
1453
+ msgb = (
1454
+ "max_div weight strategy failed: "
1455
+ "inverse correlation matrix sum contains NaN values"
1456
+ )
1457
+ if isnan(inv_corr_sum).any():
1458
+ raise MaxDiversificationNaNError(msgb)
1459
+
1439
1460
  self.weights = list(divide(inv_corr_sum, inv_corr_sum.sum()))
1440
- except linalg.LinAlgError:
1441
- self.weights = [1.0 / self.item_count] * self.item_count
1442
- elif weight_strat == "target_risk":
1461
+
1462
+ msgc = (
1463
+ "max_div weight strategy failed: "
1464
+ "final weights contain NaN values"
1465
+ )
1466
+ if any( # pragma: no cover
1467
+ isnan(weight) for weight in self.weights
1468
+ ):
1469
+ raise MaxDiversificationNaNError(msgc)
1470
+
1471
+ msgd = (
1472
+ "max_div weight strategy failed: negative weights detected"
1473
+ f" - weights: {[round(w, 6) for w in self.weights]}"
1474
+ )
1475
+ if any(weight < 0 for weight in self.weights):
1476
+ raise MaxDiversificationNegativeWeightsError(msgd)
1477
+
1478
+ except linalg.LinAlgError as e:
1479
+ msge = (
1480
+ "max_div weight strategy failed: "
1481
+ f"correlation matrix is singular - {e!s}"
1482
+ )
1483
+ raise MaxDiversificationNaNError(msge) from e
1484
+ elif weight_strat == "min_vol_overweight":
1443
1485
  vols = std(returns, axis=0, ddof=1)
1444
1486
  min_vol_idx = vols.argmin()
1445
1487
  min_vol_weight = 0.6
@@ -1693,3 +1735,271 @@ class OpenFrame(_CommonModel[SeriesFloat]):
1693
1735
  result = DataFrame(data=output, index=indx, columns=[dependent_column[0]])
1694
1736
 
1695
1737
  return result, predictions.to_cumret()
1738
+
1739
+ def rebalanced_portfolio(
1740
+ self: Self,
1741
+ name: str,
1742
+ items: list[str] | None = None,
1743
+ bal_weights: list[float] | None = None,
1744
+ frequency: int = 1,
1745
+ cash_index: OpenTimeSeries | None = None,
1746
+ *,
1747
+ equal_weights: bool = False,
1748
+ drop_extras: bool = True,
1749
+ ) -> OpenFrame:
1750
+ """Create a rebalanced portfolio from the OpenFrame constituents.
1751
+
1752
+ Parameters
1753
+ ----------
1754
+ name: str
1755
+ Name of the portfolio
1756
+ items: list[str], optional
1757
+ List of items to include in the portfolio. If None, uses all items.
1758
+ bal_weights: list[float], optional
1759
+ List of weights for rebalancing. If None, uses frame weights.
1760
+ frequency: int, default: 1
1761
+ Rebalancing frequency
1762
+ cash_index: OpenTimeSeries, optional
1763
+ Cash index series for cash component
1764
+ equal_weights: bool, default: False
1765
+ If True, use equal weights for all items
1766
+ drop_extras: bool, default: True
1767
+ If True, only return TWR series; if False, return all details
1768
+
1769
+ Returns:
1770
+ --------
1771
+ OpenFrame
1772
+ OpenFrame containing the rebalanced portfolio
1773
+
1774
+ """
1775
+ if bal_weights is None and not equal_weights:
1776
+ if self.weights is None:
1777
+ msg = "Weights must be provided."
1778
+ raise WeightsNotProvidedError(msg)
1779
+ bal_weights = list(self.weights)
1780
+
1781
+ if items is None:
1782
+ items = list(self.columns_lvl_zero)
1783
+ else:
1784
+ msg = "Items must be passed as list."
1785
+ if not isinstance(items, list):
1786
+ raise TypeError(msg)
1787
+ if not items:
1788
+ msg = "Items for portfolio must be within SeriesFrame items."
1789
+ raise PortfolioItemsNotWithinFrameError(msg)
1790
+ if not set(items) <= set(self.columns_lvl_zero):
1791
+ msg = "Items for portfolio must be within SeriesFrame items."
1792
+ raise PortfolioItemsNotWithinFrameError(msg)
1793
+
1794
+ if equal_weights:
1795
+ bal_weights = [1 / len(items)] * len(items)
1796
+
1797
+ if cash_index:
1798
+ cash_index.tsdf = cash_index.tsdf.reindex(self.tsdf.index)
1799
+ cash_values: list[float] = cast(
1800
+ "list[float]", cash_index.tsdf.iloc[:, 0].to_numpy().tolist()
1801
+ )
1802
+ else:
1803
+ cash_values = [1.0] * self.length
1804
+
1805
+ if self.tsdf.isna().to_numpy().any():
1806
+ self.value_nan_handle()
1807
+
1808
+ ccies = list({serie.currency for serie in self.constituents})
1809
+
1810
+ if len(ccies) != 1:
1811
+ msg = "Items for portfolio must be denominated in same currency."
1812
+ raise MultipleCurrenciesError(msg)
1813
+
1814
+ currency = ccies[0]
1815
+
1816
+ instruments = [*items, "cash", name]
1817
+ subheaders = [
1818
+ ValueType.PRICE,
1819
+ "buysell_qty",
1820
+ "position",
1821
+ "value",
1822
+ "twr",
1823
+ "settle",
1824
+ ]
1825
+
1826
+ output = {
1827
+ item: {
1828
+ ValueType.PRICE: [],
1829
+ "buysell_qty": [0.0] * self.length,
1830
+ "position": [0.0] * self.length,
1831
+ "value": [0.0] * self.length,
1832
+ "twr": [0.0] * self.length,
1833
+ "settle": [0.0] * self.length,
1834
+ }
1835
+ for item in items
1836
+ }
1837
+ output.update(
1838
+ {
1839
+ "cash": {
1840
+ ValueType.PRICE: cash_values,
1841
+ "buysell_qty": [0.0] * self.length,
1842
+ "position": [0.0] * self.length,
1843
+ "value": [0.0] * self.length,
1844
+ "twr": [0.0] * self.length,
1845
+ "settle": [0.0] * self.length,
1846
+ },
1847
+ name: {
1848
+ ValueType.PRICE: [1.0] + [0.0] * (self.length - 1),
1849
+ "buysell_qty": [-1.0] + [0.0] * (self.length - 1),
1850
+ "position": [-1.0] + [0.0] * (self.length - 1),
1851
+ "value": [-1.0] + [0.0] * (self.length - 1),
1852
+ "twr": [1.0] + [0.0] * (self.length - 1),
1853
+ "settle": [1.0] + [0.0] * (self.length - 1),
1854
+ },
1855
+ },
1856
+ )
1857
+
1858
+ for item, weight in zip(items, cast("list[float]", bal_weights), strict=False):
1859
+ output[item][ValueType.PRICE] = cast(
1860
+ "list[float]", self.tsdf[(item, ValueType.PRICE)].to_numpy().tolist()
1861
+ )
1862
+ output[item]["buysell_qty"][0] = (
1863
+ weight / self.tsdf[(item, ValueType.PRICE)].iloc[0]
1864
+ )
1865
+ output[item]["position"][0] = output[item]["buysell_qty"][0]
1866
+ output[item]["value"][0] = (
1867
+ output[item]["position"][0] * output[item][ValueType.PRICE][0]
1868
+ )
1869
+ output[item]["settle"][0] = (
1870
+ -output[item]["buysell_qty"][0] * output[item][ValueType.PRICE][0]
1871
+ )
1872
+ output["cash"]["buysell_qty"][0] += output[item]["settle"][0]
1873
+ output[item]["twr"][0] = (
1874
+ output[item]["value"][0] / -output[item]["settle"][0]
1875
+ )
1876
+
1877
+ output["cash"]["position"][0] = (
1878
+ output["cash"]["buysell_qty"][0] + output[name]["settle"][0]
1879
+ )
1880
+ output["cash"]["settle"][0] = -output["cash"]["position"][0]
1881
+
1882
+ counter = 1
1883
+ for day in range(1, self.length):
1884
+ portfolio_value = 0.0
1885
+ settle_value = 0.0
1886
+ if day == frequency * counter:
1887
+ for item, weight in zip(
1888
+ items, cast("list[float]", bal_weights), strict=False
1889
+ ):
1890
+ output[item]["buysell_qty"][day] = (
1891
+ weight
1892
+ - output[item]["value"][day - 1]
1893
+ / -output[name]["value"][day - 1]
1894
+ ) / output[item][ValueType.PRICE][day]
1895
+ output[item]["position"][day] = (
1896
+ output[item]["position"][day - 1]
1897
+ + output[item]["buysell_qty"][day]
1898
+ )
1899
+ output[item]["value"][day] = (
1900
+ output[item]["position"][day]
1901
+ * output[item][ValueType.PRICE][day]
1902
+ )
1903
+ portfolio_value += output[item]["value"][day]
1904
+ output[item]["twr"][day] = (
1905
+ output[item]["value"][day]
1906
+ / (
1907
+ output[item]["value"][day - 1]
1908
+ - output[item]["settle"][day]
1909
+ )
1910
+ * output[item]["twr"][day - 1]
1911
+ )
1912
+ output[item]["settle"][day] = (
1913
+ -output[item]["buysell_qty"][day]
1914
+ * output[item][ValueType.PRICE][day]
1915
+ )
1916
+ settle_value += output[item]["settle"][day]
1917
+ counter += 1
1918
+ else:
1919
+ for item in items:
1920
+ output[item]["position"][day] = output[item]["position"][day - 1]
1921
+ output[item]["value"][day] = (
1922
+ output[item]["position"][day]
1923
+ * output[item][ValueType.PRICE][day]
1924
+ )
1925
+ portfolio_value += output[item]["value"][day]
1926
+ output[item]["twr"][day] = (
1927
+ output[item]["value"][day]
1928
+ / (
1929
+ output[item]["value"][day - 1]
1930
+ - output[item]["settle"][day]
1931
+ )
1932
+ * output[item]["twr"][day - 1]
1933
+ )
1934
+ output["cash"]["buysell_qty"][day] = settle_value
1935
+ output["cash"]["position"][day] = (
1936
+ output["cash"]["position"][day - 1]
1937
+ * output["cash"][ValueType.PRICE][day]
1938
+ / output["cash"][ValueType.PRICE][day - 1]
1939
+ + output["cash"]["buysell_qty"][day]
1940
+ )
1941
+ output["cash"]["value"][day] = output["cash"]["position"][day]
1942
+ portfolio_value += output["cash"]["value"][day]
1943
+ output[name]["position"][day] = output[name]["position"][day - 1]
1944
+ output[name]["value"][day] = -portfolio_value
1945
+ output[name]["twr"][day] = (
1946
+ output[name]["value"][day] / output[name]["position"][day]
1947
+ )
1948
+ output[name][ValueType.PRICE][day] = output[name]["twr"][day]
1949
+
1950
+ result = DataFrame()
1951
+ for outvalue in output.values():
1952
+ result = concat(
1953
+ [
1954
+ result,
1955
+ DataFrame(
1956
+ data=outvalue,
1957
+ index=self.tsdf.index,
1958
+ ),
1959
+ ],
1960
+ axis="columns",
1961
+ )
1962
+ lvlone, lvltwo = [], []
1963
+ for instr in instruments:
1964
+ lvlone.extend([instr] * 6)
1965
+ lvltwo.extend(subheaders)
1966
+ result.columns = MultiIndex.from_arrays([lvlone, lvltwo])
1967
+
1968
+ series = []
1969
+ if drop_extras:
1970
+ used_constituents = [
1971
+ item for item in self.constituents if item.label in items
1972
+ ]
1973
+ series.extend(
1974
+ [
1975
+ OpenTimeSeries.from_df(
1976
+ dframe=result[(item.label, "twr")],
1977
+ valuetype=ValueType.PRICE,
1978
+ baseccy=item.currency,
1979
+ local_ccy=item.local_ccy,
1980
+ )
1981
+ for item in used_constituents
1982
+ ]
1983
+ )
1984
+ series.append(
1985
+ OpenTimeSeries.from_df(
1986
+ dframe=result[(name, "twr")],
1987
+ valuetype=ValueType.PRICE,
1988
+ baseccy=currency,
1989
+ local_ccy=True,
1990
+ ),
1991
+ )
1992
+ else:
1993
+ series.extend(
1994
+ [
1995
+ OpenTimeSeries.from_df(
1996
+ dframe=result.loc[:, col],
1997
+ valuetype=ValueType.PRICE,
1998
+ baseccy=currency,
1999
+ local_ccy=True,
2000
+ ).set_new_label(f"{col[0]}, {col[1]!s}")
2001
+ for col in result.columns
2002
+ ]
2003
+ )
2004
+
2005
+ return OpenFrame(series)
openseries/owntypes.py CHANGED
@@ -135,7 +135,9 @@ LiteralPlotlyHistogramHistNorm = Literal[
135
135
  "density",
136
136
  "probability density",
137
137
  ]
138
- LiteralPortfolioWeightings = Literal["eq_weights", "inv_vol", "max_div", "target_risk"]
138
+ LiteralPortfolioWeightings = Literal[
139
+ "eq_weights", "inv_vol", "max_div", "min_vol_overweight"
140
+ ]
139
141
  LiteralMinimizeMethods = Literal[
140
142
  "SLSQP",
141
143
  "Nelder-Mead",
@@ -374,3 +376,23 @@ class PropertiesInputValidationError(Exception):
374
376
 
375
377
  class ResampleDataLossError(Exception):
376
378
  """Raised when user attempts to run resample_to_business_period_ends on returns."""
379
+
380
+
381
+ class WeightsNotProvidedError(Exception):
382
+ """Raised when weights are not provided."""
383
+
384
+
385
+ class MultipleCurrenciesError(Exception):
386
+ """Raised when multiple currencies are provided."""
387
+
388
+
389
+ class PortfolioItemsNotWithinFrameError(Exception):
390
+ """Raised when portfolio items are not within frame."""
391
+
392
+
393
+ class MaxDiversificationNaNError(Exception):
394
+ """Raised when max_div weight strategy produces NaN values."""
395
+
396
+
397
+ class MaxDiversificationNegativeWeightsError(Exception):
398
+ """Raised when max_div weight strategy produces negative weights."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openseries
3
- Version: 2.0.0
3
+ Version: 2.0.1
4
4
  Summary: Tools for analyzing financial timeseries.
5
5
  License: # BSD 3-Clause License
6
6
 
@@ -35,11 +35,12 @@ Author: Martin Karrin
35
35
  Author-email: martin.karrin@captor.se
36
36
  Maintainer: Martin Karrin
37
37
  Maintainer-email: martin.karrin@captor.se
38
- Requires-Python: >=3.10,<3.14
38
+ Requires-Python: >=3.10,<3.15
39
39
  Classifier: Programming Language :: Python :: 3.10
40
40
  Classifier: Programming Language :: Python :: 3.11
41
41
  Classifier: Programming Language :: Python :: 3.12
42
42
  Classifier: Programming Language :: Python :: 3.13
43
+ Classifier: Programming Language :: Python :: 3.14
43
44
  Classifier: License :: OSI Approved :: BSD License
44
45
  Classifier: Intended Audience :: Financial and Insurance Industry
45
46
  Classifier: Topic :: Office/Business :: Financial :: Investment
@@ -77,6 +78,7 @@ Description-Content-Type: text/markdown
77
78
  [![Python version](https://img.shields.io/pypi/pyversions/openseries.svg)](https://www.python.org/)
78
79
  [![GitHub Action Test Suite](https://github.com/CaptorAB/openseries/actions/workflows/test.yml/badge.svg)](https://github.com/CaptorAB/openseries/actions/workflows/test.yml)
79
80
  [![codecov](https://img.shields.io/codecov/c/gh/CaptorAB/openseries?logo=codecov)](https://codecov.io/gh/CaptorAB/openseries/branch/master)
81
+ [![Documentation](https://readthedocs.org/projects/openseries/badge/?version=latest)](https://openseries.readthedocs.io/en/latest/?badge=latest)
80
82
  [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
81
83
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://beta.ruff.rs/docs/)
82
84
  [![GitHub License](https://img.shields.io/github/license/CaptorAB/openseries)](https://github.com/CaptorAB/openseries/blob/master/LICENSE.md)
@@ -1,10 +1,10 @@
1
- openseries/__init__.py,sha256=WVAO2vtZUZHp7wlXCXV_OKaLkPHfGUjfxTGgBsMdORc,1104
2
- openseries/_common_model.py,sha256=vXVsSuD6ULTDT6A_sg58uS_yfoazQQvwtjo_jxKMQHA,89131
1
+ openseries/__init__.py,sha256=vkJZmQaReCry7g6f8plQU5RaP1LBwVKe3DoD9ekF3Sc,1127
2
+ openseries/_common_model.py,sha256=y-udTW6LI0Q5er-Egzd4IrFoGHfRGSnsjHJuCu_YTIU,91628
3
3
  openseries/_risk.py,sha256=YisMnI8DQT0w9n9SQbrvq0ZZqmZHrz7-jhZtngObJRk,2094
4
4
  openseries/datefixer.py,sha256=U1Kc6QdW3UEzAp61NIUALOllyLWb-mJemiL7KLfSAto,15512
5
- openseries/frame.py,sha256=OCx4soNy_PzjOwpEzxhmdBUIbXjM2WzXv-JaW5GJ4U8,57343
5
+ openseries/frame.py,sha256=g3X3eVkUptawmj7fb-86i21odIFTX_QvwJEcMU_mtO0,69376
6
6
  openseries/load_plotly.py,sha256=sVssTMzJ2tPPHceCa9OCavI4Mv5BFgSKR6wnTltnqQQ,1997
7
- openseries/owntypes.py,sha256=I709YjKqN9W9bJNNezhCQbnmTZgTIZZnoxzoUkbHF8w,9436
7
+ openseries/owntypes.py,sha256=wxyU4d08YrxfEGL_bSI9M6ZVLjipqMLEsq376gVV9lk,10001
8
8
  openseries/plotly_captor_logo.json,sha256=F5nhMzEyxKywtjvQqMTKgKRCJQYMDIiBgDSxdte8Clo,178
9
9
  openseries/plotly_layouts.json,sha256=MvDEQuiqIhMBXBelXb1sedTOlTPheizv6NZRLeE9YS4,1431
10
10
  openseries/portfoliotools.py,sha256=v7s9-AgJFlvPIbPuPf6J7d0VjP-dDT-rsm086EoqSAE,19073
@@ -12,7 +12,7 @@ openseries/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  openseries/report.py,sha256=FYN79MroQfY8DrDSSdsSDBcy6S9PwIS0FyujnqPEQV0,14106
13
13
  openseries/series.py,sha256=Wp5G3EmzsBqABtkIPvFQ0Y4T77RhXM9uzvtJBPFWMrQ,28354
14
14
  openseries/simulation.py,sha256=J58uHuakeIbZ2Pabha-RtsaO-k-MVsRfXSdZexrMAkI,16071
15
- openseries-2.0.0.dist-info/METADATA,sha256=8L-PeC1b2pv8VfrC5upsvunrh1WNSiaOMvWg2BfxXZs,6085
16
- openseries-2.0.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
17
- openseries-2.0.0.dist-info/licenses/LICENSE.md,sha256=wNupG-KLsG0aTncb_SMNDh1ExtrKXlpxSJ6RC-g-SWs,1516
18
- openseries-2.0.0.dist-info/RECORD,,
15
+ openseries-2.0.1.dist-info/METADATA,sha256=_YFEzFjSM-xzkD9ayOMVf41bJ2NGk8dLw15E2k9yu9c,6281
16
+ openseries-2.0.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
17
+ openseries-2.0.1.dist-info/licenses/LICENSE.md,sha256=wNupG-KLsG0aTncb_SMNDh1ExtrKXlpxSJ6RC-g-SWs,1516
18
+ openseries-2.0.1.dist-info/RECORD,,