skfolio 0.2.2__py3-none-any.whl → 0.3.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 (52) hide show
  1. skfolio/datasets/__init__.py +2 -0
  2. skfolio/datasets/_base.py +51 -0
  3. skfolio/distance/_distance.py +15 -4
  4. skfolio/model_selection/_combinatorial.py +2 -2
  5. skfolio/model_selection/_validation.py +70 -15
  6. skfolio/model_selection/_walk_forward.py +3 -3
  7. skfolio/moments/__init__.py +2 -0
  8. skfolio/moments/covariance/__init__.py +11 -11
  9. skfolio/moments/covariance/_base.py +10 -9
  10. skfolio/moments/covariance/_denoise_covariance.py +181 -0
  11. skfolio/moments/covariance/_detone_covariance.py +158 -0
  12. skfolio/moments/covariance/_empirical_covariance.py +100 -0
  13. skfolio/moments/covariance/_ew_covariance.py +109 -0
  14. skfolio/moments/covariance/_gerber_covariance.py +157 -0
  15. skfolio/moments/covariance/_graphical_lasso_cv.py +194 -0
  16. skfolio/moments/covariance/_implied_covariance.py +454 -0
  17. skfolio/moments/covariance/_ledoit_wolf.py +140 -0
  18. skfolio/moments/covariance/_oas.py +115 -0
  19. skfolio/moments/covariance/_shrunk_covariance.py +104 -0
  20. skfolio/moments/expected_returns/__init__.py +4 -7
  21. skfolio/moments/expected_returns/_empirical_mu.py +63 -0
  22. skfolio/moments/expected_returns/_equilibrium_mu.py +124 -0
  23. skfolio/moments/expected_returns/_ew_mu.py +69 -0
  24. skfolio/moments/expected_returns/{_expected_returns.py → _shrunk_mu.py} +22 -200
  25. skfolio/optimization/cluster/_nco.py +46 -8
  26. skfolio/optimization/cluster/hierarchical/_base.py +21 -1
  27. skfolio/optimization/cluster/hierarchical/_herc.py +18 -4
  28. skfolio/optimization/cluster/hierarchical/_hrp.py +13 -4
  29. skfolio/optimization/convex/_base.py +10 -1
  30. skfolio/optimization/convex/_distributionally_robust.py +12 -2
  31. skfolio/optimization/convex/_maximum_diversification.py +9 -2
  32. skfolio/optimization/convex/_mean_risk.py +33 -6
  33. skfolio/optimization/convex/_risk_budgeting.py +5 -2
  34. skfolio/optimization/ensemble/_stacking.py +32 -9
  35. skfolio/optimization/naive/_naive.py +20 -2
  36. skfolio/population/_population.py +2 -0
  37. skfolio/prior/_base.py +1 -1
  38. skfolio/prior/_black_litterman.py +20 -2
  39. skfolio/prior/_empirical.py +38 -5
  40. skfolio/prior/_factor_model.py +44 -7
  41. skfolio/uncertainty_set/_base.py +30 -9
  42. skfolio/uncertainty_set/_bootstrap.py +26 -10
  43. skfolio/uncertainty_set/_empirical.py +25 -10
  44. skfolio/utils/stats.py +24 -3
  45. skfolio/utils/tools.py +213 -79
  46. {skfolio-0.2.2.dist-info → skfolio-0.3.0.dist-info}/METADATA +4 -3
  47. skfolio-0.3.0.dist-info/RECORD +91 -0
  48. {skfolio-0.2.2.dist-info → skfolio-0.3.0.dist-info}/WHEEL +1 -1
  49. skfolio/moments/covariance/_covariance.py +0 -1114
  50. skfolio-0.2.2.dist-info/RECORD +0 -79
  51. {skfolio-0.2.2.dist-info → skfolio-0.3.0.dist-info}/LICENSE +0 -0
  52. {skfolio-0.2.2.dist-info → skfolio-0.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,140 @@
1
+ """LedoitWolf Covariance Estimators."""
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
+
10
+ import numpy.typing as npt
11
+ import sklearn.covariance as skc
12
+
13
+ from skfolio.moments.covariance._base import BaseCovariance
14
+
15
+
16
+ class LedoitWolf(BaseCovariance, skc.LedoitWolf):
17
+ """LedoitWolf Covariance Estimator.
18
+
19
+ Ledoit-Wolf is a particular form of shrinkage, where the shrinkage
20
+ coefficient is computed using O. Ledoit and M. Wolf's formula as
21
+ described in [1]_.
22
+
23
+ Read more in `scikit-learn
24
+ <https://scikit-learn.org/stable/modules/generated/sklearn.covariance.ShrunkCovariance.html>`_.
25
+
26
+ Parameters
27
+ ----------
28
+ store_precision : bool, default=True
29
+ Specify if the estimated precision is stored.
30
+
31
+ assume_centered : bool, default=False
32
+ If True, data will not be centered before computation.
33
+ Useful when working with data whose mean is almost, but not exactly
34
+ zero.
35
+ If False (default), data will be centered before computation.
36
+
37
+ block_size : int, default=1000
38
+ Size of blocks into which the covariance matrix will be split
39
+ during its Ledoit-Wolf estimation. This is purely a memory
40
+ optimization and does not affect results.
41
+
42
+ nearest : bool, default=True
43
+ If this is set to True, the covariance is replaced by the nearest covariance
44
+ matrix that is positive definite and with a Cholesky decomposition than can be
45
+ computed. The variance is left unchanged.
46
+ A covariance matrix that is not positive definite often occurs in high
47
+ dimensional problems. It can be due to multicollinearity, floating-point
48
+ inaccuracies, or when the number of observations is smaller than the number of
49
+ assets. For more details, see :func:`~skfolio.utils.stats.cov_nearest`.
50
+ The default is `True`.
51
+
52
+ higham : bool, default=False
53
+ If this is set to True, the Higham & Nick (2002) algorithm is used to find the
54
+ nearest PD covariance, otherwise the eigenvalues are clipped to a threshold
55
+ above zeros (1e-13). The default is `False` and use the clipping method as the
56
+ Higham & Nick algorithm can be slow for large datasets.
57
+
58
+ higham_max_iteration : int, default=100
59
+ Maximum number of iteration of the Higham & Nick (2002) algorithm.
60
+ The default value is `100`.
61
+
62
+ Attributes
63
+ ----------
64
+ covariance_ : ndarray of shape (n_assets, n_assets)
65
+ Estimated covariance.
66
+
67
+ location_ : ndarray of shape (n_assets,)
68
+ Estimated location, i.e. the estimated mean.
69
+
70
+ precision_ : ndarray of shape (n_assets, n_assets)
71
+ Estimated pseudo inverse matrix.
72
+ (stored only if store_precision is True)
73
+
74
+ shrinkage_ : float
75
+ Coefficient in the convex combination used for the computation
76
+ of the shrunk estimate. Range is [0, 1].
77
+
78
+ n_features_in_ : int
79
+ Number of assets seen during `fit`.
80
+
81
+ feature_names_in_ : ndarray of shape (`n_features_in_`,)
82
+ Names of features seen during `fit`. Defined only when `X`
83
+ has feature names that are all strings.
84
+
85
+ Notes
86
+ -----
87
+ The regularised covariance is:
88
+
89
+ (1 - shrinkage) * cov + shrinkage * mu * np.identity(n_features)
90
+
91
+ where mu = trace(cov) / n_features
92
+ and shrinkage is given by the Ledoit and Wolf formula (see References)
93
+
94
+ References
95
+ ----------
96
+ .. [1] "A Well-Conditioned Estimator for Large-Dimensional Covariance Matrices".
97
+ Ledoit and Wolf, Journal of Multivariate Analysis, Volume 88, Issue 2.
98
+ February 2004, pages 365-41.
99
+ """
100
+
101
+ def __init__(
102
+ self,
103
+ store_precision=True,
104
+ assume_centered=False,
105
+ block_size=1000,
106
+ nearest: bool = True,
107
+ higham: bool = False,
108
+ higham_max_iteration: int = 100,
109
+ ):
110
+ super().__init__(
111
+ nearest=nearest,
112
+ higham=higham,
113
+ higham_max_iteration=higham_max_iteration,
114
+ )
115
+ skc.LedoitWolf.__init__(
116
+ self,
117
+ store_precision=store_precision,
118
+ assume_centered=assume_centered,
119
+ block_size=block_size,
120
+ )
121
+
122
+ def fit(self, X: npt.ArrayLike, y=None) -> "LedoitWolf":
123
+ """Fit the Ledoit-Wolf shrunk covariance model to X.
124
+
125
+ Parameters
126
+ ----------
127
+ X : array-like of shape (n_observations, n_assets)
128
+ Price returns of the assets.
129
+
130
+ y : Ignored
131
+ Not used, present for API consistency by convention.
132
+
133
+ Returns
134
+ -------
135
+ self : LedoitWolf
136
+ Fitted estimator.
137
+ """
138
+ skc.LedoitWolf.fit(self, X)
139
+ self._set_covariance(self.covariance_)
140
+ return self
@@ -0,0 +1,115 @@
1
+ """Oracle Approximating Shrinkage Covariance Estimators."""
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
+
10
+ import numpy.typing as npt
11
+ import sklearn.covariance as skc
12
+
13
+ from skfolio.moments.covariance._base import BaseCovariance
14
+
15
+
16
+ class OAS(BaseCovariance, skc.OAS):
17
+ """Oracle Approximating Shrinkage Estimator as proposed in [1]_.
18
+
19
+ Read more in `scikit-learn
20
+ <https://scikit-learn.org/stable/modules/generated/sklearn.covariance.ShrunkCovariance.html>`_.
21
+
22
+ Parameters
23
+ ----------
24
+ store_precision : bool, default=True
25
+ Specify if the estimated precision is stored.
26
+
27
+ assume_centered : bool, default=False
28
+ If True, data will not be centered before computation.
29
+ Useful when working with data whose mean is almost, but not exactly
30
+ zero.
31
+ If False (default), data will be centered before computation.
32
+
33
+ Attributes
34
+ ----------
35
+ covariance_ : ndarray of shape (n_assets, n_assets)
36
+ Estimated covariance.
37
+
38
+ location_ : ndarray of shape (n_assets,)
39
+ Estimated location, i.e. the estimated mean.
40
+
41
+ precision_ : ndarray of shape (n_assets, n_assets)
42
+ Estimated pseudo inverse matrix.
43
+ (stored only if store_precision is True)
44
+
45
+ shrinkage_ : float
46
+ Coefficient in the convex combination used for the computation
47
+ of the shrunk estimate. Range is [0, 1].
48
+
49
+ n_features_in_ : int
50
+ Number of assets seen during `fit`.
51
+
52
+ feature_names_in_ : ndarray of shape (`n_features_in_`,)
53
+ Names of features seen during `fit`. Defined only when `X`
54
+ has feature names that are all strings.
55
+
56
+ Notes
57
+ -----
58
+ The regularised covariance is:
59
+
60
+ (1 - shrinkage) * cov + shrinkage * mu * np.identity(n_features),
61
+
62
+ where mu = trace(cov) / n_features and shrinkage is given by the OAS formula
63
+ (see [1]_).
64
+
65
+ The shrinkage formulation implemented here differs from Eq. 23 in [1]_. In
66
+ the original article, formula (23) states that 2/p (p being the number of
67
+ features) is multiplied by Trace(cov*cov) in both the numerator and
68
+ denominator, but this operation is omitted because for a large p, the value
69
+ of 2/p is so small that it doesn't affect the value of the estimator.
70
+
71
+ References
72
+ ----------
73
+ .. [1] "Shrinkage algorithms for MMSE covariance estimation".
74
+ Chen, Y., Wiesel, A., Eldar, Y. C., & Hero, A. O.
75
+ IEEE Transactions on Signal Processing, 58(10), 5016-5029, 2010.
76
+ """
77
+
78
+ def __init__(
79
+ self,
80
+ store_precision=True,
81
+ assume_centered=False,
82
+ nearest: bool = True,
83
+ higham: bool = False,
84
+ higham_max_iteration: int = 100,
85
+ ):
86
+ super().__init__(
87
+ nearest=nearest,
88
+ higham=higham,
89
+ higham_max_iteration=higham_max_iteration,
90
+ )
91
+ skc.OAS.__init__(
92
+ self,
93
+ store_precision=store_precision,
94
+ assume_centered=assume_centered,
95
+ )
96
+
97
+ def fit(self, X: npt.ArrayLike, y=None) -> "OAS":
98
+ """Fit the Oracle Approximating Shrinkage covariance model to X.
99
+
100
+ Parameters
101
+ ----------
102
+ X : array-like of shape (n_observations, n_assets)
103
+ Price returns of the assets.
104
+
105
+ y : Ignored
106
+ Not used, present for API consistency by convention.
107
+
108
+ Returns
109
+ -------
110
+ self : OAS
111
+ Fitted estimator.
112
+ """
113
+ skc.OAS.fit(self, X)
114
+ self._set_covariance(self.covariance_)
115
+ return self
@@ -0,0 +1,104 @@
1
+ """Shrunk Covariance Estimators."""
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
+
10
+ import numpy.typing as npt
11
+ import sklearn.covariance as skc
12
+
13
+ from skfolio.moments.covariance._base import BaseCovariance
14
+
15
+
16
+ class ShrunkCovariance(BaseCovariance, skc.ShrunkCovariance):
17
+ """Covariance estimator with shrinkage.
18
+
19
+ Read more in `scikit-learn
20
+ <https://scikit-learn.org/stable/modules/generated/sklearn.covariance.ShrunkCovariance.html>`_.
21
+
22
+ Parameters
23
+ ----------
24
+ store_precision : bool, default=True
25
+ Specify if the estimated precision is stored.
26
+
27
+ assume_centered : bool, default=False
28
+ If True, data will not be centered before computation.
29
+ Useful when working with data whose mean is almost, but not exactly
30
+ zero.
31
+ If False (default), data will be centered before computation.
32
+
33
+ shrinkage : float, default=0.1
34
+ Coefficient in the convex combination used for the computation
35
+ of the shrunk estimate. Range is [0, 1].
36
+
37
+ Attributes
38
+ ----------
39
+ covariance_ : ndarray of shape (n_assets, n_assets)
40
+ Estimated covariance.
41
+
42
+ location_ : ndarray of shape (n_assets,)
43
+ Estimated location, i.e. the estimated mean.
44
+
45
+ precision_ : ndarray of shape (n_assets, n_assets)
46
+ Estimated pseudo inverse matrix.
47
+ (stored only if store_precision is True)
48
+
49
+ n_features_in_ : int
50
+ Number of assets seen during `fit`.
51
+
52
+ feature_names_in_ : ndarray of shape (`n_features_in_`,)
53
+ Names of features seen during `fit`. Defined only when `X`
54
+ has feature names that are all strings.
55
+
56
+ Notes
57
+ -----
58
+ The regularized covariance is given by:
59
+
60
+ (1 - shrinkage) * cov + shrinkage * mu * np.identity(n_features)
61
+
62
+ where mu = trace(cov) / n_features
63
+ """
64
+
65
+ def __init__(
66
+ self,
67
+ store_precision=True,
68
+ assume_centered=False,
69
+ shrinkage=0.1,
70
+ nearest: bool = True,
71
+ higham: bool = False,
72
+ higham_max_iteration: int = 100,
73
+ ):
74
+ super().__init__(
75
+ nearest=nearest,
76
+ higham=higham,
77
+ higham_max_iteration=higham_max_iteration,
78
+ )
79
+ skc.ShrunkCovariance.__init__(
80
+ self,
81
+ store_precision=store_precision,
82
+ assume_centered=assume_centered,
83
+ shrinkage=shrinkage,
84
+ )
85
+
86
+ def fit(self, X: npt.ArrayLike, y=None) -> "ShrunkCovariance":
87
+ """Fit the shrunk covariance model to X.
88
+
89
+ Parameters
90
+ ----------
91
+ X : array-like of shape (n_observations, n_assets)
92
+ Price returns of the assets.
93
+
94
+ y : Ignored
95
+ Not used, present for API consistency by convention.
96
+
97
+ Returns
98
+ -------
99
+ self : ShrunkCovariance
100
+ Fitted estimator.
101
+ """
102
+ skc.ShrunkCovariance.fit(self, X)
103
+ self._set_covariance(self.covariance_)
104
+ return self
@@ -3,13 +3,10 @@
3
3
  from skfolio.moments.expected_returns._base import (
4
4
  BaseMu,
5
5
  )
6
- from skfolio.moments.expected_returns._expected_returns import (
7
- EWMu,
8
- EmpiricalMu,
9
- EquilibriumMu,
10
- ShrunkMu,
11
- ShrunkMuMethods,
12
- )
6
+ from skfolio.moments.expected_returns._empirical_mu import EmpiricalMu
7
+ from skfolio.moments.expected_returns._equilibrium_mu import EquilibriumMu
8
+ from skfolio.moments.expected_returns._ew_mu import EWMu
9
+ from skfolio.moments.expected_returns._shrunk_mu import ShrunkMu, ShrunkMuMethods
13
10
 
14
11
  __all__ = [
15
12
  "BaseMu",
@@ -0,0 +1,63 @@
1
+ """Empirical Expected Returns (Mu) Estimator."""
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
+
10
+ import numpy as np
11
+ import numpy.typing as npt
12
+
13
+ from skfolio.moments.expected_returns._base import BaseMu
14
+
15
+
16
+ class EmpiricalMu(BaseMu):
17
+ """Empirical Expected Returns (Mu) estimator.
18
+
19
+ Estimates the expected returns with the historical mean.
20
+
21
+ Parameters
22
+ ----------
23
+ window_size : int, optional
24
+ Window size. The model is fitted on the last `window_size` observations.
25
+ The default (`None`) is to use all the data.
26
+
27
+ Attributes
28
+ ----------
29
+ mu_ : ndarray of shape (n_assets,)
30
+ Estimated expected returns of the assets.
31
+
32
+ n_features_in_ : int
33
+ Number of assets seen during `fit`.
34
+
35
+ feature_names_in_ : ndarray of shape (`n_features_in_`,)
36
+ Names of assets seen during `fit`. Defined only when `X`
37
+ has assets names that are all strings.
38
+ """
39
+
40
+ def __init__(self, window_size: int | None = None):
41
+ self.window_size = window_size
42
+
43
+ def fit(self, X: npt.ArrayLike, y=None) -> "EmpiricalMu":
44
+ """Fit the Mu Empirical estimator model.
45
+
46
+ Parameters
47
+ ----------
48
+ X : array-like of shape (n_observations, n_assets)
49
+ Price returns of the assets.
50
+
51
+ y : Ignored
52
+ Not used, present for API consistency by convention.
53
+
54
+ Returns
55
+ -------
56
+ self : EmpiricalMu
57
+ Fitted estimator.
58
+ """
59
+ X = self._validate_data(X)
60
+ if self.window_size is not None:
61
+ X = X[-self.window_size :]
62
+ self.mu_ = np.mean(X, axis=0)
63
+ return self
@@ -0,0 +1,124 @@
1
+ """Equilibrium Expected Returns (Mu) Estimators."""
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
+
10
+ import numpy as np
11
+ import numpy.typing as npt
12
+ import sklearn.utils.metadata_routing as skm
13
+
14
+ from skfolio.moments.covariance import BaseCovariance, EmpiricalCovariance
15
+ from skfolio.moments.expected_returns._base import BaseMu
16
+ from skfolio.utils.tools import check_estimator
17
+
18
+
19
+ class EquilibriumMu(BaseMu):
20
+ r"""Equilibrium Expected Returns (Mu) estimator.
21
+
22
+ The Equilibrium is defined as:
23
+
24
+ .. math:: risk\_aversion \times \Sigma \cdot w^T
25
+
26
+ For Market Cap Equilibrium, the weights are the assets Market Caps.
27
+ For Equal-weighted Equilibrium, the weights are equal-weighted (1/N).
28
+
29
+ Parameters
30
+ ----------
31
+ risk_aversion : float, default=1.0
32
+ Risk aversion factor.
33
+ The default value is `1.0`.
34
+
35
+ weights : array-like of shape (n_assets,), optional
36
+ Asset weights used to compute the Expected Return Equilibrium.
37
+ The default is to use the equal-weighted equilibrium (1/N).
38
+ For a Market Cap weighted equilibrium, you must provide the asset Market Caps.
39
+
40
+ covariance_estimator : BaseCovariance, optional
41
+ :ref:`Covariance estimator <covariance_estimator>` used to estimate the
42
+ covariance in the equilibrium formula.
43
+ The default (`None`) is to use :class:`~skfolio.moments.EmpiricalCovariance`.
44
+
45
+ Attributes
46
+ ----------
47
+ mu_ : ndarray of shape (n_assets,)
48
+ Estimated expected returns of the assets.
49
+
50
+ covariance_estimator_ : BaseCovariance
51
+ Fitted `covariance_estimator`.
52
+
53
+ n_features_in_ : int
54
+ Number of assets seen during `fit`.
55
+
56
+ feature_names_in_ : ndarray of shape (`n_features_in_`,)
57
+ Names of assets seen during `fit`. Defined only when `X`
58
+ has assets names that are all strings.
59
+ """
60
+
61
+ covariance_estimator_: BaseCovariance
62
+
63
+ def __init__(
64
+ self,
65
+ risk_aversion: float = 1,
66
+ weights: np.ndarray | None = None,
67
+ covariance_estimator: BaseCovariance | None = None,
68
+ ):
69
+ self.risk_aversion = risk_aversion
70
+ self.weights = weights
71
+ self.covariance_estimator = covariance_estimator
72
+
73
+ def get_metadata_routing(self):
74
+ # noinspection PyTypeChecker
75
+ router = skm.MetadataRouter(owner=self.__class__.__name__).add(
76
+ covariance_estimator=self.covariance_estimator,
77
+ method_mapping=skm.MethodMapping().add(caller="fit", callee="fit"),
78
+ )
79
+ return router
80
+
81
+ def fit(self, X: npt.ArrayLike, y=None, **fit_params) -> "EquilibriumMu":
82
+ """Fit the EquilibriumMu estimator model.
83
+
84
+ Parameters
85
+ ----------
86
+ X : array-like of shape (n_observations, n_assets)
87
+ Price returns of the assets.
88
+
89
+ y : Ignored
90
+ Not used, present for API consistency by convention.
91
+
92
+ **fit_params : dict
93
+ Parameters to pass to the underlying estimators.
94
+ Only available if `enable_metadata_routing=True`, which can be
95
+ set by using ``sklearn.set_config(enable_metadata_routing=True)``.
96
+ See :ref:`Metadata Routing User Guide <metadata_routing>` for
97
+ more details.
98
+
99
+ Returns
100
+ -------
101
+ self : EquilibriumMu
102
+ Fitted estimator.
103
+ """
104
+ routed_params = skm.process_routing(self, "fit", **fit_params)
105
+
106
+ # fitting estimators
107
+ self.covariance_estimator_ = check_estimator(
108
+ self.covariance_estimator,
109
+ default=EmpiricalCovariance(),
110
+ check_type=BaseCovariance,
111
+ )
112
+ # noinspection PyArgumentList
113
+ self.covariance_estimator_.fit(X, y, **routed_params.covariance_estimator.fit)
114
+
115
+ # we validate and convert to numpy after all models have been fitted to keep
116
+ # features names information.
117
+ X = self._validate_data(X)
118
+ n_assets = X.shape[1]
119
+ if self.weights is None:
120
+ weights = np.ones(n_assets) / n_assets
121
+ else:
122
+ weights = np.asarray(self.weights)
123
+ self.mu_ = self.risk_aversion * self.covariance_estimator_.covariance_ @ weights
124
+ return self
@@ -0,0 +1,69 @@
1
+ """Exponentially Weighted Expected Returns (Mu) Estimators."""
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
+
10
+ import numpy.typing as npt
11
+ import pandas as pd
12
+
13
+ from skfolio.moments.expected_returns._base import BaseMu
14
+
15
+
16
+ class EWMu(BaseMu):
17
+ r"""Exponentially Weighted Expected Returns (Mu) estimator.
18
+
19
+ Estimates the expected returns with the exponentially weighted mean (EWM).
20
+
21
+ Parameters
22
+ ----------
23
+ window_size : int, optional
24
+ Window size. The model is fitted on the last `window_size` observations.
25
+ The default (`None`) is to use all the data.
26
+
27
+ alpha : float, default=0.2
28
+ Exponential smoothing factor. The default value is `0.2`.
29
+
30
+ :math:`0 < \alpha \leq 1`.
31
+
32
+ Attributes
33
+ ----------
34
+ mu_ : ndarray of shape (n_assets,)
35
+ Estimated expected returns of the assets.
36
+
37
+ n_features_in_ : int
38
+ Number of assets seen during `fit`.
39
+
40
+ feature_names_in_ : ndarray of shape (`n_features_in_`,)
41
+ Names of assets seen during `fit`. Defined only when `X`
42
+ has assets names that are all strings.
43
+ """
44
+
45
+ def __init__(self, window_size: int | None = None, alpha: float = 0.2):
46
+ self.window_size = window_size
47
+ self.alpha = alpha
48
+
49
+ def fit(self, X: npt.ArrayLike, y=None) -> "EWMu":
50
+ """Fit the EWMu estimator model.
51
+
52
+ Parameters
53
+ ----------
54
+ X : array-like of shape (n_observations, n_assets)
55
+ Price returns of the assets.
56
+
57
+ y : Ignored
58
+ Not used, present for API consistency by convention.
59
+
60
+ Returns
61
+ -------
62
+ self : EWMu
63
+ Fitted estimator.
64
+ """
65
+ X = self._validate_data(X)
66
+ if self.window_size is not None:
67
+ X = X[-self.window_size :]
68
+ self.mu_ = pd.DataFrame(X).ewm(alpha=self.alpha).mean().iloc[-1, :].to_numpy()
69
+ return self