skfolio 0.0.10__py3-none-any.whl → 0.1.0__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.
Files changed (54) hide show
  1. skfolio/cluster/_hierarchical.py +5 -4
  2. skfolio/datasets/_base.py +5 -0
  3. skfolio/distance/_base.py +1 -0
  4. skfolio/distance/_distance.py +1 -0
  5. skfolio/exceptions.py +1 -0
  6. skfolio/measures/_enums.py +1 -0
  7. skfolio/measures/_measures.py +28 -0
  8. skfolio/metrics/_scorer.py +5 -0
  9. skfolio/model_selection/_combinatorial.py +5 -0
  10. skfolio/model_selection/_validation.py +5 -0
  11. skfolio/model_selection/_walk_forward.py +6 -1
  12. skfolio/moments/covariance/_base.py +7 -0
  13. skfolio/moments/covariance/_covariance.py +7 -0
  14. skfolio/moments/expected_returns/_base.py +4 -0
  15. skfolio/moments/expected_returns/_expected_returns.py +5 -0
  16. skfolio/optimization/_base.py +8 -0
  17. skfolio/optimization/cluster/_nco.py +5 -0
  18. skfolio/optimization/cluster/hierarchical/_base.py +4 -0
  19. skfolio/optimization/cluster/hierarchical/_herc.py +3 -1
  20. skfolio/optimization/cluster/hierarchical/_hrp.py +4 -0
  21. skfolio/optimization/convex/_base.py +4 -1
  22. skfolio/optimization/convex/_distributionally_robust.py +1 -0
  23. skfolio/optimization/convex/_maximum_diversification.py +2 -1
  24. skfolio/optimization/convex/_mean_risk.py +5 -2
  25. skfolio/optimization/convex/_risk_budgeting.py +3 -0
  26. skfolio/optimization/ensemble/_base.py +4 -0
  27. skfolio/optimization/ensemble/_stacking.py +4 -0
  28. skfolio/population/_population.py +1 -0
  29. skfolio/portfolio/_base.py +9 -9
  30. skfolio/portfolio/_multi_period_portfolio.py +9 -5
  31. skfolio/portfolio/_portfolio.py +3 -2
  32. skfolio/pre_selection/_pre_selection.py +1 -0
  33. skfolio/preprocessing/_returns.py +2 -1
  34. skfolio/prior/_base.py +1 -0
  35. skfolio/prior/_black_litterman.py +4 -0
  36. skfolio/prior/_empirical.py +2 -0
  37. skfolio/prior/_factor_model.py +5 -0
  38. skfolio/typing.py +1 -0
  39. skfolio/uncertainty_set/_base.py +1 -0
  40. skfolio/uncertainty_set/_bootstrap.py +5 -0
  41. skfolio/uncertainty_set/_empirical.py +5 -0
  42. skfolio/utils/bootstrap.py +3 -0
  43. skfolio/utils/equations.py +1 -0
  44. skfolio/utils/fixes/__init__.py +3 -0
  45. skfolio/utils/fixes/_dendrogram.py +389 -0
  46. skfolio/utils/sorting.py +1 -0
  47. skfolio/utils/stats.py +4 -1
  48. skfolio/utils/tools.py +4 -0
  49. {skfolio-0.0.10.dist-info → skfolio-0.1.0.dist-info}/METADATA +18 -7
  50. skfolio-0.1.0.dist-info/RECORD +81 -0
  51. skfolio-0.0.10.dist-info/RECORD +0 -79
  52. {skfolio-0.0.10.dist-info → skfolio-0.1.0.dist-info}/LICENSE +0 -0
  53. {skfolio-0.0.10.dist-info → skfolio-0.1.0.dist-info}/WHEEL +0 -0
  54. {skfolio-0.0.10.dist-info → skfolio-0.1.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  """Hierarchical Clustering estimators."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
6
 
@@ -7,13 +8,13 @@ from enum import auto
7
8
 
8
9
  import numpy as np
9
10
  import numpy.typing as npt
10
- import plotly.figure_factory as ff
11
11
  import plotly.graph_objects as go
12
12
  import scipy.cluster.hierarchy as sch
13
13
  import scipy.spatial.distance as scd
14
14
  import sklearn.base as skb
15
15
  import sklearn.utils.validation as skv
16
16
 
17
+ from skfolio.utils.fixes import create_dendrogram
17
18
  from skfolio.utils.stats import assert_is_distance, compute_optimal_n_clusters
18
19
  from skfolio.utils.tools import AutoEnum, default_asset_names
19
20
 
@@ -244,7 +245,7 @@ class HierarchicalClustering(skb.ClusterMixin, skb.BaseEstimator):
244
245
  asset_names = default_asset_names(n_assets=n_assets)
245
246
 
246
247
  if not heatmap:
247
- fig = ff.create_dendrogram(
248
+ fig = create_dendrogram(
248
249
  np.ones(1),
249
250
  distfun=lambda x: None,
250
251
  linkagefun=lambda x: linkage_matrix,
@@ -263,7 +264,7 @@ class HierarchicalClustering(skb.ClusterMixin, skb.BaseEstimator):
263
264
  return fig
264
265
 
265
266
  # Initialize figure by creating upper dendrogram
266
- fig = ff.create_dendrogram(
267
+ fig = create_dendrogram(
267
268
  np.ones(1),
268
269
  orientation="bottom",
269
270
  distfun=lambda x: None,
@@ -276,7 +277,7 @@ class HierarchicalClustering(skb.ClusterMixin, skb.BaseEstimator):
276
277
  fig["data"][i]["yaxis"] = "y2"
277
278
 
278
279
  # Create Side Dendrogram
279
- side_dendrogram = ff.create_dendrogram(
280
+ side_dendrogram = create_dendrogram(
280
281
  np.ones(1),
281
282
  orientation="right",
282
283
  distfun=lambda x: None,
skfolio/datasets/_base.py CHANGED
@@ -1,7 +1,12 @@
1
1
  """Datasets module."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
8
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
9
+ # Grisel Licensed under BSD 3 clause.
5
10
 
6
11
  import gzip
7
12
  import os
skfolio/distance/_base.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Base Distance Estimators"""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
6
 
@@ -1,5 +1,6 @@
1
1
  """Distance Estimators"""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
6
 
skfolio/exceptions.py CHANGED
@@ -3,6 +3,7 @@ The :mod:`skfolio.exceptions` module includes all custom warnings and error
3
3
  classes used across skfolio.
4
4
  """
5
5
 
6
+ # Copyright (c) 2023
6
7
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
7
8
  # License: BSD 3 clause
8
9
 
@@ -1,5 +1,6 @@
1
1
  """Module that includes all Measures enums used across `skfolio`."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
6
 
@@ -1,7 +1,10 @@
1
1
  """Module that includes all Measures functions used across `skfolio`."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Gini mean difference and OWA GMD weights features are derived
7
+ # from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
5
8
 
6
9
 
7
10
  import numpy as np
@@ -605,3 +608,28 @@ def gini_mean_difference(returns: np.ndarray) -> float:
605
608
  """
606
609
  w = owa_gmd_weights(len(returns))
607
610
  return float(w @ np.sort(returns, axis=0))
611
+
612
+
613
+ def effective_number_assets(weights: np.ndarray) -> float:
614
+ r"""
615
+ Computes the effective number of assets, defined as the inverse of the Herfindahl index [1]_:
616
+ .. math:: N_{eff} = \frac{1}{\Vert w \Vert_{2}^{2}}
617
+
618
+ It quantifies portfolio concentration, with a higher value indicating a more diversified portfolio.
619
+
620
+ Parameters
621
+ ----------
622
+ weights : ndarray of shape (n_assets,)
623
+ Weights of the assets.
624
+
625
+ Returns
626
+ -------
627
+ value : float
628
+ Effective number of assets.
629
+
630
+ References
631
+ ----------
632
+ .. [1] "Banking and Financial Institutions Law in a Nutshell".
633
+ Lovett, William Anthony (1988)
634
+ """
635
+ return 1.0 / (np.power(weights, 2).sum())
@@ -1,7 +1,12 @@
1
1
  """Scorer module"""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
8
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
9
+ # Grisel Licensed under BSD 3 clause.
5
10
 
6
11
  from collections.abc import Callable
7
12
 
@@ -1,7 +1,12 @@
1
1
  """Combinatorial module"""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
8
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
9
+ # Grisel Licensed under BSD 3 clause.
5
10
 
6
11
  import itertools
7
12
  import math
@@ -1,7 +1,12 @@
1
1
  """Model validation module."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
8
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
9
+ # Grisel Licensed under BSD 3 clause.
5
10
 
6
11
  import numpy as np
7
12
  import numpy.typing as npt
@@ -1,7 +1,12 @@
1
1
  """Walk Forward cross-validator"""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
8
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
9
+ # Grisel Licensed under BSD 3 clause.
5
10
 
6
11
  from collections.abc import Iterator
7
12
 
@@ -39,7 +44,7 @@ class WalkForward(skm.BaseCrossValidator):
39
44
 
40
45
  reduce_test : bool, default=False
41
46
  If this is set to True, the last train/test split will be returned even if the
42
- test set is partial (if it constains less observations than `test_size`),
47
+ test set is partial (if it contains less observations than `test_size`),
43
48
  otherwise it will be ignored.
44
49
  The default is `False`
45
50
 
@@ -1,5 +1,12 @@
1
1
  """Base Covariance Estimators."""
2
2
 
3
+ # Copyright (c) 2023
4
+ # Author: Hugo Delatte <delatte.hugo@gmail.com>
5
+ # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
8
+ # Grisel Licensed under BSD 3 clause.
9
+
3
10
  from abc import ABC, abstractmethod
4
11
 
5
12
  import numpy as np
@@ -1,5 +1,12 @@
1
1
  """Covariance Estimators."""
2
2
 
3
+ # Copyright (c) 2023
4
+ # Author: Hugo Delatte <delatte.hugo@gmail.com>
5
+ # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
8
+ # Grisel Licensed under BSD 3 clause.
9
+
3
10
  import numpy as np
4
11
  import numpy.typing as npt
5
12
  import pandas as pd
@@ -1,7 +1,11 @@
1
1
  """Base Expected returns estimators."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
8
+ # Grisel Licensed under BSD 3 clause.
5
9
 
6
10
  from abc import ABC, abstractmethod
7
11
 
@@ -1,7 +1,12 @@
1
1
  """Expected returns estimators."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
8
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
9
+ # Grisel Licensed under BSD 3 clause.
5
10
 
6
11
  from enum import auto
7
12
 
@@ -14,6 +14,14 @@ from skfolio.measures import RatioMeasure
14
14
  from skfolio.population import Population
15
15
  from skfolio.portfolio import Portfolio
16
16
 
17
+ # Copyright (c) 2023
18
+ # Author: Hugo Delatte <delatte.hugo@gmail.com>
19
+ # License: BSD 3 clause
20
+ # Implementation derived from:
21
+ # scikit-portfolio, Copyright (c) 2022, Carlo Nicolini, Licensed under MIT Licence.
22
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
23
+ # Grisel Licensed under BSD 3 clause.
24
+
17
25
 
18
26
  class BaseOptimization(skb.BaseEstimator, ABC):
19
27
  """Base class for all portfolio optimizations in skfolio.
@@ -1,7 +1,12 @@
1
1
  """Nested Clusters Optimization estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
8
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
9
+ # Grisel Licensed under BSD 3 clause.
5
10
 
6
11
  from copy import deepcopy
7
12
 
@@ -1,7 +1,11 @@
1
1
  """Base Hierarchical Clustering Optimization estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
8
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
5
9
 
6
10
  from abc import ABC, abstractmethod
7
11
 
@@ -1,8 +1,10 @@
1
1
  """Hierarchical Equal Risk Contribution estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
-
6
+ # The risk measure generalization and constraint features are derived
7
+ # from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
6
8
 
7
9
  import numpy as np
8
10
  import numpy.typing as npt
@@ -1,7 +1,11 @@
1
1
  """Hierarchical Risk Parity Optimization estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # The risk measure generalization and constraint features are derived
7
+ # from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
8
+
5
9
 
6
10
  import numpy as np
7
11
  import numpy.typing as npt
@@ -1,7 +1,10 @@
1
1
  """Base Convex Optimization estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # The optimization features are derived
7
+ # from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
5
8
 
6
9
  import warnings
7
10
  from abc import ABC, abstractmethod
@@ -43,7 +46,7 @@ class ObjectiveFunction(AutoEnum):
43
46
  MAXIMIZE_UTILITY : str
44
47
  Maximize the utility :math:`w^T\mu - \lambda \times risk(w)`.
45
48
 
46
- MAXIMIZE_UTILITY : str
49
+ MAXIMIZE_RATIO : str
47
50
  Maximize the ratio :math:`\frac{w^T\mu - R_{f}}{risk(w)}`.
48
51
  """
49
52
 
@@ -1,5 +1,6 @@
1
1
  """Distributionally Robust CVaR Optimization estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
6
 
@@ -1,5 +1,6 @@
1
1
  """Maximum Diversification Optimization estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
6
 
@@ -21,7 +22,7 @@ class MaximumDiversification(MeanRisk):
21
22
 
22
23
  It is a special case of the :class:`~skfolio.optimization.MeanRisk` estimator where
23
24
  the expected return from the objective function is replaced by the weighted
24
- volatilies.
25
+ volatilities.
25
26
 
26
27
  Parameters
27
28
  ----------
@@ -1,7 +1,10 @@
1
1
  """Mean Risk Optimization estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # The optimization features are derived
7
+ # from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
5
8
 
6
9
  import cvxpy as cp
7
10
  import numpy as np
@@ -931,7 +934,7 @@ class MeanRisk(ConvexOptimization):
931
934
  case ObjectiveFunction.MAXIMIZE_RATIO:
932
935
  if expected_return.is_affine():
933
936
  # Charnes-Cooper's variable transformation for Fractional
934
- # Programing problem :Max(f1/f2) with f2 linear
937
+ # Programming problem :Max(f1/f2) with f2 linear
935
938
  constraints += [
936
939
  expected_return * self._scale_constraints
937
940
  - cp.Constant(self.risk_free_rate)
@@ -941,7 +944,7 @@ class MeanRisk(ConvexOptimization):
941
944
  ]
942
945
  else:
943
946
  # Schaible's generalization of Charnes-Cooper's variable
944
- # transformation for Fractional Programing problem :Max(f1/f2)
947
+ # transformation for Fractional Programming problem :Max(f1/f2)
945
948
  # with f1 concave instead of linear: Schaible,"Parameter-free
946
949
  # Convex Equivalent and Dual Programs of Fractional Programming
947
950
  # Problems".
@@ -1,7 +1,10 @@
1
1
  """Risk Budgeting Optimization estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # The optimization features are derived
7
+ # from Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
5
8
 
6
9
  import cvxpy as cp
7
10
  import numpy as np
@@ -2,8 +2,12 @@
2
2
  Follow same implementation as Base composition from sklearn
3
3
  """
4
4
 
5
+ # Copyright (c) 2023
5
6
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
6
7
  # License: BSD 3 clause
8
+ # Implementation derived from:
9
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
10
+ # Grisel Licensed under BSD 3 clause.
7
11
 
8
12
  from abc import ABC, abstractmethod
9
13
  from contextlib import suppress
@@ -1,7 +1,11 @@
1
1
  """Stacking Optimization estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
8
+ # Grisel Licensed under BSD 3 clause.
5
9
 
6
10
  from copy import deepcopy
7
11
 
@@ -2,6 +2,7 @@
2
2
  A population is a collection of portfolios.
3
3
  """
4
4
 
5
+ # Copyright (c) 2023
5
6
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
6
7
  # License: BSD 3 clause
7
8
 
@@ -1,5 +1,6 @@
1
1
  """Base Portfolio module"""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
6
 
@@ -97,7 +98,7 @@ class BasePortfolio:
97
98
  compute domination.
98
99
  The default (`None`) is to use the list [PerfMeasure.MEAN, RiskMeasure.VARIANCE]
99
100
 
100
- annualized_factor : float, default=255.0
101
+ annualized_factor : float, default=252.0
101
102
  Factor used to annualize the below measures using the square-root rule:
102
103
 
103
104
  * Annualized Mean = Mean * factor
@@ -368,7 +369,6 @@ class BasePortfolio:
368
369
  _read_only_attrs: ClassVar[set] = {
369
370
  "returns",
370
371
  "observations",
371
- "n_observations",
372
372
  }
373
373
 
374
374
  # Arguments globally used in measures computation
@@ -400,7 +400,6 @@ class BasePortfolio:
400
400
  # public read-only
401
401
  "returns",
402
402
  "observations",
403
- "n_observations",
404
403
  # private
405
404
  "_loaded",
406
405
  # custom getter and setter
@@ -484,7 +483,7 @@ class BasePortfolio:
484
483
  observations: np.ndarray | list,
485
484
  name: str | None = None,
486
485
  tag: str | None = None,
487
- annualized_factor: float = 255.0,
486
+ annualized_factor: float = 252.0,
488
487
  fitness_measures: list[skt.Measure] | None = None,
489
488
  risk_free_rate: float = 0.0,
490
489
  compounded: bool = False,
@@ -520,7 +519,6 @@ class BasePortfolio:
520
519
  self._fitness_measures = [PerfMeasure.MEAN, RiskMeasure.VARIANCE]
521
520
  else:
522
521
  self._fitness_measures = fitness_measures
523
- self.n_observations = len(observations)
524
522
  self._loaded = True
525
523
 
526
524
  def __reduce__(self):
@@ -530,9 +528,6 @@ class BasePortfolio:
530
528
  [getattr(self, arg) for arg in args_names(self.__init__)]
531
529
  )
