openseries 1.2.2__py3-none-any.whl → 1.2.4__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,62 +1,64 @@
1
- """
2
- Defining the OpenFrame class
3
- """
1
+ """Defining the OpenFrame class."""
4
2
  from __future__ import annotations
5
- from copy import deepcopy
3
+
6
4
  import datetime as dt
5
+ from copy import deepcopy
7
6
  from functools import reduce
8
7
  from logging import warning
9
- from typing import cast, Optional, TypeVar, Union
10
- from ffn.core import calc_mean_var_weights, calc_inv_vol_weights, calc_erc_weights
8
+ from typing import Optional, TypeVar, Union, cast
9
+
10
+ import statsmodels.api as sm
11
+ from ffn.core import calc_erc_weights, calc_inv_vol_weights, calc_mean_var_weights
11
12
  from numpy import cov, cumprod, log, sqrt
12
13
  from pandas import (
13
- concat,
14
14
  DataFrame,
15
15
  DatetimeIndex,
16
16
  Int64Dtype,
17
- merge,
18
17
  MultiIndex,
19
18
  Series,
19
+ concat,
20
+ merge,
20
21
  )
21
22
  from pydantic import BaseModel, ConfigDict, field_validator
22
- import statsmodels.api as sm
23
23
 
24
24
  # noinspection PyProtectedMember
25
25
  from statsmodels.regression.linear_model import RegressionResults
26
26
 
27
27
  from openseries.common_model import CommonModel
28
- from openseries.series import OpenTimeSeries
29
28
  from openseries.datefixer import (
30
29
  align_dataframe_to_local_cdays,
31
30
  do_resample_to_business_period_ends,
32
31
  get_calc_range,
33
32
  )
33
+ from openseries.risk import (
34
+ drawdown_details,
35
+ ewma_calc,
36
+ )
37
+ from openseries.series import OpenTimeSeries
34
38
  from openseries.types import (
35
39
  CountriesType,
36
- LiteralHowMerge,
37
40
  LiteralBizDayFreq,
38
- LiteralPandasResampleConvention,
39
- LiteralPandasReindexMethod,
40
41
  LiteralCaptureRatio,
42
+ LiteralCovMethod,
41
43
  LiteralFrameProps,
42
- LiteralOlsFitMethod,
44
+ LiteralHowMerge,
43
45
  LiteralOlsFitCovType,
46
+ LiteralOlsFitMethod,
47
+ LiteralPandasReindexMethod,
48
+ LiteralPandasResampleConvention,
44
49
  LiteralPortfolioWeightings,
45
- LiteralCovMethod,
46
50
  LiteralRiskParityMethod,
47
51
  OpenFramePropertiesList,
48
52
  ValueType,
49
53
  )
50
- from openseries.risk import (
51
- drawdown_details,
52
- ewma_calc,
53
- )
54
54
 
55
55
  TypeOpenFrame = TypeVar("TypeOpenFrame", bound="OpenFrame")
56
56
 
57
57
 
58
- class OpenFrame(BaseModel, CommonModel):
59
- """Object of the class OpenFrame.
58
+ class OpenFrame(BaseModel, CommonModel): # type: ignore[misc]
59
+
60
+ """
61
+ Object of the class OpenFrame.
60
62
 
61
63
  Parameters
62
64
  ----------
@@ -81,11 +83,12 @@ class OpenFrame(BaseModel, CommonModel):
81
83
  revalidate_instances="always",
82
84
  )
83
85
 
84
- @field_validator("constituents")
85
- def check_labels_unique( # pylint: disable=no-self-argument
86
- cls: TypeOpenFrame, tseries: list[OpenTimeSeries]
86
+ @field_validator("constituents") # type: ignore[misc]
87
+ def check_labels_unique(
88
+ cls: TypeOpenFrame, # noqa: N805
89
+ tseries: list[OpenTimeSeries],
87
90
  ) -> list[OpenTimeSeries]:
88
- """Pydantic validator ensuring that OpenFrame labels are unique"""
91
+ """Pydantic validator ensuring that OpenFrame labels are unique."""
89
92
  labls = [x.label for x in tseries]
90
93
  if len(set(labls)) != len(labls):
91
94
  raise ValueError("TimeSeries names/labels must be unique")
@@ -96,6 +99,21 @@ class OpenFrame(BaseModel, CommonModel):
96
99
  constituents: list[OpenTimeSeries],
97
100
  weights: Optional[list[float]] = None,
98
101
  ) -> None:
102
+ """
103
+ Object of the class OpenFrame.
104
+
105
+ Parameters
106
+ ----------
107
+ constituents: list[TypeOpenTimeSeries]
108
+ List of objects of Class OpenTimeSeries
109
+ weights: list[float], optional
110
+ List of weights in float format.
111
+
112
+ Returns
113
+ -------
114
+ OpenFrame
115
+ Object of the class OpenFrame
116
+ """
99
117
  super().__init__(constituents=constituents, weights=weights)
100
118
 
101
119
  self.constituents = constituents
@@ -111,20 +129,22 @@ class OpenFrame(BaseModel, CommonModel):
111
129
  warning("OpenFrame() was passed an empty list.")
112
130
 
113
131
  def from_deepcopy(self: TypeOpenFrame) -> TypeOpenFrame:
114
- """Creates a copy of an OpenFrame object
132
+ """
133
+ Create copy of the OpenFrame object.
115
134
 
116
135
  Returns
117
136
  -------
118
137
  OpenFrame
119
138
  An OpenFrame object
120
139
  """
121
-
122
140
  return deepcopy(self)
123
141
 
124
142
  def merge_series(
125
- self: TypeOpenFrame, how: LiteralHowMerge = "outer"
143
+ self: TypeOpenFrame,
144
+ how: LiteralHowMerge = "outer",
126
145
  ) -> TypeOpenFrame:
127
- """Merges the Pandas Dataframes of the constituent OpenTimeSeries
146
+ """
147
+ Merge index of Pandas Dataframes of the constituent OpenTimeSeries.
128
148
 
129
149
  Parameters
130
150
  ----------
@@ -136,7 +156,6 @@ class OpenFrame(BaseModel, CommonModel):
136
156
  OpenFrame
137
157
  An OpenFrame object
138
158
  """
