openseries 1.7.2__py3-none-any.whl → 1.7.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.
@@ -18,7 +18,15 @@ if TYPE_CHECKING:
18
18
  from openpyxl.utils.dataframe import dataframe_to_rows
19
19
  from openpyxl.workbook.workbook import Workbook
20
20
  from openpyxl.worksheet.worksheet import Worksheet
21
- from pandas import DataFrame, DatetimeIndex, Index, MultiIndex, Series, date_range
21
+ from pandas import (
22
+ DataFrame,
23
+ DatetimeIndex,
24
+ Index,
25
+ MultiIndex,
26
+ Series,
27
+ date_range,
28
+ to_datetime,
29
+ )
22
30
  from pandas.tseries.offsets import CustomBusinessDay
23
31
  from plotly.graph_objs import Figure # type: ignore[import-untyped,unused-ignore]
24
32
  from plotly.io import to_html # type: ignore[import-untyped,unused-ignore]
@@ -351,9 +359,7 @@ class _CommonModel(BaseModel):
351
359
  """
352
360
  wmdf = self.tsdf.copy()
353
361
  wmdf.index = DatetimeIndex(wmdf.index)
354
- result = (
355
- wmdf.resample("BME").last().pct_change(fill_method=cast(str, None)).min()
356
- )
362
+ result = wmdf.resample("BME").last().pct_change().min()
357
363
 
358
364
  if self.tsdf.shape[1] == 1:
359
365
  return float(result.iloc[0])
@@ -521,8 +527,8 @@ class _CommonModel(BaseModel):
521
527
  An OpenFrame object
522
528
 
523
529
  """
