openseries 1.7.8__py3-none-any.whl → 1.8.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/frame.py CHANGED
@@ -1,15 +1,19 @@
1
1
  """Defining the OpenFrame class."""
2
2
 
3
- # mypy: disable-error-code="index,assignment,arg-type"
3
+ # mypy: disable-error-code="index,assignment,arg-type,no-any-return"
4
4
  from __future__ import annotations
5
5
 
6
6
  from copy import deepcopy
7
7
  from functools import reduce
8
- from logging import warning
8
+ from logging import getLogger
9
9
  from typing import TYPE_CHECKING, cast
10
10
 
11
- if TYPE_CHECKING:
12
- import datetime as dt # pragma: no cover
11
+ if TYPE_CHECKING: # pragma: no cover
12
+ import datetime as dt
13
+
14
+ from statsmodels.regression.linear_model import ( # type: ignore[import-untyped,unused-ignore]
15
+ OLSResults,
16
+ )
13
17
 
14
18
  import statsmodels.api as sm # type: ignore[import-untyped,unused-ignore]
15
19
  from numpy import (
@@ -35,18 +39,12 @@ from pandas import (
35
39
  )
36
40
  from pydantic import field_validator
37
41
 
38
- # noinspection PyProtectedMember
39
- from statsmodels.regression.linear_model import ( # type: ignore[import-untyped,unused-ignore]
40
- OLSResults,
41
- )
42
- from typing_extensions import Self
43
-
44
42
  from ._common_model import _CommonModel
45
43
  from .datefixer import _do_resample_to_business_period_ends
46
- from .series import OpenTimeSeries
47
- from .types import (
44
+ from .owntypes import (
48
45
  CountriesType,
49
46
  DaysInYearType,
47
+ LabelsNotUniqueError,
50
48
  LiteralBizDayFreq,
51
49
  LiteralCaptureRatio,
52
50
  LiteralFrameProps,
@@ -56,9 +54,17 @@ from .types import (
56
54
  LiteralPandasReindexMethod,
57
55
  LiteralPortfolioWeightings,
58
56
  LiteralTrunc,
57
+ MergingResultedInEmptyError,
58
+ MixedValuetypesError,
59
+ NoWeightsError,
59
60
  OpenFramePropertiesList,
61
+ RatioInputError,
62
+ Self,
60
63
  ValueType,
61
64
  )
65
+ from .series import OpenTimeSeries
66
+
67
+ logger = getLogger(__name__)
62
68
 
63
69
  __all__ = ["OpenFrame"]
64
70
 
@@ -88,8 +94,8 @@ class OpenFrame(_CommonModel):
88
94
  weights: list[float] | None = None
89
95
 
90
96
  # noinspection PyMethodParameters
91
- @field_validator("constituents") # type: ignore[misc]
92
- def _check_labels_unique(
97
+ @field_validator("constituents")
98
+ def _check_labels_unique( # type: ignore[misc]
93
99
  cls: OpenFrame, # noqa: N805
94
100
  tseries: list[OpenTimeSeries],
95
101
  ) -> list[OpenTimeSeries]:
@@ -97,7 +103,7 @@ class OpenFrame(_CommonModel):
97
103
  labls = [x.label for x in tseries]
98
104
  if len(set(labls)) != len(labls):
99
105
  msg = "TimeSeries names/labels must be unique"
100
- raise ValueError(msg)
106
+ raise LabelsNotUniqueError(msg)
101
107
  return tseries
102
108
 
103
109
  def __init__(
@@ -139,7 +145,7 @@ class OpenFrame(_CommonModel):
139
145
  [x.tsdf for x in self.constituents],
140
146
  )
141
147
  else:
142
- warning("OpenFrame() was passed an empty list.")
148
+ logger.warning("OpenFrame() was passed an empty list.")
143
149
 
144
150
  def from_deepcopy(self: Self) -> Self:
145
151
  """Create copy of the OpenFrame object.
@@ -181,7 +187,7 @@ class OpenFrame(_CommonModel):
181
187
  [x.tsdf for x in self.constituents],
182
188
  )
183
189
 
184
- mapper = dict(zip(self.columns_lvl_zero, lvl_zero))
190
+ mapper = dict(zip(self.columns_lvl_zero, lvl_zero, strict=True))
185
191
  self.tsdf = self.tsdf.rename(columns=mapper, level=0)
186
192
 
187
193
  if self.tsdf.empty:
@@ -189,7 +195,7 @@ class OpenFrame(_CommonModel):
189
195
  "Merging OpenTimeSeries DataFrames with "
190
196
  f"argument how={how} produced an empty DataFrame."
191
197
  )
192
- raise ValueError(msg)
198
+ raise MergingResultedInEmptyError(msg)
193
199
 
194
200
  if how == "inner":
195
201
  for xerie in self.constituents:
@@ -220,7 +226,7 @@ class OpenFrame(_CommonModel):
220
226
  prop_list = [
221
227
  getattr(self, x) for x in OpenFramePropertiesList.allowed_strings
222
228
  ]
223
- return cast(DataFrame, concat(prop_list, axis="columns").T)
229
+ return cast("DataFrame", concat(prop_list, axis="columns").T)
224
230
 
225
231
  @property
226
232
  def lengths_of_items(self: Self) -> Series[int]:
@@ -384,7 +390,7 @@ class OpenFrame(_CommonModel):
384
390
  returns.iloc[0] = 0
385
391
  else:
386
392
  msg = "Mix of series types will give inconsistent results"
387
- raise ValueError(msg)
393
+ raise MixedValuetypesError(msg)
388
394
 
389
395
  returns = returns.add(1.0)
390
396
  self.tsdf = returns.cumprod(axis=0) / returns.iloc[0]
@@ -521,23 +527,25 @@ class OpenFrame(_CommonModel):
521
527
  if periods_in_a_year_fixed is None:
522
528
  fraction = (later - earlier).days / 365.25
523
529
  how_many = (
524
- self.tsdf.loc[cast(int, earlier) : cast(int, later)].count().iloc[0]
530
+ self.tsdf.loc[cast("int", earlier) : cast("int", later)]
531
+ .count()
532
+ .iloc[0]
525
533
  )
526
534
  time_factor = how_many / fraction
527
535
  else:
528
536
  time_factor = periods_in_a_year_fixed
529
537
 
530
538
  corr_label = (
531
- cast(tuple[str, str], self.tsdf.iloc[:, first_column].name)[0]
539
+ cast("tuple[str, str]", self.tsdf.iloc[:, first_column].name)[0]
532
540
  + "_VS_"
533
- + cast(tuple[str, str], self.tsdf.iloc[:, second_column].name)[0]
541
+ + cast("tuple[str, str]", self.tsdf.iloc[:, second_column].name)[0]
534
542
  )
535
543
  cols = [
536
- cast(tuple[str, str], self.tsdf.iloc[:, first_column].name)[0],
537
- cast(tuple[str, str], self.tsdf.iloc[:, second_column].name)[0],
544
+ cast("tuple[str, str]", self.tsdf.iloc[:, first_column].name)[0],
545
+ cast("tuple[str, str]", self.tsdf.iloc[:, second_column].name)[0],
538
546
  ]
539
547
 
540
- data = self.tsdf.loc[cast(int, earlier) : cast(int, later)].copy()
548
+ data = self.tsdf.loc[cast("int", earlier) : cast("int", later)].copy()
541
549
 
542
550
  for rtn in cols:
543
551
  data[rtn, ValueType.RTRN] = (
@@ -653,7 +661,7 @@ class OpenFrame(_CommonModel):
653
661
  """
654
662
  if self.weights:
655
663
  new_c, new_w = [], []
656
- for serie, weight in zip(self.constituents, self.weights):
664
+ for serie, weight in zip(self.constituents, self.weights, strict=True):
657
665
  if serie.label != lvl_zero_item:
658
666
  new_c.append(serie)
659
667
  new_w.append(weight)
@@ -709,14 +717,14 @@ class OpenFrame(_CommonModel):
709
717
  f"not truncated to same start dates.\n"
710
718
  f"{self.tsdf.head()}"
711
719
  )
712
- warning(msg=msg)
720
+ logger.warning(msg=msg)
713
721
  if len(set(self.last_indices)) != 1:
714
722
  msg = (
715
723
  f"One or more constituents still "
716
724
  f"not truncated to same end dates.\n"
717
725
  f"{self.tsdf.tail()}"
718
726
  )
719
- warning(msg=msg)
727
+ logger.warning(msg=msg)
720
728
  return self
721
729
 
722
730
  def relative(
@@ -740,9 +748,9 @@ class OpenFrame(_CommonModel):
740
748
 
741
749
  """
742
750
  rel_label = (
743
- cast(tuple[str, str], self.tsdf.iloc[:, long_column].name)[0]
751
+ cast("tuple[str, str]", self.tsdf.iloc[:, long_column].name)[0]
744
752
  + "_over_"
745
- + cast(tuple[str, str], self.tsdf.iloc[:, short_column].name)[0]
753
+ + cast("tuple[str, str]", self.tsdf.iloc[:, short_column].name)[0]
746
754
  )
747
755
  if base_zero:
748
756
  self.tsdf[rel_label, ValueType.RELRTRN] = (
@@ -796,17 +804,17 @@ class OpenFrame(_CommonModel):
796
804
 
797
805
  msg = "base_column should be a tuple[str, ValueType] or an integer."
798
806
  if isinstance(base_column, tuple):
799
- shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
807
+ shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
800
808
  :,
801
809
  base_column,
802
810
  ]
803
811
  short_item = base_column
804
812
  short_label = cast(
805
- tuple[str, ValueType],
813
+ "tuple[str, ValueType]",
806
814
  self.tsdf.loc[:, base_column].name,
807
815
  )[0]
808
816
  elif isinstance(base_column, int):
809
- shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].iloc[
817
+ shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].iloc[
810
818
  :,
811
819
  base_column,
812
820
  ]
@@ -814,7 +822,9 @@ class OpenFrame(_CommonModel):
814
822
  :,
815
823
  base_column,
816
824
  ].name
817
- short_label = cast(tuple[str, str], self.tsdf.iloc[:, base_column].name)[0]
825
+ short_label = cast("tuple[str, str]", self.tsdf.iloc[:, base_column].name)[
826
+ 0
827
+ ]
818
828
  else:
819
829
  raise TypeError(msg)
820
830
 
@@ -828,7 +838,7 @@ class OpenFrame(_CommonModel):
828
838
  if item == short_item:
829
839
  terrors.append(0.0)
830
840
  else:
831
- longdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
841
+ longdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
832
842
  :,
833
843
  item,
834
844
  ]
@@ -886,17 +896,17 @@ class OpenFrame(_CommonModel):
886
896
 
887
897
  msg = "base_column should be a tuple[str, ValueType] or an integer."
888
898
  if isinstance(base_column, tuple):
889
- shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
899
+ shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
890
900
  :,
891
901
  base_column,
892
902
  ]
893
903
  short_item = base_column
894
904
  short_label = cast(
895
- tuple[str, str],
905
+ "tuple[str, str]",
896
906
  self.tsdf.loc[:, base_column].name,
897
907
  )[0]
898
908
  elif isinstance(base_column, int):
899
- shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].iloc[
909
+ shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].iloc[
900
910
  :,
901
911
  base_column,
902
912
  ]
@@ -904,7 +914,9 @@ class OpenFrame(_CommonModel):
904
914
  :,
905
915
  base_column,
906
916
  ].name
907
- short_label = cast(tuple[str, str], self.tsdf.iloc[:, base_column].name)[0]
917
+ short_label = cast("tuple[str, str]", self.tsdf.iloc[:, base_column].name)[
918
+ 0
919
+ ]
908
920
  else:
909
921
  raise TypeError(msg)
910
922
 
@@ -918,7 +930,7 @@ class OpenFrame(_CommonModel):
918
930
  if item == short_item:
919
931
  ratios.append(0.0)
920
932
  else:
921
- longdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
933
+ longdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
922
934
  :,
923
935
  item,
924
936
  ]
@@ -938,7 +950,7 @@ class OpenFrame(_CommonModel):
938
950
  dtype="float64",
939
951
  )
940
952
 
941
- def capture_ratio_func( # noqa: C901
953
+ def capture_ratio_func(
942
954
  self: Self,
943
955
  ratio: LiteralCaptureRatio,
944
956
  base_column: tuple[str, ValueType] | int = -1,
@@ -987,17 +999,17 @@ class OpenFrame(_CommonModel):
987
999
 
988
1000
  msg = "base_column should be a tuple[str, ValueType] or an integer."
989
1001
  if isinstance(base_column, tuple):
990
- shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
1002
+ shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
991
1003
  :,
992
1004
  base_column,
993
1005
  ]
994
1006
  short_item = base_column
995
1007
  short_label = cast(
996
- tuple[str, str],
1008
+ "tuple[str, str]",
997
1009
  self.tsdf.loc[:, base_column].name,
998
1010
  )[0]
999
1011
  elif isinstance(base_column, int):
1000
- shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].iloc[
1012
+ shortdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].iloc[
1001
1013
  :,
1002
1014
  base_column,
1003
1015
  ]
@@ -1005,7 +1017,9 @@ class OpenFrame(_CommonModel):
1005
1017
  :,
1006
1018
  base_column,
1007
1019
  ].name
1008
- short_label = cast(tuple[str, str], self.tsdf.iloc[:, base_column].name)[0]
1020
+ short_label = cast("tuple[str, str]", self.tsdf.iloc[:, base_column].name)[
1021
+ 0
1022
+ ]
1009
1023
  else:
1010
1024
  raise TypeError(msg)
1011
1025
 
@@ -1019,7 +1033,7 @@ class OpenFrame(_CommonModel):
1019
1033
  if item == short_item:
1020
1034
  ratios.append(0.0)
1021
1035
  else:
1022
- longdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
1036
+ longdf = self.tsdf.loc[cast("int", earlier) : cast("int", later)].loc[
1023
1037
  :,
1024
1038
  item,
1025
1039
  ]
@@ -1119,7 +1133,7 @@ class OpenFrame(_CommonModel):
1119
1133
  (up_rtrn / up_idx_return) / (down_return / down_idx_return),
1120
1134
  )
1121
1135
  else:
1122
- raise ValueError(msg)
1136
+ raise RatioInputError(msg)
1123
1137
 
1124
1138
  if ratio == "up":
1125
1139
  resultname = f"Up Capture Ratios vs {short_label}"
@@ -1188,7 +1202,7 @@ class OpenFrame(_CommonModel):
1188
1202
  )
1189
1203
  elif isinstance(asset, int):
1190
1204
  y_value = log(
1191
- self.tsdf.iloc[:, asset] / cast(float, self.tsdf.iloc[0, asset]),
1205
+ self.tsdf.iloc[:, asset] / cast("float", self.tsdf.iloc[0, asset]),
1192
1206
  )
1193
1207
  else:
1194
1208
  raise TypeError(msg)
@@ -1200,7 +1214,8 @@ class OpenFrame(_CommonModel):
1200
1214
  )
1201
1215
  elif isinstance(market, int):
1202
1216
  x_value = log(
1203
- self.tsdf.iloc[:, market] / cast(float, self.tsdf.iloc[0, market]),
1217
+ self.tsdf.iloc[:, market]
1218
+ / cast("float", self.tsdf.iloc[0, market]),
1204
1219
  )
1205
1220
  else:
1206
1221
  raise TypeError(msg)
@@ -1248,12 +1263,12 @@ class OpenFrame(_CommonModel):
1248
1263
  if isinstance(y_column, tuple):
1249
1264
  y_value = self.tsdf.loc[:, y_column]
1250
1265
  y_label = cast(
1251
- tuple[str, str],
1266
+ "tuple[str, str]",
1252
1267
  self.tsdf.loc[:, y_column].name,
1253
1268
  )[0]
1254
1269
  elif isinstance(y_column, int):
1255
1270
  y_value = self.tsdf.iloc[:, y_column]
1256
- y_label = cast(tuple[str, str], self.tsdf.iloc[:, y_column].name)[0]
1271
+ y_label = cast("tuple[str, str]", self.tsdf.iloc[:, y_column].name)[0]
1257
1272
  else:
1258
1273
  raise TypeError(msg)
1259
1274
 
@@ -1261,12 +1276,12 @@ class OpenFrame(_CommonModel):
1261
1276
  if isinstance(x_column, tuple):
1262
1277
  x_value = self.tsdf.loc[:, x_column]
1263
1278
  x_label = cast(
1264
- tuple[str, str],
1279
+ "tuple[str, str]",
1265
1280
  self.tsdf.loc[:, x_column].name,
1266
1281
  )[0]
1267
1282
  elif isinstance(x_column, int):
1268
1283
  x_value = self.tsdf.iloc[:, x_column]
1269
- x_label = cast(tuple[str, str], self.tsdf.iloc[:, x_column].name)[0]
1284
+ x_label = cast("tuple[str, str]", self.tsdf.iloc[:, x_column].name)[0]
1270
1285
  else:
1271
1286
  raise TypeError(msg)
1272
1287
 
@@ -1274,9 +1289,9 @@ class OpenFrame(_CommonModel):
1274
1289
  if fitted_series:
1275
1290
  self.tsdf[y_label, x_label] = results.predict(x_value)
1276
1291
 
1277
- return cast(OLSResults, results)
1292
+ return cast("OLSResults", results)
1278
1293
 
1279
- def jensen_alpha( # noqa: C901
1294
+ def jensen_alpha(
1280
1295
  self: Self,
1281
1296
  asset: tuple[str, ValueType] | int,
1282
1297
  market: tuple[str, ValueType] | int,
@@ -1330,17 +1345,17 @@ class OpenFrame(_CommonModel):
1330
1345
  )
1331
1346
  elif isinstance(asset, int):
1332
1347
  asset_log = log(
1333
- self.tsdf.iloc[:, asset] / cast(float, self.tsdf.iloc[0, asset]),
1348
+ self.tsdf.iloc[:, asset] / cast("float", self.tsdf.iloc[0, asset]),
1334
1349
  )
1335
1350
  if self.yearfrac > full_year:
1336
1351
  asset_cagr = (
1337
- cast(float, self.tsdf.iloc[-1, asset])
1338
- / cast(float, self.tsdf.iloc[0, asset])
1352
+ cast("float", self.tsdf.iloc[-1, asset])
1353
+ / cast("float", self.tsdf.iloc[0, asset])
1339
1354
  ) ** (1 / self.yearfrac) - 1
1340
1355
  else:
1341
1356
  asset_cagr = (
1342
- cast(float, self.tsdf.iloc[-1, asset])
1343
- / cast(float, self.tsdf.iloc[0, asset])
1357
+ cast("float", self.tsdf.iloc[-1, asset])
1358
+ / cast("float", self.tsdf.iloc[0, asset])
1344
1359
  - 1
1345
1360
  )
1346
1361
  else:
@@ -1364,17 +1379,18 @@ class OpenFrame(_CommonModel):
1364
1379
  )
1365
1380
  elif isinstance(market, int):
1366
1381
  market_log = log(
1367
- self.tsdf.iloc[:, market] / cast(float, self.tsdf.iloc[0, market]),
1382
+ self.tsdf.iloc[:, market]
1383
+ / cast("float", self.tsdf.iloc[0, market]),
1368
1384
  )
1369
1385
  if self.yearfrac > full_year:
1370
1386
  market_cagr = (
1371
- cast(float, self.tsdf.iloc[-1, market])
1372
- / cast(float, self.tsdf.iloc[0, market])
1387
+ cast("float", self.tsdf.iloc[-1, market])
1388
+ / cast("float", self.tsdf.iloc[0, market])
1373
1389
  ) ** (1 / self.yearfrac) - 1
1374
1390
  else:
1375
1391
  market_cagr = (
1376
- cast(float, self.tsdf.iloc[-1, market])
1377
- / cast(float, self.tsdf.iloc[0, market])
1392
+ cast("float", self.tsdf.iloc[-1, market])
1393
+ / cast("float", self.tsdf.iloc[0, market])
1378
1394
  - 1
1379
1395
  )
1380
1396
  else:
@@ -1401,7 +1417,7 @@ class OpenFrame(_CommonModel):
1401
1417
  raise TypeError(msg)
1402
1418
  else:
1403
1419
  msg = "Mix of series types will give inconsistent results"
1404
- raise ValueError(msg)
1420
+ raise MixedValuetypesError(msg)
1405
1421
 
1406
1422
  covariance = cov(asset_log, market_log, ddof=dlta_degr_freedms)
1407
1423
  beta = covariance[0, 1] / covariance[1, 1]
@@ -1433,7 +1449,7 @@ class OpenFrame(_CommonModel):
1433
1449
  "OpenFrame weights property must be provided "
1434
1450
  "to run the make_portfolio method."
1435
1451
  )
1436
- raise ValueError(msg)
1452
+ raise NoWeightsError(msg)
1437
1453
 
1438
1454
  vtypes = [x == ValueType.RTRN for x in self.tsdf.columns.get_level_values(1)]
1439
1455
  if not any(vtypes):
@@ -1443,7 +1459,7 @@ class OpenFrame(_CommonModel):
1443
1459
  returns = self.tsdf.copy()
1444
1460
  else:
1445
1461
  msg = "Mix of series types will give inconsistent results"
1446
- raise ValueError(msg)
1462
+ raise MixedValuetypesError(msg)
1447
1463
 
1448
1464
  msg = "Weight strategy not implemented"
1449
1465
  if weight_strat:
@@ -1494,11 +1510,11 @@ class OpenFrame(_CommonModel):
1494
1510
 
1495
1511
  """
1496
1512
  long_label = cast(
1497
- tuple[str, str],
1513
+ "tuple[str, str]",
1498
1514
  self.tsdf.iloc[:, long_column].name,
1499
1515
  )[0]
1500
1516
  short_label = cast(
1501
- tuple[str, str],
1517
+ "tuple[str, str]",
1502
1518
  self.tsdf.iloc[:, short_column].name,
1503
1519
  )[0]
1504
1520
  ratio_label = f"{long_label} / {short_label}"
@@ -1559,8 +1575,10 @@ class OpenFrame(_CommonModel):
1559
1575
  Rolling Betas
1560
1576
 
1561
1577
  """
1562
- market_label = cast(tuple[str, str], self.tsdf.iloc[:, market_column].name)[0]
1563
- asset_label = cast(tuple[str, str], self.tsdf.iloc[:, asset_column].name)[0]
1578
+ market_label = cast("tuple[str, str]", self.tsdf.iloc[:, market_column].name)[
1579
+ 0
1580
+ ]
1581
+ asset_label = cast("tuple[str, str]", self.tsdf.iloc[:, asset_column].name)[0]
1564
1582
  beta_label = f"{asset_label} / {market_label}"
1565
1583
 
1566
1584
  rolling = (
@@ -1618,9 +1636,9 @@ class OpenFrame(_CommonModel):
1618
1636
 
1619
1637
  """
1620
1638
  corr_label = (
1621
- cast(tuple[str, str], self.tsdf.iloc[:, first_column].name)[0]
1639
+ cast("tuple[str, str]", self.tsdf.iloc[:, first_column].name)[0]
1622
1640
  + "_VS_"
1623
- + cast(tuple[str, str], self.tsdf.iloc[:, second_column].name)[0]
1641
+ + cast("tuple[str, str]", self.tsdf.iloc[:, second_column].name)[0]
1624
1642
  )
1625
1643
  first_series = (
1626
1644
  self.tsdf.iloc[:, first_column]
openseries/load_plotly.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from json import load
6
- from logging import warning
6
+ from logging import getLogger
7
7
  from pathlib import Path
8
8
  from typing import TYPE_CHECKING
9
9
 
@@ -11,7 +11,9 @@ import requests
11
11
  from requests.exceptions import ConnectionError as RequestsConnectionError
12
12
 
13
13
  if TYPE_CHECKING:
14
- from .types import CaptorLogoType, PlotlyLayoutType # pragma: no cover
14
+ from .owntypes import CaptorLogoType, PlotlyLayoutType # pragma: no cover
15
+
16
+ logger = getLogger(__name__)
15
17
 
16
18
  __all__ = ["load_plotly_dict"]
17
19
 
@@ -69,7 +71,7 @@ def load_plotly_dict(
69
71
 
70
72
  if _check_remote_file_existence(url=logo["source"]) is False:
71
73
  msg = f"Failed to add logo image from URL {logo['source']}"
72
- warning(msg)
74
+ logger.warning(msg)
73
75
  logo = {}
74
76
 
75
77
  fig["config"].update({"responsive": responsive})
@@ -10,9 +10,14 @@ from typing import Annotated, ClassVar, Literal, Union
10
10
  from numpy import datetime64
11
11
  from pandas import Timestamp
12
12
  from pydantic import BaseModel, Field, StringConstraints, conlist, conset
13
- from typing_extensions import Self
14
13
 
15
- __all__ = ["ValueType"]
14
+ try:
15
+ from typing import Self # type: ignore[attr-defined,unused-ignore]
16
+ except ImportError: # pragma: no cover
17
+ from typing_extensions import Self
18
+
19
+
20
+ __all__ = ["Self", "ValueType"]
16
21
 
17
22
 
18
23
  CountryStringType = Annotated[
@@ -30,7 +35,7 @@ CountryListType = conset(
30
35
  item_type=CountryStringType,
31
36
  min_length=1,
32
37
  )
33
- CountriesType = Union[CountryListType, CountryStringType] # type: ignore[valid-type]
38
+ CountriesType = Union[CountryListType, CountryStringType] # type: ignore[valid-type] # noqa: UP007
34
39
 
35
40
 
36
41
  class Countries(BaseModel):
@@ -262,7 +267,7 @@ class PropertiesList(list[str]):
262
267
  if len(duplicates) != 0:
263
268
  msg += f"Duplicate string(s): {list(duplicates)}."
264
269
  if len(msg) != 0:
265
- raise ValueError(msg)
270
+ raise PropertiesInputValidationError(msg)
266
271
 
267
272
 
268
273
  class OpenTimeSeriesPropertiesList(PropertiesList):
@@ -316,3 +321,59 @@ class ValueType(str, Enum):
316
321
  ROLLRTRN = "Rolling returns"
317
322
  ROLLVAR = "Rolling VaR"
318
323
  ROLLVOL = "Rolling volatility"
324
+
325
+
326
+ class MixedValuetypesError(Exception):
327
+ """Raised when provided timeseries valuetypes are not the same."""
328
+
329
+
330
+ class AtLeastOneFrameError(Exception):
331
+ """Raised when none of the possible frame inputs is provided."""
332
+
333
+
334
+ class DateAlignmentError(Exception):
335
+ """Raised when date input is not aligned with existing range."""
336
+
337
+
338
+ class NumberOfItemsAndLabelsNotSameError(Exception):
339
+ """Raised when number of labels is not matching the number of timeseries."""
340
+
341
+
342
+ class InitialValueZeroError(Exception):
343
+ """Raised when a calculation cannot be performed due to initial value(s) zero."""
344
+
345
+
346
+ class CountriesNotStringNorListStrError(Exception):
347
+ """Raised when countries argument is not provided in correct format."""
348
+
349
+
350
+ class TradingDaysNotAboveZeroError(Exception):
351
+ """Raised when trading days argument is not above zero."""
352
+
353
+
354
+ class BothStartAndEndError(Exception):
355
+ """Raised when both start and end dates are provided."""
356
+
357
+
358
+ class NoWeightsError(Exception):
359
+ """Raised when no weights are provided to function where necessary."""
360
+
361
+
362
+ class LabelsNotUniqueError(Exception):
363
+ """Raised when provided label names are not unique."""
364
+
365
+
366
+ class RatioInputError(Exception):
367
+ """Raised when ratio keyword not provided correctly."""
368
+
369
+
370
+ class MergingResultedInEmptyError(Exception):
371
+ """Raised when a merge resulted in an empty DataFrame."""
372
+
373
+
374
+ class IncorrectArgumentComboError(Exception):
375
+ """Raised when correct combination of arguments is not provided."""
376
+
377
+
378
+ class PropertiesInputValidationError(Exception):
379
+ """Raised when duplicate strings are provided."""
@@ -54,14 +54,14 @@
54
54
  "plot_bgcolor": "rgba(0, 0, 0, 0)",
55
55
  "showlegend": true,
56
56
  "title": {
57
+ "font": {
58
+ "size": 24
59
+ },
57
60
  "x": 0.5,
58
61
  "xanchor": "center",
59
62
  "y": 0.95,
60
63
  "yanchor": "top"
61
64
  },
62
- "titlefont": {
63
- "size": 24
64
- },
65
65
  "xaxis": {
66
66
  "gridcolor": "#EEEEEE",
67
67
  "tickangle": -45,