139
-
140
159
  self.tsdf = reduce(
141
160
  lambda left, right: merge(
142
161
  left=left,
@@ -150,7 +169,7 @@ class OpenFrame(BaseModel, CommonModel):
150
169
  if self.tsdf.empty:
151
170
  raise ValueError(
152
171
  f"Merging OpenTimeSeries DataFrames with "
153
- f"argument how={how} produced an empty DataFrame."
172
+ f"argument how={how} produced an empty DataFrame.",
154
173
  )
155
174
  if how == "inner":
156
175
  for xerie in self.constituents:
@@ -158,9 +177,11 @@ class OpenFrame(BaseModel, CommonModel):
158
177
  return self
159
178
 
160
179
  def all_properties(
161
- self: TypeOpenFrame, properties: Optional[list[LiteralFrameProps]] = None
180
+ self: TypeOpenFrame,
181
+ properties: Optional[list[LiteralFrameProps]] = None,
162
182
  ) -> DataFrame:
163
- """Calculates the chosen timeseries properties
183
+ """
184
+ Calculate chosen timeseries properties.
164
185
 
165
186
  Parameters
166
187
  ----------
@@ -188,7 +209,8 @@ class OpenFrame(BaseModel, CommonModel):
188
209
  from_dt: Optional[dt.date] = None,
189
210
  to_dt: Optional[dt.date] = None,
190
211
  ) -> tuple[dt.date, dt.date]:
191
- """Creates user defined date range
212
+ """
213
+ Create user defined date range.
192
214
 
193
215
  Parameters
194
216
  ----------
@@ -206,14 +228,18 @@ class OpenFrame(BaseModel, CommonModel):
206
228
  Start and end date of the chosen date range
207
229
  """
208
230
  return get_calc_range(
209
- data=self.tsdf, months_offset=months_offset, from_dt=from_dt, to_dt=to_dt
231
+ data=self.tsdf,
232
+ months_offset=months_offset,
233
+ from_dt=from_dt,
234
+ to_dt=to_dt,
210
235
  )
211
236
 
212
237
  def align_index_to_local_cdays(
213
- self: TypeOpenFrame, countries: CountriesType = "SE"
238
+ self: TypeOpenFrame,
239
+ countries: CountriesType = "SE",
214
240
  ) -> TypeOpenFrame:
215
- """Changes the index of the associated Pandas DataFrame .tsdf to align with
216
- local calendar business days
241
+ """
242
+ Align the index of .tsdf with local calendar business days.
217
243
 
218
244
  Returns
219
245
  -------
@@ -226,12 +252,13 @@ class OpenFrame(BaseModel, CommonModel):
226
252
  @property
227
253
  def lengths_of_items(self: TypeOpenFrame) -> Series:
228
254
  """
255
+ Number of observations of all constituents.
256
+
229
257
  Returns
230
258
  -------
231
259
  Pandas.Series
232
260
  Number of observations of all constituents
233
261
  """
234
-
235
262
  return Series(
236
263
  data=[self.tsdf.loc[:, d].count() for d in self.tsdf],
237
264
  index=self.tsdf.columns,
@@ -242,72 +269,83 @@ class OpenFrame(BaseModel, CommonModel):
242
269
  @property
243
270
  def item_count(self: TypeOpenFrame) -> int:
244
271
  """
272
+ Number of constituents.
273
+
245
274
  Returns
246
275
  -------
247
276
  int
248
277
  Number of constituents
249
278
  """
250
-
251
279
  return len(self.constituents)
252
280
 
253
281
  @property
254
282
  def columns_lvl_zero(self: TypeOpenFrame) -> list[str]:
255
283
  """
284
+ Level 0 values of the MultiIndex columns in the .tsdf DataFrame.
285
+
256
286
  Returns
257
287
  -------
258
288
  list[str]
259
- Level 0 values of the Pandas.MultiIndex columns in the .tsdf
260
- Pandas.DataFrame
289
+ Level 0 values of the MultiIndex columns in the .tsdf DataFrame
261
290
  """
262
-
263
291
  return list(self.tsdf.columns.get_level_values(0))
264
292
 
265
293
  @property
266
294
  def columns_lvl_one(self: TypeOpenFrame) -> list[str]:
267
295
  """
296
+ Level 1 values of the MultiIndex columns in the .tsdf DataFrame.
297
+
268
298
  Returns
269
299
  -------
270
300
  list[str]
271
- Level 1 values of the Pandas.MultiIndex columns in the .tsdf
272
- Pandas.DataFrame
301
+ Level 1 values of the MultiIndex columns in the .tsdf DataFrame
273
302
  """
274
-
275
303
  return list(self.tsdf.columns.get_level_values(1))
276
304
 
277
305
  @property
278
306
  def first_indices(self: TypeOpenFrame) -> Series:
279
307
  """
308
+ The first dates in the timeseries of all constituents.
309
+
280
310
  Returns
281
311
  -------
282
312
  Pandas.Series
283
313
  The first dates in the timeseries of all constituents
284
314
  """
285
-
286
315
  return Series(
287
316
  data=[i.first_idx for i in self.constituents],
288
317
  index=self.tsdf.columns,
289
318
  name="first indices",
290
- )
319
+ dtype="datetime64[ns]",
320
+ ).dt.date
291
321
 
292
322
  @property
293
323
  def last_indices(self: TypeOpenFrame) -> Series:
294
324
  """
325
+ The last dates in the timeseries of all constituents.
326
+
295
327
  Returns
296
328
  -------
297
329
  Pandas.Series
298
330
  The last dates in the timeseries of all constituents
299
331
  """
300
-
301
332
  return Series(
302
333
  data=[i.last_idx for i in self.constituents],
303
334
  index=self.tsdf.columns,
304
335
  name="last indices",
305
- )
336
+ dtype="datetime64[ns]",
337
+ ).dt.date
306
338
 
307
339
  @property
308
340
  def span_of_days_all(self: TypeOpenFrame) -> Series:
309
341
  """
310
342
  Number of days from the first date to the last for all items in the frame.
343
+
344
+ Returns
345
+ -------
346
+ Pandas.Series
347
+ Number of days from the first date to the last for all
348
+ items in the frame.
311
349
  """
312
350
  return Series(
313
351
  data=[c.span_of_days for c in self.constituents],
@@ -322,12 +360,15 @@ class OpenFrame(BaseModel, CommonModel):
322
360
  market: Union[tuple[str, ValueType], int],
323
361
  riskfree_rate: float = 0.0,
324
362
  ) -> float:
325
- """The Jensen's measure, or Jensen's alpha, is a risk-adjusted performance
363
+ """
364
+ Jensen's alpha.
365
+
366
+ The Jensen's measure, or Jensen's alpha, is a risk-adjusted performance
326
367
  measure that represents the average return on a portfolio or investment,
327
368
  above or below that predicted by the capital asset pricing model (CAPM),
328
369
  given the portfolio's or investment's beta and the average market return.
329
370
  This metric is also commonly referred to as simply alpha.
330
- https://www.investopedia.com/terms/j/jensensmeasure.asp
371
+ https://www.investopedia.com/terms/j/jensensmeasure.asp.
331
372
 
332
373
  Parameters
333
374
  ----------
@@ -344,7 +385,8 @@ class OpenFrame(BaseModel, CommonModel):
344
385
  Jensen's alpha
345
386
  """
346
387
  if all(
347
- x == ValueType.RTRN for x in self.tsdf.columns.get_level_values(1).values
388
+ x == ValueType.RTRN
389
+ for x in self.tsdf.columns.get_level_values(1).to_numpy()
348
390
  ):
349
391
  if isinstance(asset, tuple):
350
392
  asset_log = self.tsdf.loc[:, asset]
@@ -354,7 +396,7 @@ class OpenFrame(BaseModel, CommonModel):
354
396
  asset_cagr = asset_log.mean()
355
397
  else:
356
398
  raise ValueError(
357
- "asset should be a tuple[str, ValueType] or an integer."
399
+ "asset should be a tuple[str, ValueType] or an integer.",
358
400
  )
359
401
  if isinstance(market, tuple):
360
402
  market_log = self.tsdf.loc[:, market]
@@ -364,12 +406,12 @@ class OpenFrame(BaseModel, CommonModel):
364
406
  market_cagr = market_log.mean()
365
407
  else:
366
408
  raise ValueError(
367
- "market should be a tuple[str, ValueType] or an integer."
409
+ "market should be a tuple[str, ValueType] or an integer.",
368
410
  )
369
411
  else:
370
412
  if isinstance(asset, tuple):
371
413
  asset_log = log(
372
- self.tsdf.loc[:, asset] / self.tsdf.loc[:, asset].iloc[0]
414
+ self.tsdf.loc[:, asset] / self.tsdf.loc[:, asset].iloc[0],
373
415
  )
374
416
  if self.yearfrac > 1.0:
375
417
  asset_cagr = (
@@ -394,11 +436,11 @@ class OpenFrame(BaseModel, CommonModel):
394
436
  )
395
437
  else:
396
438
  raise ValueError(
397
- "asset should be a tuple[str, ValueType] or an integer."
439
+ "asset should be a tuple[str, ValueType] or an integer.",
398
440
  )
399
441
  if isinstance(market, tuple):
400
442
  market_log = log(
401
- self.tsdf.loc[:, market] / self.tsdf.loc[:, market].iloc[0]
443
+ self.tsdf.loc[:, market] / self.tsdf.loc[:, market].iloc[0],
402
444
  )
403
445
  if self.yearfrac > 1.0:
404
446
  market_cagr = (
@@ -423,7 +465,7 @@ class OpenFrame(BaseModel, CommonModel):
423
465
  )
424
466
  else:
425
467
  raise ValueError(
426
- "market should be a tuple[str, ValueType] or an integer."
468
+ "market should be a tuple[str, ValueType] or an integer.",
427
469
  )
428
470
 
429
471
  covariance = cov(asset_log, market_log, ddof=1)
@@ -434,12 +476,13 @@ class OpenFrame(BaseModel, CommonModel):
434
476
  @property
435
477
  def worst_month(self: TypeOpenFrame) -> Series:
436
478
  """
479
+ Most negative month.
480
+
437
481
  Returns
438
482
  -------
439
483
  Pandas.Series
440
484
  Most negative month
441
485
  """
442
-
443
486
  wdf = self.tsdf.copy()
444
487
  wdf.index = DatetimeIndex(wdf.index)
445
488
  return Series(
@@ -450,12 +493,13 @@ class OpenFrame(BaseModel, CommonModel):
450
493
 
451
494
  def value_to_ret(self: TypeOpenFrame) -> TypeOpenFrame:
452
495
  """
496
+ Convert series of values into series of returns.
497
+
453
498
  Returns
454
499
  -------
455
500
  OpenFrame
456
501
  The returns of the values in the series
457
502
  """
458
-
459
503
  self.tsdf = self.tsdf.pct_change()
460
504
  self.tsdf.iloc[0] = 0
461
505
  new_labels = [ValueType.RTRN] * self.item_count
@@ -464,7 +508,8 @@ class OpenFrame(BaseModel, CommonModel):
464
508
  return self
465
509
 
466
510
  def value_to_diff(self: TypeOpenFrame, periods: int = 1) -> TypeOpenFrame:
467
- """Converts valueseries to series of their period differences
511
+ """
512
+ Convert series of values to series of their period differences.
468
513
 
469
514
  Parameters
470
515
  ----------
@@ -477,7 +522,6 @@ class OpenFrame(BaseModel, CommonModel):
477
522
  OpenFrame
478
523
  An OpenFrame object
479
524
  """
480
-
481
525
  self.tsdf = self.tsdf.diff(periods=periods)
482
526
  self.tsdf.iloc[0] = 0
483
527
  new_labels = [ValueType.RTRN] * self.item_count
@@ -486,7 +530,8 @@ class OpenFrame(BaseModel, CommonModel):
486
530
  return self
487
531
 
488
532
  def to_cumret(self: TypeOpenFrame) -> TypeOpenFrame:
489
- """Converts returnseries into cumulative valueseries
533
+ """
534
+ Convert series of returns into cumulative series of values.
490
535
 
491
536
  Returns
492
537
  -------
@@ -494,7 +539,8 @@ class OpenFrame(BaseModel, CommonModel):
494
539
  An OpenFrame object
495
540
  """
496
541
  if any(
497
- x == ValueType.PRICE for x in self.tsdf.columns.get_level_values(1).values
542
+ x == ValueType.PRICE
543
+ for x in self.tsdf.columns.get_level_values(1).to_numpy()
498
544
  ):
499
545
  self.value_to_ret()
500
546
 
@@ -506,9 +552,11 @@ class OpenFrame(BaseModel, CommonModel):
506
552
  return self
507
553
 
508
554
  def resample(
509
- self: TypeOpenFrame, freq: Union[LiteralBizDayFreq, str] = "BM"
555
+ self: TypeOpenFrame,
556
+ freq: Union[LiteralBizDayFreq, str] = "BM",
510
557
  ) -> TypeOpenFrame:
511
- """Resamples the timeseries frequency
558
+ """
559
+ Resample the timeseries frequency.
512
560
 
513
561
  Parameters
514
562
  ----------
@@ -521,7 +569,6 @@ class OpenFrame(BaseModel, CommonModel):
521
569
  OpenFrame
522
570
  An OpenFrame object
523
571
  """
524
-
525
572
  self.tsdf.index = DatetimeIndex(self.tsdf.index)
526
573
  self.tsdf = self.tsdf.resample(freq).last()
527
574
  self.tsdf.index = [d.date() for d in DatetimeIndex(self.tsdf.index)]
@@ -541,15 +588,16 @@ class OpenFrame(BaseModel, CommonModel):
541
588
  convention: LiteralPandasResampleConvention = "end",
542
589
  method: LiteralPandasReindexMethod = "nearest",
543
590
  ) -> TypeOpenFrame:
544
- """Resamples timeseries frequency to the business calendar
545
- month end dates of each period while leaving any stubs
546
- in place. Stubs will be aligned to the shortest stub
591
+ """
592
+ Resamples timeseries frequency to the business calendar month end dates.
593
+
594
+ Stubs left in place. Stubs will be aligned to the shortest stub.
547
595
 
548
596
  Parameters
549
597
  ----------
550
598
  freq: LiteralBizDayFreq, default BM
551
599
  The date offset string that sets the resampled frequency
552
- countries: list | str, default: "SE"
600
+ countries: CountriesType, default: "SE"
553
601
  (List of) country code(s) according to ISO 3166-1 alpha-2
554
602
  to create a business day calendar used for date adjustments
555
603
  convention: LiteralPandasResampleConvention, default; end
@@ -562,7 +610,6 @@ class OpenFrame(BaseModel, CommonModel):
562
610
  OpenFrame
563
611
  An OpenFrame object
564
612
  """
565
-
566
613
  head = self.tsdf.loc[self.first_indices.max()].copy()
567
614
  tail = self.tsdf.loc[self.last_indices.min()].copy()
568
615
  dates = do_resample_to_business_period_ends(
@@ -576,13 +623,14 @@ class OpenFrame(BaseModel, CommonModel):
576
623
  self.tsdf = self.tsdf.reindex([deyt.date() for deyt in dates], method=method)
577
624
  for xerie in self.constituents:
578
625
  xerie.tsdf = xerie.tsdf.reindex(
579
- [deyt.date() for deyt in dates], method=method
626
+ [deyt.date() for deyt in dates],
627
+ method=method,
580
628
  )
581
629
  return self
582
630
 
583
631
  def drawdown_details(self: TypeOpenFrame, min_periods: int = 1) -> DataFrame:
584
- """Calculates 'Max Drawdown', 'Start of drawdown', 'Date of bottom',
585
- 'Days from start to bottom', & 'Average fall per day'
632
+ """
633
+ Details of the maximum drawdown.
586
634
 
587
635
  Parameters
588
636
  ----------
@@ -592,9 +640,12 @@ class OpenFrame(BaseModel, CommonModel):
592
640
  Returns
593
641
  -------
594
642
  Pandas.DataFrame
595
- Drawdown details
643
+ Max Drawdown
644
+ Start of drawdown
645
+ Date of bottom
646
+ Days from start to bottom
647
+ Average fall per day
596
648
  """
597
-
598
649
  mxdwndf = DataFrame()
599
650
  for i in self.constituents:
600
651
  tmpdf = i.tsdf.copy()
@@ -616,15 +667,17 @@ class OpenFrame(BaseModel, CommonModel):
616
667
  to_date: Optional[dt.date] = None,
617
668
  periods_in_a_year_fixed: Optional[int] = None,
618
669
  ) -> DataFrame:
619
- """Exponentially Weighted Moving Average Model for Volatilities and
620
- Correlation.
621
- https://www.investopedia.com/articles/07/ewma.asp
670
+ """
671
+ Exponentially Weighted Moving Average Volatilities and Correlation.
672
+
673
+ Exponentially Weighted Moving Average (EWMA) for Volatilities and
674
+ Correlation. https://www.investopedia.com/articles/07/ewma.asp.
622
675
 
623
676
  Parameters
624
677
  ----------
625
678
  lmbda: float, default: 0.94
626
679
  Scaling factor to determine weighting.
627
- day_chunk: int, default: 0
680
+ day_chunk: int, default: 11
628
681
  Sampling the data which is assumed to be daily.
629
682
  dlta_degr_freedms: int, default: 0
630
683
  Variance bias factor taking the value 0 or 1.
@@ -648,7 +701,6 @@ class OpenFrame(BaseModel, CommonModel):
648
701
  Pandas.DataFrame
649
702
  Series volatilities and correlation
650
703
  """
651
-
652
704
  earlier, later = self.calc_range(months_from_last, from_date, to_date)
653
705
  if periods_in_a_year_fixed is None:
654
706
  fraction = (later - earlier).days / 365.25
@@ -680,20 +732,20 @@ class OpenFrame(BaseModel, CommonModel):
680
732
  data.loc[:, (cols[0], "Returns")]
681
733
  .iloc[1:day_chunk]
682
734
  .std(ddof=dlta_degr_freedms)
683
- * sqrt(time_factor)
735
+ * sqrt(time_factor),
684
736
  ]
685
737
  raw_two = [
686
738
  data.loc[:, (cols[1], "Returns")]
687
739
  .iloc[1:day_chunk]
688
740
  .std(ddof=dlta_degr_freedms)
689
- * sqrt(time_factor)
741
+ * sqrt(time_factor),
690
742
  ]
691
743
  raw_cov = [
692
744
  cov(
693
745
  m=data.loc[:, (cols[0], "Returns")].iloc[1:day_chunk].to_numpy(),
694
746
  y=data.loc[:, (cols[1], "Returns")].iloc[1:day_chunk].to_numpy(),
695
747
  ddof=dlta_degr_freedms,
696
- )[0][1]
748
+ )[0][1],
697
749
  ]
698
750
  raw_corr = [raw_cov[0] / (2 * raw_one[0] * raw_two[0])]
699
751
 
@@ -724,7 +776,7 @@ class OpenFrame(BaseModel, CommonModel):
724
776
  raw_corr.append(tmp_raw_corr)
725
777
 
726
778
  return DataFrame(
727
- index=cols + [corr_label],
779
+ index=[*cols, corr_label],
728
780
  columns=data.index,
729
781
  data=[raw_one, raw_two, raw_corr],
730
782
  ).T
@@ -732,6 +784,8 @@ class OpenFrame(BaseModel, CommonModel):
732
784
  @property
733
785
  def correl_matrix(self: TypeOpenFrame) -> DataFrame:
734
786
  """
787
+ Correlation matrix.
788
+
735
789
  Returns
736
790
  -------
737
791
  Pandas.DataFrame
@@ -744,9 +798,12 @@ class OpenFrame(BaseModel, CommonModel):
744
798
  return corr_matrix
745
799
 
746
800
  def add_timeseries(
747
- self: TypeOpenFrame, new_series: OpenTimeSeries
801
+ self: TypeOpenFrame,
802
+ new_series: OpenTimeSeries,
748
803
  ) -> TypeOpenFrame:
749
804
  """
805
+ To add an OpenTimeSeries object.
806
+
750
807
  Parameters
751
808
  ----------
752
809
  new_series: OpenTimeSeries
@@ -763,6 +820,8 @@ class OpenFrame(BaseModel, CommonModel):
763
820
 
764
821
  def delete_timeseries(self: TypeOpenFrame, lvl_zero_item: str) -> TypeOpenFrame:
765
822
  """
823
+ To delete an OpenTimeSeries object.
824
+
766
825
  Parameters
767
826
  ----------
768
827
  lvl_zero_item: str
@@ -785,7 +844,7 @@ class OpenFrame(BaseModel, CommonModel):
785
844
  self.constituents = [
786
845
  item for item in self.constituents if item.label != lvl_zero_item
787
846
  ]
788
- self.tsdf.drop(lvl_zero_item, axis="columns", level=0, inplace=True)
847
+ self.tsdf = self.tsdf.drop(lvl_zero_item, axis="columns", level=0)
789
848
  return self
790
849
 
791
850
  def trunc_frame(
@@ -795,7 +854,8 @@ class OpenFrame(BaseModel, CommonModel):
795
854
  before: bool = True,
796
855
  after: bool = True,
797
856
  ) -> TypeOpenFrame:
798
- """Truncates DataFrame such that all timeseries have the same time span
857
+ """
858
+ Truncate DataFrame such that all timeseries have the same time span.
799
859
 
800
860
  Parameters
801
861
  ----------
@@ -815,29 +875,30 @@ class OpenFrame(BaseModel, CommonModel):
815
875
  OpenFrame
816
876
  An OpenFrame object
817
877
  """
818
-
819
878
  if not start_cut and before:
820
879
  start_cut = self.first_indices.max()
821
880
  if not end_cut and after:
822
881
  end_cut = self.last_indices.min()
823
- self.tsdf.sort_index(inplace=True)
882
+ self.tsdf = self.tsdf.sort_index()
824
883
  self.tsdf = self.tsdf.truncate(before=start_cut, after=end_cut, copy=False)
825
884
 
826
885
  for xerie in self.constituents:
827
886
  xerie.tsdf = xerie.tsdf.truncate(
828
- before=start_cut, after=end_cut, copy=False
887
+ before=start_cut,
888
+ after=end_cut,
889
+ copy=False,
829
890
  )
830
891
  if len(set(self.first_indices)) != 1:
831
892
  warning(
832
893
  f"One or more constituents still not truncated to same "
833
894
  f"start dates.\n"
834
- f"{self.tsdf.head()}"
895
+ f"{self.tsdf.head()}",
835
896
  )
836
897
  if len(set(self.last_indices)) != 1:
837
898
  warning(
838
899
  f"One or more constituents still not truncated to same "
839
900
  f"end dates.\n"
840
- f"{self.tsdf.tail()}"
901
+ f"{self.tsdf.tail()}",
841
902
  )
842
903
  return self
843
904
 
@@ -847,8 +908,8 @@ class OpenFrame(BaseModel, CommonModel):
847
908
  short_column: int = 1,
848
909
  base_zero: bool = True,
849
910
  ) -> None:
