]> Piment Noir Git Repositories - freqai-strategies.git/commitdiff
fix(qav3): default thresholds smoothing between the mean and the max/min
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Tue, 29 Jul 2025 23:15:10 +0000 (01:15 +0200)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Tue, 29 Jul 2025 23:15:10 +0000 (01:15 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py
quickadapter/user_data/strategies/QuickAdapterV3.py
quickadapter/user_data/strategies/Utils.py

index e756fbb1ab70f2668a5b5863223c9b600b2834c7..0ce97f7875f30b241bd1845c7bd413775f92ff48 100644 (file)
@@ -51,7 +51,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
     https://github.com/sponsors/robcaulk
     """
 
-    version = "3.7.101"
+    version = "3.7.102"
 
     @cached_property
     def _optuna_config(self) -> dict[str, Any]:
@@ -528,10 +528,10 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
         )
         pred_extrema = pred_df.get(EXTREMA_COLUMN).iloc[-thresholds_candles:]
         thresholds_smoothing = str(
-            self.freqai_info.get("prediction_thresholds_smoothing", "logsumexp")
+            self.freqai_info.get("prediction_thresholds_smoothing", "exp_weighted_mean")
         )
         thresholds_smoothing_methods = {
-            "logsumexp",
+            "exp_weighted_mean",
             "isodata",
             "li",
             "mean",
@@ -540,12 +540,12 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
             "triangle",
             "yen",
         }
-        if thresholds_smoothing == "logsumexp":
-            thresholds_temperature = float(
-                self.freqai_info.get("prediction_thresholds_temperature", 200.0)
+        if thresholds_smoothing == "exp_weighted_mean":
+            thresholds_alpha = float(
+                self.freqai_info.get("prediction_thresholds_alpha", 0.25)
             )
-            return QuickAdapterRegressorV3.logsumexp_min_max(
-                pred_extrema, thresholds_temperature
+            return QuickAdapterRegressorV3.exp_weighted_mean_min_max(
+                pred_extrema, thresholds_alpha
             )
         elif thresholds_smoothing in thresholds_smoothing_methods:
             thresholds_ratio = float(
@@ -560,11 +560,11 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
             )
 
     @staticmethod
-    def logsumexp_min_max(
-        pred_extrema: pd.Series, temperature: float
+    def exp_weighted_mean_min_max(
+        pred_extrema: pd.Series, alpha: float
     ) -> tuple[float, float]:
-        min_val = smoothed_min(pred_extrema, temperature=temperature)
-        max_val = smoothed_max(pred_extrema, temperature=temperature)
+        min_val = smoothed_min(pred_extrema, alpha=alpha)
+        max_val = smoothed_max(pred_extrema, alpha=alpha)
         return min_val, max_val
 
     @staticmethod
@@ -1530,7 +1530,7 @@ def zigzag(
 
     def calculate_slopes_ok_threshold(
         pos: int,
-        min_threshold: float = 0.75,
+        min_threshold: float = 0.85,
         max_threshold: float = 0.95,
     ) -> float:
         volatility_quantile = calculate_volatility_quantile(pos)
@@ -1776,26 +1776,30 @@ def label_objective(
     return np.median(pivots_thresholds), len(pivots_values)
 
 
-def smoothed_max(series: pd.Series, temperature=1.0) -> float:
-    data_array = series.to_numpy()
-    if data_array.size == 0:
+def exponential_weighted_mean(series: pd.Series, alpha: float) -> float:
+    np_array = series.to_numpy()
+    if np_array.size == 0:
         return np.nan
-    if temperature < 0:
-        raise ValueError("temperature must be non-negative")
-    if np.isclose(temperature, 0):
-        return data_array.max()
-    return sp.special.logsumexp(temperature * data_array) / temperature
-
-
-def smoothed_min(series: pd.Series, temperature=1.0) -> float:
-    data_array = series.to_numpy()
-    if data_array.size == 0:
-        return np.nan
-    if temperature < 0:
-        raise ValueError("temperature must be non-negative")
-    if np.isclose(temperature, 0):
-        return data_array.min()
-    return -sp.special.logsumexp(-temperature * data_array) / temperature
+    if np.isclose(alpha, 0):
+        return np.mean(np_array)
+    scaled_data = alpha * np_array
+    max_scaled_data = np.max(scaled_data)
+    if np.isinf(max_scaled_data):
+        return np_array[np.argmax(scaled_data)]
+    shifted_exponentials = np.exp(scaled_data - max_scaled_data)
+    numerator = np.sum(np_array * shifted_exponentials)
+    denominator = np.sum(shifted_exponentials)
+    if np.isclose(denominator, 0):
+        return np.max(np_array)
+    return numerator / denominator
+
+
+def smoothed_max(series: pd.Series, alpha: float = 1.0) -> float:
+    return exponential_weighted_mean(series, alpha)
+
+
+def smoothed_min(series: pd.Series, alpha: float = 1.0) -> float:
+    return exponential_weighted_mean(series, -alpha)
 
 
 def round_to_nearest_int(value: float, step: int) -> int:
index 2e199e81749f2ab6f9d73b0e66f86ab8507416b2..05c10e6f23ed18341bf331bc8b959fa84cff6066 100644 (file)
@@ -65,7 +65,7 @@ class QuickAdapterV3(IStrategy):
     INTERFACE_VERSION = 3
 
     def version(self) -> str:
-        return "3.3.109"
+        return "3.3.110"
 
     timeframe = "5m"
 
index bd1dc39bc9ee62cecc655563b35a94741d756ef8..9d6bd972cce385fbbec329187eb9e42804cc7887 100644 (file)
@@ -451,7 +451,7 @@ def zigzag(
 
     def calculate_slopes_ok_threshold(
         pos: int,
-        min_threshold: float = 0.75,
+        min_threshold: float = 0.85,
         max_threshold: float = 0.95,
     ) -> float:
         volatility_quantile = calculate_volatility_quantile(pos)