openseries 1.9.4__py3-none-any.whl → 1.9.6__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/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,
@@ -144,7 +144,7 @@ def holiday_calendar(
144
144
  for country in countries:
145
145
  staging = country_holidays(country=country, years=years)
146
146
  countryholidays += list(staging)
147
- hols = list(countryholidays)
147
+ hols = cast("list[dt.date]", list(countryholidays))
148
148
  else:
149
149
  msg = (
150
150
  "Argument countries must be a string country code or "
@@ -154,7 +154,9 @@ def holiday_calendar(
154
154
 
155
155
  if markets:
156
156
  market_hols = market_holidays(
157
- startyear=startyear, endyear=endyear, markets=markets
157
+ startyear=startyear,
158
+ endyear=endyear,
159
+ markets=markets,
158
160
  )
159
161
  dt_mkt_hols = [date_fix(fixerdate=ddate) for ddate in market_hols]
160
162
  hols.extend(dt_mkt_hols)
@@ -163,7 +165,7 @@ def holiday_calendar(
163
165
  custom_list = (
164
166
  [custom_holidays]
165
167
  if isinstance(custom_holidays, str)
166
- else list(custom_holidays) # type: ignore[arg-type]
168
+ else list(custom_holidays)
167
169
  )
168
170
  hols.extend([date_fix(fixerdate=ddate) for ddate in custom_list])
169
171
 
@@ -255,7 +257,7 @@ def date_offset_foll(
255
257
  while not is_busday(dates=new_date, busdaycal=calendar):
256
258
  new_date += day_delta
257
259
 
258
- return new_date # type: ignore[no-any-return]
260
+ return new_date
259
261
 
260
262
 
261
263
  def get_previous_business_day_before_today(
@@ -505,7 +507,7 @@ def _do_resample_to_business_period_ends(
505
507
  dates = DatetimeIndex(
506
508
  [copydata.index[0]]
507
509
  + [
508
- date_offset_foll( # type: ignore[misc]
510
+ date_offset_foll(
509
511
  raw_date=dt.date(d.year, d.month, 1)
510
512
  + relativedelta(months=1)
511
513
  - dt.timedelta(days=1),
openseries/frame.py CHANGED
@@ -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="assignment,no-any-return"
11
10
  from __future__ import annotations
12
11
 
13
12
  from copy import deepcopy
@@ -15,12 +14,11 @@ from functools import reduce
15
14
  from logging import getLogger
16
15
  from typing import TYPE_CHECKING, Any, cast
17
16
 
18
- if TYPE_CHECKING: # pragma: no cover
19
- import datetime as dt
20
-
21
17
  from numpy import (
22
18
  array,
19
+ concatenate,
23
20
  cov,
21
+ diff,
24
22
  divide,
25
23
  isinf,
26
24
  log,
@@ -37,8 +35,19 @@ from pandas import (
37
35
  concat,
38
36
  merge,
39
37
  )
38
+
39
+ if TYPE_CHECKING: # pragma: no cover
40
+ import datetime as dt
41
+
42
+ from pandas import Series as _Series
43
+ from pandas import Timestamp
44
+
45
+ SeriesFloat = _Series[float]
46
+ else:
47
+ SeriesFloat = Series
48
+
40
49
  from pydantic import field_validator
41
- from sklearn.linear_model import LinearRegression
50
+ from sklearn.linear_model import LinearRegression # type: ignore[import-untyped]
42
51
 
43
52
  from ._common_model import _CommonModel
44
53
  from .datefixer import _do_resample_to_business_period_ends
@@ -69,7 +78,7 @@ __all__ = ["OpenFrame"]
69
78
 
70
79
 
71
80
  # noinspection PyUnresolvedReferences,PyTypeChecker
72
- class OpenFrame(_CommonModel): # type: ignore[misc]
81
+ class OpenFrame(_CommonModel[SeriesFloat]):
73
82
  """OpenFrame objects hold OpenTimeSeries in the list constituents.
74
83
 
75
84
  The intended use is to allow comparisons across these timeseries.
@@ -93,9 +102,9 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
93
102
  weights: list[float] | None = None
94
103
 
95
104
  # noinspection PyMethodParameters
96
- @field_validator("constituents") # type: ignore[misc]
97
- def _check_labels_unique( # type: ignore[misc]
98
- cls: OpenFrame, # noqa: N805
105
+ @field_validator("constituents")
106
+ def _check_labels_unique(
107
+ cls: type[OpenFrame], # noqa: N805
99
108
  tseries: list[OpenTimeSeries],
100
109
  ) -> list[OpenTimeSeries]:
101
110
  """Pydantic validator ensuring that OpenFrame labels are unique."""
@@ -240,7 +249,7 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
240
249
 
241
250
  """
242
251
  return Series(
243
- data=[self.tsdf.loc[:, d].count() for d in self.tsdf],
252
+ data=[self.tsdf[col].count() for col in self.tsdf.columns],
244
253
  index=self.tsdf.columns,
245
254
  name="observations",
246
255
  ).astype(int)
@@ -556,12 +565,14 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
556
565
 
557
566
  """
558
567
  earlier, later = self.calc_range(
559
- months_offset=months_from_last, from_dt=from_date, to_dt=to_date
568
+ months_offset=months_from_last,
569
+ from_dt=from_date,
570
+ to_dt=to_date,
560
571
  )
561
572
  if periods_in_a_year_fixed is None:
562
573
  fraction = (later - earlier).days / 365.25
563
574
  how_many = (
564
- self.tsdf.loc[cast("int", earlier) : cast("int", later)]
575
+ self.tsdf.loc[cast("Timestamp", earlier) : cast("Timestamp", later)]
565
576
  .count()
566
577
  .iloc[0]
567
578
  )
@@ -579,33 +590,36 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
579
590
  cast("tuple[str, str]", self.tsdf.iloc[:, second_column].name)[0],
580
591
  ]
581
592
 
582
- data = self.tsdf.loc[cast("int", earlier) : cast("int", later)].copy()
593
+ data = self.tsdf.loc[
594
+ cast("Timestamp", earlier) : cast("Timestamp", later)
595
+ ].copy()
583
596
 
584
597
  for rtn in cols:
585
- data[rtn, ValueType.RTRN] = log(data.loc[:, (rtn, ValueType.PRICE)]).diff()
598
+ arr = concatenate([array([nan]), diff(log(data[(rtn, ValueType.PRICE)]))])
599
+ data[rtn, ValueType.RTRN] = arr
586
600
 
587
601
  raw_one = [
588
- data.loc[:, (cols[0], ValueType.RTRN)]
602
+ data[(cols[0], ValueType.RTRN)]
589
603
  .iloc[1:day_chunk]
590
604
  .std(ddof=dlta_degr_freedms)
591
605
  * sqrt(time_factor),
592
606
  ]
593
607
  raw_two = [
594
- data.loc[:, (cols[1], ValueType.RTRN)]
608
+ data[(cols[1], ValueType.RTRN)]
595
609
  .iloc[1:day_chunk]
596
610
  .std(ddof=dlta_degr_freedms)
597
611
  * sqrt(time_factor),
598
612
  ]
599
613
  raw_cov = [
600
614
  cov(
601
- m=data.loc[:, (cols[0], ValueType.RTRN)].iloc[1:day_chunk].to_numpy(),
602
- y=data.loc[:, (cols[1], ValueType.RTRN)].iloc[1:day_chunk].to_numpy(),
615
+ m=data[(cols[0], ValueType.RTRN)].iloc[1:day_chunk].to_numpy(),
616
+ y=data[(cols[1], ValueType.RTRN)].iloc[1:day_chunk].to_numpy(),
603
617
  ddof=dlta_degr_freedms,
604
618
  )[0][1],
605
619
  ]
606
620
 
607
- r1 = data.loc[:, (cols[0], ValueType.RTRN)]
608
- r2 = data.loc[:, (cols[1], ValueType.RTRN)]
621
+ r1 = data[(cols[0], ValueType.RTRN)]
622
+ r2 = data[(cols[1], ValueType.RTRN)]
609
623
 
610
624
  alpha = 1.0 - lmbda
611
625
 
@@ -837,30 +851,30 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
837
851
 
838
852
  """
839
853
  earlier, later = self.calc_range(
840
- months_offset=months_from_last, from_dt=from_date, to_dt=to_date
854
+ months_offset=months_from_last,
855
+ from_dt=from_date,
856
+ to_dt=to_date,
841
857
  )
842
- fraction = (later - earlier).days / 365.25
858
+ fraction: float = (later - earlier).days / 365.25
843
859
 
844
860
  msg = "base_column should be a tuple[str, ValueType] or an integer."
845
861
  if isinstance(base_column, tuple):
846
- shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
847
- :,
848
- base_column,
849
- ]
862
+ shortdf = self.tsdf.loc[
863
+ cast("Timestamp", earlier) : cast("Timestamp", later)
864
+ ][base_column]
850
865
  short_item = base_column
851
866
  short_label = cast(
852
867
  "tuple[str, ValueType]",
853
- self.tsdf.loc[:, base_column].name,
868
+ self.tsdf[base_column].name,
854
869
  )[0]
855
870
  elif isinstance(base_column, int):
856
- shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].iloc[
857
- :,
858
- base_column,
859
- ]
860
- short_item = self.tsdf.iloc[
861
- :,
862
- base_column,
863
- ].name
871
+ shortdf = self.tsdf.loc[
872
+ cast("Timestamp", earlier) : cast("Timestamp", later)
873
+ ].iloc[:, base_column]
874
+ short_item = cast(
875
+ "tuple[str, ValueType]",
876
+ self.tsdf.iloc[:, base_column].name,
877
+ )
864
878
  short_label = cast("tuple[str, str]", self.tsdf.iloc[:, base_column].name)[
865
879
  0
866
880
  ]
@@ -870,17 +884,16 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
870
884
  if periods_in_a_year_fixed:
871
885
  time_factor = float(periods_in_a_year_fixed)
872
886
  else:
873
- time_factor = float(shortdf.count() / fraction)
887
+ time_factor = shortdf.count() / fraction
874
888
 
875
889
  terrors = []
876
890
  for item in self.tsdf:
877
891
  if item == short_item:
878
892
  terrors.append(0.0)
879
893
  else:
880
- longdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
881
- :,
882
- item,
883
- ]
894
+ longdf = self.tsdf.loc[
895
+ cast("Timestamp", earlier) : cast("Timestamp", later)
896
+ ][item]
884
897
  relative = longdf.ffill().pct_change() - shortdf.ffill().pct_change()
885
898
  vol = float(relative.std() * sqrt(time_factor))
886
899
  terrors.append(vol)
@@ -929,30 +942,33 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
929
942
 
930
943
  """
931
944
  earlier, later = self.calc_range(
932
- months_offset=months_from_last, from_dt=from_date, to_dt=to_date
945
+ months_offset=months_from_last,
946
+ from_dt=from_date,
947
+ to_dt=to_date,
933
948
  )
934
- fraction = (later - earlier).days / 365.25
949
+ fraction: float = (later - earlier).days / 365.25
935
950
 
936
951
  msg = "base_column should be a tuple[str, ValueType] or an integer."
937
952
  if isinstance(base_column, tuple):
938
- shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
939
- :,
940
- base_column,
941
- ]
953
+ shortdf = self.tsdf.loc[
954
+ cast("Timestamp", earlier) : cast("Timestamp", later)
955
+ ][base_column]
942
956
  short_item = base_column
943
957
  short_label = cast(
944
958
  "tuple[str, str]",
945
- self.tsdf.loc[:, base_column].name,
959
+ self.tsdf[base_column].name,
946
960
  )[0]
947
961
  elif isinstance(base_column, int):
948
- shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].iloc[
949
- :,
950
- base_column,
951
- ]
952
- short_item = self.tsdf.iloc[
953
- :,
954
- base_column,
955
- ].name
962
+ shortdf = self.tsdf.loc[
963
+ cast("Timestamp", earlier) : cast("Timestamp", later)
964
+ ].iloc[:, base_column]
965
+ short_item = cast(
966
+ "tuple[str, ValueType]",
967
+ self.tsdf.iloc[
968
+ :,
969
+ base_column,
970
+ ].name,
971
+ )
956
972
  short_label = cast("tuple[str, str]", self.tsdf.iloc[:, base_column].name)[
957
973
  0
958
974
  ]
@@ -962,17 +978,16 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
962
978
  if periods_in_a_year_fixed:
963
979
  time_factor = float(periods_in_a_year_fixed)
964
980
  else:
965
- time_factor = float(shortdf.count() / fraction)
981
+ time_factor = shortdf.count() / fraction
966
982
 
967
983
  ratios = []
968
984
  for item in self.tsdf:
969
985
  if item == short_item:
970
986
  ratios.append(0.0)
971
987
  else:
972
- longdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
973
- :,
974
- item,
975
- ]
988
+ longdf = self.tsdf.loc[
989
+ cast("Timestamp", earlier) : cast("Timestamp", later)
990
+ ][item]
976
991
  relative = longdf.ffill().pct_change() - shortdf.ffill().pct_change()
977
992
  ret = float(relative.mean() * time_factor)
978
993
  vol = float(relative.std() * sqrt(time_factor))
@@ -1030,30 +1045,33 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1030
1045
  """
1031
1046
  loss_limit: float = 0.0
1032
1047
  earlier, later = self.calc_range(
1033
- months_offset=months_from_last, from_dt=from_date, to_dt=to_date
1048
+ months_offset=months_from_last,
1049
+ from_dt=from_date,
1050
+ to_dt=to_date,
1034
1051
  )
1035
- fraction = (later - earlier).days / 365.25
1052
+ fraction: float = (later - earlier).days / 365.25
1036
1053
 
1037
1054
  msg = "base_column should be a tuple[str, ValueType] or an integer."
1038
1055
  if isinstance(base_column, tuple):
1039
- shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
1040
- :,
1041
- base_column,
1042
- ]
1056
+ shortdf = self.tsdf.loc[
1057
+ cast("Timestamp", earlier) : cast("Timestamp", later)
1058
+ ][base_column]
1043
1059
  short_item = base_column
1044
1060
  short_label = cast(
1045
1061
  "tuple[str, str]",
1046
- self.tsdf.loc[:, base_column].name,
1062
+ self.tsdf[base_column].name,
1047
1063
  )[0]
1048
1064
  elif isinstance(base_column, int):
1049
- shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].iloc[
1050
- :,
1051
- base_column,
1052
- ]
1053
- short_item = self.tsdf.iloc[
1054
- :,
1055
- base_column,
1056
- ].name
1065
+ shortdf = self.tsdf.loc[
1066
+ cast("Timestamp", earlier) : cast("Timestamp", later)
1067
+ ].iloc[:, base_column]
1068
+ short_item = cast(
1069
+ "tuple[str, ValueType]",
1070
+ self.tsdf.iloc[
1071
+ :,
1072
+ base_column,
1073
+ ].name,
1074
+ )
1057
1075
  short_label = cast("tuple[str, str]", self.tsdf.iloc[:, base_column].name)[
1058
1076
  0
1059
1077
  ]
@@ -1063,17 +1081,16 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1063
1081
  if periods_in_a_year_fixed:
1064
1082
  time_factor = float(periods_in_a_year_fixed)
1065
1083
  else:
1066
- time_factor = float(shortdf.count() / fraction)
1084
+ time_factor = shortdf.count() / fraction
1067
1085
 
1068
1086
  ratios = []
1069
1087
  for item in self.tsdf:
1070
1088
  if item == short_item:
1071
1089
  ratios.append(0.0)
1072
1090
  else:
1073
- longdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
1074
- :,
1075
- item,
1076
- ]
1091
+ longdf = self.tsdf.loc[
1092
+ cast("Timestamp", earlier) : cast("Timestamp", later)
1093
+ ][item]
1077
1094
  msg = "ratio must be one of 'up', 'down' or 'both'."
1078
1095
  if ratio == "up":
1079
1096
  uparray = (
@@ -1216,7 +1233,7 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1216
1233
  if all(vtypes):
1217
1234
  msg = "asset should be a tuple[str, ValueType] or an integer."
1218
1235
  if isinstance(asset, tuple):
1219
- y_value = self.tsdf.loc[:, asset]
1236
+ y_value = self.tsdf[asset]
1220
1237
  elif isinstance(asset, int):
1221
1238
  y_value = self.tsdf.iloc[:, asset]
1222
1239
  else:
@@ -1224,7 +1241,7 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1224
1241
 
1225
1242
  msg = "market should be a tuple[str, ValueType] or an integer."
1226
1243
  if isinstance(market, tuple):
1227
- x_value = self.tsdf.loc[:, market]
1244
+ x_value = self.tsdf[market]
1228
1245
  elif isinstance(market, int):
1229
1246
  x_value = self.tsdf.iloc[:, market]
1230
1247
  else:
@@ -1232,7 +1249,7 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1232
1249
  elif not any(vtypes):
1233
1250
  msg = "asset should be a tuple[str, ValueType] or an integer."
1234
1251
  if isinstance(asset, tuple):
1235
- y_value = self.tsdf.loc[:, asset].ffill().pct_change().iloc[1:]
1252
+ y_value = self.tsdf[asset].ffill().pct_change().iloc[1:]
1236
1253
  elif isinstance(asset, int):
1237
1254
  y_value = self.tsdf.iloc[:, asset].ffill().pct_change().iloc[1:]
1238
1255
  else:
@@ -1240,7 +1257,7 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1240
1257
  msg = "market should be a tuple[str, ValueType] or an integer."
1241
1258
 
1242
1259
  if isinstance(market, tuple):
1243
- x_value = self.tsdf.loc[:, market].ffill().pct_change().iloc[1:]
1260
+ x_value = self.tsdf[market].ffill().pct_change().iloc[1:]
1244
1261
  elif isinstance(market, int):
1245
1262
  x_value = self.tsdf.iloc[:, market].ffill().pct_change().iloc[1:]
1246
1263
  else:
@@ -1283,10 +1300,10 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1283
1300
  """
1284
1301
  msg = "y_column should be a tuple[str, ValueType] or an integer."
1285
1302
  if isinstance(y_column, tuple):
1286
- y_value = self.tsdf.loc[:, y_column].to_numpy()
1303
+ y_value = self.tsdf[y_column].to_numpy()
1287
1304
  y_label = cast(
1288
1305
  "tuple[str, str]",
1289
- self.tsdf.loc[:, y_column].name,
1306
+ self.tsdf[y_column].name,
1290
1307
  )[0]
1291
1308
  elif isinstance(y_column, int):
1292
1309
  y_value = self.tsdf.iloc[:, y_column].to_numpy()
@@ -1296,10 +1313,10 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1296
1313
 
1297
1314
  msg = "x_column should be a tuple[str, ValueType] or an integer."
1298
1315
  if isinstance(x_column, tuple):
1299
- x_value = self.tsdf.loc[:, x_column].to_numpy().reshape(-1, 1)
1316
+ x_value = self.tsdf[x_column].to_numpy().reshape(-1, 1)
1300
1317
  x_label = cast(
1301
1318
  "tuple[str, str]",
1302
- self.tsdf.loc[:, x_column].name,
1319
+ self.tsdf[x_column].name,
1303
1320
  )[0]
1304
1321
  elif isinstance(x_column, int):
1305
1322
  x_value = self.tsdf.iloc[:, x_column].to_numpy().reshape(-1, 1)
@@ -1354,7 +1371,7 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1354
1371
  if not any(vtypes):
1355
1372
  msg = "asset should be a tuple[str, ValueType] or an integer."
1356
1373
  if isinstance(asset, tuple):
1357
- asset_rtn = self.tsdf.loc[:, asset].ffill().pct_change().iloc[1:]
1374
+ asset_rtn = self.tsdf[asset].ffill().pct_change().iloc[1:]
1358
1375
  asset_rtn_mean = float(asset_rtn.mean() * self.periods_in_a_year)
1359
1376
  elif isinstance(asset, int):
1360
1377
  asset_rtn = self.tsdf.iloc[:, asset].ffill().pct_change().iloc[1:]
@@ -1364,7 +1381,7 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1364
1381
 
1365
1382
  msg = "market should be a tuple[str, ValueType] or an integer."
1366
1383
  if isinstance(market, tuple):
1367
- market_rtn = self.tsdf.loc[:, market].ffill().pct_change().iloc[1:]
1384
+ market_rtn = self.tsdf[market].ffill().pct_change().iloc[1:]
1368
1385
  market_rtn_mean = float(market_rtn.mean() * self.periods_in_a_year)
1369
1386
  elif isinstance(market, int):
1370
1387
  market_rtn = self.tsdf.iloc[:, market].ffill().pct_change().iloc[1:]
@@ -1374,7 +1391,7 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1374
1391
  elif all(vtypes):
1375
1392
  msg = "asset should be a tuple[str, ValueType] or an integer."
1376
1393
  if isinstance(asset, tuple):
1377
- asset_rtn = self.tsdf.loc[:, asset]
1394
+ asset_rtn = self.tsdf[asset]
1378
1395
  asset_rtn_mean = float(asset_rtn.mean() * self.periods_in_a_year)
1379
1396
  elif isinstance(asset, int):
1380
1397
  asset_rtn = self.tsdf.iloc[:, asset]
@@ -1384,7 +1401,7 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1384
1401
 
1385
1402
  msg = "market should be a tuple[str, ValueType] or an integer."
1386
1403
  if isinstance(market, tuple):
1387
- market_rtn = self.tsdf.loc[:, market]
1404
+ market_rtn = self.tsdf[market]
1388
1405
  market_rtn_mean = float(market_rtn.mean() * self.periods_in_a_year)
1389
1406
  elif isinstance(market, int):
1390
1407
  market_rtn = self.tsdf.iloc[:, market]
@@ -1399,7 +1416,7 @@ class OpenFrame(_CommonModel): # type: ignore[misc]
1399
1416
  beta = covariance[0, 1] / covariance[1, 1]
1400
1417
 
1401
1418
  return float(
1402
- asset_rtn_mean - riskfree_rate - beta * (market_rtn_mean - riskfree_rate)
1419
+ asset_rtn_mean - riskfree_rate - beta * (market_rtn_mean - riskfree_rate),
1403
1420
  )
1404
1421
 
1405
1422
  def make_portfolio(
openseries/owntypes.py CHANGED
@@ -12,14 +12,22 @@ from __future__ import annotations
12
12
  import datetime as dt
13
13
  from enum import Enum
14
14
  from pprint import pformat
15
- from typing import Annotated, ClassVar, Literal, Union
15
+ from typing import TYPE_CHECKING, Annotated, ClassVar, Literal, TypeAlias, TypeVar
16
16
 
17
+ from annotated_types import MinLen
17
18
  from numpy import datetime64
18
- from pandas import Timestamp
19
- from pydantic import BaseModel, Field, StringConstraints, conlist, conset
19
+ from pandas import Series, Timestamp
20
+ from pydantic import BaseModel, Field, StringConstraints
21
+
22
+ if TYPE_CHECKING:
23
+ from pandas import Series as _Series
24
+
25
+ SeriesFloat = _Series[float]
26
+ else:
27
+ SeriesFloat = Series
20
28
 
21
29
  try:
22
- from typing import Self # type: ignore[attr-defined,unused-ignore]
30
+ from typing import Self
23
31
  except ImportError: # pragma: no cover
24
32
  from typing_extensions import Self
25
33
 
@@ -27,6 +35,9 @@ except ImportError: # pragma: no cover
27
35
  __all__ = ["Self", "ValueType"]
28
36
 
29
37
 
38
+ Combo_co = TypeVar("Combo_co", float, SeriesFloat, covariant=True)
39
+
40
+
30
41
  CountryStringType = Annotated[
31
42
  str,
32
43
  StringConstraints(
@@ -38,14 +49,11 @@ CountryStringType = Annotated[
38
49
  strict=True,
39
50
  ),
40
51
  ]
41
- CountryListType = conset(
42
- item_type=CountryStringType,
43
- min_length=1,
44
- )
45
- CountriesType = Union[CountryListType, CountryStringType] # type: ignore[valid-type] # noqa: UP007
52
+ CountrySetType: TypeAlias = Annotated[set[CountryStringType], MinLen(1)]
53
+ CountriesType: TypeAlias = CountrySetType | CountryStringType
46
54
 
47
55
 
48
- class Countries(BaseModel): # type: ignore[misc]
56
+ class Countries(BaseModel):
49
57
  """Declare Countries."""
50
58
 
51
59
  countryinput: CountriesType
@@ -64,69 +72,62 @@ CurrencyStringType = Annotated[
64
72
  ]
65
73
 
66
74
 
67
- class Currency(BaseModel): # type: ignore[misc]
75
+ class Currency(BaseModel):
68
76
  """Declare Currency."""
69
77
 
70
78
  ccy: CurrencyStringType
71
79
 
72
80
 
73
- DateListType = Annotated[
74
- list[str],
75
- conset(
76
- Annotated[
77
- str,
78
- StringConstraints(
79
- pattern=r"^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$",
80
- strip_whitespace=True,
81
- strict=True,
82
- min_length=10,
83
- max_length=10,
84
- ),
85
- ],
86
- min_length=1,
81
+ DateStringType = Annotated[
82
+ str,
83
+ StringConstraints(
84
+ pattern=r"^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$",
85
+ strip_whitespace=True,
86
+ strict=True,
87
+ min_length=10,
88
+ max_length=10,
87
89
  ),
88
90
  ]
91
+ DateListType: TypeAlias = Annotated[list[DateStringType], MinLen(1)]
89
92
 
90
- ValueListType = Annotated[list[float], conlist(float, min_length=1)]
93
+ ValueListType: TypeAlias = Annotated[list[float], MinLen(1)]
91
94
 
92
95
  DaysInYearType = Annotated[int, Field(strict=True, ge=1, le=366)]
93
96
 
94
97
  DateType = str | dt.date | dt.datetime | datetime64 | Timestamp
95
98
 
96
- PlotlyLayoutType = dict[
97
- str,
99
+ PlotlyConfigType = (
98
100
  str
99
101
  | int
100
102
  | float
101
103
  | bool
102
104
  | list[str]
103
- | dict[str, str | int | float | bool | list[str]],
104
- ]
105
+ | dict[str, str | int | float | bool | list[str]]
106
+ )
107
+
108
+ PlotlyLayoutType = dict[str, PlotlyConfigType]
105
109
 
106
110
  CaptorLogoType = dict[str, str | float]
107
111
 
108
112
  LiteralJsonOutput = Literal["values", "tsdf"]
109
113
  LiteralTrunc = Literal["before", "after", "both"]
110
- LiteralLinePlotMode = Literal[
111
- "lines",
112
- "markers",
113
- "lines+markers",
114
- "lines+text",
115
- "markers+text",
116
- "lines+markers+text",
117
- None,
118
- ]
114
+ LiteralLinePlotMode = (
115
+ Literal[
116
+ "lines",
117
+ "markers",
118
+ "lines+markers",
119
+ "lines+text",
120
+ "markers+text",
121
+ "lines+markers+text",
122
+ ]
123
+ | None
124
+ )
119
125
  LiteralHowMerge = Literal["outer", "inner"]
120
126
  LiteralQuantileInterp = Literal["linear", "lower", "higher", "midpoint", "nearest"]
121
127
  LiteralBizDayFreq = Literal["B", "BME", "BQE", "BYE"]
122
- LiteralPandasReindexMethod = Literal[
123
- None,
124
- "pad",
125
- "ffill",
126
- "backfill",
127
- "bfill",
128
- "nearest",
129
- ]
128
+ LiteralPandasReindexMethod = (
129
+ Literal["pad", "ffill", "backfill", "bfill", "nearest"] | None
130
+ )
130
131
  LiteralNanMethod = Literal["fill", "drop"]
131
132
  LiteralCaptureRatio = Literal["up", "down", "both"]
132
133
  LiteralBarPlotMode = Literal["stack", "group", "overlay", "relative"]
@@ -16,7 +16,7 @@
16
16
  ],
17
17
  "responsive": true,
18
18
  "toImageButtonOptions": {
19
- "filename": "captor_plot_image",
19
+ "filename": "openseries_plot",
20
20
  "format": "png"
21
21
  }
22
22
  },