850
- """Calculates cumulative relative return between two series.
851
- A new series is added to the frame.
911
+ """
912
+ Calculate cumulative relative return between two series.
852
913
 
853
914
  Parameters
854
915
  ----------
@@ -860,7 +921,6 @@ class OpenFrame(BaseModel, CommonModel):
860
921
  If set to False 1.0 is added to allow for a capital base and
861
922
  to allow a volatility calculation
862
923
  """
863
-
864
924
  rel_label = (
865
925
  self.tsdf.iloc[:, long_column].name[0]
866
926
  + "_over_"
@@ -883,9 +943,12 @@ class OpenFrame(BaseModel, CommonModel):
883
943
  to_date: Optional[dt.date] = None,
884
944
  periods_in_a_year_fixed: Optional[int] = None,
885
945
  ) -> Series:
886
- """Calculates the Tracking Error which is the standard deviation of the
887
- difference between the fund and its index returns. \n
888
- https://www.investopedia.com/terms/t/trackingerror.asp
946
+ """
947
+ Tracking Error.
948
+
949
+ Calculates Tracking Error which is the standard deviation of the
950
+ difference between the fund and its index returns.
951
+ https://www.investopedia.com/terms/t/trackingerror.asp.
889
952
 
890
953
  Parameters
891
954
  ----------
@@ -907,25 +970,26 @@ class OpenFrame(BaseModel, CommonModel):
907
970
  Pandas.Series
908
971
  Tracking Errors
909
972
  """