524
- startyear = DatetimeIndex(self.tsdf.index)[0].year
525
- endyear = DatetimeIndex(self.tsdf.index)[-1].year
530
+ startyear = to_datetime(self.tsdf.index[0]).year
531
+ endyear = to_datetime(self.tsdf.index[-1]).year
526
532
  calendar = holiday_calendar(
527
533
  startyear=startyear,
528
534
  endyear=endyear,
@@ -1021,9 +1027,7 @@ class _CommonModel(BaseModel):
1021
1027
  time_factor = how_many / fraction
1022
1028
 
1023
1029
  result = (
1024
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1025
- .pct_change(fill_method=cast(str, None))
1026
- .mean()
1030
+ self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change().mean()
1027
1031
  * time_factor
1028
1032
  )
1029
1033
 
@@ -1081,9 +1085,7 @@ class _CommonModel(BaseModel):
1081
1085
  time_factor = how_many / fraction
1082
1086
 
1083
1087
  data = self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1084
- result = (
1085
- data.pct_change(fill_method=cast(str, None)).std().mul(sqrt(time_factor))
1086
- )
1088
+ result = data.pct_change().std().mul(sqrt(time_factor))
1087
1089
 
1088
1090
  if self.tsdf.shape[1] == 1:
1089
1091
  return float(cast(SupportsFloat, result.iloc[0]))
@@ -1279,22 +1281,20 @@ class _CommonModel(BaseModel):
1279
1281
  if drift_adjust:
1280
1282
  imp_vol = (-sqrt(time_factor) / norm.ppf(level)) * (
1281
1283
  self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1282
- .pct_change(fill_method=cast(str, None))
1284
+ .pct_change()
1283
1285
  .quantile(1 - level, interpolation=interpolation)
1284
1286
  - self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1285
- .pct_change(fill_method=cast(str, None))
1287
+ .pct_change()
1286
1288
  .sum()
1287
1289
  / len(
1288
- self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change(
1289
- fill_method=cast(str, None),
1290
- ),
1290
+ self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change(),
1291
1291
  )
1292
1292
  )
1293
1293
  else:
1294
1294
  imp_vol = (
1295
1295
  -sqrt(time_factor)
1296
1296
  * self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1297
- .pct_change(fill_method=cast(str, None))
1297
+ .pct_change()
1298
1298
  .quantile(1 - level, interpolation=interpolation)
1299
1299
  / norm.ppf(level)
1300
1300
  )
@@ -1357,14 +1357,14 @@ class _CommonModel(BaseModel):
1357
1357
  cvar_df = self.tsdf.loc[cast(int, earlier) : cast(int, later)].copy(deep=True)
1358
1358
  result = [
1359
1359
  cvar_df.loc[:, x] # type: ignore[call-overload,index]
1360
- .pct_change(fill_method=cast(str, None))
1360
+ .pct_change()
1361
1361
  .sort_values()
1362
1362
  .iloc[
1363
1363
  : int(
1364
1364
  ceil(
1365
1365
  (1 - level)
1366
1366
  * cvar_df.loc[:, x] # type: ignore[index]
1367
- .pct_change(fill_method=cast(str, None))
1367
+ .pct_change()
1368
1368
  .count(),
1369
1369
  ),
1370
1370
  )
@@ -1424,7 +1424,7 @@ class _CommonModel(BaseModel):
1424
1424
  )
1425
1425
  how_many = (
1426
1426
  self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1427
- .pct_change(fill_method=cast(str, None))
1427
+ .pct_change()
1428
1428
  .count(numeric_only=True)
1429
1429
  )
1430
1430
  if periods_in_a_year_fixed:
@@ -1439,7 +1439,7 @@ class _CommonModel(BaseModel):
1439
1439
 
1440
1440
  dddf = (
1441
1441
  self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1442
- .pct_change(fill_method=cast(str, None))
1442
+ .pct_change()
1443
1443
  .sub(min_accepted_return / time_factor)
1444
1444
  )
1445
1445
 
@@ -1542,7 +1542,7 @@ class _CommonModel(BaseModel):
1542
1542
  )
1543
1543
  result: NDArray[float64] = skew(
1544
1544
  a=self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1545
- .pct_change(fill_method=cast(str, None))
1545
+ .pct_change()
1546
1546
  .to_numpy(),
1547
1547
  bias=True,
1548
1548
  nan_policy="omit",
@@ -1589,9 +1589,7 @@ class _CommonModel(BaseModel):
1589
1589
  to_dt=to_date,
1590
1590
  )
1591
1591
  result: NDArray[float64] = kurtosis(
1592
- self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change(
1593
- fill_method=cast(str, None),
1594
- ),
1592
+ self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change(),
1595
1593
  fisher=True,
1596
1594
  bias=True,
1597
1595
  nan_policy="omit",
@@ -1687,19 +1685,13 @@ class _CommonModel(BaseModel):
1687
1685
  )
1688
1686
  pos = (
1689
1687
  self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1690
- .pct_change(fill_method=cast(str, None))[1:][
1691
- self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change(
1692
- fill_method=cast(str, None),
1693
- )[1:]
1688
+ .pct_change()[1:][
1689
+ self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change()[1:]
1694
1690
  > zero
1695
1691
  ]
1696
1692
  .count()
1697
1693
  )
1698
- tot = (
1699
- self.tsdf.loc[cast(int, earlier) : cast(int, later)]
1700
- .pct_change(fill_method=cast(str, None))[1:]
1701
- .count()
1702
- )
1694
+ tot = self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change().count()
1703
1695
  share = pos / tot
1704
1696
  if self.tsdf.shape[1] == 1:
1705
1697
  return float(share.iloc[0])
@@ -1875,9 +1867,7 @@ class _CommonModel(BaseModel):
1875
1867
  from_dt=from_date,
1876
1868
  to_dt=to_date,
1877
1869
  )
1878
- retdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change(
1879
- fill_method=cast(str, None),
1880
- )
1870
+ retdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change()
1881
1871
  pos = retdf[retdf > min_accepted_return].sub(min_accepted_return).sum()
1882
1872
  neg = retdf[retdf < min_accepted_return].sub(min_accepted_return).sum()
1883
1873
  ratio = pos / -neg
@@ -1965,7 +1955,7 @@ class _CommonModel(BaseModel):
1965
1955
  period = "-".join([str(year), str(month).zfill(2)])
1966
1956
  vrdf = self.tsdf.copy()
1967
1957
  vrdf.index = DatetimeIndex(vrdf.index)
1968
- resultdf = DataFrame(vrdf.pct_change(fill_method=None)) # type: ignore[arg-type]
1958
+ resultdf = DataFrame(vrdf.pct_change())
1969
1959
  result = resultdf.loc[period] + 1
1970
1960
  cal_period = result.cumprod(axis="index").iloc[-1] - 1
1971
1961
  if self.tsdf.shape[1] == 1:
@@ -2017,7 +2007,7 @@ class _CommonModel(BaseModel):
2017
2007
  )
2018
2008
  result = (
2019
2009
  self.tsdf.loc[cast(int, earlier) : cast(int, later)]
2020
- .pct_change(fill_method=cast(str, None))
2010
+ .pct_change()
2021
2011
  .quantile(1 - level, interpolation=interpolation)
2022
2012
  )
2023
2013
 
@@ -2065,7 +2055,7 @@ class _CommonModel(BaseModel):
2065
2055
  )
