]> Piment Noir Git Repositories - freqai-strategies.git/commitdiff
fix(quickadapter): align extrema retention defaults (#304)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Wed, 4 Nov 2026 15:37:25 +0000 (16:37 +0100)
committerGitHub <noreply@github.com>
Wed, 4 Nov 2026 15:37:25 +0000 (16:37 +0100)
quickadapter/README.md
quickadapter/tests/test_label_transformer.py
quickadapter/tests/test_regressor_prediction_history.py
quickadapter/tests/test_regressor_selection.py
quickadapter/user_data/config-template.json
quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py
quickadapter/user_data/strategies/LabelTransformer.py

index c092d4dedeb7da11fb16e39f77a8deb516599f4e..05e7cedc276b4c4e923d95c2be14c0077d0f6621 100644 (file)
@@ -446,7 +446,7 @@ default five splits.
 | freqai.label_prediction.threshold_method    | `mean`           | enum {`mean`,`isodata`,`li`,`minimum`,`otsu`,`triangle`,`yen`,`median`,`soft_extremum`} | Thresholding method for prediction thresholds.                                                                                                                                                                                                                                                        |
 | freqai.label_prediction.soft_extremum_alpha | 12.0             | float >= 0                                                                              | Alpha for `soft_extremum` threshold method.                                                                                                                                                                                                                                                           |
 | freqai.label_prediction.outlier_quantile    | 0.999            | float (0,1)                                                                             | Quantile threshold for predictions outlier filtering.                                                                                                                                                                                                                                                 |
-| freqai.label_prediction.keep_fraction       | 0.0075           | float (0,1]                                                                             | Fraction of extrema used for thresholds. 1 uses all, lower values keep only most significant. Applies to `rank_extrema` and `rank_peaks`; ignored for `partition`.                                                                                                                                    |
+| freqai.label_prediction.keep_fraction       | 0.005            | float (0,1]                                                                             | Fraction of extrema used for thresholds. 1 uses all, lower values keep only most significant. Applies to `rank_extrema` and `rank_peaks`; ignored for `partition`.                                                                                                                                    |
 
 ### Optuna / HPO
 
index 28f12347991772ccc744a65417c7f620730b85c7..9f3a91c0035d72f05b6e6b6fd76360c076ef14f1 100644 (file)
@@ -332,8 +332,8 @@ class LabelTransformerTest(QaTestCase):
         self.assertEqual(DEFAULTS_LABEL_PIPELINE, before)
 
     def test_a_flat_config_dict_is_read_as_the_default_block(self):
-        # The regressor hands over whatever get_label_pipeline_config produced, which may carry
-        # pipeline keys at the top level instead of under "default".
+        # Direct LabelTransformer callers may pass a flat pipeline configuration.
+        # The regressor instead passes the normalized "default"/"columns" mapping.
         #
         # A round trip alone is satisfied by ANY invertible pipeline, including one that
         # ignores the flat dict entirely and falls back to `DEFAULTS_LABEL_PIPELINE`
index 6e87c0a30d75a00fac28853f4176311dba7bd015..03bf6c41e7da2f1f9b69a34c0653bf1f6acc1589 100644 (file)
@@ -9,6 +9,7 @@ import numpy as np
 import pandas as pd
 import scipy as sp
 from freqtrade.freqai.freqai_interface import IFreqaiModel
+from LabelTransformer import DEFAULTS_LABEL_PREDICTION
 from qa_support import PAIR, QaTestCase
 from Utils import _OPTUNA_NAMESPACES, DEFAULTS_LABEL_PIPELINE
 
@@ -132,14 +133,7 @@ def calibrated(
     model = regressor(
         _fit_live_predictions_candles=candles if budget is None else budget,
         label_prediction={
-            "default": {
-                "method": "thresholding",
-                "selection_method": "rank_extrema",
-                "threshold_method": "mean",
-                "outlier_quantile": 0.999,
-                "soft_extremum_alpha": 12.0,
-                "keep_fraction": 0.0075,
-            },
+            "default": dict(DEFAULTS_LABEL_PREDICTION),
             "columns": {},
         },
         min_max_pred=lambda *args, **kwargs: (-0.4, 0.6),
index e823b0285d7c3a28b1b7d764e2b703a2dab7e2d8..98c90137537a3778e1667d3d91bd48d5d6c928dd 100644 (file)
@@ -1233,10 +1233,10 @@ class RegressorSelectionTest(QaTestCase):
         # size 0 is the one case that may legitimately keep nothing, and keep_fraction
         # does not force a candidate out of a set that does not exist.
         self.assertEqual(QuickAdapterRegressorV3._calculate_n_kept_extrema(0, 1.0), 0)
-        self.assertEqual(QuickAdapterRegressorV3._calculate_n_kept_extrema(0, 0.0075), 0)
+        self.assertEqual(QuickAdapterRegressorV3._calculate_n_kept_extrema(0, 0.005), 0)
 
     def test_a_non_empty_extremum_set_always_keeps_at_least_one(self):
-        for keep_fraction in (0.0, 0.0075, 0.5, 1.0):
+        for keep_fraction in (0.0, 0.005, 0.5, 1.0):
             with self.subTest(keep_fraction=keep_fraction):
                 self.assertGreaterEqual(
                     QuickAdapterRegressorV3._calculate_n_kept_extrema(10, keep_fraction), 1
@@ -1244,10 +1244,10 @@ class RegressorSelectionTest(QaTestCase):
 
     def test_the_kept_count_uses_banker_rounding_not_ceiling(self):
         # round() is round-half-to-even: 133 * 0.5 == 66.5 keeps 66, not 67, and a
-        # keep_fraction of 0.0075 over 133 extrema still keeps only the floor of one.
+        # keep_fraction of 0.005 over 133 extrema still keeps only the floor of one.
         self.assertEqual(QuickAdapterRegressorV3._calculate_n_kept_extrema(133, 0.5), 66)
         self.assertEqual(QuickAdapterRegressorV3._calculate_n_kept_extrema(3, 0.5), 2)
-        self.assertEqual(QuickAdapterRegressorV3._calculate_n_kept_extrema(133, 0.0075), 1)
+        self.assertEqual(QuickAdapterRegressorV3._calculate_n_kept_extrema(133, 0.005), 1)
         self.assertEqual(QuickAdapterRegressorV3._calculate_n_kept_extrema(10, 0.5), 5)
         self.assertEqual(QuickAdapterRegressorV3._calculate_n_kept_extrema(10, 1.0), 10)
 
index 70395cb8ec05cfff02f657264a24d95a0975f3f5..def77314a59af362869f82c13186661c70ded684 100644 (file)
     "label_prediction": {
       "method": "thresholding",
       "threshold_method": "isodata",
-      "keep_fraction": 0.0075
+      "keep_fraction": 0.005
       // Alternative per-label format: remove the sibling keys above, then uncomment:
       // "default": {
       //   "method": "thresholding",
index abc1cc835e4c4b4dccce02426838b5d884929cbf..29389d417596a500c4da6ed32e9cf02795296297 100644 (file)
@@ -3603,10 +3603,10 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
         """Chronological train/test split using sklearn's TimeSeriesSplit final fold.
 
         ``n_splits`` controls train/test proportions (higher = larger train).
-        ``gap`` excludes samples between train and test; when 0, auto-derived
-        from ``label_period_candles``. ``max_train_size`` enables sliding
-        window mode. ``weights`` is sliced positionally via ``train_idx`` /
-        ``test_idx``.
+        ``gap`` excludes samples between train and test. When 0, it uses the label
+        horizon in causal mode, otherwise ``label_period_candles``.
+        ``max_train_size`` enables sliding windows. ``weights`` is sliced
+        positionally via ``train_idx`` / ``test_idx``.
         """
         feat_dict = self.ft_params
         causal_mode = self._causal_mode
index b5101426ff79805e422c792e71a9262e65522b09..0d732ccab8888db992d674e2122f65101548385c 100644 (file)
@@ -236,7 +236,7 @@ DEFAULTS_LABEL_PREDICTION: Final[dict[str, Any]] = {
     "threshold_method": SKIMAGE_THRESHOLD_METHODS[0],  # "mean"
     "outlier_quantile": 0.999,
     "soft_extremum_alpha": 12.0,
-    "keep_fraction": 0.0075,
+    "keep_fraction": 0.005,
 }