910
-
911
973
  earlier, later = self.calc_range(months_from_last, from_date, to_date)
912
974
  fraction = (later - earlier).days / 365.25
913
975
 
914
976
  if isinstance(base_column, tuple):
915
977
  shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
916
- :, base_column
978
+ :,
979
+ base_column,
917
980
  ]
918
981
  short_item = base_column
919
982
  short_label = self.tsdf.loc[:, base_column].name[0]
920
983
  elif isinstance(base_column, int):
921
984
  shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].iloc[
922
- :, base_column
985
+ :,
986
+ base_column,
923
987
  ]
924
988
  short_item = self.tsdf.iloc[:, base_column].name
925
989
  short_label = self.tsdf.iloc[:, base_column].name[0]
926
990
  else:
927
991
  raise ValueError(
928
- "base_column should be a tuple[str, ValueType] or an integer."
992
+ "base_column should be a tuple[str, ValueType] or an integer.",
929
993
  )
930
994
 
931
995
  if periods_in_a_year_fixed:
@@ -939,7 +1003,8 @@ class OpenFrame(BaseModel, CommonModel):
939
1003
  terrors.append(0.0)
940
1004
  else:
941
1005
  longdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
942
- :, item
1006
+ :,
1007
+ item,
943
1008
  ]