2066
2056
  result = (
2067
2057
  self.tsdf.loc[cast(int, earlier) : cast(int, later)]
2068
- .pct_change(fill_method=cast(str, None))
2058
+ .pct_change()
2069
2059
  .rolling(observations, min_periods=observations)
2070
2060
  .sum()
2071
2061
  .min()
@@ -2111,9 +2101,7 @@ class _CommonModel(BaseModel):
2111
2101
  from_dt=from_date,
2112
2102
  to_dt=to_date,
2113
2103
  )
2114
- zscframe = self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change(
2115
- fill_method=cast(str, None),
2116
- )
2104
+ zscframe = self.tsdf.loc[cast(int, earlier) : cast(int, later)].pct_change()
2117
2105
  result = (zscframe.iloc[-1] - zscframe.mean()) / zscframe.std()
2118
2106
 
2119
2107
  if self.tsdf.shape[1] == 1:
@@ -2182,7 +2170,7 @@ class _CommonModel(BaseModel):
2182
2170
  ret_label = cast(tuple[str], self.tsdf.iloc[:, column].name)[0]
2183
2171
  retseries = (
2184
2172
  self.tsdf.iloc[:, column]
2185
- .pct_change(fill_method=cast(str, None))
2173
+ .pct_change()
2186
2174
  .rolling(observations, min_periods=observations)
2187
2175
  .sum()
2188
2176
  )
@@ -2259,7 +2247,7 @@ class _CommonModel(BaseModel):
2259
2247
  else:
2260
2248
  time_factor = self.periods_in_a_year
2261
2249
  vol_label = cast(tuple[str, ValueType], self.tsdf.iloc[:, column].name)[0]
