skfolio 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- skfolio/__init__.py +29 -0
- skfolio/cluster/__init__.py +8 -0
- skfolio/cluster/_hierarchical.py +387 -0
- skfolio/datasets/__init__.py +20 -0
- skfolio/datasets/_base.py +389 -0
- skfolio/datasets/data/__init__.py +0 -0
- skfolio/datasets/data/factors_dataset.csv.gz +0 -0
- skfolio/datasets/data/sp500_dataset.csv.gz +0 -0
- skfolio/datasets/data/sp500_index.csv.gz +0 -0
- skfolio/distance/__init__.py +26 -0
- skfolio/distance/_base.py +55 -0
- skfolio/distance/_distance.py +574 -0
- skfolio/exceptions.py +30 -0
- skfolio/measures/__init__.py +76 -0
- skfolio/measures/_enums.py +355 -0
- skfolio/measures/_measures.py +607 -0
- skfolio/metrics/__init__.py +3 -0
- skfolio/metrics/_scorer.py +121 -0
- skfolio/model_selection/__init__.py +18 -0
- skfolio/model_selection/_combinatorial.py +407 -0
- skfolio/model_selection/_validation.py +194 -0
- skfolio/model_selection/_walk_forward.py +221 -0
- skfolio/moments/__init__.py +41 -0
- skfolio/moments/covariance/__init__.py +29 -0
- skfolio/moments/covariance/_base.py +101 -0
- skfolio/moments/covariance/_covariance.py +1108 -0
- skfolio/moments/expected_returns/__init__.py +21 -0
- skfolio/moments/expected_returns/_base.py +31 -0
- skfolio/moments/expected_returns/_expected_returns.py +415 -0
- skfolio/optimization/__init__.py +36 -0
- skfolio/optimization/_base.py +147 -0
- skfolio/optimization/cluster/__init__.py +13 -0
- skfolio/optimization/cluster/_nco.py +348 -0
- skfolio/optimization/cluster/hierarchical/__init__.py +13 -0
- skfolio/optimization/cluster/hierarchical/_base.py +440 -0
- skfolio/optimization/cluster/hierarchical/_herc.py +406 -0
- skfolio/optimization/cluster/hierarchical/_hrp.py +368 -0
- skfolio/optimization/convex/__init__.py +16 -0
- skfolio/optimization/convex/_base.py +1944 -0
- skfolio/optimization/convex/_distributionally_robust.py +392 -0
- skfolio/optimization/convex/_maximum_diversification.py +417 -0
- skfolio/optimization/convex/_mean_risk.py +974 -0
- skfolio/optimization/convex/_risk_budgeting.py +560 -0
- skfolio/optimization/ensemble/__init__.py +6 -0
- skfolio/optimization/ensemble/_base.py +87 -0
- skfolio/optimization/ensemble/_stacking.py +326 -0
- skfolio/optimization/naive/__init__.py +3 -0
- skfolio/optimization/naive/_naive.py +173 -0
- skfolio/population/__init__.py +3 -0
- skfolio/population/_population.py +883 -0
- skfolio/portfolio/__init__.py +13 -0
- skfolio/portfolio/_base.py +1096 -0
- skfolio/portfolio/_multi_period_portfolio.py +610 -0
- skfolio/portfolio/_portfolio.py +842 -0
- skfolio/pre_selection/__init__.py +7 -0
- skfolio/pre_selection/_pre_selection.py +342 -0
- skfolio/preprocessing/__init__.py +3 -0
- skfolio/preprocessing/_returns.py +114 -0
- skfolio/prior/__init__.py +18 -0
- skfolio/prior/_base.py +63 -0
- skfolio/prior/_black_litterman.py +238 -0
- skfolio/prior/_empirical.py +163 -0
- skfolio/prior/_factor_model.py +268 -0
- skfolio/typing.py +50 -0
- skfolio/uncertainty_set/__init__.py +23 -0
- skfolio/uncertainty_set/_base.py +108 -0
- skfolio/uncertainty_set/_bootstrap.py +281 -0
- skfolio/uncertainty_set/_empirical.py +237 -0
- skfolio/utils/__init__.py +0 -0
- skfolio/utils/bootstrap.py +115 -0
- skfolio/utils/equations.py +350 -0
- skfolio/utils/sorting.py +117 -0
- skfolio/utils/stats.py +466 -0
- skfolio/utils/tools.py +567 -0
- skfolio-0.0.1.dist-info/LICENSE +29 -0
- skfolio-0.0.1.dist-info/METADATA +568 -0
- skfolio-0.0.1.dist-info/RECORD +79 -0
- skfolio-0.0.1.dist-info/WHEEL +5 -0
- skfolio-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,326 @@
|
|
1
|
+
"""Stacking Optimization estimator."""
|
2
|
+
|
3
|
+
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
|
+
# License: BSD 3 clause
|
5
|
+
|
6
|
+
from copy import deepcopy
|
7
|
+
|
8
|
+
import numpy as np
|
9
|
+
import numpy.typing as npt
|
10
|
+
import sklearn as sk
|
11
|
+
import sklearn.model_selection as skm
|
12
|
+
import sklearn.utils as sku
|
13
|
+
import sklearn.utils.parallel as skp
|
14
|
+
import sklearn.utils.validation as skv
|
15
|
+
|
16
|
+
import skfolio.typing as skt
|
17
|
+
from skfolio.measures import RatioMeasure
|
18
|
+
from skfolio.model_selection import BaseCombinatorialCV, cross_val_predict
|
19
|
+
from skfolio.optimization._base import BaseOptimization
|
20
|
+
from skfolio.optimization.convex import MeanRisk
|
21
|
+
from skfolio.optimization.ensemble._base import BaseComposition
|
22
|
+
from skfolio.utils.tools import check_estimator, fit_single_estimator
|
23
|
+
|
24
|
+
|
25
|
+
class StackingOptimization(BaseOptimization, BaseComposition):
|
26
|
+
"""Stack of optimizations with a final optimization.
|
27
|
+
|
28
|
+
Stacking Optimization is an ensemble method that consists in stacking the output of
|
29
|
+
individual optimization estimators with a final optimization estimator.
|
30
|
+
|
31
|
+
The weights are the dot-product of individual estimators weights with the final
|
32
|
+
estimator weights. Stacking allows to use the strength of each individual estimator
|
33
|
+
by using their output as input of a final estimator.
|
34
|
+
|
35
|
+
To avoid data leakage, out-of-sample estimates are used to fit the outer
|
36
|
+
optimization.
|
37
|
+
|
38
|
+
Note that `estimators_` are fitted on the full `X` while `final_estimator_`
|
39
|
+
is trained using cross-validated predictions of the base estimators using
|
40
|
+
`cross_val_predict`.
|
41
|
+
|
42
|
+
Parameters
|
43
|
+
----------
|
44
|
+
estimators : list[tuple[str, BaseOptimization]]
|
45
|
+
:ref:`Optimization estimators <optimization>` which will be stacked together.
|
46
|
+
Each element of the list is defined as a tuple of string (i.e. name) and an
|
47
|
+
:ref:`optimization estimator <optimization>`.
|
48
|
+
|
49
|
+
final_estimator : BaseOptimization, optional
|
50
|
+
A final :ref:`optimization estimator <optimization>` which will be used to
|
51
|
+
combine the base estimators.
|
52
|
+
The default (`None`) is to use :class:`~skfolio.optimization.MeanRisk`.
|
53
|
+
|
54
|
+
cv : BaseCrossValidator | BaseCombinatorialCV | int | "prefit" | "ignore", optional
|
55
|
+
Determines the cross-validation splitting strategy used in `cross_val_predict`
|
56
|
+
to train the `final_estimator`.
|
57
|
+
The default (`None`) is to use the 5-fold cross validation `KFold()`.
|
58
|
+
Possible inputs for `cv` are:
|
59
|
+
|
60
|
+
* "ignore": no cross-validation is used (note that it will likely lead to data leakage with a high risk of overfitting)
|
61
|
+
* integer, to specify the number of folds in a `KFold`
|
62
|
+
* An object to be used as a cross-validation generator
|
63
|
+
* An iterable yielding train, test splits
|
64
|
+
* "prefit" to assume the `estimators` are prefit, and skip cross validation
|
65
|
+
* A :class:`~skfolio.model_selection.CombinatorialPurgedCV`
|
66
|
+
|
67
|
+
If a `CombinatorialCV` cross-validator is used, each cluster out-of-sample
|
68
|
+
outputs becomes a collection of multiple paths instead of one single path. The
|
69
|
+
selected out-of-sample path among this collection of paths is chosen according
|
70
|
+
to the `quantile` and `quantile_measure` parameters.
|
71
|
+
|
72
|
+
If "prefit" is passed, it is assumed that all `estimators` have been fitted
|
73
|
+
already. The `final_estimator_` is trained on the `estimators` predictions on
|
74
|
+
the full training set and are **not** cross validated predictions.
|
75
|
+
Please note that if the models have been trained on the same data to train the
|
76
|
+
stacking model, there is a very high risk of overfitting.
|
77
|
+
|
78
|
+
n_jobs : int, optional
|
79
|
+
The number of jobs to run in parallel for `fit` of all `estimators`.
|
80
|
+
The value `-1` means using all processors.
|
81
|
+
The default (`None`) means 1 unless in a `joblib.parallel_backend` context.
|
82
|
+
|
83
|
+
|
84
|
+
quantile : float, default=0.5
|
85
|
+
Quantile for a given measure (`quantile_measure`) of the out-of-sample
|
86
|
+
inner-estimator paths when the `cv` parameter is a
|
87
|
+
:class:`~skfolio.model_selection.CombinatorialPurgedCV` cross-validator.
|
88
|
+
The default value is `0.5` corresponding to the path with the median measure.
|
89
|
+
(see `cv`)
|
90
|
+
|
91
|
+
quantile_measure : PerfMeasure or RatioMeasure or RiskMeasure or ExtraRiskMeasure, default=RatioMeasure.SHARPE_RATIO
|
92
|
+
Measure used for the quantile path selection (see `quantile` and `cv`).
|
93
|
+
The default is `RatioMeasure.SHARPE_RATIO`.
|
94
|
+
|
95
|
+
verbose : int, default=0
|
96
|
+
The verbosity level. The default value is `0`.
|
97
|
+
|
98
|
+
portfolio_params : dict, optional
|
99
|
+
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
100
|
+
`score` methods. If not provided, the `name` is copied from the optimization
|
101
|
+
model and systematically passed to the portfolio.
|
102
|
+
|
103
|
+
Attributes
|
104
|
+
----------
|
105
|
+
weights_ : ndarray of shape (n_assets,)
|
106
|
+
Weights of the assets.
|
107
|
+
|
108
|
+
estimators_ : list[BaseOptimization]
|
109
|
+
The elements of the `estimators` parameter, having been fitted on the
|
110
|
+
training data. When `cv="prefit"`, `estimators_`
|
111
|
+
is set to `estimators` and is not fitted again.
|
112
|
+
|
113
|
+
named_estimators_ : dict[str, BaseOptimization]
|
114
|
+
Attribute to access any fitted sub-estimators by name.
|
115
|
+
|
116
|
+
final_estimator_ : BaseOptimization
|
117
|
+
The fitted `final_estimator`.
|
118
|
+
|
119
|
+
n_features_in_ : int
|
120
|
+
Number of assets seen during `fit`.
|
121
|
+
|
122
|
+
feature_names_in_ : ndarray of shape (`n_features_in_`,)
|
123
|
+
Names of assets seen during `fit`. Defined only when `X`
|
124
|
+
has assets names that are all strings.
|
125
|
+
"""
|
126
|
+
|
127
|
+
estimators_: list[BaseOptimization]
|
128
|
+
final_estimator_: BaseOptimization
|
129
|
+
named_estimators_: dict[str, BaseOptimization]
|
130
|
+
|
131
|
+
def __init__(
|
132
|
+
self,
|
133
|
+
estimators: list[tuple[str, BaseOptimization]],
|
134
|
+
final_estimator: BaseOptimization | None = None,
|
135
|
+
cv: skm.BaseCrossValidator | BaseCombinatorialCV | str | int | None = None,
|
136
|
+
quantile: float = 0.5,
|
137
|
+
quantile_measure: skt.Measure = RatioMeasure.SHARPE_RATIO,
|
138
|
+
n_jobs: int | None = None,
|
139
|
+
verbose: int = 0,
|
140
|
+
portfolio_params: dict | None = None,
|
141
|
+
):
|
142
|
+
super().__init__(portfolio_params=portfolio_params)
|
143
|
+
self.estimators = estimators
|
144
|
+
self.final_estimator = final_estimator
|
145
|
+
self.cv = cv
|
146
|
+
self.quantile = quantile
|
147
|
+
self.quantile_measure = quantile_measure
|
148
|
+
self.n_jobs = n_jobs
|
149
|
+
self.verbose = verbose
|
150
|
+
|
151
|
+
@property
|
152
|
+
def named_estimators(self):
|
153
|
+
"""Dictionary to access any fitted sub-estimators by name.
|
154
|
+
|
155
|
+
Returns
|
156
|
+
-------
|
157
|
+
:class:`~sklearn.utils.Bunch`
|
158
|
+
"""
|
159
|
+
return sku.Bunch(**dict(self.estimators))
|
160
|
+
|
161
|
+
def _validate_estimators(self) -> tuple[list[str], list[BaseOptimization]]:
|
162
|
+
"""Validate the `estimators` parameter.
|
163
|
+
|
164
|
+
Returns
|
165
|
+
-------
|
166
|
+
names : list[str]
|
167
|
+
The list of estimators names.
|
168
|
+
estimators : list[BaseOptimization
|
169
|
+
The list of optimization estimators.
|
170
|
+
"""
|
171
|
+
if self.estimators is None or len(self.estimators) == 0:
|
172
|
+
raise ValueError(
|
173
|
+
"Invalid 'estimators' attribute, 'estimators' should be a list"
|
174
|
+
" of (string, estimator) tuples."
|
175
|
+
)
|
176
|
+
names, estimators = zip(*self.estimators, strict=True)
|
177
|
+
# defined by MetaEstimatorMixin
|
178
|
+
self._validate_names(names)
|
179
|
+
|
180
|
+
return names, estimators
|
181
|
+
|
182
|
+
def set_params(self, **params):
|
183
|
+
"""Set the parameters of an estimator from the ensemble.
|
184
|
+
|
185
|
+
Valid parameter keys can be listed with `get_params()`. Note that you
|
186
|
+
can directly set the parameters of the estimators contained in
|
187
|
+
`estimators`.
|
188
|
+
|
189
|
+
Parameters
|
190
|
+
----------
|
191
|
+
**params : keyword arguments
|
192
|
+
Specific parameters using e.g.
|
193
|
+
`set_params(parameter_name=new_value)`. In addition, to setting the
|
194
|
+
parameters of the estimator, the individual estimator of the
|
195
|
+
estimators can also be set, or can be removed by setting them to
|
196
|
+
'drop'.
|
197
|
+
|
198
|
+
Returns
|
199
|
+
-------
|
200
|
+
self : object
|
201
|
+
Estimator instance.
|
202
|
+
"""
|
203
|
+
super()._set_params("estimators", **params)
|
204
|
+
return self
|
205
|
+
|
206
|
+
def get_params(self, deep=True):
|
207
|
+
"""Get the parameters of an estimator from the ensemble.
|
208
|
+
|
209
|
+
Returns the parameters given in the constructor as well as the
|
210
|
+
estimators contained within the `estimators` parameter.
|
211
|
+
|
212
|
+
Parameters
|
213
|
+
----------
|
214
|
+
deep : bool, default=True
|
215
|
+
Setting it to True gets the various estimators and the parameters
|
216
|
+
of the estimators as well.
|
217
|
+
|
218
|
+
Returns
|
219
|
+
-------
|
220
|
+
params : dict
|
221
|
+
Parameter and estimator names mapped to their values or parameter
|
222
|
+
names mapped to their values.
|
223
|
+
"""
|
224
|
+
return super()._get_params("estimators", deep=deep)
|
225
|
+
|
226
|
+
def fit(
|
227
|
+
self, X: npt.ArrayLike, y: npt.ArrayLike | None = None
|
228
|
+
) -> "StackingOptimization":
|
229
|
+
"""Fit the Stacking Optimization estimator.
|
230
|
+
|
231
|
+
Parameters
|
232
|
+
----------
|
233
|
+
X : array-like of shape (n_observations, n_assets)
|
234
|
+
Price returns of the assets.
|
235
|
+
|
236
|
+
y : array-like of shape (n_observations, n_targets), optional
|
237
|
+
Price returns of factors or a target benchmark.
|
238
|
+
The default is `None`.
|
239
|
+
|
240
|
+
Returns
|
241
|
+
-------
|
242
|
+
self : StackingOptimization
|
243
|
+
Fitted estimator.
|
244
|
+
"""
|
245
|
+
names, all_estimators = self._validate_estimators()
|
246
|
+
self.final_estimator_ = check_estimator(
|
247
|
+
self.final_estimator,
|
248
|
+
default=MeanRisk(),
|
249
|
+
check_type=BaseOptimization,
|
250
|
+
)
|
251
|
+
|
252
|
+
if self.cv == "prefit":
|
253
|
+
self.estimators_ = []
|
254
|
+
for estimator in all_estimators:
|
255
|
+
skv.check_is_fitted(estimator)
|
256
|
+
self.estimators_.append(estimator)
|
257
|
+
else:
|
258
|
+
# Fit the base estimators on the whole training data. Those
|
259
|
+
# base estimators will be used to retrieve the inner weights.
|
260
|
+
# They are exposed publicly.
|
261
|
+
# noinspection PyCallingNonCallable
|
262
|
+
self.estimators_ = skp.Parallel(n_jobs=self.n_jobs)(
|
263
|
+
skp.delayed(fit_single_estimator)(sk.clone(est), X, y)
|
264
|
+
for est in all_estimators
|
265
|
+
)
|
266
|
+
|
267
|
+
self.named_estimators_ = {
|
268
|
+
name: estimator
|
269
|
+
for name, estimator in zip(names, self.estimators_, strict=True)
|
270
|
+
}
|
271
|
+
|
272
|
+
inner_weights = np.array([estimator.weights_ for estimator in self.estimators_])
|
273
|
+
|
274
|
+
# To train the final-estimator using the most data as possible, we use
|
275
|
+
# a cross-validation to obtain the output of the stacked estimators.
|
276
|
+
# To ensure that the data provided to each estimator are the same,
|
277
|
+
# we need to set the random state of the cv if there is one and we
|
278
|
+
# need to take a copy.
|
279
|
+
if self.cv in ["prefit", "ignore"]:
|
280
|
+
X_pred = np.array(
|
281
|
+
[estimator.predict(X) for estimator in self.estimators_]
|
282
|
+
).T
|
283
|
+
else:
|
284
|
+
cv = skm.check_cv(self.cv)
|
285
|
+
if hasattr(cv, "random_state") and cv.random_state is None:
|
286
|
+
cv.random_state = np.random.RandomState()
|
287
|
+
# noinspection PyCallingNonCallable
|
288
|
+
cv_predictions = skp.Parallel(n_jobs=self.n_jobs)(
|
289
|
+
skp.delayed(cross_val_predict)(
|
290
|
+
sk.clone(estimator),
|
291
|
+
X,
|
292
|
+
y,
|
293
|
+
cv=deepcopy(cv),
|
294
|
+
method="predict",
|
295
|
+
n_jobs=self.n_jobs,
|
296
|
+
verbose=self.verbose,
|
297
|
+
)
|
298
|
+
for estimator in all_estimators
|
299
|
+
)
|
300
|
+
|
301
|
+
# We validate and convert to numpy array only after base-estimator fitting
|
302
|
+
# to keep the assets names in case they are used in the estimator.
|
303
|
+
if y is not None:
|
304
|
+
_, y = self._validate_data(X, y, multi_output=True)
|
305
|
+
else:
|
306
|
+
_ = self._validate_data(X)
|
307
|
+
|
308
|
+
if isinstance(self.cv, BaseCombinatorialCV):
|
309
|
+
X_pred = np.array(
|
310
|
+
[
|
311
|
+
pred.quantile(measure=self.quantile_measure, q=self.quantile)
|
312
|
+
for pred in cv_predictions
|
313
|
+
]
|
314
|
+
).T
|
315
|
+
else:
|
316
|
+
X_pred = np.array(cv_predictions).T
|
317
|
+
if y is not None:
|
318
|
+
test_indices = np.sort(
|
319
|
+
np.concatenate([test for _, test in cv.split(X, y)])
|
320
|
+
)
|
321
|
+
y = y[test_indices]
|
322
|
+
|
323
|
+
fit_single_estimator(self.final_estimator_, X_pred, y)
|
324
|
+
outer_weights = self.final_estimator_.weights_
|
325
|
+
self.weights_ = outer_weights @ inner_weights
|
326
|
+
return self
|
@@ -0,0 +1,173 @@
|
|
1
|
+
"""Naive estimators."""
|
2
|
+
|
3
|
+
# Author: Hugo Delatte <delatte.hugo@gmail.com>
|
4
|
+
# License: BSD 3 clause
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
import numpy.typing as npt
|
8
|
+
|
9
|
+
from skfolio.optimization._base import BaseOptimization
|
10
|
+
from skfolio.prior import BasePrior, EmpiricalPrior
|
11
|
+
from skfolio.utils.stats import rand_weights_dirichlet
|
12
|
+
from skfolio.utils.tools import check_estimator
|
13
|
+
|
14
|
+
|
15
|
+
class InverseVolatility(BaseOptimization):
|
16
|
+
"""Inverse Volatility estimator.
|
17
|
+
|
18
|
+
Each asset weight is computed using the inverse of its volatility and rescaled to
|
19
|
+
have a sum of weights equal to one. The assets volatilities are derived from the
|
20
|
+
prior estimator's covariance matrix.
|
21
|
+
|
22
|
+
Parameters
|
23
|
+
----------
|
24
|
+
prior_estimator : BasePrior, optional
|
25
|
+
:ref:`Prior estimator <prior>`.
|
26
|
+
The prior estimator is used to estimate the :class:`~skfolio.prior.PriorModel`
|
27
|
+
containing the estimation of assets expected returns, covariance matrix,
|
28
|
+
returns and Cholesky decomposition of the covariance.
|
29
|
+
The default (`None`) is to use :class:`~skfolio.prior.EmpiricalPrior`.
|
30
|
+
|
31
|
+
portfolio_params : dict, optional
|
32
|
+
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
33
|
+
`score` methods. If not provided, the `name`, `transaction_costs`,
|
34
|
+
`management_fees` and `previous_weights` are copied from the optimization
|
35
|
+
model and systematically passed to the portfolio.
|
36
|
+
|
37
|
+
Attributes
|
38
|
+
----------
|
39
|
+
weights_ : ndarray of shape (n_assets,) or (n_optimizations, n_assets)
|
40
|
+
Weights of the assets.
|
41
|
+
|
42
|
+
prior_estimator_ : BasePrior
|
43
|
+
Fitted `prior_estimator`.
|
44
|
+
"""
|
45
|
+
|
46
|
+
prior_estimator_: BasePrior
|
47
|
+
|
48
|
+
def __init__(
|
49
|
+
self,
|
50
|
+
prior_estimator: BasePrior | None = None,
|
51
|
+
portfolio_params: dict | None = None,
|
52
|
+
):
|
53
|
+
super().__init__(portfolio_params=portfolio_params)
|
54
|
+
self.prior_estimator = prior_estimator
|
55
|
+
|
56
|
+
def fit(
|
57
|
+
self, X: npt.ArrayLike, y: npt.ArrayLike | None = None
|
58
|
+
) -> "InverseVolatility":
|
59
|
+
"""Fit the Inverse Volatility estimator.
|
60
|
+
|
61
|
+
Parameters
|
62
|
+
----------
|
63
|
+
X : array-like of shape (n_observations, n_assets)
|
64
|
+
Price returns of the assets.
|
65
|
+
|
66
|
+
y : array-like of shape (n_observations, n_targets), optional
|
67
|
+
Price returns of factors or a target benchmark.
|
68
|
+
The default is `None`.
|
69
|
+
|
70
|
+
Returns
|
71
|
+
-------
|
72
|
+
self : InverseVolatility
|
73
|
+
Fitted estimator.
|
74
|
+
"""
|
75
|
+
# fitting prior estimator
|
76
|
+
self.prior_estimator_ = check_estimator(
|
77
|
+
self.prior_estimator,
|
78
|
+
default=EmpiricalPrior(),
|
79
|
+
check_type=BasePrior,
|
80
|
+
)
|
81
|
+
self.prior_estimator_.fit(X, y)
|
82
|
+
covariance = self.prior_estimator_.prior_model_.covariance
|
83
|
+
w = 1 / np.sqrt(np.diag(covariance))
|
84
|
+
self.weights_ = w / sum(w)
|
85
|
+
return self
|
86
|
+
|
87
|
+
|
88
|
+
class EqualWeighted(BaseOptimization):
|
89
|
+
"""Equally Weighted estimator.
|
90
|
+
|
91
|
+
Each asset weight is equal to `1/n_assets`.
|
92
|
+
|
93
|
+
Parameters
|
94
|
+
----------
|
95
|
+
portfolio_params : dict, optional
|
96
|
+
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
97
|
+
`score` methods. If not provided, the `name`, `transaction_costs`,
|
98
|
+
`management_fees` and `previous_weights` are copied from the optimization
|
99
|
+
model and systematically passed to the portfolio.
|
100
|
+
|
101
|
+
Attributes
|
102
|
+
----------
|
103
|
+
weights_ : ndarray of shape (n_assets,) or (n_optimizations, n_assets)
|
104
|
+
Weights of the assets.
|
105
|
+
"""
|
106
|
+
|
107
|
+
def __init__(self, portfolio_params: dict | None = None):
|
108
|
+
super().__init__(portfolio_params=portfolio_params)
|
109
|
+
|
110
|
+
def fit(self, X: npt.ArrayLike, y=None) -> "EqualWeighted":
|
111
|
+
"""Fit the Equal Weighted estimator.
|
112
|
+
|
113
|
+
Parameters
|
114
|
+
----------
|
115
|
+
X : array-like of shape (n_observations, n_assets)
|
116
|
+
Price returns of the assets.
|
117
|
+
|
118
|
+
y : Ignored
|
119
|
+
Not used, present for API consistency by convention.
|
120
|
+
|
121
|
+
Returns
|
122
|
+
-------
|
123
|
+
self : EqualWeighted
|
124
|
+
Fitted estimator.
|
125
|
+
"""
|
126
|
+
X = self._validate_data(X)
|
127
|
+
n_assets = X.shape[1]
|
128
|
+
self.weights_ = np.ones(n_assets) / n_assets
|
129
|
+
return self
|
130
|
+
|
131
|
+
|
132
|
+
class Random(BaseOptimization):
|
133
|
+
"""Random weight estimator.
|
134
|
+
|
135
|
+
The assets weight are drawn from a Dirichlet distribution and sum to one.
|
136
|
+
|
137
|
+
Parameters
|
138
|
+
----------
|
139
|
+
portfolio_params : dict, optional
|
140
|
+
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
141
|
+
`score` methods. If not provided, the `name`, `transaction_costs`,
|
142
|
+
`management_fees` and `previous_weights` are copied from the optimization
|
143
|
+
model and systematically passed to the portfolio.
|
144
|
+
|
145
|
+
Attributes
|
146
|
+
----------
|
147
|
+
weights_ : ndarray of shape (n_assets,) or (n_optimizations, n_assets)
|
148
|
+
Weights of the assets.
|
149
|
+
"""
|
150
|
+
|
151
|
+
def __init__(self, portfolio_params: dict | None = None):
|
152
|
+
super().__init__(portfolio_params=portfolio_params)
|
153
|
+
|
154
|
+
def fit(self, X: npt.ArrayLike, y=None):
|
155
|
+
"""Fit the Random Weighted estimator.
|
156
|
+
|
157
|
+
Parameters
|
158
|
+
----------
|
159
|
+
X : array-like of shape (n_observations, n_assets)
|
160
|
+
Price returns of the assets.
|
161
|
+
|
162
|
+
y : Ignored
|
163
|
+
Not used, present for API consistency by convention.
|
164
|
+
|
165
|
+
Returns
|
166
|
+
-------
|
167
|
+
self : EqualWeighted
|
168
|
+
Fitted estimator.
|
169
|
+
"""
|
170
|
+
X = self._validate_data(X)
|
171
|
+
n_assets = X.shape[1]
|
172
|
+
self.weights_ = rand_weights_dirichlet(n=n_assets)
|
173
|
+
return self
|