]> Piment Noir Git Repositories - freqai-strategies.git/commit
fix(quickadapter): validate coupled/bounded config inputs at the option layer (#141)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Tue, 28 Jul 2026 12:11:07 +0000 (14:11 +0200)
committerGitHub <noreply@github.com>
Tue, 28 Jul 2026 12:11:07 +0000 (14:11 +0200)
commit3a0acb8bc7c4b04c0d43c3461da766704b68e181
tree0e8d5b28241fb91e4f7ecb91c979b4811f0b26c7
parent371435f2f79dc5b956d8960f2f2d0cb960c4054e
fix(quickadapter): validate coupled/bounded config inputs at the option layer (#141)

* fix(quickadapter): validate smoothing modes per method

* refactor(quickadapter): consolidate smoothing cross-field validation

Route the label_smoothing method x mode check through the shared label-kind
machinery instead of a bespoke loop in the smoothing getter:

- fold the per-kind coupled-field validator into _LABEL_KIND_REGISTRY as a
  third tuple element (single source of truth), with a named
  CrossFieldValidatorFn type alias matching the existing ValidateParamsFn
- get_label_kind_config runs the registered validator on each resolved
  per-column config; all four label-kind getters are now symmetric one-liners
- derive the bot_start wrap/causal-mode check's mode-aware method set from
  SMOOTHING_METHOD_MODES, dropping the duplicate _SMOOTHING_GAUSSIAN_FILTER1D
- README: keep the per-method mode matrix in the type column, drop the
  code-behavior narration from the description

* fix(quickadapter): validate exit calibration and leverage bounds at the option layer

Two config inputs bypassed the option-layer validation their siblings use:

- exit_pricing.thresholds_calibration.decline_quantile was merged raw:
  documented float (0,1) but enforced nowhere (only a consumer-side guard
  that raised TypeError on non-numeric input). Route it through a validated
  get_exit_thresholds_calibration_config using the shared _validate_params
  machinery, with DEFAULTS_EXIT_THRESHOLDS_CALIBRATION as single source of
  truth (drops the duplicate class-var default). Invalid values warn and
  fall back to 0.5.
- leverage() applied only the upper bound; the documented lower bound 1.0
  (README: float [1.0, max_leverage]) and non-numeric guarding were missing.
  Clamp to [1.0, max_leverage], falling back to proposed_leverage on
  non-numeric input.

* fix(quickadapter): validate custom_protections config at the option layer

custom_protections was the last config section read ad-hoc with hard int()/
float() casts that crashed on non-numeric input, inconsistent with the
warn-and-fall-back contract every other section uses.

Add get_custom_protections_config on the shared _validate_params machinery
(new _BoolValidator for the enabled flags; nested cooldown/drawdown/stoploss
sub-dicts validated per section) with single-source DEFAULTS_*; the
protections property consumes the validated, typed config. Invalid or
non-numeric values now warn and fall back to their documented defaults
instead of raising.

* fix(quickadapter): validate fit_live_predictions_candles at the option layer

The last strategy-side config value read with a raw int() cast (protections
and startup_candle_count) crashed on non-numeric input. Route it through a
validated get_fit_live_predictions_candles (positive int, warn and fall back
to the default) on the shared _validate_params machinery; drop the now-unused
DEFAULT_FIT_LIVE_PREDICTIONS_CANDLES import.

* refactor(quickadapter): address PR review nits

- harmonize get_exit_thresholds_calibration_config to accept the parent
  exit_pricing dict and deref thresholds_calibration internally, removing the
  double as_dict at the call site (mirrors get_custom_protections_config)
- reject bool in _NumericValidator: bool is not a valid numeric input,
  consistent with is_finite_number / leverage() / _BoolValidator
- drop the dead commented minimal_roi block referencing the removed
  DEFAULT_FIT_LIVE_PREDICTIONS_CANDLES import
- align comment terminology on 'cross-field' (matches CrossFieldValidatorFn)

* refactor(quickadapter): address second-round review nits

- warn instead of silently resetting when a config section is present but not
  a mapping: new as_config_section helper applied to custom_protections
  (+ cooldown/drawdown/stoploss) and exit_pricing.thresholds_calibration
- re-add default_exit_thresholds_calibration ClassVar as a compat alias to the
  canonical DEFAULTS_EXIT_THRESHOLDS_CALIBRATION (public API stability)
- move the minimal_roi rationale note directly above its assignment

* refactor(quickadapter): address third-round review nits

- Warn on non-mapping config sections by routing the section getters
  (label kinds, exit_pricing, reversal_confirmation, fit_live) through
  as_config_section, matching the custom_protections pattern (N-1).
- Honor the default_exit_thresholds_calibration override via an optional
  overrides argument merged over the canonical defaults; user config
  still wins in _validate_params (N-2).
- Resolve fit_live_predictions_candles once through the canonical
  validator in the regressor so an explicit 0 floors to 100, fixing the
  .iloc[-0:] whole-frame slice (N-3).

* fix(quickadapter): warn on invalid leverage before fallback

Route the configured leverage through a cached validator that logs a
harmonized warning when the value is non-numeric or a boolean before
falling back to proposed_leverage, instead of silently discarding the
user setting. The warning fires once (cached) to avoid per-call spam.

* fix(quickadapter): warn on sub-minimum leverage; cache protections

- Warn once (via the _configured_leverage cached_property) when a numeric
  leverage is below the 1.0 floor before the leverage() hook clamps it;
  the per-pair max_leverage ceiling is only known at entry time, so
  above-ceiling values stay clamped silently.
- Promote protections to a cached_property, aligning it with the sibling
  config-derived accessors and collapsing duplicate warnings on a
  malformed custom_protections/freqai section to one per strategy
  instance (reload re-instantiates the strategy, so the cache is fresh).

* refactor(quickadapter): drop dead FIT_LIVE_PREDICTIONS_CANDLES_DEFAULT

Both consumers were rewired to the resolved self._fit_live_predictions_candles,
leaving the ClassVar and its DEFAULT_FIT_LIVE_PREDICTIONS_CANDLES import unused.
Remove both (not a public-library API surface).

* style(quickadapter): drop redundant comments in exit-calibration getter

The as_dict coercion and defaults merge are self-explanatory; keep
comments only where the code is not clear on its own.

* fix(quickadapter): guard leverage finiteness; warn-once on fit-live warmup

- Reject non-finite leverage (NaN/Inf) via the shared is_finite_number
  guard before falling back to proposed_leverage, instead of letting it
  reach the clamp silently.
- Back startup_candle_count and protections with a cached
  _fit_live_predictions_candles helper so an invalid fit_live_predictions_candles
  warns once instead of on every access. startup_candle_count stays a plain
  property so the StrategyResolver keeps protecting it from config override
  (cached_property is not a property subclass).

* style(quickadapter): drop redundant _LABEL_KIND_REGISTRY comment

The tuple type (CrossFieldValidatorFn | None) and the named unpacking
(cross_field_validator) already document the third element.

* fix(quickadapter): validate exit-calibration override before use

Route the override (e.g. a subclass default_exit_thresholds_calibration)
through _validate_params against the canonical defaults so an invalid
subclass value falls back to the canonical default instead of being
trusted blindly (previously it could be returned as-is with a misleading
warning, or crash on output_type coercion). User config still wins over
the override, which still wins over the canonical default. Also restore a
concise note on the intentional silent parent coercion.

* style(quickadapter): tighten exit-calibration rationale comments
README.md
quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py
quickadapter/user_data/strategies/LabelTransformer.py
quickadapter/user_data/strategies/QuickAdapterV3.py
quickadapter/user_data/strategies/Utils.py