944
1009
  relative = 1.0 + longdf - shortdf
945
1010
  vol = float(relative.pct_change().std() * sqrt(time_factor))
@@ -960,7 +1025,10 @@ class OpenFrame(BaseModel, CommonModel):
960
1025
  to_date: Optional[dt.date] = None,
961
1026
  periods_in_a_year_fixed: Optional[int] = None,
962
1027
  ) -> Series:
963
- """The Information Ratio equals ( fund return less index return ) divided
1028
+ """
1029
+ Information Ratio.
1030
+
1031
+ The Information Ratio equals ( fund return less index return ) divided
964
1032
  by the Tracking Error. And the Tracking Error is the standard deviation of
965
1033
  the difference between the fund and its index returns.
966
1034
  The ratio is calculated using the annualized arithmetic mean of returns.
@@ -985,25 +1053,26 @@ class OpenFrame(BaseModel, CommonModel):
985
1053
  Pandas.Series
986
1054
  Information Ratios
987
1055
  """
988
-
989
1056
  earlier, later = self.calc_range(months_from_last, from_date, to_date)
990
1057
  fraction = (later - earlier).days / 365.25
991
1058
 
992
1059
  if isinstance(base_column, tuple):
993
1060
  shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
994
- :, base_column
1061
+ :,
1062
+ base_column,
995
1063
  ]
996
1064
  short_item = base_column
997
1065
  short_label = self.tsdf.loc[:, base_column].name[0]
998
1066
  elif isinstance(base_column, int):
999
1067
  shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].iloc[
1000
- :, base_column
1068
+ :,
1069
+ base_column,
1001
1070
  ]
1002
1071
  short_item = self.tsdf.iloc[:, base_column].name
1003
1072
  short_label = self.tsdf.iloc[:, base_column].name[0]
1004
1073
  else:
1005
1074
  raise ValueError(
1006
- "base_column should be a tuple[str, ValueType] or an integer."
1075
+ "base_column should be a tuple[str, ValueType] or an integer.",
1007
1076
  )
1008
1077
 
1009
1078
  if periods_in_a_year_fixed:
@@ -1017,7 +1086,8 @@ class OpenFrame(BaseModel, CommonModel):
1017
1086
  ratios.append(0.0)
1018
1087
  else:
1019
1088
  longdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
1020
- :, item
1089
+ :,
1090
+ item,
1021
1091
  ]
1022
1092
  relative = 1.0 + longdf - shortdf
1023
1093
  ret = float(relative.pct_change().mean() * time_factor)
@@ -1040,14 +1110,17 @@ class OpenFrame(BaseModel, CommonModel):
1040
1110
  to_date: Optional[dt.date] = None,
1041
1111
  periods_in_a_year_fixed: Optional[int] = None,
1042
1112
  ) -> Series:
1043
- """The Up (Down) Capture Ratio is calculated by dividing the CAGR
1113
+ """
1114
+ Capture Ratio.
1115
+
1116
+ The Up (Down) Capture Ratio is calculated by dividing the CAGR
1044
1117
  of the asset during periods that the benchmark returns are positive (negative)
1045
1118
  by the CAGR of the benchmark during the same periods.
1046
1119
  CaptureRatio.BOTH is the Up ratio divided by the Down ratio.
1047
1120
  Source: 'Capture Ratios: A Popular Method of Measuring Portfolio Performance
1048
1121
  in Practice', Don R. Cox and Delbert C. Goff, Journal of Economics and
1049
1122
  Finance Education (Vol 2 Winter 2013).
1050
- https://www.economics-finance.org/jefe/volume12-2/11ArticleCox.pdf
1123
+ https://www.economics-finance.org/jefe/volume12-2/11ArticleCox.pdf.
1051
1124
 
1052
1125
  Parameters
1053
1126
  ----------
@@ -1082,19 +1155,21 @@ class OpenFrame(BaseModel, CommonModel):
1082
1155
 
1083
1156
  if isinstance(base_column, tuple):
1084
1157
  shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
1085
- :, base_column
1158
+ :,
1159
+ base_column,
1086
1160
  ]
1087
1161
  short_item = base_column
1088
1162
  short_label = self.tsdf.loc[:, base_column].name[0]
1089
1163
  elif isinstance(base_column, int):
1090
1164
  shortdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].iloc[
1091
- :, base_column
1165
+ :,
1166
+ base_column,
1092
1167
  ]
1093
1168
  short_item = self.tsdf.iloc[:, base_column].name
1094
1169
  short_label = self.tsdf.iloc[:, base_column].name[0]
1095
1170
  else:
1096
1171
  raise ValueError(
1097
- "base_column should be a tuple[str, ValueType] or an integer."
1172
+ "base_column should be a tuple[str, ValueType] or an integer.",
1098
1173
  )
1099
1174
 
1100
1175
  if periods_in_a_year_fixed:
@@ -1108,21 +1183,22 @@ class OpenFrame(BaseModel, CommonModel):
1108
1183
  ratios.append(0.0)
1109
1184
  else:
1110
1185
  longdf = self.tsdf.loc[cast(int, earlier) : cast(int, later)].loc[
1111
- :, item
1186
+ :,
1187
+ item,
1112
1188
  ]
1113
1189
  if ratio == "up":
1114
1190
  uparray = (
1115
- longdf.pct_change()[shortdf.pct_change().values > 0.0]
1191
+ longdf.pct_change()[shortdf.pct_change().to_numpy() > 0.0]
1116
1192
  .add(1)
1117
- .values
1193
+ .to_numpy()
1118
1194
  )
1119
1195
  up_return = (
1120
1196
  uparray.prod() ** (1 / (len(uparray) / time_factor)) - 1
1121
1197
  )
1122
1198
  upidxarray = (
1123
- shortdf.pct_change()[shortdf.pct_change().values > 0.0]
1199
+ shortdf.pct_change()[shortdf.pct_change().to_numpy() > 0.0]
1124
1200
  .add(1)
1125
- .values
1201
+ .to_numpy()
1126
1202
  )
1127
1203
  up_idx_return = (
1128
1204
  upidxarray.prod() ** (1 / (len(upidxarray) / time_factor)) - 1
@@ -1130,17 +1206,17 @@ class OpenFrame(BaseModel, CommonModel):
1130
1206
  ratios.append(up_return / up_idx_return)
1131
1207
  elif ratio == "down":
1132
1208
  downarray = (
1133
- longdf.pct_change()[shortdf.pct_change().values < 0.0]
1209
+ longdf.pct_change()[shortdf.pct_change().to_numpy() < 0.0]
1134
1210
  .add(1)
1135
- .values
1211
+ .to_numpy()
1136
1212
  )
1137
1213
  down_return = (
1138
1214
  downarray.prod() ** (1 / (len(downarray) / time_factor)) - 1
1139
1215
  )
1140
1216
  downidxarray = (
1141
- shortdf.pct_change()[shortdf.pct_change().values < 0.0]
1217
+ shortdf.pct_change()[shortdf.pct_change().to_numpy() < 0.0]
1142
1218
  .add(1)
1143
- .values
1219
+ .to_numpy()
1144
1220
  )
1145
1221
  down_idx_return = (
1146
1222
  downidxarray.prod() ** (1 / (len(downidxarray) / time_factor))
@@ -1149,40 +1225,40 @@ class OpenFrame(BaseModel, CommonModel):
1149
1225
  ratios.append(down_return / down_idx_return)
1150
1226
  elif ratio == "both":
1151
1227
  uparray = (
1152
- longdf.pct_change()[shortdf.pct_change().values > 0.0]
1228
+ longdf.pct_change()[shortdf.pct_change().to_numpy() > 0.0]
1153
1229
  .add(1)
1154
- .values
1230
+ .to_numpy()
1155
1231
  )
1156
1232
  up_return = (
1157
1233
  uparray.prod() ** (1 / (len(uparray) / time_factor)) - 1
1158
1234
  )
1159
1235
  upidxarray = (
1160
- shortdf.pct_change()[shortdf.pct_change().values > 0.0]
1236
+ shortdf.pct_change()[shortdf.pct_change().to_numpy() > 0.0]
1161
1237
  .add(1)
1162
- .values
1238
+ .to_numpy()
1163
1239
  )
1164
1240
  up_idx_return = (
1165
1241
  upidxarray.prod() ** (1 / (len(upidxarray) / time_factor)) - 1
1166
1242
  )
1167
1243
  downarray = (
1168
- longdf.pct_change()[shortdf.pct_change().values < 0.0]
1244
+ longdf.pct_change()[shortdf.pct_change().to_numpy() < 0.0]
1169
1245
  .add(1)
1170
- .values
1246
+ .to_numpy()
1171
1247
  )
1172
1248
  down_return = (
1173
1249
  downarray.prod() ** (1 / (len(downarray) / time_factor)) - 1
1174
1250
  )
1175
1251
  downidxarray = (
1176
- shortdf.pct_change()[shortdf.pct_change().values < 0.0]
1252
+ shortdf.pct_change()[shortdf.pct_change().to_numpy() < 0.0]
1177
1253
  .add(1)
1178
- .values
1254
+ .to_numpy()
1179
1255
  )
1180
1256
  down_idx_return = (
1181
1257
  downidxarray.prod() ** (1 / (len(downidxarray) / time_factor))
1182
1258
  - 1
1183
1259
  )
1184
1260
  ratios.append(
1185
- (up_return / up_idx_return) / (down_return / down_idx_return)
1261
+ (up_return / up_idx_return) / (down_return / down_idx_return),
1186
1262
  )
1187
1263
 
1188
1264
  if ratio == "up":
@@ -1204,8 +1280,11 @@ class OpenFrame(BaseModel, CommonModel):
1204
1280
  asset: Union[tuple[str, ValueType], int],
1205
1281
  market: Union[tuple[str, ValueType], int],
1206
1282
  ) -> float:
1207
- """https://www.investopedia.com/terms/b/beta.asp
1208
- Calculates Beta as Co-variance of asset & market divided by Variance of market
1283
+ """
1284
+ Market Beta.
1285
+
1286
+ Calculates Beta as Co-variance of asset & market divided by Variance
1287
+ of the market. https://www.investopedia.com/terms/b/beta.asp.
1209
1288
 
1210
1289
  Parameters
1211
1290
  ----------
@@ -1221,7 +1300,7 @@ class OpenFrame(BaseModel, CommonModel):
1221
1300
  """
