]> Piment Noir Git Repositories - freqai-strategies.git/commitdiff
fix(zigzag): default normalize to False to prevent label magnitude leak (#71)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Sat, 23 May 2026 22:23:35 +0000 (00:23 +0200)
committerGitHub <noreply@github.com>
Sat, 23 May 2026 22:23:35 +0000 (00:23 +0200)
When set_freqai_targets is invoked by FreqAI's backtesting loop, the dataframe
passed to _generate_extrema_label spans the full historical window
(right-truncated to the current train-window stop), not just train_period_days.
With normalize=True, zigzag applies a global minmax scaling across all detected
pivots in that wider window to amplitudes, amplitude_threshold_ratios,
volume_rates and speeds. The resulting label magnitudes therefore depend on the
global pivot distribution, including pivots outside the current training slice
— a magnitude leak from out-of-train data into training labels.

Switching the zigzag default to normalize=False emits raw log-amplitude values
(|log(P2/P1)|) and defers any scaling to LabelTransformer, which is fitted
strictly on the train slice and is therefore leak-free. The two existing call
sites — _generate_extrema_label (label generation) and label_objective (Optuna
hyperopt) — both want the unnormalized output, so the redundant
normalize=False kwargs are dropped at the call sites in favor of the default.

Strategy and regressor patch versions are bumped to 3.11.8.

Caveat: with apply_label_weighting strategy="combined", the metrics now sit on
heterogeneous scales (raw log-amplitudes ~[0.005, 0.5] mix with bounded ratios
in [0, 1] like efficiency_ratio). Users relying on "combined" aggregation
(power means, weighted_median, softmax) may need to introduce metric-specific
rescaling on the train slice before aggregation. Direction-only (strategy=
"none") and single-metric strategies (e.g. strategy="amplitude") are
unaffected.

quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py
quickadapter/user_data/strategies/QuickAdapterV3.py
quickadapter/user_data/strategies/Utils.py

index 7f7b19bec5f47f810adb3c7151c9760736fa6704..a9aebce4fa6fe98381ebcca6118832970bfddfd8 100644 (file)
@@ -98,7 +98,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
     https://github.com/sponsors/robcaulk
     """
 
-    version = "3.11.7"
+    version = "3.11.8"
 
     _TEST_SIZE: Final[float] = 0.1
 
@@ -3592,7 +3592,6 @@ def label_objective(
         df,
         natr_period=label_period_candles,
         natr_multiplier=label_natr_multiplier,
-        normalize=False,
     )
 
     median_amplitude = np.nanmedian(np.asarray(pivots_amplitudes, dtype=float))
index d7a235854af35e5d4c8887940fab2ed7f73afed3..f02eba277468075aa70477618f7bedf9b98d8744 100644 (file)
@@ -109,7 +109,7 @@ class QuickAdapterV3(IStrategy):
     _PLOT_EXTREMA_MIN_EPS: Final[float] = 0.01
 
     def version(self) -> str:
-        return "3.11.7"
+        return "3.11.8"
 
     timeframe = "5m"
     timeframe_minutes = timeframe_to_minutes(timeframe)
index c5f8e7405cdd918b506942982bf99cce31ed969a..a1def001e1f331e1d15219f061152338961d1cb1 100644 (file)
@@ -1669,7 +1669,7 @@ def zigzag(
     df: pd.DataFrame,
     natr_period: int = 14,
     natr_multiplier: float = 9.0,
-    normalize: bool = True,
+    normalize: bool = False,
 ) -> tuple[
     list[int],
     list[float],