532
530
 
533
- def __len__(self) -> int:
534
- return len(self.observations)
535
-
536
531
  def __repr__(self) -> str:
537
532
  return f"<{type(self).__name__} {self.name}>"
538
533
 
@@ -678,6 +673,11 @@ class BasePortfolio:
678
673
  return mt.get_drawdowns(returns=self.returns, compounded=self.compounded)
679
674
 
680
675
  # Classic property
676
+ @property
677
+ def n_observations(self) -> int:
678
+ """Number of observations"""
679
+ return len(self.observations)
680
+
681
681
  @property
682
682
  def returns_df(self) -> pd.Series:
683
683
  """Portfolio returns DataFrame."""
@@ -705,7 +705,7 @@ class BasePortfolio:
705
705
  return self.__copy__()
706
706
 
707
707
  def clear(self) -> None:
708
- """CLear all measures, fitness, cumulative returns and drawdowns in slots"""
708
+ """Clear all measures, fitness, cumulative returns and drawdowns in slots"""
709
709
  attrs = ["_fitness", "_cumulative_returns", "_drawdowns"]
710
710
  for attr in attrs + list(_MEASURES_VALUES):
711
711
  delattr(self, attr)
@@ -3,6 +3,7 @@
3
3
  `MultiPeriodPortfolio` is a list of `Portfolio`.
