skfolio 0.1.3__py3-none-any.whl → 0.2.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/model_selection/__init__.py +2 -0
- skfolio/model_selection/_combinatorial.py +162 -13
- skfolio/optimization/_base.py +8 -3
- skfolio/optimization/cluster/hierarchical/_base.py +5 -4
- skfolio/optimization/cluster/hierarchical/_herc.py +2 -2
- skfolio/optimization/cluster/hierarchical/_hrp.py +2 -2
- skfolio/optimization/convex/_base.py +5 -4
- skfolio/optimization/convex/_distributionally_robust.py +2 -2
- skfolio/optimization/convex/_maximum_diversification.py +2 -2
- skfolio/optimization/convex/_mean_risk.py +2 -2
- skfolio/optimization/convex/_risk_budgeting.py +2 -2
- skfolio/optimization/naive/_naive.py +6 -6
- skfolio/population/_population.py +23 -21
- skfolio/prior/_black_litterman.py +1 -1
- skfolio/prior/_empirical.py +5 -4
- skfolio/prior/_factor_model.py +2 -1
- skfolio/utils/tools.py +8 -7
- {skfolio-0.1.3.dist-info → skfolio-0.2.1.dist-info}/METADATA +16 -16
- {skfolio-0.1.3.dist-info → skfolio-0.2.1.dist-info}/RECORD +22 -22
- {skfolio-0.1.3.dist-info → skfolio-0.2.1.dist-info}/LICENSE +0 -0
- {skfolio-0.1.3.dist-info → skfolio-0.2.1.dist-info}/WHEEL +0 -0
- {skfolio-0.1.3.dist-info → skfolio-0.2.1.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@
|
|
6
6
|
from skfolio.model_selection._combinatorial import (
|
7
7
|
BaseCombinatorialCV,
|
8
8
|
CombinatorialPurgedCV,
|
9
|
+
optimal_folds_number,
|
9
10
|
)
|
10
11
|
from skfolio.model_selection._validation import cross_val_predict
|
11
12
|
from skfolio.model_selection._walk_forward import WalkForward
|
@@ -15,4 +16,5 @@ __all__ = [
|
|
15
16
|
"WalkForward",
|
16
17
|
"BaseCombinatorialCV",
|
17
18
|
"CombinatorialPurgedCV",
|
19
|
+
"optimal_folds_number",
|
18
20
|
]
|
@@ -197,19 +197,13 @@ class CombinatorialPurgedCV(BaseCombinatorialCV):
|
|
197
197
|
@property
|
198
198
|
def n_splits(self) -> int:
|
199
199
|
"""Number of splits"""
|
200
|
-
return
|
201
|
-
math.factorial(self.n_folds)
|
202
|
-
/ (
|
203
|
-
math.factorial(self.n_test_folds)
|
204
|
-
* math.factorial(self.n_folds - self.n_test_folds)
|
205
|
-
)
|
206
|
-
)
|
200
|
+
return _n_splits(n_folds=self.n_folds, n_test_folds=self.n_test_folds)
|
207
201
|
|
208
202
|
@property
|
209
203
|
def n_test_paths(self) -> int:
|
210
204
|
"""Number of test paths that can be reconstructed from the train/test
|
211
205
|
combinations"""
|
212
|
-
return self.
|
206
|
+
return _n_test_paths(n_folds=self.n_folds, n_test_folds=self.n_test_folds)
|
213
207
|
|
214
208
|
@property
|
215
209
|
def test_set_index(self) -> np.ndarray:
|
@@ -320,17 +314,20 @@ class CombinatorialPurgedCV(BaseCombinatorialCV):
|
|
320
314
|
yield train_index, test_index_list
|
321
315
|
|
322
316
|
def summary(self, X) -> pd.Series:
|
323
|
-
|
317
|
+
n_observations = X.shape[0]
|
318
|
+
avg_train_size = _avg_train_size(
|
319
|
+
n_observations=n_observations,
|
320
|
+
n_folds=self.n_folds,
|
321
|
+
n_test_folds=self.n_test_folds,
|
322
|
+
)
|
324
323
|
return pd.Series(
|
325
324
|
{
|
326
|
-
"Number of Observations":
|
325
|
+
"Number of Observations": n_observations,
|
327
326
|
"Total Number of Folds": self.n_folds,
|
328
327
|
"Number of Test Folds": self.n_test_folds,
|
329
328
|
"Purge Size": self.purged_size,
|
330
329
|
"Embargo Size": self.embargo_size,
|
331
|
-
"Average Training Size": int(
|
332
|
-
n_samples / self.n_folds * (self.n_folds - self.n_test_folds)
|
333
|
-
),
|
330
|
+
"Average Training Size": int(avg_train_size),
|
334
331
|
"Number of Test Paths": self.n_test_paths,
|
335
332
|
"Number of Training Combinations": self.n_splits,
|
336
333
|
}
|
@@ -410,3 +407,155 @@ class CombinatorialPurgedCV(BaseCombinatorialCV):
|
|
410
407
|
)
|
411
408
|
|
412
409
|
return fig
|
410
|
+
|
411
|
+
|
412
|
+
def _n_splits(n_folds: int, n_test_folds: int) -> int:
|
413
|
+
"""Number of splits.
|
414
|
+
|
415
|
+
Parameters
|
416
|
+
----------
|
417
|
+
n_folds : int
|
418
|
+
Number of folds.
|
419
|
+
|
420
|
+
n_test_folds : int
|
421
|
+
Number of test folds.
|
422
|
+
|
423
|
+
Returns
|
424
|
+
-------
|
425
|
+
n_splits : int
|
426
|
+
Number of splits
|
427
|
+
"""
|
428
|
+
return int(math.comb(n_folds, n_test_folds))
|
429
|
+
|
430
|
+
|
431
|
+
def _n_test_paths(n_folds: int, n_test_folds: int) -> int:
|
432
|
+
"""Number of test paths that can be reconstructed from the train/test
|
433
|
+
combinations
|
434
|
+
|
435
|
+
Parameters
|
436
|
+
----------
|
437
|
+
n_folds : int
|
438
|
+
Number of folds.
|
439
|
+
|
440
|
+
n_test_folds : int
|
441
|
+
Number of test folds.
|
442
|
+
|
443
|
+
Returns
|
444
|
+
-------
|
445
|
+
n_splits : int
|
446
|
+
Number of test paths.
|
447
|
+
"""
|
448
|
+
return (
|
449
|
+
_n_splits(n_folds=n_folds, n_test_folds=n_test_folds) * n_test_folds // n_folds
|
450
|
+
)
|
451
|
+
|
452
|
+
|
453
|
+
def _avg_train_size(n_observations: int, n_folds: int, n_test_folds: int) -> float:
|
454
|
+
"""Average number of observations contained in each training set.
|
455
|
+
|
456
|
+
Parameters
|
457
|
+
----------
|
458
|
+
n_observations : int
|
459
|
+
Number of observations.
|
460
|
+
|
461
|
+
n_folds : int
|
462
|
+
Number of folds.
|
463
|
+
|
464
|
+
n_test_folds : int
|
465
|
+
Number of test folds.
|
466
|
+
|
467
|
+
Returns
|
468
|
+
-------
|
469
|
+
avg_train_size : float
|
470
|
+
Average number of observations contained in each training set.
|
471
|
+
"""
|
472
|
+
return n_observations / n_folds * (n_folds - n_test_folds)
|
473
|
+
|
474
|
+
|
475
|
+
def optimal_folds_number(
|
476
|
+
n_observations: int,
|
477
|
+
target_train_size: int,
|
478
|
+
target_n_test_paths: int,
|
479
|
+
weight_train_size: float = 1,
|
480
|
+
weight_n_test_paths: float = 1,
|
481
|
+
) -> tuple[int, int]:
|
482
|
+
r"""Find the optimal number of folds (total folds and test folds) for a target
|
483
|
+
training size and a target number of test paths.
|
484
|
+
|
485
|
+
We find `x = n_folds` and `y = n_test_folds` that minimizes the below
|
486
|
+
cost function of the relative distance from the two targets:
|
487
|
+
|
488
|
+
.. math::
|
489
|
+
cost(x,y) = w_{f} \times \lvert\frac{f(x,y)-f_{target}}{f_{target}}\rvert + w_{g} \times \lvert\frac{g(x,y)-g_{target}}{g_{target}}\rvert
|
490
|
+
|
491
|
+
with :math:`w_{f}` and :math:`w_{g}` the weights assigned to the distance
|
492
|
+
from each target and :math:`f(x,y)` and :math:`g(x,y)` the average training size
|
493
|
+
and the number of test paths as a function of the number of total folds and test
|
494
|
+
folds.
|
495
|
+
|
496
|
+
This is a combinatorial problem with :math:`\frac{T\times(T-3)}{2}` combinations,
|
497
|
+
with :math:`T` the number of observations.
|
498
|
+
|
499
|
+
We reduce the search space by using the combinatorial symetry
|
500
|
+
:math:`{n \choose k}={n \choose n-k}` and skipping cost computation above 1e5.
|
501
|
+
|
502
|
+
Parameters
|
503
|
+
----------
|
504
|
+
n_observations : int
|
505
|
+
Number of observations.
|
506
|
+
|
507
|
+
target_train_size : int
|
508
|
+
The target number of observation in the training set.
|
509
|
+
|
510
|
+
target_n_test_paths : int
|
511
|
+
The target number of test paths (that can be reconstructed from the train/test
|
512
|
+
combinations).
|
513
|
+
|
514
|
+
weight_train_size : float, default=1
|
515
|
+
The weight assigned to the distance from the target train size.
|
516
|
+
The default value is 1.
|
517
|
+
|
518
|
+
weight_n_test_paths : float, default=1
|
519
|
+
The weight assigned to the distance from the target number of test paths.
|
520
|
+
The default value is 1.
|
521
|
+
|
522
|
+
Returns
|
523
|
+
-------
|
524
|
+
n_folds : int
|
525
|
+
Optimal number of total folds.
|
526
|
+
|
527
|
+
n_test_folds : int
|
528
|
+
Optimal number of test folds.
|
529
|
+
"""
|
530
|
+
|
531
|
+
def _cost(
|
532
|
+
x: int,
|
533
|
+
y: int,
|
534
|
+
) -> float:
|
535
|
+
n_test_paths = _n_test_paths(n_folds=x, n_test_folds=y)
|
536
|
+
avg_train_size = _avg_train_size(
|
537
|
+
n_observations=n_observations, n_folds=x, n_test_folds=y
|
538
|
+
)
|
539
|
+
return (
|
540
|
+
weight_n_test_paths
|
541
|
+
* abs(n_test_paths - target_n_test_paths)
|
542
|
+
/ target_n_test_paths
|
543
|
+
+ weight_train_size
|
544
|
+
* abs(avg_train_size - target_train_size)
|
545
|
+
/ target_train_size
|
546
|
+
)
|
547
|
+
|
548
|
+
costs = []
|
549
|
+
res = []
|
550
|
+
for n_folds in range(3, n_observations + 1):
|
551
|
+
i = None
|
552
|
+
for n_test_folds in range(2, n_folds):
|
553
|
+
if i is None or n_folds - n_test_folds <= i:
|
554
|
+
cost = _cost(x=n_folds, y=n_test_folds)
|
555
|
+
costs.append(cost)
|
556
|
+
res.append((n_folds, n_test_folds))
|
557
|
+
if i is None and cost > 1e5:
|
558
|
+
i = n_test_folds
|
559
|
+
|
560
|
+
j = np.argmin(costs)
|
561
|
+
return res[j]
|
skfolio/optimization/_base.py
CHANGED
@@ -29,8 +29,8 @@ class BaseOptimization(skb.BaseEstimator, ABC):
|
|
29
29
|
portfolio_params : dict, optional
|
30
30
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
31
31
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
32
|
-
`management_fees` and `
|
33
|
-
model and
|
32
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
33
|
+
optimization model and passed to the portfolio.
|
34
34
|
|
35
35
|
Attributes
|
36
36
|
----------
|
@@ -84,7 +84,12 @@ class BaseOptimization(skb.BaseEstimator, ABC):
|
|
84
84
|
ptf_kwargs = self.portfolio_params.copy()
|
85
85
|
|
86
86
|
# Set the default portfolio parameters equal to the optimization parameters
|
87
|
-
for param in [
|
87
|
+
for param in [
|
88
|
+
"transaction_costs",
|
89
|
+
"management_fees",
|
90
|
+
"previous_weights",
|
91
|
+
"risk_free_rate",
|
92
|
+
]:
|
88
93
|
if param not in ptf_kwargs and hasattr(self, param):
|
89
94
|
ptf_kwargs[param] = getattr(self, param)
|
90
95
|
|
@@ -8,6 +8,7 @@
|
|
8
8
|
# scikit-learn, Copyright (c) 2007-2010 David Cournapeau, Fabian Pedregosa, Olivier
|
9
9
|
|
10
10
|
from abc import ABC, abstractmethod
|
11
|
+
from typing import Any
|
11
12
|
|
12
13
|
import numpy as np
|
13
14
|
import numpy.typing as npt
|
@@ -183,8 +184,8 @@ class BaseHierarchicalOptimization(BaseOptimization, ABC):
|
|
183
184
|
portfolio_params : dict, optional
|
184
185
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
185
186
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
186
|
-
`management_fees` and `
|
187
|
-
model and
|
187
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
188
|
+
optimization model and passed to the portfolio.
|
188
189
|
|
189
190
|
Attributes
|
190
191
|
----------
|
@@ -235,7 +236,7 @@ class BaseHierarchicalOptimization(BaseOptimization, ABC):
|
|
235
236
|
self,
|
236
237
|
value: float | dict | np.ndarray | list,
|
237
238
|
n_assets: int,
|
238
|
-
fill_value:
|
239
|
+
fill_value: Any,
|
239
240
|
name: str,
|
240
241
|
) -> np.ndarray:
|
241
242
|
"""Convert input to cleaned 1D array
|
@@ -250,7 +251,7 @@ class BaseHierarchicalOptimization(BaseOptimization, ABC):
|
|
250
251
|
n_assets : int
|
251
252
|
Number of assets. Used to verify the shape of the converted array.
|
252
253
|
|
253
|
-
fill_value :
|
254
|
+
fill_value : Any
|
254
255
|
When `items` is a dictionary, elements that are not in `asset_names` are
|
255
256
|
filled with `fill_value` in the converted array.
|
256
257
|
|
@@ -204,8 +204,8 @@ class HierarchicalEqualRiskContribution(BaseHierarchicalOptimization):
|
|
204
204
|
portfolio_params : dict, optional
|
205
205
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
206
206
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
207
|
-
`management_fees` and `
|
208
|
-
model and
|
207
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
208
|
+
optimization model and passed to the portfolio.
|
209
209
|
|
210
210
|
Attributes
|
211
211
|
----------
|
@@ -204,8 +204,8 @@ class HierarchicalRiskParity(BaseHierarchicalOptimization):
|
|
204
204
|
portfolio_params : dict, optional
|
205
205
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
206
206
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
207
|
-
`management_fees` and `
|
208
|
-
model and
|
207
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
208
|
+
optimization model and passed to the portfolio.
|
209
209
|
|
210
210
|
Attributes
|
211
211
|
----------
|
@@ -9,6 +9,7 @@
|
|
9
9
|
import warnings
|
10
10
|
from abc import ABC, abstractmethod
|
11
11
|
from enum import auto
|
12
|
+
from typing import Any
|
12
13
|
|
13
14
|
import cvxpy as cp
|
14
15
|
import cvxpy.constraints.constraint as cpc
|
@@ -403,8 +404,8 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
403
404
|
portfolio_params : dict, optional
|
404
405
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
405
406
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
406
|
-
`management_fees` and `
|
407
|
-
model and
|
407
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
408
|
+
optimization model and passed to the portfolio.
|
408
409
|
|
409
410
|
Attributes
|
410
411
|
----------
|
@@ -575,7 +576,7 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
575
576
|
self,
|
576
577
|
value: float | dict | npt.ArrayLike | None,
|
577
578
|
n_assets: int,
|
578
|
-
fill_value:
|
579
|
+
fill_value: Any,
|
579
580
|
name: str,
|
580
581
|
) -> float | np.ndarray:
|
581
582
|
"""Convert input to cleaned float or ndarray.
|
@@ -588,7 +589,7 @@ class ConvexOptimization(BaseOptimization, ABC):
|
|
588
589
|
n_assets : int
|
589
590
|
Number of assets. Used to verify the shape of the converted array.
|
590
591
|
|
591
|
-
fill_value :
|
592
|
+
fill_value : Any
|
592
593
|
When `items` is a dictionary, elements that are not in `asset_names` are
|
593
594
|
filled with `fill_value` in the converted array.
|
594
595
|
|
@@ -208,8 +208,8 @@ class DistributionallyRobustCVaR(ConvexOptimization):
|
|
208
208
|
portfolio_params : dict, optional
|
209
209
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
210
210
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
211
|
-
`management_fees` and `
|
212
|
-
model and
|
211
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
212
|
+
optimization model and passed to the portfolio.
|
213
213
|
|
214
214
|
Attributes
|
215
215
|
----------
|
@@ -303,8 +303,8 @@ class MaximumDiversification(MeanRisk):
|
|
303
303
|
portfolio_params : dict, optional
|
304
304
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
305
305
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
306
|
-
`management_fees` and `
|
307
|
-
model and
|
306
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
307
|
+
optimization model and passed to the portfolio.
|
308
308
|
|
309
309
|
Attributes
|
310
310
|
----------
|
@@ -510,8 +510,8 @@ class MeanRisk(ConvexOptimization):
|
|
510
510
|
portfolio_params : dict, optional
|
511
511
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
512
512
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
513
|
-
`management_fees` and `
|
514
|
-
model and
|
513
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
514
|
+
optimization model and passed to the portfolio.
|
515
515
|
|
516
516
|
Attributes
|
517
517
|
----------
|
@@ -340,8 +340,8 @@ class RiskBudgeting(ConvexOptimization):
|
|
340
340
|
portfolio_params : dict, optional
|
341
341
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
342
342
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
343
|
-
`management_fees` and `
|
344
|
-
model and
|
343
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
344
|
+
optimization model and passed to the portfolio.
|
345
345
|
|
346
346
|
Attributes
|
347
347
|
----------
|
@@ -31,8 +31,8 @@ class InverseVolatility(BaseOptimization):
|
|
31
31
|
portfolio_params : dict, optional
|
32
32
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
33
33
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
34
|
-
`management_fees` and `
|
35
|
-
model and
|
34
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
35
|
+
optimization model and passed to the portfolio.
|
36
36
|
|
37
37
|
Attributes
|
38
38
|
----------
|
@@ -95,8 +95,8 @@ class EqualWeighted(BaseOptimization):
|
|
95
95
|
portfolio_params : dict, optional
|
96
96
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
97
97
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
98
|
-
`management_fees` and `
|
99
|
-
model and
|
98
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
99
|
+
optimization model and passed to the portfolio.
|
100
100
|
|
101
101
|
Attributes
|
102
102
|
----------
|
@@ -139,8 +139,8 @@ class Random(BaseOptimization):
|
|
139
139
|
portfolio_params : dict, optional
|
140
140
|
Portfolio parameters passed to the portfolio evaluated by the `predict` and
|
141
141
|
`score` methods. If not provided, the `name`, `transaction_costs`,
|
142
|
-
`management_fees` and `
|
143
|
-
model and
|
142
|
+
`management_fees`, `previous_weights` and `risk_free_rate` are copied from the
|
143
|
+
optimization model and passed to the portfolio.
|
144
144
|
|
145
145
|
Attributes
|
146
146
|
----------
|
@@ -7,6 +7,7 @@ A population is a collection of portfolios.
|
|
7
7
|
# License: BSD 3 clause
|
8
8
|
|
9
9
|
import inspect
|
10
|
+
from typing import Any
|
10
11
|
|
11
12
|
import numpy as np
|
12
13
|
import pandas as pd
|
@@ -15,7 +16,7 @@ import plotly.graph_objects as go
|
|
15
16
|
import scipy.interpolate as sci
|
16
17
|
|
17
18
|
import skfolio.typing as skt
|
18
|
-
from skfolio.portfolio import BasePortfolio, MultiPeriodPortfolio
|
19
|
+
from skfolio.portfolio import BasePortfolio, MultiPeriodPortfolio
|
19
20
|
from skfolio.utils.sorting import non_denominated_sort
|
20
21
|
from skfolio.utils.tools import deduplicate_names
|
21
22
|
|
@@ -30,14 +31,14 @@ class Population(list):
|
|
30
31
|
|
31
32
|
Parameters
|
32
33
|
----------
|
33
|
-
iterable : list[
|
34
|
+
iterable : list[BasePortfolio]
|
34
35
|
The list of portfolios. Each item can be of type
|
35
36
|
:class:`~skfolio.portfolio.Portfolio` and/or
|
36
37
|
:class:`~skfolio.portfolio.MultiPeriodPortfolio`.
|
37
38
|
Empty list are accepted.
|
38
39
|
"""
|
39
40
|
|
40
|
-
def __init__(self, iterable: list[
|
41
|
+
def __init__(self, iterable: list[BasePortfolio]) -> None:
|
41
42
|
super().__init__(self._validate_item(item) for item in iterable)
|
42
43
|
|
43
44
|
def __repr__(self) -> str:
|
@@ -45,43 +46,43 @@ class Population(list):
|
|
45
46
|
|
46
47
|
def __getitem__(
|
47
48
|
self, indices: int | list[int] | slice
|
48
|
-
) -> "
|
49
|
+
) -> "BasePortfolio | Population":
|
49
50
|
item = super().__getitem__(indices)
|
50
51
|
if isinstance(item, list):
|
51
52
|
return self.__class__(item)
|
52
53
|
return item
|
53
54
|
|
54
|
-
def __setitem__(self, index: int, item:
|
55
|
+
def __setitem__(self, index: int, item: BasePortfolio) -> None:
|
55
56
|
super().__setitem__(index, self._validate_item(item))
|
56
57
|
|
57
|
-
def __add__(self, other:
|
58
|
+
def __add__(self, other: BasePortfolio) -> "Population":
|
58
59
|
if not isinstance(other, Population):
|
59
60
|
raise TypeError(
|
60
61
|
f"Cannot add a Population with an object of type {type(other)}"
|
61
62
|
)
|
62
63
|
return self.__class__(super().__add__(other))
|
63
64
|
|
64
|
-
def insert(self, index, item:
|
65
|
+
def insert(self, index, item: BasePortfolio) -> None:
|
65
66
|
"""Insert portfolio before index."""
|
66
67
|
super().insert(index, self._validate_item(item))
|
67
68
|
|
68
|
-
def append(self, item:
|
69
|
+
def append(self, item: BasePortfolio) -> None:
|
69
70
|
"""Append portfolio to the end of the population list."""
|
70
71
|
super().append(self._validate_item(item))
|
71
72
|
|
72
|
-
def extend(self, other:
|
73
|
+
def extend(self, other: BasePortfolio) -> None:
|
73
74
|
"""Extend population list by appending elements from the iterable."""
|
74
75
|
if isinstance(other, type(self)):
|
75
76
|
super().extend(other)
|
76
77
|
else:
|
77
78
|
super().extend(self._validate_item(item) for item in other)
|
78
79
|
|
79
|
-
def set_portfolio_params(self, **params:
|
80
|
+
def set_portfolio_params(self, **params: Any) -> "Population":
|
80
81
|
"""Set the parameters of all the portfolios.
|
81
82
|
|
82
83
|
Parameters
|
83
84
|
----------
|
84
|
-
**params :
|
85
|
+
**params : Any
|
85
86
|
Portfolio parameters.
|
86
87
|
|
87
88
|
Returns
|
@@ -111,13 +112,14 @@ class Population(list):
|
|
111
112
|
|
112
113
|
@staticmethod
|
113
114
|
def _validate_item(
|
114
|
-
item:
|
115
|
-
) ->
|
115
|
+
item: BasePortfolio,
|
116
|
+
) -> BasePortfolio:
|
116
117
|
"""Validate that items are of type Portfolio or MultiPeriodPortfolio."""
|
117
|
-
if isinstance(item,
|
118
|
+
if isinstance(item, BasePortfolio):
|
118
119
|
return item
|
119
120
|
raise TypeError(
|
120
|
-
"Population only accept items
|
121
|
+
"Population only accept items that inherit from BasePortfolio such as "
|
122
|
+
"Portfolio or MultiPeriodPortfolio"
|
121
123
|
f", got {type(item).__name__}"
|
122
124
|
)
|
123
125
|
|
@@ -324,7 +326,7 @@ class Population(list):
|
|
324
326
|
q: float,
|
325
327
|
names: skt.Names | None = None,
|
326
328
|
tags: skt.Tags | None = None,
|
327
|
-
) ->
|
329
|
+
) -> BasePortfolio:
|
328
330
|
"""Returns the portfolio corresponding to the `q` quantile for a given portfolio
|
329
331
|
measure.
|
330
332
|
|
@@ -344,7 +346,7 @@ class Population(list):
|
|
344
346
|
|
345
347
|
Returns
|
346
348
|
-------
|
347
|
-
values :
|
349
|
+
values : BasePortfolio
|
348
350
|
Portfolio corresponding to the `q` quantile for the measure.
|
349
351
|
"""
|
350
352
|
if not 0 <= q <= 1:
|
@@ -360,7 +362,7 @@ class Population(list):
|
|
360
362
|
measure: skt.Measure,
|
361
363
|
names: skt.Names | None = None,
|
362
364
|
tags: skt.Tags | None = None,
|
363
|
-
) ->
|
365
|
+
) -> BasePortfolio:
|
364
366
|
"""Returns the portfolio with the minimum measure.
|
365
367
|
|
366
368
|
Parameters
|
@@ -376,7 +378,7 @@ class Population(list):
|
|
376
378
|
|
377
379
|
Returns
|
378
380
|
-------
|
379
|
-
values :
|
381
|
+
values : BasePortfolio
|
380
382
|
The portfolio with minimum measure.
|
381
383
|
"""
|
382
384
|
return self.quantile(measure=measure, q=0, names=names, tags=tags)
|
@@ -386,7 +388,7 @@ class Population(list):
|
|
386
388
|
measure: skt.Measure,
|
387
389
|
names: skt.Names | None = None,
|
388
390
|
tags: skt.Tags | None = None,
|
389
|
-
) ->
|
391
|
+
) -> BasePortfolio:
|
390
392
|
"""Returns the portfolio with the maximum measure.
|
391
393
|
|
392
394
|
Parameters
|
@@ -402,7 +404,7 @@ class Population(list):
|
|
402
404
|
|
403
405
|
Returns
|
404
406
|
-------
|
405
|
-
values :
|
407
|
+
values : BasePortfolio
|
406
408
|
The portfolio with maximum measure.
|
407
409
|
"""
|
408
410
|
return self.quantile(measure=measure, q=1, names=names, tags=tags)
|
@@ -156,7 +156,7 @@ class BlackLitterman(BasePrior):
|
|
156
156
|
check_type=BasePrior,
|
157
157
|
)
|
158
158
|
# fitting prior estimator
|
159
|
-
self.prior_estimator_.fit(X)
|
159
|
+
self.prior_estimator_.fit(X, y)
|
160
160
|
|
161
161
|
prior_mu = self.prior_estimator_.prior_model_.mu
|
162
162
|
prior_covariance = self.prior_estimator_.prior_model_.covariance
|
skfolio/prior/_empirical.py
CHANGED
@@ -120,11 +120,11 @@ class EmpiricalPrior(BasePrior):
|
|
120
120
|
"`is_log_normal` is `False`"
|
121
121
|
)
|
122
122
|
# Expected returns
|
123
|
-
self.mu_estimator_.fit(X)
|
123
|
+
self.mu_estimator_.fit(X, y)
|
124
124
|
mu = self.mu_estimator_.mu_
|
125
125
|
|
126
126
|
# Covariance
|
127
|
-
self.covariance_estimator_.fit(X)
|
127
|
+
self.covariance_estimator_.fit(X, y)
|
128
128
|
covariance = self.covariance_estimator_.covariance_
|
129
129
|
else:
|
130
130
|
if self.investment_horizon is None:
|
@@ -134,14 +134,15 @@ class EmpiricalPrior(BasePrior):
|
|
134
134
|
)
|
135
135
|
# Convert linear returns to log returns
|
136
136
|
X_log = np.log(1 + X)
|
137
|
+
y_log = np.log(1 + y) if y is not None else None
|
137
138
|
|
138
139
|
# Estimates the moments on the log returns
|
139
140
|
# Expected returns
|
140
|
-
self.mu_estimator_.fit(X_log)
|
141
|
+
self.mu_estimator_.fit(X_log, y_log)
|
141
142
|
mu = self.mu_estimator_.mu_
|
142
143
|
|
143
144
|
# Covariance
|
144
|
-
self.covariance_estimator_.fit(X_log)
|
145
|
+
self.covariance_estimator_.fit(X_log, y_log)
|
145
146
|
covariance = self.covariance_estimator_.covariance_
|
146
147
|
|
147
148
|
# Using the property of aggregation across time we scale this distribution
|
skfolio/prior/_factor_model.py
CHANGED
@@ -9,6 +9,7 @@
|
|
9
9
|
# Grisel Licensed under BSD 3 clause.
|
10
10
|
|
11
11
|
from abc import ABC, abstractmethod
|
12
|
+
from typing import Any
|
12
13
|
|
13
14
|
import numpy as np
|
14
15
|
import numpy.typing as npt
|
@@ -196,7 +197,7 @@ class FactorModel(BasePrior):
|
|
196
197
|
self.max_iteration = max_iteration
|
197
198
|
|
198
199
|
# noinspection PyMethodOverriding, PyPep8Naming
|
199
|
-
def fit(self, X: npt.ArrayLike, y:
|
200
|
+
def fit(self, X: npt.ArrayLike, y: Any):
|
200
201
|
"""Fit the Factor Model estimator.
|
201
202
|
|
202
203
|
Parameters
|
skfolio/utils/tools.py
CHANGED
@@ -10,6 +10,7 @@
|
|
10
10
|
from collections.abc import Callable, Iterator
|
11
11
|
from enum import Enum
|
12
12
|
from functools import wraps
|
13
|
+
from typing import Any
|
13
14
|
|
14
15
|
import numpy as np
|
15
16
|
import numpy.typing as npt
|
@@ -41,7 +42,7 @@ class AutoEnum(str, Enum):
|
|
41
42
|
|
42
43
|
@staticmethod
|
43
44
|
def _generate_next_value_(
|
44
|
-
name: str, start: int, count: int, last_values:
|
45
|
+
name: str, start: int, count: int, last_values: Any
|
45
46
|
) -> str:
|
46
47
|
"""Overriding `auto()`"""
|
47
48
|
return name.lower()
|
@@ -179,7 +180,7 @@ def args_names(func: object) -> list[str]:
|
|
179
180
|
|
180
181
|
|
181
182
|
def check_estimator(
|
182
|
-
estimator: skb.BaseEstimator | None, default: skb.BaseEstimator, check_type:
|
183
|
+
estimator: skb.BaseEstimator | None, default: skb.BaseEstimator, check_type: Any
|
183
184
|
):
|
184
185
|
"""Check the estimator type and returns its cloned version it provided, otherwise
|
185
186
|
return the default estimator.
|
@@ -192,7 +193,7 @@ def check_estimator(
|
|
192
193
|
default : BaseEstimator
|
193
194
|
Default estimator to return when `estimator` is `None`.
|
194
195
|
|
195
|
-
check_type :
|
196
|
+
check_type : Any
|
196
197
|
Expected type of the estimator to check against.
|
197
198
|
|
198
199
|
Returns
|
@@ -211,7 +212,7 @@ def check_estimator(
|
|
211
212
|
def input_to_array(
|
212
213
|
items: dict | npt.ArrayLike,
|
213
214
|
n_assets: int,
|
214
|
-
fill_value:
|
215
|
+
fill_value: Any,
|
215
216
|
dim: int,
|
216
217
|
assets_names: np.ndarray | None,
|
217
218
|
name: str,
|
@@ -228,7 +229,7 @@ def input_to_array(
|
|
228
229
|
Expected number of assets.
|
229
230
|
Used to verify the shape of the converted array.
|
230
231
|
|
231
|
-
fill_value :
|
232
|
+
fill_value : Any
|
232
233
|
When `items` is a dictionary, elements that are not in `asset_names` are filled
|
233
234
|
with `fill_value` in the converted array.
|
234
235
|
|
@@ -424,7 +425,7 @@ def safe_split(
|
|
424
425
|
|
425
426
|
|
426
427
|
def fit_single_estimator(
|
427
|
-
estimator:
|
428
|
+
estimator: Any,
|
428
429
|
X: npt.ArrayLike,
|
429
430
|
y: npt.ArrayLike | None = None,
|
430
431
|
indices: np.ndarray | None = None,
|
@@ -463,7 +464,7 @@ def fit_single_estimator(
|
|
463
464
|
|
464
465
|
|
465
466
|
def fit_and_predict(
|
466
|
-
estimator:
|
467
|
+
estimator: Any,
|
467
468
|
X: npt.ArrayLike,
|
468
469
|
y: npt.ArrayLike | None,
|
469
470
|
train: np.ndarray,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: skfolio
|
3
|
-
Version: 0.1
|
3
|
+
Version: 0.2.1
|
4
4
|
Summary: Portfolio optimization built on top of scikit-learn
|
5
5
|
Author-email: Hugo Delatte <delatte.hugo@gmail.com>
|
6
6
|
Maintainer-email: Hugo Delatte <delatte.hugo@gmail.com>
|
@@ -84,37 +84,37 @@ Requires-Dist: ruff ; extra == 'tests'
|
|
84
84
|
|
85
85
|
.. -*- mode: rst -*-
|
86
86
|
|
87
|
-
|Licence|
|
87
|
+
|Licence| |Codecov| |Black| |PythonVersion| |PyPi| |CI/CD| |Downloads| |Ruff| |Contribution| |Website|
|
88
88
|
|
89
89
|
.. |Licence| image:: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg
|
90
|
-
|
90
|
+
:target: https://github.com/skfolio/skfolio/blob/main/LICENSE
|
91
91
|
|
92
92
|
.. |Codecov| image:: https://codecov.io/gh/skfolio/skfolio/graph/badge.svg?token=KJ0SE4LHPV
|
93
|
-
|
93
|
+
:target: https://codecov.io/gh/skfolio/skfolio
|
94
94
|
|
95
|
-
.. |PythonVersion| image:: https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue
|
96
|
-
|
95
|
+
.. |PythonVersion| image:: https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue.svg
|
96
|
+
:target: https://pypi.org/project/skfolio/
|
97
97
|
|
98
98
|
.. |PyPi| image:: https://img.shields.io/pypi/v/skfolio
|
99
|
-
|
99
|
+
:target: https://pypi.org/project/skfolio
|
100
100
|
|
101
101
|
.. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
102
|
-
|
102
|
+
:target: https://github.com/psf/black
|
103
103
|
|
104
|
-
.. |CI/CD| image:: https://img.shields.io/github/actions/workflow/status/skfolio/skfolio/release.yml?logo=github
|
105
|
-
|
104
|
+
.. |CI/CD| image:: https://img.shields.io/github/actions/workflow/status/skfolio/skfolio/release.yml.svg?logo=github
|
105
|
+
:target: https://github.com/skfolio/skfolio/raw/main/LICENSE
|
106
106
|
|
107
107
|
.. |Downloads| image:: https://static.pepy.tech/badge/skfolio
|
108
|
-
|
108
|
+
:target: https://pepy.tech/project/skfolio
|
109
109
|
|
110
110
|
.. |Ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
111
|
-
|
111
|
+
:target: https://github.com/astral-sh/ruff
|
112
112
|
|
113
113
|
.. |Contribution| image:: https://img.shields.io/badge/Contributions-Welcome-blue
|
114
|
-
|
114
|
+
:target: https://github.com/skfolio/skfolio/blob/main/CONTRIBUTING.md
|
115
115
|
|
116
|
-
.. |Website| image:: https://img.shields.io/website
|
117
|
-
|
116
|
+
.. |Website| image:: https://img.shields.io/website.svg?down_color=red&down_message=down&up_color=53cc0d&up_message=up&url=https://skfolio.org
|
117
|
+
:target: https://skfolio.org
|
118
118
|
|
119
119
|
.. |PythonMinVersion| replace:: 3.10
|
120
120
|
.. |NumpyMinVersion| replace:: 1.23.4
|
@@ -563,7 +563,7 @@ Black & Litterman Factor Model
|
|
563
563
|
------------------------------
|
564
564
|
.. code-block:: python
|
565
565
|
|
566
|
-
factor_views = ["MTUM - QUAL == 0.03 ", "
|
566
|
+
factor_views = ["MTUM - QUAL == 0.03 ", "VLUE == 0.06"]
|
567
567
|
model = MeanRisk(
|
568
568
|
objective_function=ObjectiveFunction.MAXIMIZE_RATIO,
|
569
569
|
prior_estimator=FactorModel(
|
@@ -17,8 +17,8 @@ skfolio/measures/_enums.py,sha256=NJcngwg9b2JMMiekwkWU9POfnDvgfUgtYtyV2VSFDVM,89
|
|
17
17
|
skfolio/measures/_measures.py,sha256=VU9tahJKJvt8PaJZNxWkmFkFU4PsN0FfYO32Je6D53E,16829
|
18
18
|
skfolio/metrics/__init__.py,sha256=MomHJ5_bgjq4qUwGS2bfhNmG_ld0oQ4wK6y0Yy_Eonc,75
|
19
19
|
skfolio/metrics/_scorer.py,sha256=h1VuZk-zzn4rIChHl9FvM7RxqVT3b-jR1CEB-cr9F2s,4306
|
20
|
-
skfolio/model_selection/__init__.py,sha256=
|
21
|
-
skfolio/model_selection/_combinatorial.py,sha256=
|
20
|
+
skfolio/model_selection/__init__.py,sha256=8j9Z5tpbgBScjFbn8ZsCm_6rZO7RkPQ1QIF8BqYMVA8,507
|
21
|
+
skfolio/model_selection/_combinatorial.py,sha256=DHfbnjqaOGV5bT_9pGuoR23JN6iEp8Tn0Kr6sLq1Xmg,19045
|
22
22
|
skfolio/model_selection/_validation.py,sha256=Rs3j6LFfm8HJCnaoUXlPnNsqxIyjRmMhmGWvvJ0olXA,7686
|
23
23
|
skfolio/model_selection/_walk_forward.py,sha256=pbMD8VM1OoKiEtrlPeCLG29xVijsq1RArSjTvGO1FnU,7539
|
24
24
|
skfolio/moments/__init__.py,sha256=BaP6FjU-CEsV_mcC2AOKXNFy-_wXZsk-jqzJG6mTpsM,746
|
@@ -29,26 +29,26 @@ skfolio/moments/expected_returns/__init__.py,sha256=NETEcKKYKsQvzUU4ZIq6mgT14zdr
|
|
29
29
|
skfolio/moments/expected_returns/_base.py,sha256=xk9mzi48uCOHaMTGQBMr3FU7Ai_shxYhmGeOsVwjv9Q,871
|
30
30
|
skfolio/moments/expected_returns/_expected_returns.py,sha256=T_tt_dsYgXrjIM2REUii7_4_R8IEc3xdtuv45yYM-5o,13388
|
31
31
|
skfolio/optimization/__init__.py,sha256=vXIbwWJL48zJ1jy7ZB2PPBVx7rZo0vVA8QQQuD-L6ts,1021
|
32
|
-
skfolio/optimization/_base.py,sha256=
|
32
|
+
skfolio/optimization/_base.py,sha256=LoRONJP70AwbFpdgqVS_g145pCx0JGkazjWvkQzT_iM,5748
|
33
33
|
skfolio/optimization/cluster/__init__.py,sha256=M3xVdYhNKp4e9CB7hzb4yjTxkkNCHh7Mt_KGFFrkOgs,388
|
34
34
|
skfolio/optimization/cluster/_nco.py,sha256=MKzNvDIAbqS_D41Vp7tNa7OrXYq_bYVSQEVbVCHOEWA,14834
|
35
35
|
skfolio/optimization/cluster/hierarchical/__init__.py,sha256=YnfcPHvjwB6kcG4hoQqc0NqIJKaG7OjBtmXNbOxCq08,405
|
36
|
-
skfolio/optimization/cluster/hierarchical/_base.py,sha256=
|
37
|
-
skfolio/optimization/cluster/hierarchical/_herc.py,sha256=
|
38
|
-
skfolio/optimization/cluster/hierarchical/_hrp.py,sha256=
|
36
|
+
skfolio/optimization/cluster/hierarchical/_base.py,sha256=BVJ8Yq2Yg3Z0iqgx0HwS2OxPLVruoSzrelyYQmt_AiQ,17281
|
37
|
+
skfolio/optimization/cluster/hierarchical/_herc.py,sha256=p7VdEeJOD5ykA1H9FOLaEqIL6D_YYF8VXXfAqTSRdjs,17238
|
38
|
+
skfolio/optimization/cluster/hierarchical/_hrp.py,sha256=i3bliOjWxDX3V3vekhl_Gf_MZ4HroU4r_3rAR-QnyK0,16123
|
39
39
|
skfolio/optimization/convex/__init__.py,sha256=F6BPFikTo0B-7JCKazqLGEwM3RkgTNbFm5GAGkaq9Uo,570
|
40
|
-
skfolio/optimization/convex/_base.py,sha256=
|
41
|
-
skfolio/optimization/convex/_distributionally_robust.py,sha256=
|
42
|
-
skfolio/optimization/convex/_maximum_diversification.py,sha256=
|
43
|
-
skfolio/optimization/convex/_mean_risk.py,sha256=
|
44
|
-
skfolio/optimization/convex/_risk_budgeting.py,sha256=
|
40
|
+
skfolio/optimization/convex/_base.py,sha256=xvhgSQ5X7Lqz6kzGtldlDLIMV61gwrphW9_qMQSG498,75439
|
41
|
+
skfolio/optimization/convex/_distributionally_robust.py,sha256=PHAZvXb_zRXpeDD90fgxVEQAuYH5EY8fTpYQ8jO9shA,17311
|
42
|
+
skfolio/optimization/convex/_maximum_diversification.py,sha256=vqUJZdR28X75LQ18XXWLGd7yX9-f5wvOByN0UVSyd_Y,19140
|
43
|
+
skfolio/optimization/convex/_mean_risk.py,sha256=q9aH_thsmKeK_3-8jQOLN1S9z9DXvZmYGgj0a2ezAl0,43134
|
44
|
+
skfolio/optimization/convex/_risk_budgeting.py,sha256=pRMTA-CCTsV-yysADA3b9RDjLGPk4Jw0eP5ZVQ5W_sc,23622
|
45
45
|
skfolio/optimization/ensemble/__init__.py,sha256=8TXxcxH2_gG3C1xtgQj9OHHr0Le8lhdejtlURL6T3ZY,158
|
46
46
|
skfolio/optimization/ensemble/_base.py,sha256=GaNDQu6ivosYuwMrb-b0PhToCsNrmhSYyXkxeM8W4rU,3399
|
47
47
|
skfolio/optimization/ensemble/_stacking.py,sha256=2PodXf_JahToShSAbwQRXosEer3ESOAhpsnB6SCwztk,13148
|
48
48
|
skfolio/optimization/naive/__init__.py,sha256=Dkr55R48urC-jfYN007NTbei16N91Na_EDYLVqzhGgQ,147
|
49
|
-
skfolio/optimization/naive/_naive.py,sha256=
|
49
|
+
skfolio/optimization/naive/_naive.py,sha256=lXBUK0-JZd_30xUqnwv3IN1-yvKNVrub2A4ZVdfjmnY,5570
|
50
50
|
skfolio/population/__init__.py,sha256=rsPPMUv95aTK7vmpPeQwF8NzFuBwk6RDo5g4HNaPzNM,80
|
51
|
-
skfolio/population/_population.py,sha256=
|
51
|
+
skfolio/population/_population.py,sha256=DNBjR2Ob_Rz3B70mwLIrWd_ElOvHxDWjN3EQs-rGHCk,29148
|
52
52
|
skfolio/portfolio/__init__.py,sha256=YYtcAPmA2zeCxFGTXegg2FXcA7py6CxOX7IMTdYuXl0,586
|
53
53
|
skfolio/portfolio/_base.py,sha256=jO6YKXu8oxE-uldl2CayLvbA40yfeCA1vNa_xm-WcWo,38313
|
54
54
|
skfolio/portfolio/_multi_period_portfolio.py,sha256=63RwL0oTn-3L5LON6f2e9J58zw9q8S2gQ5c8JfSnN28,22759
|
@@ -59,9 +59,9 @@ skfolio/preprocessing/__init__.py,sha256=15A1bzfPsbfxxXgGP1gstf4R0E_347Wn18z5W5j
|
|
59
59
|
skfolio/preprocessing/_returns.py,sha256=_7UtXugQPWitNrrZ3M2dUOAun8aVr0lI45Ms6KFiS94,3826
|
60
60
|
skfolio/prior/__init__.py,sha256=jql8NTiWlykPKJUXTOPdqm531mP8Pul1QAR6hXTXA6c,446
|
61
61
|
skfolio/prior/_base.py,sha256=Dx6rX0X6ymDiieFOI-ik3xMNNFhYEtwLSXOdajf5wZY,1927
|
62
|
-
skfolio/prior/_black_litterman.py,sha256=
|
63
|
-
skfolio/prior/_empirical.py,sha256=
|
64
|
-
skfolio/prior/_factor_model.py,sha256=
|
62
|
+
skfolio/prior/_black_litterman.py,sha256=sVx8113xeP4B6LA4rICKp0cgw7w3F46aQzIQY_34QwQ,9400
|
63
|
+
skfolio/prior/_empirical.py,sha256=eHBSXVzIEvA2oBAnv6HW9D8iy3xKDxpCN_xsg8j8Ye8,5821
|
64
|
+
skfolio/prior/_factor_model.py,sha256=xC-womM7oC91jRaIQP7MSiRPkwm5b7pkWHi-5mOb2jY,9665
|
65
65
|
skfolio/uncertainty_set/__init__.py,sha256=LlMHtYv9G9fgtM7m4sCSToS9et57Pm2Q2gGchTVrj6c,617
|
66
66
|
skfolio/uncertainty_set/_base.py,sha256=rZ3g2AhDKFQTPajgh6Fz5S5TTf0qM4Ie6RGxPhp32D8,3301
|
67
67
|
skfolio/uncertainty_set/_bootstrap.py,sha256=Rig8ZGc8rEOnYygPyTaDSu5lp3wGuZrQVRoJTm3UuAA,10301
|
@@ -71,11 +71,11 @@ skfolio/utils/bootstrap.py,sha256=3zY2kO_GQURKEcQMCasJOSByde9Mt2IAi3KJH0_a4mk,35
|
|
71
71
|
skfolio/utils/equations.py,sha256=w0HsYjA7cS0mHYsI9MpixHLkof3HN26nc14ZfqFrHlE,11047
|
72
72
|
skfolio/utils/sorting.py,sha256=lSjMvH2L-sSj-06B3MlwBrH1rtjCeGEe4hG894W7TE0,3504
|
73
73
|
skfolio/utils/stats.py,sha256=IP36nMc5j5Hcqjbg7lvDIsGp1GWRdOh5jU3W6Z8nkYs,13132
|
74
|
-
skfolio/utils/tools.py,sha256=
|
74
|
+
skfolio/utils/tools.py,sha256=xa42f7U3Ki8-CJS6g8w7bKCLI_QMJ8D6LxLBjlEM7Ok,15374
|
75
75
|
skfolio/utils/fixes/__init__.py,sha256=knHau8PRZP07XDHR59CW8VWxkpTP0gdr6RAHJrO-zaA,95
|
76
76
|
skfolio/utils/fixes/_dendrogram.py,sha256=9aIhSnMwpQHJhQx7IpXC3jlw6YJ3H4XQnnx_d4nMllQ,13551
|
77
|
-
skfolio-0.1.
|
78
|
-
skfolio-0.1.
|
79
|
-
skfolio-0.1.
|
80
|
-
skfolio-0.1.
|
81
|
-
skfolio-0.1.
|
77
|
+
skfolio-0.2.1.dist-info/LICENSE,sha256=F6Gi-ZJX5BlVzYK8R9NcvAkAsKa7KO29xB1OScbrH6Q,1526
|
78
|
+
skfolio-0.2.1.dist-info/METADATA,sha256=OzW5QNHb_EG_jHtP5lPqWas8EAMyl6FPgS3XP6CXulQ,19585
|
79
|
+
skfolio-0.2.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
80
|
+
skfolio-0.2.1.dist-info/top_level.txt,sha256=NXEaoS9Ms7t32gxkb867nV0OKlU0KmssL7IJBVo0fJs,8
|
81
|
+
skfolio-0.2.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|