]> Piment Noir Git Repositories - freqai-strategies.git/commitdiff
feat(quickadapter): add TOPSIS metric for multi-objective HPO trial selection
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Wed, 31 Dec 2025 00:01:40 +0000 (01:01 +0100)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Wed, 31 Dec 2025 00:01:40 +0000 (01:01 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
README.md
ReforceXY/reward_space_analysis/reward_space_analysis.py
quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py
quickadapter/user_data/strategies/QuickAdapterV3.py

index 11138adde70b24d5b4e9ca3f884fd147a747765a..8c9b3bea452a2df43c34400612076f588316ec37 100644 (file)
--- a/README.md
+++ b/README.md
@@ -88,16 +88,17 @@ docker compose up -d --build
 | freqai.feature_parameters.min_label_natr_multiplier   | 9.0                           | float > 0                                                                                                                                  | Minimum labeling NATR multiplier used for reversals labeling HPO. (Deprecated alias: `freqai.feature_parameters.min_label_natr_ratio`)                                                                                                                                                                                                                                  |
 | freqai.feature_parameters.max_label_natr_multiplier   | 12.0                          | float > 0                                                                                                                                  | Maximum labeling NATR multiplier used for reversals labeling HPO. (Deprecated alias: `freqai.feature_parameters.max_label_natr_ratio`)                                                                                                                                                                                                                                  |
 | freqai.feature_parameters.label_frequency_candles     | `auto`                        | int >= 2 \| `auto`                                                                                                                         | Reversals labeling frequency. `auto` = max(2, 2 \* number of whitelisted pairs).                                                                                                                                                                                                                                                                                        |
-| freqai.feature_parameters.label_metric                | `euclidean`                   | string (supported: `euclidean`,`minkowski`,`cityblock`,`chebyshev`,`mahalanobis`,`seuclidean`,`jensenshannon`,`sqeuclidean`,...)           | Metric used in distance calculations to ideal point.                                                                                                                                                                                                                                                                                                                    |
+| freqai.feature_parameters.label_metric                | `euclidean`                   | string                                                                                                                                     | Metric for Pareto front trial selection (SciPy distance metrics or selection metrics like `topsis`, `medoid`, `kmeans`, `kmedoids`, ...).                                                                                                                                                                                                                               |
 | freqai.feature_parameters.label_weights               | [1/7,1/7,1/7,1/7,1/7,1/7,1/7] | list[float]                                                                                                                                | Per-objective weights used in distance calculations to ideal point. Objectives: (1) number of detected reversals, (2) median swing amplitude, (3) median (swing amplitude / median volatility-threshold ratio), (4) median swing volume per candle, (5) median swing speed, (6) median swing efficiency ratio, (7) median swing volume-weighted efficiency ratio.       |
-| freqai.feature_parameters.label_p_order               | `None`                        | float \| None                                                                                                                              | p-order used by `minkowski` / `power_mean` (optional).                                                                                                                                                                                                                                                                                                                  |
-| freqai.feature_parameters.label_medoid_metric         | `euclidean`                   | string                                                                                                                                     | Metric used with `medoid`.                                                                                                                                                                                                                                                                                                                                              |
-| freqai.feature_parameters.label_kmeans_metric         | `euclidean`                   | string                                                                                                                                     | Metric used for k-means clustering.                                                                                                                                                                                                                                                                                                                                     |
+| freqai.feature_parameters.label_p_order               | `None`                        | float \| None                                                                                                                              | p-order for Minkowski distance. Used by `minkowski`, `power_mean`, `medoid`, `kmeans`, `kmedoids`, `knn`, `topsis` when their sub-metric is `minkowski`.                                                                                                                                                                                                                |
+| freqai.feature_parameters.label_medoid_metric         | `euclidean`                   | string                                                                                                                                     | Distance metric used with `medoid`.                                                                                                                                                                                                                                                                                                                                     |
+| freqai.feature_parameters.label_kmeans_metric         | `euclidean`                   | string                                                                                                                                     | Distance metric used for k-means clustering.                                                                                                                                                                                                                                                                                                                            |
 | freqai.feature_parameters.label_kmeans_selection      | `min`                         | enum {`min`,`medoid`}                                                                                                                      | Strategy to select trial in the best k-means cluster.                                                                                                                                                                                                                                                                                                                   |
-| freqai.feature_parameters.label_kmedoids_metric       | `euclidean`                   | string                                                                                                                                     | Metric used for k-medoids clustering.                                                                                                                                                                                                                                                                                                                                   |
+| freqai.feature_parameters.label_kmedoids_metric       | `euclidean`                   | string                                                                                                                                     | Distance metric used for k-medoids clustering.                                                                                                                                                                                                                                                                                                                          |
 | freqai.feature_parameters.label_kmedoids_selection    | `min`                         | enum {`min`,`medoid`}                                                                                                                      | Strategy to select trial in the best k-medoids cluster.                                                                                                                                                                                                                                                                                                                 |
+| freqai.feature_parameters.label_topsis_metric         | `euclidean`                   | string                                                                                                                                     | Distance metric for TOPSIS ideal/anti-ideal point calculations.                                                                                                                                                                                                                                                                                                         |
 | freqai.feature_parameters.label_knn_metric            | `minkowski`                   | string                                                                                                                                     | Distance metric for KNN.                                                                                                                                                                                                                                                                                                                                                |
-| freqai.feature_parameters.label_knn_p_order           | `None`                        | float \| None                                                                                                                              | Tunable for KNN neighbor distances aggregation methods: p-order (`knn_power_mean`, default: 1.0) or quantile (`knn_quantile`, default: 0.5). (optional)                                                                                                                                                                                                                 |
+| freqai.feature_parameters.label_knn_p_order           | `None`                        | float \| None                                                                                                                              | Tunable for KNN neighbor distances aggregation methods: p-order (`knn_power_mean`, default: 1.0) or quantile (`knn_quantile`, default: 0.5).                                                                                                                                                                                                                            |
 | freqai.feature_parameters.label_knn_n_neighbors       | 5                             | int >= 1                                                                                                                                   | Number of neighbors for KNN.                                                                                                                                                                                                                                                                                                                                            |
 | _Predictions extrema_                                 |                               |                                                                                                                                            |                                                                                                                                                                                                                                                                                                                                                                         |
 | freqai.predictions_extrema.selection_method           | `rank_extrema`                | enum {`rank_extrema`,`rank_peaks`,`partition`}                                                                                             | Extrema selection method. `rank_extrema` ranks extrema values, `rank_peaks` ranks detected peak values, `partition` uses sign-based partitioning.                                                                                                                                                                                                                       |
index 7a4e5372d0d5e305306ab89ba7378de6ea182fa3..bef107468116fdc5ed099e5ddb6e820f2088ac56 100644 (file)
@@ -3250,7 +3250,7 @@ def compute_pbrs_components(
     ----------------------
     R'(s,a,s') = R(s,a,s') + Δ(s,a,s')
 
-    Non Canonical PBRS Formula
+    Non-Canonical PBRS Formula
     --------------------------
     R'(s,a,s') = R(s,a,s') + Δ(s,a,s') + entry_additive + exit_additive
 
