openseries 1.5.6__tar.gz → 1.5.7__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.6
3
+ Version: 1.5.7
4
4
  Summary: Tools for analyzing financial timeseries.
5
5
  Home-page: https://github.com/CaptorAB/openseries
6
6
  License: BSD-3-Clause
@@ -20,8 +20,8 @@ Classifier: Programming Language :: Python :: 3.10
20
20
  Classifier: Programming Language :: Python :: 3.11
21
21
  Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Topic :: Office/Business :: Financial :: Investment
23
- Requires-Dist: holidays (>=0.30,<0.50)
24
- Requires-Dist: numpy (>=1.23.2,<=2.0.0)
23
+ Requires-Dist: holidays (>=0.30,<1.0)
24
+ Requires-Dist: numpy (>=1.23.2,<=3.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)
@@ -6,19 +6,11 @@ from math import ceil
6
6
  from typing import Union, cast
7
7
 
8
8
  from numpy import (
9
- divide,
10
- float64,
11
- isinf,
12
9
  mean,
13
- nan,
14
10
  nan_to_num,
15
11
  quantile,
16
12
  sort,
17
- sqrt,
18
- square,
19
- std,
20
13
  )
21
- from numpy.typing import NDArray
22
14
  from pandas import DataFrame, Series
23
15
 
24
16
  from openseries.types import LiteralQuantileInterp