2262
- dframe = self.tsdf.iloc[:, column].pct_change(fill_method=cast(str, None))
2250
+ dframe = self.tsdf.iloc[:, column].pct_change()
2263
2251
  volseries = dframe.rolling(
2264
2252
  observations,
2265
2253
  min_periods=observations,
openseries/datefixer.py CHANGED
@@ -336,7 +336,7 @@ def generate_calendar_date_range(
336
336
  )
337
337
  calendar = holiday_calendar(
338
338
  startyear=start.year,
339
- endyear=tmp_range[-1].year,
339
+ endyear=date_fix(tmp_range[-1]).year,
340
340
  countries=countries,
341
341
  )
342
342
  return [
@@ -351,7 +351,7 @@ def generate_calendar_date_range(
351
351
  if end and not start:
352
352
  tmp_range = date_range(end=end, periods=trading_days * 365 // 252, freq="D")
353
353
  calendar = holiday_calendar(
354
- startyear=tmp_range[0].year,
354
+ startyear=date_fix(tmp_range[0]).year,
355
355
  endyear=end.year,
356
356
  countries=countries,
357
357
  )
@@ -371,7 +371,6 @@ def generate_calendar_date_range(
371
371
  raise ValueError(msg)
372
372
 
373
373
 
374
- # noinspection PyUnusedLocal
375
374
  def _do_resample_to_business_period_ends(
376
375
  data: DataFrame,
377
376
  freq: LiteralBizDayFreq,
@@ -421,4 +420,4 @@ def _do_resample_to_business_period_ends(
421
420
  ]
422
421
  + [copydata.index[-1]],
423
422
  )
424
- return dates.drop_duplicates()
423
+ return DatetimeIndex(dates.drop_duplicates())
openseries/frame.py CHANGED
@@ -336,7 +336,7 @@ class OpenFrame(_CommonModel):
336
336
  The returns of the values in the series
337
337
 
338
338
  """
339
- self.tsdf = self.tsdf.pct_change(fill_method=None)
339
+ self.tsdf = self.tsdf.pct_change()
340
340
  self.tsdf.iloc[0] = 0
341
341
  new_labels = [ValueType.RTRN] * self.item_count
342
342
  arrays = [self.tsdf.columns.get_level_values(0), new_labels]
@@ -449,7 +449,8 @@ class OpenFrame(_CommonModel):
449
449
  countries=countries,
450
450
  )
451
451
  xerie.tsdf = xerie.tsdf.reindex(
452
- [deyt.date() for deyt in dates], method=method,
452
+ [deyt.date() for deyt in dates],
453
+ method=method,
453
454
  )
454
455
 
455
456
  self._set_tsdf()
@@ -588,7 +589,7 @@ class OpenFrame(_CommonModel):
588
589
  Correlation matrix
589
590
 
590
591
  """
591
- corr_matrix = self.tsdf.pct_change(fill_method=None).corr(
592
+ corr_matrix = self.tsdf.pct_change().corr(
592
593
  method="pearson",
593
594
  min_periods=1,
594
595
  )
@@ -615,7 +616,6 @@ class OpenFrame(_CommonModel):
615
616
 
616
617
  """
617
618
  self.constituents += [new_series]
618
- # noinspection PyUnreachableCode
619
619
  self.tsdf = concat([self.tsdf, new_series.tsdf], axis="columns", sort=True)
620
620
  return self
621
621
 
@@ -814,10 +814,9 @@ class OpenFrame(_CommonModel):
814
814
  :,
815
815
  item,
816
816
  ]
817
- # noinspection PyTypeChecker
818
817
  relative = 1.0 + longdf - shortdf
819
818
  vol = float(
820
- relative.pct_change(fill_method=None).std() * sqrt(time_factor),
819
+ relative.pct_change().std() * sqrt(time_factor),
821
820
  )
822
821
  terrors.append(vol)
823
822
 
@@ -905,13 +904,12 @@ class OpenFrame(_CommonModel):
905
904
  :,
906
905
  item,
907
906
  ]
908
- # noinspection PyTypeChecker
909
907
  relative = 1.0 + longdf - shortdf
910
908
  ret = float(
911
- relative.pct_change(fill_method=None).mean() * time_factor,
909
+ relative.pct_change().mean() * time_factor,
912
910
  )
913
911
  vol = float(
914
- relative.pct_change(fill_method=None).std() * sqrt(time_factor),
912
+ relative.pct_change().std() * sqrt(time_factor),
915
913
  )
916
914
  ratios.append(ret / vol)
917
915
 
@@ -1010,18 +1008,16 @@ class OpenFrame(_CommonModel):
1010
1008
  msg = "ratio must be one of 'up', 'down' or 'both'."
1011
1009
  if ratio == "up":
1012
1010
  uparray = (
1013
- longdf.pct_change(fill_method=None)[
1014
- shortdf.pct_change(fill_method=None).to_numpy()
1015
- > loss_limit
1011
+ longdf.pct_change()[
1012
+ shortdf.pct_change().to_numpy() > loss_limit
1016
1013
  ]
1017
1014
  .add(1)
1018
1015
  .to_numpy()
1019
1016
  )
1020
1017
  up_rtrn = uparray.prod() ** (1 / (len(uparray) / time_factor)) - 1
1021
1018
  upidxarray = (
1022
- shortdf.pct_change(fill_method=None)[
1023
- shortdf.pct_change(fill_method=None).to_numpy()
1024
- > loss_limit
1019
+ shortdf.pct_change()[
1020
+ shortdf.pct_change().to_numpy() > loss_limit
1025
1021
  ]
1026
1022
  .add(1)
1027
1023
  .to_numpy()
@@ -1032,9 +1028,8 @@ class OpenFrame(_CommonModel):
1032
1028
  ratios.append(up_rtrn / up_idx_return)
1033
1029
  elif ratio == "down":
1034
1030
  downarray = (
1035
- longdf.pct_change(fill_method=None)[
1036
- shortdf.pct_change(fill_method=None).to_numpy()
1037
- < loss_limit
1031
+ longdf.pct_change()[
1032
+ shortdf.pct_change().to_numpy() < loss_limit
1038
1033
  ]
1039
1034
  .add(1)
1040
1035
  .to_numpy()
@@ -1043,9 +1038,8 @@ class OpenFrame(_CommonModel):
1043
1038
  downarray.prod() ** (1 / (len(downarray) / time_factor)) - 1
1044
1039
  )
1045
1040
  downidxarray = (
1046
- shortdf.pct_change(fill_method=None)[
1047
- shortdf.pct_change(fill_method=None).to_numpy()
1048
- < loss_limit
1041
+ shortdf.pct_change()[
1042
+ shortdf.pct_change().to_numpy() < loss_limit
1049
1043
  ]
1050
1044
  .add(1)
1051
1045
  .to_numpy()
@@ -1057,18 +1051,16 @@ class OpenFrame(_CommonModel):
1057
1051
  ratios.append(down_return / down_idx_return)
1058
1052
  elif ratio == "both":
1059
1053
  uparray = (
1060
- longdf.pct_change(fill_method=None)[
1061
- shortdf.pct_change(fill_method=None).to_numpy()
1062
- > loss_limit
1054
+ longdf.pct_change()[
1055
+ shortdf.pct_change().to_numpy() > loss_limit
1063
1056
  ]
1064
1057
  .add(1)
1065
1058
  .to_numpy()
1066
1059
  )
1067
1060
  up_rtrn = uparray.prod() ** (1 / (len(uparray) / time_factor)) - 1
1068
1061
  upidxarray = (
1069
- shortdf.pct_change(fill_method=None)[
1070
- shortdf.pct_change(fill_method=None).to_numpy()
1071
- > loss_limit
1062
+ shortdf.pct_change()[
1063
+ shortdf.pct_change().to_numpy() > loss_limit
1072
1064
  ]
1073
1065
  .add(1)
1074
1066
  .to_numpy()
@@ -1077,9 +1069,8 @@ class OpenFrame(_CommonModel):
1077
1069
  upidxarray.prod() ** (1 / (len(upidxarray) / time_factor)) - 1
1078
1070
  )
1079
1071
  downarray = (
1080
- longdf.pct_change(fill_method=None)[
1081
- shortdf.pct_change(fill_method=None).to_numpy()
1082
- < loss_limit
1072
+ longdf.pct_change()[
1073
+ shortdf.pct_change().to_numpy() < loss_limit
1083
1074
  ]
1084
1075
  .add(1)
1085
1076
  .to_numpy()
@@ -1088,9 +1079,8 @@ class OpenFrame(_CommonModel):
1088
1079
  downarray.prod() ** (1 / (len(downarray) / time_factor)) - 1
1089
1080
  )
1090
1081
  downidxarray = (
1091
- shortdf.pct_change(fill_method=None)[
1092
- shortdf.pct_change(fill_method=None).to_numpy()
1093
- < loss_limit
1082
+ shortdf.pct_change()[
1083
+ shortdf.pct_change().to_numpy() < loss_limit
1094
1084
  ]
1095
1085
  .add(1)
1096
1086
  .to_numpy()
@@ -1422,7 +1412,7 @@ class OpenFrame(_CommonModel):
1422
1412
  x == ValueType.RTRN
1423
1413
  for x in self.tsdf.columns.get_level_values(1).to_numpy()
1424
1414
  ):
1425
- dframe = dframe.pct_change(fill_method=None)
1415
+ dframe = dframe.pct_change()
1426
1416
  dframe.iloc[0] = 0
1427
1417
 
1428
1418
  msg = "Weight strategy not implemented"
@@ -1492,13 +1482,11 @@ class OpenFrame(_CommonModel):
1492
1482
  )
1493
1483
 
1494
1484
  retseries = (
1495
- relative.pct_change(fill_method=None)
1496
- .rolling(observations, min_periods=observations)
1497
- .sum()
1485
+ relative.pct_change().rolling(observations, min_periods=observations).sum()
1498
1486
  )
1499
1487
  retdf = retseries.dropna().to_frame()
1500
1488
 
1501
- voldf = relative.pct_change(fill_method=None).rolling(
1489
+ voldf = relative.pct_change().rolling(
1502
1490
  observations,
1503
1491
  min_periods=observations,
1504
1492
  ).std() * sqrt(time_factor)
@@ -1542,7 +1530,7 @@ class OpenFrame(_CommonModel):
1542
1530
  asset_label = cast(tuple[str, str], self.tsdf.iloc[:, asset_column].name)[0]
1543
1531
  beta_label = f"{asset_label} / {market_label}"
1544
1532
 
1545
- rolling = self.tsdf.pct_change(fill_method=None).rolling(
1533
+ rolling = self.tsdf.pct_change().rolling(
1546
1534
  observations,
1547
1535
  min_periods=observations,
1548
1536
  )
@@ -1599,12 +1587,10 @@ class OpenFrame(_CommonModel):
1599
1587
  )
1600
1588
  first_series = (
1601
1589
  self.tsdf.iloc[:, first_column]
1602
- .pct_change(fill_method=None)[1:]
1590
+ .pct_change()[1:]
1603
1591
  .rolling(observations, min_periods=observations)
1604
1592
  )
1605
- second_series = self.tsdf.iloc[:, second_column].pct_change(
1606
- fill_method=None,
1607
- )[1:]
1593
+ second_series = self.tsdf.iloc[:, second_column].pct_change()[1:]
1608
1594
  corrdf = first_series.corr(other=second_series).dropna().to_frame()
1609
1595
  corrdf.columns = MultiIndex.from_arrays(
1610
1596
  [
@@ -105,15 +105,14 @@ def simulate_portfolios(
105
105
  weights = weights / npsum(weights)
106
106
  all_weights[x, :] = weights
107
107
 
108
- vol_arr[x] = sqrt(weights.T @
109
- (log_ret.cov() * simframe.periods_in_a_year @ weights),
108
+ vol_arr[x] = sqrt(
109
+ weights.T @ (log_ret.cov() * simframe.periods_in_a_year @ weights),
110
110
  )
111
111
 
112
112
  ret_arr[x] = npsum(log_ret.mean() * weights * simframe.periods_in_a_year)
113
113
 
114
114
  sharpe_arr[x] = ret_arr[x] / vol_arr[x]
115
115
 
116
- # noinspection PyUnreachableCode
117
116
  simdf = concat(
118
117
  [
119
118
  DataFrame({"stdev": vol_arr, "ret": ret_arr, "sharpe": sharpe_arr}),
@@ -284,7 +283,6 @@ def efficient_frontier( # noqa: C901
284
283
  frontier_x.append(result["fun"])
285
284
  frontier_weights.append(result["x"])
286
285
 
287
- # noinspection PyUnreachableCode
288
286
  line_df = concat(
289
287
  [
290
288
  DataFrame(data=frontier_weights, columns=eframe.columns_lvl_zero),
openseries/series.py CHANGED
@@ -205,7 +205,7 @@ class OpenTimeSeries(_CommonModel):
205
205
  @classmethod
206
206
  def from_df(
207
207
  cls: type[OpenTimeSeries],
208
- dframe: DataFrame | Series[float],
208
+ dframe: Series[float] | DataFrame,
209
209
  column_nmbr: int = 0,
210
210
  valuetype: ValueType = ValueType.PRICE,
211
211
  baseccy: CurrencyStringType = "SEK",
@@ -234,10 +234,8 @@ class OpenTimeSeries(_CommonModel):
234
234
 
235
235
  """
236
236
  msg = "Argument dframe must be pandas Series or DataFrame."
237
- if isinstance(dframe, Series): # type: ignore[unreachable,unused-ignore]
238
- if isinstance( # type: ignore[unreachable,unused-ignore]
239
- dframe.name, tuple,
240
- ):
237
+ if isinstance(dframe, Series):
238
+ if isinstance(dframe.name, tuple):
241
239
  label, _ = dframe.name
242
240
  else:
243
241
  label = dframe.name
@@ -436,7 +434,7 @@ class OpenTimeSeries(_CommonModel):
436
434
  The returns of the values in the series
437
435
 
438
436
  """
439
- self.tsdf = self.tsdf.pct_change(fill_method=None) # type: ignore[arg-type]
437
+ self.tsdf = self.tsdf.pct_change()
440
438
  self.tsdf.iloc[0] = 0
441
439
  self.valuetype = ValueType.RTRN
442
440
  self.tsdf.columns = MultiIndex.from_arrays(
@@ -696,7 +694,7 @@ class OpenTimeSeries(_CommonModel):
696
694
  returns_input = True
697
695
  else:
698
696
  values = [cast(float, self.tsdf.iloc[0, 0])]
699
- ra_df = self.tsdf.pct_change(fill_method=None) # type: ignore[arg-type]
697
+ ra_df = self.tsdf.pct_change()
700
698
  returns_input = False
701
699
  ra_df = ra_df.dropna()
702
700
 
openseries/simulation.py CHANGED
@@ -122,10 +122,7 @@ class ReturnSimulation(BaseModel):
122
122
  """
123
123
  return cast(
124
124
  float,
125
- (
126
- self.results.pct_change(fill_method=cast(str, None)).mean()
127
- * self.trading_days_in_year
128
- ).iloc[0],
125
+ (self.results.pct_change().mean() * self.trading_days_in_year).iloc[0],
129
126
  )
130
127
 
131
128
  @property
@@ -140,10 +137,9 @@ class ReturnSimulation(BaseModel):
140
137
  """
141
138
  return cast(
142
139
  float,
143
- (
144
- self.results.pct_change(fill_method=cast(str, None)).std()
145
- * sqrt(self.trading_days_in_year)
146
- ).iloc[0],
140
+ (self.results.pct_change().std() * sqrt(self.trading_days_in_year)).iloc[
141
+ 0
142
+ ],
147
143
  )
148
144
 
149
145
  @classmethod
openseries/types.py CHANGED
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import datetime as dt
6
6
  from enum import Enum
7
+ from pprint import pformat
7
8
  from typing import Annotated, ClassVar, Literal, Union
8
9
 
9
10
  from numpy import datetime64
@@ -225,8 +226,8 @@ LiteralFrameProps = Literal[
225
226
  ]
226
227
 
227
228
 
228
- class OpenTimeSeriesPropertiesList(list[str]):
229
- """Allowed property arguments for the OpenTimeSeries class."""
229
+ class PropertiesList(list[str]):
230
+ """Base class for allowed property arguments definition."""
230
231
 
231
232
  allowed_strings: ClassVar[set[str]] = {
232
233
  "value_ret",
@@ -246,9 +247,38 @@ class OpenTimeSeriesPropertiesList(list[str]):
246
247
  "vol_from_var",
247
248
  "worst",
248
249
  "worst_month",
249
- "max_drawdown_cal_year",
250
250
  "max_drawdown",
251
251
  "max_drawdown_date",
252
+ "max_drawdown_cal_year",
253
+ }
254
+
255
+ def _validate(self: Self) -> None:
256
+ """Validate the string input of the all_properties method."""
257
+ seen = set()
258
+ invalids = set()
259
+ duplicates = set()
260
+ msg = ""
261
+ for item in self:
262
+ if item not in self.allowed_strings:
263
+ invalids.add(item)
264
+ if item in seen:
265
+ duplicates.add(item)
266
+ seen.add(item)
267
+ if len(invalids) != 0:
268
+ msg += (
269
+ f"Invalid string(s): {list(invalids)}.\nAllowed strings are:"
270
+ f"\n{pformat(self.allowed_strings)}\n"
271
+ )
272
+ if len(duplicates) != 0:
273
+ msg += f"Duplicate string(s): {list(duplicates)}."
274
+ if len(msg) != 0:
275
+ raise ValueError(msg)
276
+
277
+
278
+ class OpenTimeSeriesPropertiesList(PropertiesList):
279
+ """Allowed property arguments for the OpenTimeSeries class."""
280
+
281
+ allowed_strings: ClassVar[set[str]] = PropertiesList.allowed_strings | {
252
282
  "first_idx",
253
283
  "last_idx",
254
284
  "length",
@@ -265,44 +295,11 @@ class OpenTimeSeriesPropertiesList(list[str]):
265
295
  super().__init__(args)
266
296
  self._validate()
267
297
 
268
- def _validate(self: Self) -> None:
269
- seen = set()
270
- for item in self:
271
- if item not in self.allowed_strings:
272
- msg = (
273
- f"Invalid string: {item}. Allowed strings: {self.allowed_strings}"
274
- )
275
- raise ValueError(msg)
276
- if item in seen:
277
- msg = f"Duplicate string: {item}"
278
- raise ValueError(msg)
279
- seen.add(item)
280
-
281
298
 
282
- class OpenFramePropertiesList(list[str]):
299
+ class OpenFramePropertiesList(PropertiesList):
283
300
  """Allowed property arguments for the OpenFrame class."""
284
301
 
285
- allowed_strings: ClassVar[set[str]] = {
286
- "value_ret",
287
- "geo_ret",
288
- "arithmetic_ret",
289
- "vol",
290
- "downside_deviation",
291
- "ret_vol_ratio",
292
- "sortino_ratio",
293
- "omega_ratio",
294
- "z_score",
295
- "skew",
296
- "kurtosis",
297
- "positive_share",
298
- "var_down",
299
- "cvar_down",
300
- "vol_from_var",
301
- "worst",
302
- "worst_month",
303
- "max_drawdown",
304
- "max_drawdown_date",
305
- "max_drawdown_cal_year",
302
+ allowed_strings: ClassVar[set[str]] = PropertiesList.allowed_strings | {
306
303
  "first_indices",
307
304
  "last_indices",
308
305
  "lengths_of_items",
@@ -314,19 +311,6 @@ class OpenFramePropertiesList(list[str]):
314
311
  super().__init__(args)
315
312
  self._validate()
316
313
 
317
- def _validate(self: Self) -> None:
318
- seen = set()
319
- for item in self:
320
- if item not in self.allowed_strings:
321
- msg = (
322
- f"Invalid string: {item}. Allowed strings: {self.allowed_strings}"
323
- )
324
- raise ValueError(msg)
325
- if item in seen:
326
- msg = f"Duplicate string: {item}"
327
- raise ValueError(msg)
328
- seen.add(item)
329
-
330
314
 
331
315
  class ValueType(str, Enum):
332
316
  """Enum types of OpenTimeSeries to identify the output."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openseries
3
- Version: 1.7.2
3
+ Version: 1.7.3
4
4
  Summary: Tools for analyzing financial timeseries.
5
5
  Home-page: https://github.com/CaptorAB/openseries
6
6
  License: BSD-3-Clause
@@ -28,7 +28,7 @@ Requires-Dist: pyarrow (>=14.0.2,<18.0.0)
28
28
  Requires-Dist: pydantic (>=2.5.2,<3.0.0)
29
29
  Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
30
30
  Requires-Dist: requests (>=2.20.0,<3.0.0)
31
- Requires-Dist: scipy (>=1.11.4,<1.14.1)
31
+ Requires-Dist: scipy (>=1.11.4,<2.0.0)
32
32
  Requires-Dist: statsmodels (>=0.14.0,<1.0.0)
33
33
  Project-URL: Repository, https://github.com/CaptorAB/openseries
34
34
  Description-Content-Type: text/markdown
@@ -0,0 +1,16 @@
1
+ openseries/__init__.py,sha256=gD2dMKRTJ9HMXLca_5sR67xGiU5sWExwaNUi-9N_RGQ,1032
2
+ openseries/_common_model.py,sha256=t3UeFpNIgWeeGW7lcsVAPF0lsdzNoBDvh__B1TMzigk,72772
3
+ openseries/_risk.py,sha256=PReIfkzhInvIgJkzI4k1wYvhmLZ4cCurYKuQAvlHLlE,2082
4
+ openseries/datefixer.py,sha256=3E8Ddf3Ps9k5qUWqL13ONelyMECZsvtyrsFJg7r3bxE,12298
5
+ openseries/frame.py,sha256=xXBPgKGpuHE2M3P4aHitWiJaeWXE3dbd5oEvxrg_7wY,54065
6
+ openseries/load_plotly.py,sha256=Uuk_-iIY4_C6Z5U3rAneOh8ZlGYWbkuis9s4Amxzko4,1921
7
+ openseries/plotly_captor_logo.json,sha256=F5nhMzEyxKywtjvQqMTKgKRCJQYMDIiBgDSxdte8Clo,178
8
+ openseries/plotly_layouts.json,sha256=ahx8-dL4_RPzvHtBOX0SiL0AH7xQJzNRSDhGrSmU-Og,1429
9
+ openseries/portfoliotools.py,sha256=pI-vUYKbht2u3dTpQ0MHiME67fP27HcaOCxuayW7v-0,18856
10
+ openseries/series.py,sha256=IvwyXu9hpsItw6LVPQAJ9YNe_bYCXO9ZyNEleuPCLIo,27766
11
+ openseries/simulation.py,sha256=jghKf0d2deXK-rqn8BeJ_s3JQVLL9FuE_IW29Kf1_-k,13816
12
+ openseries/types.py,sha256=IbzW9iVuA3MEx3WtjyB6QYeSd3TkGjTOkFnnpSADKlY,7491
13
+ openseries-1.7.3.dist-info/LICENSE.md,sha256=IQ8_IMXgHxyv4M48G14fJsjcrkiSASdalASTXWCOsj4,1515
14
+ openseries-1.7.3.dist-info/METADATA,sha256=HmKHHb3SqFrzGIzkKk-l5LXj3PiXagx5CkWH829gbxo,43973
15
+ openseries-1.7.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
16
+ openseries-1.7.3.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- openseries/__init__.py,sha256=gD2dMKRTJ9HMXLca_5sR67xGiU5sWExwaNUi-9N_RGQ,1032
2
- openseries/_common_model.py,sha256=QuwlYSGCaTr6q0pPiHg6ni63ZvA8I2HfbgqSj4HmBHY,73657
3
- openseries/_risk.py,sha256=PReIfkzhInvIgJkzI4k1wYvhmLZ4cCurYKuQAvlHLlE,2082
4
- openseries/datefixer.py,sha256=IJiR43fVAf7hLJk0-5-xzQXNv6vAYxoJgRHH_EM1a9c,12292
5
- openseries/frame.py,sha256=WNih33lji-oHkHQMLqMQ3CWz-jmfS4eLQrcXE0wzbys,54885
6
- openseries/load_plotly.py,sha256=Uuk_-iIY4_C6Z5U3rAneOh8ZlGYWbkuis9s4Amxzko4,1921
7
- openseries/plotly_captor_logo.json,sha256=F5nhMzEyxKywtjvQqMTKgKRCJQYMDIiBgDSxdte8Clo,178
8
- openseries/plotly_layouts.json,sha256=ahx8-dL4_RPzvHtBOX0SiL0AH7xQJzNRSDhGrSmU-Og,1429
9
- openseries/portfoliotools.py,sha256=WQSf2OdrSojx-uOhLWQzz6IMnCOZjWjh-8yPJmqTZcA,18933
10
- openseries/series.py,sha256=PaXIVp7dLM4VsxPOe4B8tqM1MMDtJtPSDFvhv1Yblhc,27971
11
- openseries/simulation.py,sha256=P5nkX7o3O_3pOL22MAZTqL_i4TB2IUmPGCodegsAM04,13932
12
- openseries/types.py,sha256=C_t7WLyc7BgKun728CpLXnJS1GisGdNnxYAMBNAcVXU,7830
13
- openseries-1.7.2.dist-info/LICENSE.md,sha256=IQ8_IMXgHxyv4M48G14fJsjcrkiSASdalASTXWCOsj4,1515
14
- openseries-1.7.2.dist-info/METADATA,sha256=58Cg-cbff8KR3P_vvGUMEFvJg3SEy271FXNhEbIQudI,43974
15
- openseries-1.7.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
16
- openseries-1.7.2.dist-info/RECORD,,