From 371435f2f79dc5b956d8960f2f2d0cb960c4054e Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Mon, 27 Jul 2026 20:54:23 +0200 Subject: [PATCH] fix(quickadapter): enforce reversal NATR fraction bounds (#140) Enforce the documented [0, 1] upper bound for both reversal NATR multiplier fractions at the configuration boundary: - reject config values above 1 (warn and fall back per component) - fail fast with ValueError when canonical defaults exceed max_value - canonicalize the out-of-range component warning to the single "<= {max_value}" form, dropping the wrong "[-inf, x]" notation - clarify the strict min < max ordering in the README tunables table with symmetric "(< upper bound)" / "(> lower bound)" hints Fixes #137 --- README.md | 4 ++-- quickadapter/user_data/strategies/Utils.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5cc51c9..cc15092 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ docker compose up -d --build | _Reversal confirmation_ | | | | | reversal_confirmation.lookback_period_candles | 0 | int >= 0 | Prior confirming candles; 0 = none. | | reversal_confirmation.decay_fraction | 0.5 | float (0,1] | Geometric per-candle volatility adjusted reversal threshold relaxation factor. | -| reversal_confirmation.min_natr_multiplier_fraction | 0.0095 | float [0,1] | Lower bound fraction for volatility adjusted reversal threshold. | -| reversal_confirmation.max_natr_multiplier_fraction | 0.0125 | float [0,1] | Upper bound fraction (>= lower bound) for volatility adjusted reversal threshold. | +| reversal_confirmation.min_natr_multiplier_fraction | 0.0095 | float [0,1] | Lower bound fraction (< upper bound) for volatility adjusted reversal threshold. | +| reversal_confirmation.max_natr_multiplier_fraction | 0.0125 | float [0,1] | Upper bound fraction (> lower bound) for volatility adjusted reversal threshold. | | _Regressor model_ | | | | | freqai.regressor | `xgboost` | enum {`xgboost`,`lightgbm`,`histgradientboostingregressor`,`ngboost`,`catboost`} | Machine learning regressor algorithm. | | _Model training parameters_ | | | | diff --git a/quickadapter/user_data/strategies/Utils.py b/quickadapter/user_data/strategies/Utils.py index 0337dce..f9ce4b5 100644 --- a/quickadapter/user_data/strategies/Utils.py +++ b/quickadapter/user_data/strategies/Utils.py @@ -1281,6 +1281,7 @@ def get_reversal_confirmation_config( allow_equal=False, non_negative=True, finite_only=True, + max_value=1, ) return { @@ -5185,6 +5186,7 @@ def validate_range( allow_equal: bool = False, non_negative: bool = True, finite_only: bool = True, + max_value: float | int | None = None, ) -> tuple[float | int, float | int]: min_name = f"min_{name}" max_name = f"max_{name}" @@ -5201,6 +5203,11 @@ def validate_range( f"Invalid {name}: defaults ordering must have min < max, " f"got min={default_min!r}, max={default_max!r}" ) + if max_value is not None and (default_min > max_value or default_max > max_value): + raise ValueError( + f"Invalid {name}: defaults must be <= {max_value!r}, " + f"got min={default_min!r}, max={default_max!r}" + ) def _validate_component( value: float | int | None, name: str, default_value: float | int @@ -5211,12 +5218,15 @@ def validate_range( if non_negative: constraints.append("non-negative") constraints.append("numeric") + if max_value is not None: + constraints.append(f"<= {max_value}") constraint_str = " ".join(constraints) if ( not isinstance(value, (int, float)) or isinstance(value, bool) or (finite_only and not _is_finite_value(value)) or (non_negative and value < 0) + or (max_value is not None and value > max_value) ): logger.warning( f"Invalid {name} {value!r}: must be {constraint_str}, using default {default_value!r}" -- 2.53.0