@@ -87,56 +79,3 @@ def _var_down_calc(
87
79
  clean = nan_to_num(data)
88
80
  ret = clean[1:] / clean[:-1] - 1
89
81
  return cast(float, quantile(ret, 1 - level, method=interpolation))
90
-
91
-
92
- def _ewma_calc(
93
- reeturn: float,
94
- prev_ewma: float,
95
- time_factor: float,
96
- lmbda: float = 0.94,
97
- ) -> float:
98
- """
99
- Calculate Exponentially Weighted Moving Average volatility.
100
-
101
- Parameters
102
- ----------
103
- reeturn : float
104
- Return value
105
- prev_ewma : float
106
- Previous EWMA volatility value
107
- time_factor : float
108
- Scaling factor to annualize
109
- lmbda: float, default: 0.94
110
- Scaling factor to determine weighting.
111
-
112
- Returns
113
- -------
114
- float
115
- EWMA volatility value
116
-
117
- """
118
- return cast(
119
- float,
120
- sqrt(square(reeturn) * time_factor * (1 - lmbda) + square(prev_ewma) * lmbda),
121
- )
122
-
123
-
124
- def _calc_inv_vol_weights(returns: DataFrame) -> NDArray[float64]:
125
- """
126
- Calculate weights proportional to inverse volatility.
127
-
128
- Parameters
129
- ----------
130
- returns: pandas.DataFrame
131
- returns data
132
-
133
- Returns
134
- -------
135
- NDArray[float64]
136
- Calculated weights
137
-
138
- """
139
- vol = divide(1.0, std(returns, axis=0, ddof=1))
140
- vol[isinf(vol)] = nan
141
- volsum = vol.sum()
142
- return cast(NDArray[float64], divide(vol, volsum))
@@ -62,7 +62,7 @@ def holiday_calendar(
62
62
  endyear += 1
63
63
  if startyear == endyear:
64
64
  endyear += 1
65
- years = list(range(int(startyear), int(endyear)))
65
+ years = list(range(startyear, endyear))
66
66
 
67
67
  if isinstance(countries, str) and countries in list_supported_countries():
68
68
  staging = country_holidays(country=countries, years=years)
@@ -116,15 +116,11 @@ def date_fix(
116
116
  if isinstance(fixerdate, datetime64):
117
117
  return (
118
118
  dt.datetime.strptime(str(fixerdate)[:10], "%Y-%m-%d")
119
- .replace(tzinfo=dt.timezone.utc)
119
+ .astimezone()
120
120
  .date()
121
121
  )
122
122
  if isinstance(fixerdate, str):
123
- return (
124
- dt.datetime.strptime(fixerdate, "%Y-%m-%d")
125
- .replace(tzinfo=dt.timezone.utc)
126
- .date()
127
- )
123
+ return dt.datetime.strptime(fixerdate, "%Y-%m-%d").astimezone().date()
128
124
  msg = f"Unknown date format {fixerdate!s} of type {type(fixerdate)!s} encountered"
129
125
  raise TypeError(
130
126
  msg,
@@ -212,7 +208,7 @@ def get_previous_business_day_before_today(
212
208
 
213
209
  """
214
210
  if today is None:
215
- today = dt.datetime.now(tz=dt.timezone.utc).date()
211
+ today = dt.datetime.now().astimezone().date()
216
212
 
217
213
  return date_offset_foll(
218
214
  today - dt.timedelta(days=1),
@@ -241,8 +237,8 @@ def offset_business_days(
241
237
  ddate: datetime.date
242
238
  A starting date that does not have to be a business day
243
239
  days: int
244
- The number of business days to offset from the business day that is
245
- the closest preceding the day given
240
+ The number of business days to offset from the business day that is given
241
+ If days is set as anything other than an integer its value is set to zero
246
242
  countries: CountriesType, default: "SE"
247
243
  (List of) country code(s) according to ISO 3166-1 alpha-2
248
244
  custom_holidays: HolidayType, optional
@@ -255,6 +251,11 @@ def offset_business_days(
255
251
  The new offset business day
256
252
 
257
253
  """
254
+ try:
255
+ days = int(days)
256
+ except TypeError:
257
+ days = 0
258
+
258
259
  if days <= 0:
259
260
  scaledtoyeardays = int((days * 372 / 250) // 1) - 365
260
261
  ndate = ddate + dt.timedelta(days=scaledtoyeardays)
@@ -17,13 +17,17 @@ from numpy import (
17
17
  array,
18
18
  cov,
19
19
  cumprod,
20
+ divide,
20
21
  dot,
21
22
  float64,
22
23
  inf,
24
+ isinf,
23
25
  linspace,
24
26
  log,
25
27
  nan,
26
28
  sqrt,
29
+ square,
30
+ std,
27
31
  zeros,
28
32
  )
29
33
  from numpy import (
@@ -53,10 +57,6 @@ from statsmodels.regression.linear_model import ( # type: ignore[import-untyped
53
57
  from typing_extensions import Self
54
58
 
55
59
  from openseries._common_model import _CommonModel
56
- from openseries._risk import (
57
- _calc_inv_vol_weights,
58
- _ewma_calc,
59
- )
60
60
  from openseries.datefixer import do_resample_to_business_period_ends
61
61
  from openseries.load_plotly import load_plotly_dict
62
62
  from openseries.series import OpenTimeSeries
@@ -591,17 +591,13 @@ class OpenFrame(_CommonModel):
591
591
  raw_corr = [raw_cov[0] / (2 * raw_one[0] * raw_two[0])]
592
592
 
593
593
  for _, row in data.iloc[1:].iterrows():
594
- tmp_raw_one = _ewma_calc(
595
- reeturn=row.loc[cols[0], ValueType.RTRN],
596
- prev_ewma=raw_one[-1],
597
- time_factor=time_factor,
598
- lmbda=lmbda,
594
+ tmp_raw_one = sqrt(
595
+ square(row.loc[cols[0], ValueType.RTRN]) * time_factor * (1 - lmbda)
596
+ + square(raw_one[-1]) * lmbda,
599
597
  )
600
- tmp_raw_two = _ewma_calc(
601
- reeturn=row.loc[cols[1], ValueType.RTRN],
602
- prev_ewma=raw_two[-1],
603
- time_factor=time_factor,
604
- lmbda=lmbda,
598
+ tmp_raw_two = sqrt(
599
+ square(row.loc[cols[1], ValueType.RTRN]) * time_factor * (1 - lmbda)
600
+ + square(raw_two[-1]) * lmbda,
605
601
  )
606
602
  tmp_raw_cov = (
607
603
  row.loc[cols[0], ValueType.RTRN]
@@ -1508,8 +1504,9 @@ class OpenFrame(_CommonModel):
1508
1504
  if weight_strat == "eq_weights":
1509
1505
  self.weights = [1.0 / self.item_count] * self.item_count
1510
1506
  elif weight_strat == "inv_vol":
1511
- weight_calc = list(_calc_inv_vol_weights(returns=dframe))
1512
- self.weights = weight_calc
1507
+ vol = divide(1.0, std(dframe, axis=0, ddof=1))
1508
+ vol[isinf(vol)] = nan
1509
+ self.weights = list(divide(vol, vol.sum()))
1513
1510
  else:
1514
1511
  msg = "Weight strategy not implemented"
1515
1512
  raise NotImplementedError(msg)
@@ -2188,7 +2185,7 @@ def sharpeplot( # noqa: C901
2188
2185
  xhoverformat=".2%",
2189
2186
  yhoverformat=".2%",
2190
2187
  hovertext=[point_frame.loc["text", col]],
2191
- hovertemplate=("Return %{y}<br>Vol %{x}%{hovertext}"),
2188
+ hovertemplate="Return %{y}<br>Vol %{x}%{hovertext}",
2192
2189
  hoverlabel_align="right",
2193
2190
  marker={"size": 20, "color": clr},
2194
2191
  mode=point_frame_mode,
@@ -15,6 +15,7 @@ from numpy import (
15
15
  isnan,
16
16
  log,
17
17
  sqrt,
18
+ square,
18
19
  )
19
20
  from pandas import (
20
21
  DataFrame,
@@ -28,9 +29,6 @@ from pydantic import model_validator
28
29
  from typing_extensions import Self
29
30
 
30
31
  from openseries._common_model import _CommonModel
31
- from openseries._risk import (
32
- _ewma_calc,
33
- )
34
32
  from openseries.datefixer import date_fix, do_resample_to_business_period_ends
35
33
  from openseries.types import (
36
34
  Countries,
@@ -675,13 +673,10 @@ class OpenTimeSeries(_CommonModel):
675
673
  ]
676
674
 
677
675
  for item in data.loc[:, cast(int, (self.label, ValueType.RTRN))].iloc[1:]:
678
- previous = rawdata[-1]
676
+ prev = rawdata[-1]
679
677
  rawdata.append(
680
- _ewma_calc(
681
- reeturn=cast(float, item),
682
- prev_ewma=previous,
683
- time_factor=time_factor,
684
- lmbda=lmbda,
678
+ sqrt(
679
+ square(item) * time_factor * (1 - lmbda) + square(prev) * lmbda,
685
680
  ),
686
681
  )
687
682
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "openseries"
3
- version = "1.5.6"
3
+ version = "1.5.7"
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"
@@ -34,8 +34,8 @@ keywords = [
34
34
 
35
35
  [tool.poetry.dependencies]
36
36
  python = ">=3.9,<3.13"
37
- holidays = ">=0.30,<0.50"
38
- numpy = ">=1.23.2,<=2.0.0"
37
+ holidays = ">=0.30,<1.0"
38
+ numpy = ">=1.23.2,<=3.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"
@@ -47,16 +47,16 @@ 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.5.3"
50
+ coverage = "^7.5.4"
51
51
  coverage-badge = "^1.1.1"
52
52
  mypy = "^1.10.0"
53
53
  pandas-stubs = "^2.2.2.240603"
54
54
  pre-commit = "^3.7.1"
55
- pytest = "^8.2.1"
56
- ruff = "^0.4.7"
57
- types-openpyxl = "^3.1.2.20240601"
55
+ pytest = "^8.2.2"
56
+ ruff = "^0.4.10"
57
+ types-openpyxl = "^3.1.4.20240621"
58
58
  types-python-dateutil = "^2.9.0.20240316"
59
- types-requests = "^2.32.0.20240602"
59
+ types-requests = "^2.32.0.20240622"
60
60
 
61
61
  [build-system]
62
62
  requires = ["poetry-core>=1.8.3"]
@@ -107,3 +107,9 @@ max-statements = 66
107
107
 
108
108
  [tool.ruff.lint.pyupgrade]
109
109
  keep-runtime-typing = true
110
+
111
+ [tool.pytest.ini_options]
112
+ filterwarnings = [
113
+ "error",
114
+ "ignore::DeprecationWarning:holidays.*"
115
+ ]
File without changes
File without changes