4
4
  """
5
5
 
6
+ # Copyright (c) 2023
6
7
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
7
8
  # License: BSD 3 clause
8
9
 
@@ -45,7 +46,7 @@ class MultiPeriodPortfolio(BasePortfolio):
45
46
  compute domination.
46
47
  The default (`None`) is to use the list [PerfMeasure.MEAN, RiskMeasure.VARIANCE]
47
48
 
48
- annualized_factor : float, default=255.0
49
+ annualized_factor : float, default=252.0
49
50
  Factor used to annualize the below measures using the square-root rule:
50
51
 
51
52
  * Annualized Mean = Mean * factor
@@ -332,7 +333,7 @@ class MultiPeriodPortfolio(BasePortfolio):
332
333
  name: str | None = None,
333
334
  tag: str | None = None,
334
335
  risk_free_rate: float = 0,
335
- annualized_factor: float = 255.0,
336
+ annualized_factor: float = 252.0,
336
337
  fitness_measures: list[skt.Measure] | None = None,
337
338
  compounded: bool = False,
338
339
  min_acceptable_return: float | None = None,
@@ -368,6 +369,9 @@ class MultiPeriodPortfolio(BasePortfolio):
368
369
  self.check_observations_order = check_observations_order
369
370
  self._set_portfolios(portfolios=portfolios)
370
371
 
372
+ def __len__(self) -> int:
373
+ return len(self.portfolios)
374
+
371
375
  def __getitem__(self, key: int | slice) -> Portfolio | list[Portfolio]:
372
376
  return self._portfolios[key]
373
377
 
@@ -570,12 +574,12 @@ class MultiPeriodPortfolio(BasePortfolio):
570
574
  """