index 7c05c64d4f669bf15dcf4106b35823124243520f..949382e4c968df92c50867e6f7e53110035e3f25 100644 (file)
@@ -72,7 +72,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
     https://github.com/sponsors/robcaulk
     """
 
-    version = "3.8.4"
+    version = "3.8.5"
 
     _TEST_SIZE: Final[float] = 0.1
 
@@ -170,6 +170,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
         "knn_min",
         "knn_max",
         "medoid",
+        "topsis",
     )
 
     _METRICS: Final[tuple[str, ...]] = (
@@ -685,6 +686,18 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
                     label_p_order_reason = (
                         f"{label_metric} (via label_knn_metric={label_knn_metric})"
                     )
+            elif (
+                label_metric == QuickAdapterRegressorV3._CUSTOM_METRICS[17]
+            ):  # "topsis"
+                label_topsis_metric = self.ft_params.get(
+                    "label_topsis_metric",
+                    QuickAdapterRegressorV3._SCIPY_METRICS[2],  # "euclidean" default
+                )
+                if (
+                    label_topsis_metric == QuickAdapterRegressorV3._SCIPY_METRICS[5]
+                ):  # "minkowski"
+                    label_p_order_is_used = True
+                    label_p_order_reason = f"{label_metric} (via label_topsis_metric={label_topsis_metric})"
 
             if label_p_order_config is not None:
                 logger.info(
@@ -808,6 +821,16 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
                     f"  label_knn_p_order: {format_number(label_knn_p_order_default)} (default for {label_metric})"
                 )
 
+            label_topsis_metric_config = self.ft_params.get("label_topsis_metric")
+            if label_topsis_metric_config is not None:
+                logger.info(f"  label_topsis_metric: {label_topsis_metric_config}")
+            elif (
+                label_metric == QuickAdapterRegressorV3._CUSTOM_METRICS[17]
+            ):  # "topsis"
+                logger.info(
+                    f"  label_topsis_metric: {QuickAdapterRegressorV3._SCIPY_METRICS[2]} (default for {label_metric})"
+                )
+
         logger.info("Predictions Extrema Configuration:")
         predictions_extrema = self.predictions_extrema
         logger.info(
@@ -2191,6 +2214,39 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
                 return np.nanmin(neighbor_distances, axis=1)
             elif metric == QuickAdapterRegressorV3._CUSTOM_METRICS[15]:  # "knn_max"
                 return np.nanmax(neighbor_distances, axis=1)
+        elif metric == QuickAdapterRegressorV3._CUSTOM_METRICS[17]:  # "topsis"
+            # TOPSIS (Hwang & Yoon, 1981): returns D+ / (D+ + D-) for argmin selection
+            # where D+ = distance to ideal [1,1,...], D- = distance to anti-ideal [0,0,...]
+            label_topsis_metric = self.ft_params.get(
+                "label_topsis_metric",
+                QuickAdapterRegressorV3._SCIPY_METRICS[2],  # "euclidean"
+            )
+            cdist_kwargs: dict[str, Any] = {
+                "metric": label_topsis_metric,
+                "w": np_weights,
+            }
+            if (
+                label_topsis_metric
+                == QuickAdapterRegressorV3._SCIPY_METRICS[5]  # "minkowski"
+            ):
+                cdist_kwargs["p"] = (
+                    label_p_order
+                    if label_p_order is not None and np.isfinite(label_p_order)
+                    else self._get_label_p_order_default(label_topsis_metric)
+                )
+            dist_to_ideal = sp.spatial.distance.cdist(
+                normalized_matrix, ideal_point_2d, **cdist_kwargs
+            ).flatten()
+            dist_to_anti_ideal = sp.spatial.distance.cdist(
+                normalized_matrix, np.zeros((1, n_objectives)), **cdist_kwargs
+            ).flatten()
+
+            denominator = dist_to_ideal + dist_to_anti_ideal
+            zero_mask = np.isclose(denominator, 0.0)
+            denominator[zero_mask] = 1.0
+            topsis_score = dist_to_ideal / denominator
+            topsis_score[zero_mask] = 0.5
+            return topsis_score
         else:
             raise ValueError(
                 f"Invalid label metric {metric!r}. Supported: {', '.join(metrics)}"
index 00fa93cbd9299b4d21fe626ff2da76e6d788fc05..1b3a467ba76e96cb46028f4525509f9e42ab0f72 100644 (file)
@@ -106,7 +106,7 @@ class QuickAdapterV3(IStrategy):
     _TRADING_MODES: Final[tuple[TradingMode, ...]] = ("spot", "margin", "futures")
 
     def version(self) -> str:
-        return "3.8.4"
+        return "3.8.5"
 
     timeframe = "5m"