openseries 1.9.5__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/report.py CHANGED
@@ -75,7 +75,7 @@ def calendar_period_returns(
75
75
  cldr.index = Index([d.year for d in cldr.index])
76
76
  elif freq.upper() == "BQE":
77
77
  cldr.index = Index(
78
- [Timestamp(d).to_period("Q").strftime("Q%q %Y") for d in cldr.index]
78
+ [Timestamp(d).to_period("Q").strftime("Q%q %Y") for d in cldr.index],
79
79
  )
80
80
  else:
81
81
  cldr.index = Index([d.strftime("%b %y") for d in cldr.index])
@@ -266,7 +266,7 @@ def report_html(
266
266
 
267
267
  # noinspection PyTypeChecker
268
268
  rpt_df = copied.all_properties(
269
- properties=cast("list[LiteralFrameProps]", properties)
269
+ properties=cast("list[LiteralFrameProps]", properties),
270
270
  )
271
271
  alpha_frame = copied.from_deepcopy()
272
272
  alpha_frame.to_cumret()
@@ -282,7 +282,9 @@ def report_html(
282
282
  ]
283
283
  alphas.append("")
284
284
  ar = DataFrame(
285
- data=alphas, index=copied.tsdf.columns, columns=["Jensen's Alpha"]
285
+ data=alphas,
286
+ index=copied.tsdf.columns,
287
+ columns=["Jensen's Alpha"],
286
288
  ).T
287
289
  rpt_df = concat([rpt_df, ar])
288
290
  ir = copied.info_ratio_func()
@@ -294,7 +296,7 @@ def report_html(
294
296
  te_frame.resample("7D")
295
297
  with catch_warnings():
296
298
  simplefilter("ignore")
297
- te = te_frame.tracking_error_func()
299
+ te: Series[float] | Series[str] = te_frame.tracking_error_func()
298
300
  if te.hasnans:
299
301
  te = Series(
300
302
  data=[""] * te_frame.item_count,
@@ -318,7 +320,7 @@ def report_html(
318
320
  with catch_warnings():
319
321
  simplefilter("ignore")
320
322
  try:
321
- cru = crm.capture_ratio_func(ratio="both")
323
+ cru: Series[float] | Series[str] = crm.capture_ratio_func(ratio="both")
322
324
  except ZeroDivisionError as exc: # pragma: no cover
323
325
  msg = f"Capture ratio calculation error: {exc!s}" # pragma: no cover
324
326
  logger.warning(msg=msg) # pragma: no cover
@@ -358,14 +360,9 @@ def report_html(
358
360
 
359
361
  this_year = copied.last_idx.year
360
362
  this_month = copied.last_idx.month
361
- ytd = cast("Series[float]", copied.value_ret_calendar_period(year=this_year)).map(
362
- "{:.2%}".format
363
- )
363
+ ytd = copied.value_ret_calendar_period(year=this_year).map("{:.2%}".format)
364
364
  ytd.name = "Year-to-Date"
365
- mtd = cast(
366
- "Series[float]",
367
- copied.value_ret_calendar_period(year=this_year, month=this_month),
368
- ).map(
365
+ mtd = copied.value_ret_calendar_period(year=this_year, month=this_month).map(
369
366
  "{:.2%}".format,
370
367
  )
371
368
  mtd.name = "Month-to-Date"
@@ -430,7 +427,8 @@ def report_html(
430
427
 
431
428
  figure.update_layout(fig.get("layout"))
432
429
  colorway: list[str] = cast("dict[str, list[str]]", fig["layout"]).get(
433
- "colorway", []
430
+ "colorway",
431
+ [],
434
432
  )
435
433
 
436
434
  if vertical_legend:
@@ -457,7 +455,7 @@ def report_html(
457
455
  figure.update_xaxes(gridcolor="#EEEEEE", automargin=True, tickangle=-45)
458
456
  figure.update_yaxes(tickformat=".2%", gridcolor="#EEEEEE", automargin=True)
459
457
 
460
- if isinstance(title, str):
458
+ if title:
461
459
  figure.update_layout(
462
460
  {"title": {"text": f"<b>{title}</b><br>", "font": {"size": 36}}},
463
461
  )
openseries/series.py CHANGED
@@ -17,10 +17,14 @@ if TYPE_CHECKING: # pragma: no cover
17
17
  import datetime as dt
18
18
  from collections.abc import Callable
19
19
 
20
+ from numpy.typing import NDArray
21
+ from pandas import Timestamp
22
+
20
23
  from numpy import (
21
24
  append,
22
25
  array,
23
26
  cumprod,
27
+ float64,
24
28
  insert,
25
29
  isnan,
26
30
  log,
@@ -70,7 +74,7 @@ TypeOpenTimeSeries = TypeVar("TypeOpenTimeSeries", bound="OpenTimeSeries")
70
74
 
71
75
 
72
76
  # noinspection PyUnresolvedReferences,PyNestedDecorators
73
- class OpenTimeSeries(_CommonModel):
77
+ class OpenTimeSeries(_CommonModel[float]):
74
78
  """OpenTimeSeries objects are at the core of the openseries package.
75
79
 
76
80
  The intended use is to allow analyses of financial timeseries.
@@ -143,7 +147,8 @@ class OpenTimeSeries(_CommonModel):
143
147
  @FieldValidator("markets", mode="before")
144
148
  @classmethod
145
149
  def _validate_markets(
146
- cls, value: list[str] | str | None
150
+ cls,
151
+ value: list[str] | str | None,
147
152
  ) -> list[str] | str | None:
148
153
  """Pydantic validator to ensure markets field is validated."""
149
154
  msg = (
@@ -168,9 +173,6 @@ class OpenTimeSeries(_CommonModel):
168
173
  if dates_list_length != dates_set_length:
169
174
  msg = "Dates are not unique"
170
175
  raise ValueError(msg)
171
- if values_list_length < 1:
172
- msg = "There must be at least 1 value"
173
- raise ValueError(msg)
174
176
  if (
175
177
  (dates_list_length != values_list_length)
176
178
  or (len(self.tsdf.index) != self.tsdf.shape[0])
@@ -470,12 +472,13 @@ class OpenTimeSeries(_CommonModel):
470
472
  The returns of the values in the series
471
473
 
472
474
  """
475
+ # noinspection PyCallingNonCallable
473
476
  returns = self.tsdf.ffill().pct_change()
474
477
  returns.iloc[0] = 0
475
478
  self.valuetype = ValueType.RTRN
476
479
  arrays = [[self.label], [self.valuetype]]
477
480
  returns.columns = MultiIndex.from_arrays(
478
- arrays=arrays # type: ignore[arg-type]
481
+ arrays=arrays, # type: ignore[arg-type]
479
482
  )
480
483
  self.tsdf = returns.copy()
481
484
  return self
@@ -550,11 +553,16 @@ class OpenTimeSeries(_CommonModel):
550
553
  An OpenTimeSeries object
551
554
 
552
555
  """
553
- arr = array(self.values) / divider
556
+ arr: NDArray[float64] = array(self.values) / divider
554
557
 
555
558
  deltas = array([i.days for i in self.tsdf.index[1:] - self.tsdf.index[:-1]])
556
- arr = cumprod(
557
- a=insert(arr=1.0 + deltas * arr[:-1] / days_in_year, obj=0, values=1.0)
559
+ arr = cast(
560
+ "NDArray[float64]",
561
+ cumprod(
562
+ a=insert(
563
+ arr=1.0 + deltas * arr[:-1] / days_in_year, obj=0, values=1.0
564
+ ),
565
+ ),
558
566
  )
559
567
 
560
568
  self.dates = [d.strftime("%Y-%m-%d") for d in self.tsdf.index]
@@ -672,32 +680,37 @@ class OpenTimeSeries(_CommonModel):
672
680
 
673
681
  """
674
682
  earlier, later = self.calc_range(
675
- months_offset=months_from_last, from_dt=from_date, to_dt=to_date
683
+ months_offset=months_from_last,
684
+ from_dt=from_date,
685
+ to_dt=to_date,
676
686
  )
677
687
  if periods_in_a_year_fixed:
678
688
  time_factor = float(periods_in_a_year_fixed)
679
689
  else:
680
- how_many = self.tsdf.loc[
681
- cast("int", earlier) : cast("int", later),
682
- self.tsdf.columns.to_numpy()[0],
683
- ].count()
690
+ how_many = (
691
+ self.tsdf.loc[cast("Timestamp", earlier) : cast("Timestamp", later)]
692
+ .count()
693
+ .iloc[0]
694
+ )
684
695
  fraction = (later - earlier).days / 365.25
685
- time_factor = cast("int", how_many) / fraction
696
+ time_factor = how_many / fraction
686
697
 
687
- data = self.tsdf.loc[cast("int", earlier) : cast("int", later)].copy()
698
+ data = self.tsdf.loc[
699
+ cast("Timestamp", earlier) : cast("Timestamp", later)
700
+ ].copy()
688
701
 
689
702
  data[self.label, ValueType.RTRN] = log(
690
- data.loc[:, self.tsdf.columns.to_numpy()[0]]
703
+ data.loc[:, self.tsdf.columns.to_numpy()[0]],
691
704
  ).diff()
692
705
 
693
706
  rawdata = [
694
- data.loc[:, cast("int", (self.label, ValueType.RTRN))]
707
+ data[(self.label, ValueType.RTRN)]
695
708
  .iloc[1:day_chunk]
696
709
  .std(ddof=dlta_degr_freedms)
697
710
  * sqrt(time_factor),
698
711
  ]
699
712
 
700
- for item in data.loc[:, cast("int", (self.label, ValueType.RTRN))].iloc[1:]:
713
+ for item in data[(self.label, ValueType.RTRN)].iloc[1:]:
701
714
  prev = rawdata[-1]
702
715
  rawdata.append(
703
716
  sqrt(
@@ -865,6 +878,7 @@ def timeseries_chain(
865
878
 
866
879
  dates.extend([x.strftime("%Y-%m-%d") for x in new.tsdf.index])
867
880
 
881
+ # noinspection PyTypeChecker
868
882
  return back.__class__(
869
883
  timeseries_id=new.timeseries_id,
870
884
  instrument_id=new.instrument_id,
openseries/simulation.py CHANGED
@@ -60,7 +60,7 @@ def _random_generator(seed: int | None) -> Generator:
60
60
  return Generator(bit_generator=bg)
61
61
 
62
62
 
63
- class ReturnSimulation(BaseModel): # type: ignore[misc]
63
+ class ReturnSimulation(BaseModel):
64
64
  """The class ReturnSimulation allows for simulating financial timeseries.
65
65
 
66
66
  Parameters
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: openseries
3
- Version: 1.9.5
3
+ Version: 1.9.6
4
4
  Summary: Tools for analyzing financial timeseries.
5
5
  License: # BSD 3-Clause License
6
6
 
@@ -29,6 +29,7 @@ License: # BSD 3-Clause License
29
29
  however caused and on any theory of liability, whether in contract, strict liability,
30
30
  or tort (including negligence or otherwise) arising in any way out of the use of this
31
31
  software, even if advised of the possibility of such damage.
32
+ License-File: LICENSE.md
32
33
  Keywords: python,finance,fintech,data-science,timeseries,timeseries-data,timeseries-analysis,investment,investment-analysis,investing
33
34
  Author: Martin Karrin
34
35
  Author-email: martin.karrin@captor.se
@@ -119,7 +120,7 @@ _,_=series.plot_series()
119
120
 
120
121
  ### Sample output using the report_html() function:
121
122
 
122
- <img src="https://raw.githubusercontent.com/CaptorAB/openseries/master/captor_plot_image.png" alt="Two Assets Compared" width="1000" />
123
+ <img src="https://raw.githubusercontent.com/CaptorAB/openseries/master/openseries_plot.png" alt="Two Assets Compared" width="1000" />
123
124
 
124
125
  ## Development Instructions
125
126
 
@@ -0,0 +1,17 @@
1
+ openseries/__init__.py,sha256=WAh79oE-ceGG_yl4nBukkp3UPvmLk4u_GySL2xOKbxE,1375
2
+ openseries/_common_model.py,sha256=oZDTfUiQ4OG0T0RsBwTwRPHQcFpdOv1X7447hssoma4,82768
3
+ openseries/_risk.py,sha256=8XKZWWXrECo0Vd9r2kbcn4dzyPuo93DAEO8eSkv4w20,2357
4
+ openseries/datefixer.py,sha256=eVhxaFj_la_XZQuPQHvinTWEzCCn8ct_AnZEYPOpY6U,15775
5
+ openseries/frame.py,sha256=BG_Qnp0PMIZ7ZiShqTjO3Koj7Gs04n4WyzApCvtcUQY,57953
6
+ openseries/load_plotly.py,sha256=C6iQyabfi5ubSONuis3yRHb3bUktBtTDlovsDIaeHNQ,2266
7
+ openseries/owntypes.py,sha256=4IZvwl_YtoUZKlmVX65j5fp1zmfmOJLvaX8vxEIzIAY,9665
8
+ openseries/plotly_captor_logo.json,sha256=F5nhMzEyxKywtjvQqMTKgKRCJQYMDIiBgDSxdte8Clo,178
9
+ openseries/plotly_layouts.json,sha256=MvDEQuiqIhMBXBelXb1sedTOlTPheizv6NZRLeE9YS4,1431
10
+ openseries/portfoliotools.py,sha256=NqSTMlVv9Szu2usXtYzt__661VJoLsAf059ThfBr99Q,19677
11
+ openseries/report.py,sha256=pnxiEfbbkDmzj-lPcWgmYao1vtv1DUG8b73QDQKJa8o,14379
12
+ openseries/series.py,sha256=NgEZ2YPiLG55Bba48XS2zSh5dRLqz8hyGm-CGG1jNmY,29122
13
+ openseries/simulation.py,sha256=8J_iDlKjDVDiHMTWQAzHdXWxNR81pOz2RfdfUMHKSqQ,14337
14
+ openseries-1.9.6.dist-info/METADATA,sha256=GrNCO5aUnS5cuboqOsyU8sTN1YyHARF3RhFT1keNM3s,48324
15
+ openseries-1.9.6.dist-info/WHEEL,sha256=M5asmiAlL6HEcOq52Yi5mmk9KmTVjY2RDPtO4p9DMrc,88
16
+ openseries-1.9.6.dist-info/licenses/LICENSE.md,sha256=wNupG-KLsG0aTncb_SMNDh1ExtrKXlpxSJ6RC-g-SWs,1516
17
+ openseries-1.9.6.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.2.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,17 +0,0 @@
1
- openseries/__init__.py,sha256=WAh79oE-ceGG_yl4nBukkp3UPvmLk4u_GySL2xOKbxE,1375
2
- openseries/_common_model.py,sha256=Nug9DIp54q7tt0yHFEQKAZPrG9c1Oy6VpyqoRWKOC4I,85499
3
- openseries/_risk.py,sha256=8XKZWWXrECo0Vd9r2kbcn4dzyPuo93DAEO8eSkv4w20,2357
4
- openseries/datefixer.py,sha256=Z3AKLvULzy9MPQOndKhay0nGx2EgYcjVFNjT9qReoHk,15727
5
- openseries/frame.py,sha256=a3TPLdvapnvHU_wbhPO0G95UHaHlJLXnsSmm-Ti_2sw,58579
6
- openseries/load_plotly.py,sha256=C6iQyabfi5ubSONuis3yRHb3bUktBtTDlovsDIaeHNQ,2266
7
- openseries/owntypes.py,sha256=P9CKoLtjUaFiktLb_axihrlVR5bJfdDbSFJC72kQG2o,9584
8
- openseries/plotly_captor_logo.json,sha256=F5nhMzEyxKywtjvQqMTKgKRCJQYMDIiBgDSxdte8Clo,178
9
- openseries/plotly_layouts.json,sha256=MvDEQuiqIhMBXBelXb1sedTOlTPheizv6NZRLeE9YS4,1431
10
- openseries/portfoliotools.py,sha256=NMSp-dYjPRjBJpZ9W20IKDrQmBDk7e4qkTPT4QFx6io,19721
11
- openseries/report.py,sha256=iWe68o883EIU9B_t-61fl41wzTY2e6p_ZHgST2uoH3g,14393
12
- openseries/series.py,sha256=HT2U_gUhiZMhvt7hzpUPakKBEXI64Ur2WqoaUhXV11k,28925
13
- openseries/simulation.py,sha256=t2LFlAT9lcfPqqGEXOUoEgIG2gDEuGps3Qd3IgN_GLk,14359
14
- openseries-1.9.5.dist-info/LICENSE.md,sha256=wNupG-KLsG0aTncb_SMNDh1ExtrKXlpxSJ6RC-g-SWs,1516
15
- openseries-1.9.5.dist-info/METADATA,sha256=XYz8k9ujPY_MuwGjyHEyxyCgjKppszQJfmFq3tZ67U4,48301
16
- openseries-1.9.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
- openseries-1.9.5.dist-info/RECORD,,