571
575
  df = super().summary(formatted=formatted)
572
576
  portfolios_number = len(self)
573
- avg_assets_per_portfolio = np.mean([len(p) for p in self])
577
+ avg_assets_per_portfolio = np.mean([p.n_assets for p in self])
574
578
  if formatted:
575
579
  portfolios_number = str(int(portfolios_number))
576
580
  avg_assets_per_portfolio = f"{avg_assets_per_portfolio:0.1f}"
577
- df["Portfolios number"] = portfolios_number
578
- df["Avg nb of assets per portfolio"] = avg_assets_per_portfolio
581
+ df["Portfolios Number"] = portfolios_number
582
+ df["Avg nb of Assets per Portfolio"] = avg_assets_per_portfolio
579
583
  return df
580
584
 
581
585
  # Public methods
@@ -4,6 +4,7 @@ It needs to be homogenous to the convex optimization problems meaning that `Port
4
4
  is the dot product of the assets weights with the assets returns.
5
5
  """
6
6
 
7
+ # Copyright (c) 2023
7
8
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
8
9
  # License: BSD 3 clause
9
10
 
@@ -132,7 +133,7 @@ class Portfolio(BasePortfolio):
132
133
  compute domination.
133
134
  The default (`None`) is to use the list [PerfMeasure.MEAN, RiskMeasure.VARIANCE]
134
135
 
135
- annualized_factor : float, default=255.0
136
+ annualized_factor : float, default=252.0
136
137
  Factor used to annualize the below measures using the square-root rule:
137
138
 
138
139
  * Annualized Mean = Mean * factor
@@ -439,7 +440,7 @@ class Portfolio(BasePortfolio):
439
440
  risk_free_rate: float = 0,
440
441
  name: str | None = None,
441
442
  tag: str | None = None,
442
- annualized_factor: float = 255,
443
+ annualized_factor: float = 252,
443
444
  fitness_measures: list[skt.Measure] | None = None,
444
445
  compounded: bool = False,
445
446
  min_acceptable_return: float | None = None,
@@ -1,5 +1,6 @@
1
1
  """pre-selection estimators module"""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
6
 
@@ -1,5 +1,6 @@
1
1
  """Preprocessing module to transform X to returns."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
6
 
@@ -81,7 +82,7 @@ def prices_to_returns(
81
82
  else:
82
83
  if not isinstance(y, pd.DataFrame):
83
84
  raise TypeError("`y` must be a DataFrame")
84
- df = pd.concat([X, y], join=join, axis=1)
85
+ df = X.join(y, how="outer")
85
86
 
86
87
  n_observations, n_assets = X.shape
87
88
 
skfolio/prior/_base.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Base Prior estimator"""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
5
6
 
@@ -1,7 +1,11 @@
1
1
  """Black & Litterman Prior Model estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+ # Implementation derived from:
7
+ # Riskfolio-Lib, Copyright (c) 2020-2023, Dany Cajas, Licensed under BSD 3 clause.
8
+ # PyPortfolioOpt, Copyright (c) 2018 Robert Andrew Martin, Licensed under MIT Licence.
5
9
 
6
10
  import numpy as np
7
11
  import numpy.typing as npt
@@ -1,7 +1,9 @@
1
1
  """Empirical Prior Model estimator."""
2
2
 
3
+ # Copyright (c) 2023
3
4
  # Author: Hugo Delatte <delatte.hugo@gmail.com>
4
5
  # License: BSD 3 clause
6
+
5
7
  import numpy as np
6
8
  import numpy.typing as npt
7
9