1222
1301
  if all(
1223
1302
  x_value == ValueType.RTRN
1224
- for x_value in self.tsdf.columns.get_level_values(1).values
1303
+ for x_value in self.tsdf.columns.get_level_values(1).to_numpy()
1225
1304
  ):
1226
1305
  if isinstance(asset, tuple):
1227
1306
  y_value = self.tsdf.loc[:, asset]
@@ -1229,7 +1308,7 @@ class OpenFrame(BaseModel, CommonModel):
1229
1308
  y_value = self.tsdf.iloc[:, asset]
1230
1309
  else:
1231
1310
  raise ValueError(
1232
- "asset should be a tuple[str, ValueType] or an integer."
1311
+ "asset should be a tuple[str, ValueType] or an integer.",
1233
1312
  )
1234
1313
  if isinstance(market, tuple):
1235
1314
  x_value = self.tsdf.loc[:, market]
@@ -1237,28 +1316,28 @@ class OpenFrame(BaseModel, CommonModel):
1237
1316
  x_value = self.tsdf.iloc[:, market]
1238
1317
  else:
1239
1318
  raise ValueError(
1240
- "market should be a tuple[str, ValueType] or an integer."
1319
+ "market should be a tuple[str, ValueType] or an integer.",
1241
1320
  )
1242
1321
  else:
1243
1322
  if isinstance(asset, tuple):
1244
1323
  y_value = log(
1245
- self.tsdf.loc[:, asset] / self.tsdf.loc[:, asset].iloc[0]
1324
+ self.tsdf.loc[:, asset] / self.tsdf.loc[:, asset].iloc[0],
1246
1325
  )
1247
1326
  elif isinstance(asset, int):
1248
1327
  y_value = log(self.tsdf.iloc[:, asset] / self.tsdf.iloc[0, asset])
1249
1328
  else:
1250
1329
  raise ValueError(
1251
- "asset should be a tuple[str, ValueType] or an integer."
1330
+ "asset should be a tuple[str, ValueType] or an integer.",
1252
1331
  )
1253
1332
  if isinstance(market, tuple):
1254
1333
  x_value = log(
1255
- self.tsdf.loc[:, market] / self.tsdf.loc[:, market].iloc[0]
1334
+ self.tsdf.loc[:, market] / self.tsdf.loc[:, market].iloc[0],
1256
1335
  )
1257
1336
  elif isinstance(market, int):
1258
1337
  x_value = log(self.tsdf.iloc[:, market] / self.tsdf.iloc[0, market])
1259
1338
  else:
1260
1339
  raise ValueError(
1261
- "market should be a tuple[str, ValueType] or an integer."
1340
+ "market should be a tuple[str, ValueType] or an integer.",
1262
1341
  )
1263
1342
 
1264
1343
  covariance = cov(y_value, x_value, ddof=1)
@@ -1274,9 +1353,12 @@ class OpenFrame(BaseModel, CommonModel):
1274
1353
  method: LiteralOlsFitMethod = "pinv",
1275
1354
  cov_type: LiteralOlsFitCovType = "nonrobust",
1276
1355
  ) -> RegressionResults:
1277
- """https://www.statsmodels.org/stable/examples/notebooks/generated/ols.html
1356
+ """
1357
+ Ordinary Least Squares fit.
1358
+
1278
1359
  Performs a linear regression and adds a new column with a fitted line
1279
1360
  using Ordinary Least Squares fit
1361
+ https://www.statsmodels.org/stable/examples/notebooks/generated/ols.html.
1280
1362
 
