RiskLabAI 1.0.2__tar.gz → 1.0.4__tar.gz
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.
- {risklabai-1.0.2 → risklabai-1.0.4}/PKG-INFO +1 -1
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/__init__.py +1 -1
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/backtest_overfitting_simulation.py +16 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/backtest_statistics.py +6 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/bet_sizing.py +8 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/probabilistic_sharpe_ratio.py +6 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/probability_of_backtest_overfitting.py +9 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/__init__.py +4 -4
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/clustered_feature_importance_mda.py +14 -3
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/feature_importance_mda.py +10 -4
- risklabai-1.0.4/RiskLabAI/utils/publication_plots.py +180 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI.egg-info/PKG-INFO +1 -1
- {risklabai-1.0.2 → risklabai-1.0.4}/pyproject.toml +1 -1
- risklabai-1.0.2/RiskLabAI/utils/publication_plots.py +0 -75
- {risklabai-1.0.2 → risklabai-1.0.4}/LICENSE +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/README.md +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/backtest_synthetic_data.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/strategy_risk.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/test_set_overfitting.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/adaptive_combinatorial_purged.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/bagged_combinatorial_purged.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/combinatorial_purged.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/cross_validator_controller.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/cross_validator_factory.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/cross_validator_interface.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/kfold.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/purged_kfold.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/walk_forward.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/cluster/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/cluster/clustering.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/controller/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/controller/bars_initializer.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/controller/data_structure_controller.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/denoise/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/denoise/denoising.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/differentiation/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/differentiation/differentiation.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/distance/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/distance/distance_metric.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/labeling/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/labeling/financial_labels.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/labeling/labeling.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/structures/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/structures/abstract_bars.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/structures/abstract_imbalance_bars.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/structures/abstract_information_driven_bars.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/structures/abstract_run_bars.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/structures/imbalance_bars.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/structures/run_bars.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/structures/standard_bars.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/structures/time_bars.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/synthetic_data/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/synthetic_data/drift_burst_hypothesis.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/synthetic_data/simulation.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/synthetic_data/synthetic_controlled_environment.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/weights/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/weights/sample_weights.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/ensemble/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/ensemble/bagging_classifier_accuracy.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/ensemble/empirical_bagging_accuracy.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/entropy_features/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/entropy_features/entropy.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/entropy_features/kontoyiannis.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/entropy_features/lempel_ziv.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/entropy_features/plug_in.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/entropy_features/pmf.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/entropy_features/shannon.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/clustered_feature_importance_mdi.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/feature_importance_controller.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/feature_importance_factory.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/feature_importance_mdi.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/feature_importance_sfi.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/feature_importance_strategy.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/generate_synthetic_data.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/orthogonal_features.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/weighted_tau.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/microstructural_features/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/microstructural_features/bekker_parkinson_volatility_estimator.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/microstructural_features/corwin_schultz.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/structural_breaks/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/structural_breaks/structural_breaks.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/hpc/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/hpc/hpc.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/optimization/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/optimization/hedging.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/optimization/hrp.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/optimization/hyper_parameter_tuning.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/optimization/nco.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/pde/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/pde/equation.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/pde/model.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/pde/solver.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/utils/__init__.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/utils/constants.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/utils/ewma.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/utils/momentum_mean_reverting_strategy_sides.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/utils/progress.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/utils/smoothing_average.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/utils/update_figure_layout.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/utils/utilities_lopez.py +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI.egg-info/SOURCES.txt +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI.egg-info/dependency_links.txt +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI.egg-info/entry_points.txt +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI.egg-info/requires.txt +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI.egg-info/top_level.txt +0 -0
- {risklabai-1.0.2 → risklabai-1.0.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: RiskLabAI
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: Financial AI using Python, based on 'Advances in Financial Machine Learning' and 'Machine Learning for Asset Managers'.
|
|
5
5
|
Author-email: RiskLab <arian@risklab.ai>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -16,6 +16,7 @@ from .backtest_statistics import (
|
|
|
16
16
|
calculate_hhi_concentration,
|
|
17
17
|
calculate_hhi,
|
|
18
18
|
compute_drawdowns_time_under_water,
|
|
19
|
+
sharpe_ratio as pbo_sharpe_ratio,
|
|
19
20
|
)
|
|
20
21
|
from .backtest_synthetic_data import synthetic_back_testing
|
|
21
22
|
from .bet_sizing import (
|
|
@@ -52,7 +53,6 @@ from .test_set_overfitting import (
|
|
|
52
53
|
strategy_type2_error_probability,
|
|
53
54
|
)
|
|
54
55
|
from .probability_of_backtest_overfitting import (
|
|
55
|
-
sharpe_ratio as pbo_sharpe_ratio, # aliased
|
|
56
56
|
performance_evaluation,
|
|
57
57
|
probability_of_backtest_overfitting,
|
|
58
58
|
)
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Orchestrates complex backtest overfitting and simulation scenarios,
|
|
3
|
+
including PBO, DSR, and hardware performance profiling.
|
|
4
|
+
|
|
5
|
+
## TODO:
|
|
6
|
+
- [ ] **Refactor Feature Generation:** Move hardcoded parameters in
|
|
7
|
+
`financial_features_backtest_overfitting_simulation`
|
|
8
|
+
(e.g., volatility=100, rolling=20, TA-lib windows)
|
|
9
|
+
to a configuration dictionary or function arguments.
|
|
10
|
+
- [ ] **Refactor Profiling:** Move hardware/performance functions
|
|
11
|
+
(`get_cpu_info`, `format_cpu_info`,
|
|
12
|
+
`measure_computational_requirements`, etc.)
|
|
13
|
+
to a separate `RiskLabAI.utils.profiling` module
|
|
14
|
+
to reduce this module's responsibilities.
|
|
15
|
+
"""
|
|
16
|
+
|
|
1
17
|
import platform
|
|
2
18
|
import time
|
|
3
19
|
import numpy as np
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Calculates various backtest statistics like holding period,
|
|
3
3
|
concentration, and drawdowns.
|
|
4
|
+
|
|
5
|
+
## TODO:
|
|
6
|
+
- [ ] Optimize `calculate_holding_period` with Numba (@jit)
|
|
7
|
+
to improve performance, similar to the `sharpe_ratio` function.
|
|
8
|
+
- [ ] Add an `annualized_sharpe_ratio` helper function that
|
|
9
|
+
wraps the Numba `sharpe_ratio` and scales it by sqrt(freq).
|
|
4
10
|
"""
|
|
5
11
|
|
|
6
12
|
from typing import Tuple, Optional
|
|
@@ -3,6 +3,14 @@ Functions for calculating bet size based on model probabilities and
|
|
|
3
3
|
other strategy parameters.
|
|
4
4
|
|
|
5
5
|
Includes implementations from de Prado (2018).
|
|
6
|
+
|
|
7
|
+
## TODO:
|
|
8
|
+
- [ ] **HPC Dependency:** The `mpPandasObj` placeholder in
|
|
9
|
+
`strategy_bet_sizing` and `avgActiveSignals` should be
|
|
10
|
+
hardened. If `RiskLabAI.hpc` is a core dependency,
|
|
11
|
+
consider raising an `ImportError` instead of using a
|
|
12
|
+
placeholder that returns an empty DataFrame, which could
|
|
13
|
+
fail silently.
|
|
6
14
|
"""
|
|
7
15
|
|
|
8
16
|
from typing import Optional, Any
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Implements the Probabilistic Sharpe Ratio (PSR) and related metrics
|
|
3
3
|
as described by Marcos Lopez de Prado.
|
|
4
|
+
|
|
5
|
+
## TODO:
|
|
6
|
+
- [ ] Add a `compute_psr_curve` helper function (as seen in the
|
|
7
|
+
original notebook) that iterates `probabilistic_sharpe_ratio`
|
|
8
|
+
over a range of `observed_sharpe_ratio` values to
|
|
9
|
+
easily plot the PSR curve.
|
|
4
10
|
"""
|
|
5
11
|
|
|
6
12
|
from typing import List
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/probability_of_backtest_overfitting.py
RENAMED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Implements the Probability of Backtest Overfitting (PBO) calculation.
|
|
3
|
+
|
|
4
|
+
## TODO:
|
|
5
|
+
- [ ] Add a `get_pbo` wrapper function (as seen in the
|
|
6
|
+
original notebook) that simplifies the call to
|
|
7
|
+
`probability_of_backtest_overfitting` and returns
|
|
8
|
+
only the PBO value.
|
|
9
|
+
- [ ] Add a `pbo_overfitting_plot` helper function to
|
|
10
|
+
visualize the logit distribution (as seen in the
|
|
11
|
+
original notebook).
|
|
3
12
|
"""
|
|
4
13
|
|
|
5
14
|
from typing import Tuple, Callable, List, Optional
|
|
@@ -28,10 +28,10 @@ __all__ = [
|
|
|
28
28
|
# Validators
|
|
29
29
|
"KFold",
|
|
30
30
|
"PurgedKFold",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
31
|
+
"WalkForward", # <-- Fix
|
|
32
|
+
"CombinatorialPurged", # <-- Fix
|
|
33
|
+
"BaggedCombinatorialPurged", # <-- Fix
|
|
34
|
+
"AdaptiveCombinatorialPurged", # <-- Fix
|
|
35
35
|
|
|
36
36
|
# Utilities
|
|
37
37
|
"CrossValidatorFactory",
|
|
@@ -18,11 +18,13 @@ class ClusteredFeatureImportanceMDA(FeatureImportanceStrategy):
|
|
|
18
18
|
and measures the decrease in model performance.
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
+
|
|
21
22
|
def __init__(
|
|
22
23
|
self,
|
|
23
24
|
classifier: object,
|
|
24
25
|
clusters: Dict[str, List[str]],
|
|
25
26
|
n_splits: int = 10,
|
|
27
|
+
random_state: int = 42, # <-- ADD THIS
|
|
26
28
|
):
|
|
27
29
|
"""
|
|
28
30
|
Initialize the strategy.
|
|
@@ -39,6 +41,8 @@ class ClusteredFeatureImportanceMDA(FeatureImportanceStrategy):
|
|
|
39
41
|
self.classifier = classifier
|
|
40
42
|
self.clusters = clusters
|
|
41
43
|
self.n_splits = n_splits
|
|
44
|
+
self.random_state = random_state
|
|
45
|
+
|
|
42
46
|
|
|
43
47
|
def compute(self, x: pd.DataFrame, y: pd.Series, **kwargs: Any) -> pd.DataFrame:
|
|
44
48
|
"""
|
|
@@ -68,8 +72,11 @@ class ClusteredFeatureImportanceMDA(FeatureImportanceStrategy):
|
|
|
68
72
|
if score_weights is None:
|
|
69
73
|
score_weights = np.ones(x.shape[0])
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
cv_generator = KFold(n_splits=self.n_splits, shuffle=True, random_state=self.random_state)
|
|
72
78
|
baseline_scores = pd.Series(dtype=float)
|
|
79
|
+
|
|
73
80
|
shuffled_scores = pd.DataFrame(columns=self.clusters.keys(), dtype=float)
|
|
74
81
|
|
|
75
82
|
for i, (train_idx, test_idx) in enumerate(cv_generator.split(X=x)):
|
|
@@ -98,17 +105,21 @@ class ClusteredFeatureImportanceMDA(FeatureImportanceStrategy):
|
|
|
98
105
|
sample_weight=w_test,
|
|
99
106
|
)
|
|
100
107
|
|
|
108
|
+
|
|
101
109
|
# Get scores for each shuffled *cluster*
|
|
110
|
+
rng = np.random.default_rng(self.random_state + i)
|
|
102
111
|
for cluster_name in shuffled_scores.columns:
|
|
103
112
|
x_test_shuffled = x_test.copy(deep=True)
|
|
104
113
|
for feature in self.clusters[cluster_name]:
|
|
105
|
-
|
|
114
|
+
rng.shuffle(x_test_shuffled[feature].values) # <-- This is correct
|
|
106
115
|
|
|
107
116
|
prob = classifier_fit.predict_proba(x_test_shuffled)
|
|
108
117
|
shuffled_scores.loc[i, cluster_name] = -log_loss(
|
|
109
|
-
y_test, prob, labels=self.classifier.classes_
|
|
118
|
+
y_test, prob, labels=self.classifier.classes_,
|
|
119
|
+
sample_weight=w_test
|
|
110
120
|
)
|
|
111
121
|
|
|
122
|
+
|
|
112
123
|
# Calculate importance as the simple drop in score
|
|
113
124
|
importances = shuffled_scores.rsub(baseline_scores, axis=0)
|
|
114
125
|
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/feature_importance_mda.py
RENAMED
|
@@ -17,7 +17,8 @@ class FeatureImportanceMDA(FeatureImportanceStrategy):
|
|
|
17
17
|
much the model's performance (e.g., log loss) decreases.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
def __init__(self, classifier: object, n_splits: int = 10, random_state: int = 42):
|
|
21
22
|
"""
|
|
22
23
|
Initialize the strategy.
|
|
23
24
|
|
|
@@ -30,6 +31,8 @@ class FeatureImportanceMDA(FeatureImportanceStrategy):
|
|
|
30
31
|
"""
|
|
31
32
|
self.classifier = classifier
|
|
32
33
|
self.n_splits = n_splits
|
|
34
|
+
self.random_state = random_state
|
|
35
|
+
|
|
33
36
|
|
|
34
37
|
def compute(self, x: pd.DataFrame, y: pd.Series, **kwargs: Any) -> pd.DataFrame:
|
|
35
38
|
"""
|
|
@@ -58,7 +61,8 @@ class FeatureImportanceMDA(FeatureImportanceStrategy):
|
|
|
58
61
|
if score_weights is None:
|
|
59
62
|
score_weights = np.ones(x.shape[0])
|
|
60
63
|
|
|
61
|
-
|
|
64
|
+
|
|
65
|
+
cv_generator = KFold(n_splits=self.n_splits, shuffle=True, random_state=self.random_state)
|
|
62
66
|
baseline_scores = pd.Series(dtype=float)
|
|
63
67
|
shuffled_scores = pd.DataFrame(columns=x.columns, dtype=float)
|
|
64
68
|
|
|
@@ -90,11 +94,13 @@ class FeatureImportanceMDA(FeatureImportanceStrategy):
|
|
|
90
94
|
)
|
|
91
95
|
|
|
92
96
|
# Get scores for each shuffled feature
|
|
97
|
+
rng = np.random.default_rng(self.random_state + i)
|
|
93
98
|
for feature in x.columns:
|
|
94
99
|
x_test_shuffled = x_test.copy(deep=True)
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
rng.shuffle(x_test_shuffled[feature].values) # <-- USE SEEDED SHUFFLE
|
|
101
|
+
|
|
97
102
|
shuffled_proba = fitted_classifier.predict_proba(x_test_shuffled)
|
|
103
|
+
|
|
98
104
|
shuffled_scores.loc[i, feature] = -log_loss(
|
|
99
105
|
y_test,
|
|
100
106
|
shuffled_proba,
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utilities for creating publication-quality plots with Matplotlib
|
|
3
|
+
and Seaborn, using Times New Roman font and high DPI.
|
|
4
|
+
|
|
5
|
+
Provides 6 themes and a configuration-based saving function.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
import matplotlib.figure as fig # For type hinting
|
|
10
|
+
import seaborn as sns
|
|
11
|
+
import os
|
|
12
|
+
from typing import Optional, Dict, Any
|
|
13
|
+
|
|
14
|
+
# [THEMES dictionary remains the same]
|
|
15
|
+
THEMES: Dict[str, Dict[str, Any]] = {
|
|
16
|
+
'light': {
|
|
17
|
+
'figure.facecolor': '#FFFFFF',
|
|
18
|
+
'axes.facecolor': '#FFFFFF',
|
|
19
|
+
'text.color': '#000000',
|
|
20
|
+
'axes.labelcolor': '#000000',
|
|
21
|
+
'axes.edgecolor': '#000000',
|
|
22
|
+
'xtick.color': '#000000',
|
|
23
|
+
'ytick.color': '#000000',
|
|
24
|
+
'grid.color': '#CCCCCC',
|
|
25
|
+
'legend.facecolor': '#FFFFFF',
|
|
26
|
+
'legend.edgecolor': '#B0B0B0',
|
|
27
|
+
},
|
|
28
|
+
'medium': {
|
|
29
|
+
'figure.facecolor': '#B0B0B0', # A more solid, medium grey
|
|
30
|
+
'axes.facecolor': '#B0B0B0',
|
|
31
|
+
'text.color': '#FFFFFF', # White text (like the dark theme)
|
|
32
|
+
'axes.labelcolor': '#FFFFFF',
|
|
33
|
+
'axes.edgecolor': '#FFFFFF',
|
|
34
|
+
'xtick.color': '#FFFFFF',
|
|
35
|
+
'ytick.color': '#FFFFFF',
|
|
36
|
+
'grid.color': '#E0E0E0', # Lighter grid lines on medium bg
|
|
37
|
+
'legend.facecolor': '#B0B0B0',
|
|
38
|
+
'legend.edgecolor': '#FFFFFF',
|
|
39
|
+
},
|
|
40
|
+
'dark': {
|
|
41
|
+
'figure.facecolor': '#2E2E2E',
|
|
42
|
+
'axes.facecolor': '#2E2E2E',
|
|
43
|
+
'text.color': '#F0F0F0',
|
|
44
|
+
'axes.labelcolor': '#F0F0F0',
|
|
45
|
+
'axes.edgecolor': '#F0F0F0',
|
|
46
|
+
'xtick.color': '#F0F0F0',
|
|
47
|
+
'ytick.color': '#F0F0F0',
|
|
48
|
+
'grid.color': '#6A6A6A',
|
|
49
|
+
'legend.facecolor': '#2E2E2E',
|
|
50
|
+
'legend.edgecolor': '#F0F0F0',
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# --- MODULE-LEVEL CONFIGURATION ---
|
|
55
|
+
# This dictionary will store the settings from setup_publication_style
|
|
56
|
+
_CONFIG = {
|
|
57
|
+
'save_plots': False,
|
|
58
|
+
'save_dir': 'figs'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# --- UPDATED FUNCTION ---
|
|
62
|
+
def setup_publication_style(
|
|
63
|
+
theme: str = 'light',
|
|
64
|
+
quality: int = 300,
|
|
65
|
+
save_plots: bool = False, # <-- New parameter
|
|
66
|
+
save_dir: str = 'figs' # <-- New parameter
|
|
67
|
+
) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Sets the global Matplotlib rcParams and saving configuration.
|
|
70
|
+
|
|
71
|
+
Call this function once at the beginning of your notebook.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
theme : str, optional
|
|
76
|
+
The theme to apply. Defaults to 'light'.
|
|
77
|
+
quality : int, optional
|
|
78
|
+
The DPI for the figures. Defaults to 300.
|
|
79
|
+
save_plots : bool, optional
|
|
80
|
+
Global switch to enable/disable saving plots. Defaults to False.
|
|
81
|
+
save_dir : str, optional
|
|
82
|
+
The directory to save figures in. Defaults to 'figs'.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
# [All the theme parsing and styling code remains the same]
|
|
86
|
+
# ... (omitted for brevity) ...
|
|
87
|
+
is_transparent = False
|
|
88
|
+
base_theme_name = theme
|
|
89
|
+
if theme.endswith('-transparent'):
|
|
90
|
+
is_transparent = True
|
|
91
|
+
base_theme_name = theme.replace('-transparent', '')
|
|
92
|
+
if base_theme_name not in THEMES:
|
|
93
|
+
base_theme_name = 'light'
|
|
94
|
+
params = THEMES[base_theme_name].copy()
|
|
95
|
+
common_params = {
|
|
96
|
+
'font.size': 12, 'axes.labelsize': 12, 'axes.titlesize': 14,
|
|
97
|
+
'axes.titleweight': 'bold', 'xtick.labelsize': 12, 'ytick.labelsize': 12,
|
|
98
|
+
'legend.fontsize': 12, 'legend.title_fontsize': 13,
|
|
99
|
+
'figure.dpi': quality, 'savefig.dpi': quality, 'axes.grid': True,
|
|
100
|
+
'grid.linestyle': '--', 'grid.alpha': 0.7, 'axes.linewidth': 1.2,
|
|
101
|
+
}
|
|
102
|
+
params.update(common_params)
|
|
103
|
+
if is_transparent:
|
|
104
|
+
params['figure.facecolor'] = (0, 0, 0, 0)
|
|
105
|
+
params['axes.facecolor'] = (0, 0, 0, 0)
|
|
106
|
+
params['savefig.transparent'] = True
|
|
107
|
+
params['legend.facecolor'] = (0, 0, 0, 0)
|
|
108
|
+
else:
|
|
109
|
+
params['savefig.transparent'] = False
|
|
110
|
+
try:
|
|
111
|
+
plt.rc('font', family='Times New Roman')
|
|
112
|
+
except:
|
|
113
|
+
print("Warning: Times New Roman not found. Defaulting to serif.")
|
|
114
|
+
plt.rc('font', family='serif')
|
|
115
|
+
plt.rcParams.update(params)
|
|
116
|
+
sns_style = "darkgrid" if base_theme_name == 'dark' else "whitegrid"
|
|
117
|
+
sns.set_style(sns_style, rc=params)
|
|
118
|
+
|
|
119
|
+
# --- Store saving configuration ---
|
|
120
|
+
_CONFIG['save_plots'] = save_plots
|
|
121
|
+
_CONFIG['save_dir'] = save_dir
|
|
122
|
+
|
|
123
|
+
print(f"Matplotlib style updated. Theme: '{theme}', Quality: {quality} DPI.")
|
|
124
|
+
if save_plots:
|
|
125
|
+
print(f"Plot saving enabled. Saving to: '{save_dir}'")
|
|
126
|
+
else:
|
|
127
|
+
print("Plot saving disabled.")
|
|
128
|
+
|
|
129
|
+
# [apply_plot_style function remains exactly the same]
|
|
130
|
+
def apply_plot_style(
|
|
131
|
+
ax: plt.Axes,
|
|
132
|
+
title: str,
|
|
133
|
+
xlabel: str,
|
|
134
|
+
ylabel: str,
|
|
135
|
+
legend_title: Optional[str] = None
|
|
136
|
+
) -> None:
|
|
137
|
+
ax.set_title(title)
|
|
138
|
+
ax.set_xlabel(xlabel)
|
|
139
|
+
ax.set_ylabel(ylabel)
|
|
140
|
+
if ax.get_legend() and legend_title is not None:
|
|
141
|
+
ax.legend(title=legend_title)
|
|
142
|
+
|
|
143
|
+
# --- UPDATED FUNCTION ---
|
|
144
|
+
def finalize_plot(
|
|
145
|
+
fig: fig.Figure,
|
|
146
|
+
filename: str
|
|
147
|
+
) -> None:
|
|
148
|
+
"""
|
|
149
|
+
Shows the plot and saves it *if* saving was enabled in
|
|
150
|
+
setup_publication_style.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
fig : plt.Figure
|
|
155
|
+
The figure object to save.
|
|
156
|
+
filename : str
|
|
157
|
+
The name of the file (e.g., 'model_performance.png').
|
|
158
|
+
This is required, but only used if saving is enabled.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
# --- 1. Save the figure if global switch is on ---
|
|
162
|
+
if _CONFIG['save_plots']:
|
|
163
|
+
save_dir = _CONFIG['save_dir']
|
|
164
|
+
|
|
165
|
+
# Create the directory if it doesn't exist
|
|
166
|
+
os.makedirs(save_dir, exist_ok=True)
|
|
167
|
+
|
|
168
|
+
# Construct the full path
|
|
169
|
+
full_path = os.path.join(save_dir, filename)
|
|
170
|
+
|
|
171
|
+
# Save the figure
|
|
172
|
+
fig.savefig(full_path, bbox_inches='tight')
|
|
173
|
+
|
|
174
|
+
print(f"Figure saved to: {full_path}")
|
|
175
|
+
|
|
176
|
+
# --- 2. Always show the plot ---
|
|
177
|
+
plt.show()
|
|
178
|
+
|
|
179
|
+
# --- 3. Close the figure object ---
|
|
180
|
+
plt.close(fig)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: RiskLabAI
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: Financial AI using Python, based on 'Advances in Financial Machine Learning' and 'Machine Learning for Asset Managers'.
|
|
5
5
|
Author-email: RiskLab <arian@risklab.ai>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Utilities for creating publication-quality plots with Matplotlib
|
|
3
|
-
and Seaborn, using Times New Roman font and high DPI.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import matplotlib.pyplot as plt
|
|
7
|
-
import seaborn as sns
|
|
8
|
-
from typing import Optional, List
|
|
9
|
-
|
|
10
|
-
def setup_publication_style() -> None:
|
|
11
|
-
"""
|
|
12
|
-
Sets the global Matplotlib rcParams for a consistent,
|
|
13
|
-
publication-quality (Times New Roman, 300 DPI) style.
|
|
14
|
-
|
|
15
|
-
Call this function once at the beginning of your notebook.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
# Check if Times New Roman is available
|
|
19
|
-
try:
|
|
20
|
-
plt.rc('font', family='Times New Roman')
|
|
21
|
-
except:
|
|
22
|
-
print("Warning: Times New Roman not found. Defaulting to serif.")
|
|
23
|
-
plt.rc('font', family='serif')
|
|
24
|
-
|
|
25
|
-
params = {
|
|
26
|
-
'font.size': 12,
|
|
27
|
-
'axes.labelsize': 12,
|
|
28
|
-
'axes.titlesize': 14,
|
|
29
|
-
'xtick.labelsize': 12,
|
|
30
|
-
'ytick.labelsize': 12,
|
|
31
|
-
'legend.fontsize': 12,
|
|
32
|
-
'figure.dpi': 300,
|
|
33
|
-
'savefig.dpi': 300,
|
|
34
|
-
'savefig.transparent': True,
|
|
35
|
-
'axes.grid': True,
|
|
36
|
-
'grid.linestyle': '--',
|
|
37
|
-
'grid.alpha': 0.5,
|
|
38
|
-
'axes.edgecolor': 'black',
|
|
39
|
-
'axes.linewidth': 1.2,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
plt.rcParams.update(params)
|
|
43
|
-
sns.set_style("whitegrid", params)
|
|
44
|
-
print("Matplotlib style updated for publication.")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def apply_plot_style(
|
|
48
|
-
ax: plt.Axes,
|
|
49
|
-
title: str,
|
|
50
|
-
xlabel: str,
|
|
51
|
-
ylabel: str,
|
|
52
|
-
legend_title: Optional[str] = None
|
|
53
|
-
) -> None:
|
|
54
|
-
"""
|
|
55
|
-
Applies the standardized style to a specific Matplotlib Axes object.
|
|
56
|
-
|
|
57
|
-
Parameters
|
|
58
|
-
----------
|
|
59
|
-
ax : plt.Axes
|
|
60
|
-
The Matplotlib axes to style.
|
|
61
|
-
title : str
|
|
62
|
-
The title for the plot.
|
|
63
|
-
xlabel : str
|
|
64
|
-
The label for the x-axis.
|
|
65
|
-
ylabel : str
|
|
66
|
-
The label for the y-axis.
|
|
67
|
-
legend_title : str, optional
|
|
68
|
-
Title for the legend, if any.
|
|
69
|
-
"""
|
|
70
|
-
ax.set_title(title, fontsize=14, fontweight='bold')
|
|
71
|
-
ax.set_xlabel(xlabel, fontsize=12)
|
|
72
|
-
ax.set_ylabel(ylabel, fontsize=12)
|
|
73
|
-
|
|
74
|
-
if ax.get_legend():
|
|
75
|
-
ax.legend(title=legend_title, fontsize=12)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/adaptive_combinatorial_purged.py
RENAMED
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/bagged_combinatorial_purged.py
RENAMED
|
File without changes
|
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/cross_validator_controller.py
RENAMED
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/cross_validator_factory.py
RENAMED
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/backtest/validation/cross_validator_interface.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/data/structures/abstract_information_driven_bars.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/feature_importance_mdi.py
RENAMED
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/feature_importance_sfi.py
RENAMED
|
File without changes
|
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/generate_synthetic_data.py
RENAMED
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/feature_importance/orthogonal_features.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/microstructural_features/corwin_schultz.py
RENAMED
|
File without changes
|
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/features/structural_breaks/structural_breaks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{risklabai-1.0.2 → risklabai-1.0.4}/RiskLabAI/utils/momentum_mean_reverting_strategy_sides.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|