1281
1363
  Parameters
1282
1364
  ----------
@@ -1296,7 +1378,6 @@ class OpenFrame(BaseModel, CommonModel):
1296
1378
  RegressionResults
1297
1379
  The Statsmodels regression output
1298
1380
  """
1299
-
1300
1381
  if isinstance(y_column, tuple):
1301
1382
  y_value = self.tsdf.loc[:, y_column]
1302
1383
  y_label = self.tsdf.loc[:, y_column].name[0]
@@ -1305,7 +1386,7 @@ class OpenFrame(BaseModel, CommonModel):
1305
1386
  y_label = self.tsdf.iloc[:, y_column].name[0]
1306
1387
  else:
1307
1388
  raise ValueError(
1308
- "y_column should be a tuple[str, ValueType] or an integer."
1389
+ "y_column should be a tuple[str, ValueType] or an integer.",
1309
1390
  )
1310
1391
 
1311
1392
  if isinstance(x_column, tuple):
@@ -1316,7 +1397,7 @@ class OpenFrame(BaseModel, CommonModel):
1316
1397
  x_label = self.tsdf.iloc[:, x_column].name[0]
1317
1398
  else:
1318
1399
  raise ValueError(
1319
- "x_column should be a tuple[str, ValueType] or an integer."
1400
+ "x_column should be a tuple[str, ValueType] or an integer.",
1320
1401
  )
1321
1402
 
1322
1403
  results = sm.OLS(y_value, x_value).fit(method=method, cov_type=cov_type)
@@ -1339,7 +1420,8 @@ class OpenFrame(BaseModel, CommonModel):
1339
1420
  covar_method: LiteralCovMethod = "ledoit-wolf",
1340
1421
  options: Optional[dict[str, int]] = None,
1341
1422
  ) -> DataFrame:
1342
- """Calculates a basket timeseries based on the supplied weights
1423
+ """
1424
+ Calculate a basket timeseries based on the supplied weights.
1343
1425
 
1344
1426
  Parameters
1345
1427
  ----------
@@ -1374,11 +1456,12 @@ class OpenFrame(BaseModel, CommonModel):
1374
1456
  if self.weights is None and weight_strat is None:
1375
1457
  raise ValueError(
1376
1458
  "OpenFrame weights property must be provided to run the "
1377
- "make_portfolio method."
1459
+ "make_portfolio method.",
1378
1460
  )
1379
1461
  dframe = self.tsdf.copy()
1380
1462
  if not any(
1381
- x == ValueType.RTRN for x in self.tsdf.columns.get_level_values(1).values
1463
+ x == ValueType.RTRN
1464
+ for x in self.tsdf.columns.get_level_values(1).to_numpy()
1382
1465
  ):
1383
1466
  dframe = dframe.pct_change()
1384
1467
  dframe.iloc[0] = 0
@@ -1427,7 +1510,10 @@ class OpenFrame(BaseModel, CommonModel):
1427
1510
  observations: int = 21,
1428
1511
  periods_in_a_year_fixed: Optional[int] = None,
1429
1512
  ) -> DataFrame:
1430
- """The Information Ratio equals ( fund return less index return ) divided by
1513
+ """
1514
+ Calculate rolling Information Ratio.
1515
+
1516
+ The Information Ratio equals ( fund return less index return ) divided by
1431
1517
  the Tracking Error. And the Tracking Error is the standard deviation of the
1432
1518
  difference between the fund and its index returns.
1433
1519
 
@@ -1447,7 +1533,6 @@ class OpenFrame(BaseModel, CommonModel):
1447
1533
  Pandas.DataFrame
1448
1534
  Rolling Information Ratios
1449
1535
  """
1450
-
1451
1536
  ratio_label = (
1452
1537
  f"{self.tsdf.iloc[:, long_column].name[0]}"
1453
1538
  f" / {self.tsdf.iloc[:, short_column].name[0]}"
@@ -1467,7 +1552,8 @@ class OpenFrame(BaseModel, CommonModel):
1467
1552
  retdf = retdf.dropna().to_frame()
1468
1553
 
1469
1554
  voldf = relative.pct_change().rolling(
1470
- observations, min_periods=observations
1555
+ observations,
1556
+ min_periods=observations,
1471
1557
  ).std() * sqrt(time_factor)
1472
1558
  voldf = voldf.dropna().to_frame()
1473
1559
 
@@ -1482,8 +1568,11 @@ class OpenFrame(BaseModel, CommonModel):
1482
1568
  market_column: int = 1,
1483
1569
  observations: int = 21,
1484
1570
  ) -> DataFrame:
1485
- """https://www.investopedia.com/terms/b/beta.asp
1486
- Calculates Beta as Co-variance of asset & market divided by Variance of market
1571
+ """
1572
+ Calculate rolling Market Beta.
1573
+
1574
+ Calculates Beta as Co-variance of asset & market divided by Variance
1575
+ of the market. https://www.investopedia.com/terms/b/beta.asp.
1487
1576
 
1488
1577
  Parameters
1489
1578
  ----------
@@ -1506,10 +1595,11 @@ class OpenFrame(BaseModel, CommonModel):
1506
1595
  rolling = rolling.pct_change().rolling(observations, min_periods=observations)
1507
1596
 
1508
1597
  rcov = rolling.cov()
1509
- rcov.dropna(inplace=True)
1598
+ rcov = rcov.dropna()
1510
1599
 
1511
1600
  rollbeta = rcov.iloc[:, asset_column].xs(market_label, level=1) / rcov.iloc[
1512
- :, market_column
1601
+ :,
1602
+ market_column,
1513
1603
  ].xs(market_label, level=1)
1514
1604
  rollbeta = rollbeta.to_frame()
1515
1605
  rollbeta.index = rollbeta.index.droplevel(level=1)
@@ -1523,7 +1613,10 @@ class OpenFrame(BaseModel, CommonModel):
1523
1613
  second_column: int = 1,
1524
1614
  observations: int = 21,
1525
1615
  ) -> DataFrame:
1526
- """Calculates correlation between two series. The period with
1616
+ """
1617
+ Calculate rolling Correlation.
1618
+
1619
+ Calculates correlation between two series. The period with
1527
1620
  at least the given number of observations is the first period calculated.
1528
1621
 
1529
1622
  Parameters
@@ -1540,7 +1633,6 @@ class OpenFrame(BaseModel, CommonModel):
1540
1633
  Pandas.DataFrame
1541
1634
  Rolling Correlations
1542
1635
  """
1543
-
1544
1636
  corr_label = (
1545
1637
  self.tsdf.iloc[:, first_column].name[0]
1546
1638
  + "_VS_"