]> Piment Noir Git Repositories - freqai-strategies.git/commit
fix(quickadapter): address PR #78-#81 review-comment findings (#90)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Mon, 22 Jun 2026 00:07:11 +0000 (02:07 +0200)
committerGitHub <noreply@github.com>
Mon, 22 Jun 2026 00:07:11 +0000 (02:07 +0200)
commit7390cdc426768252842e528c22dfcf20f11f7be1
treef4fc237ea20ddbdac7b98a14b3ad9776c2dde8e0
parentadd1fb785a308ea27ad862dd87165770a2d499f0
fix(quickadapter): address PR #78-#81 review-comment findings (#90)

Consolidates P1/P2 findings from `chatgpt-codex-connector` review
comments on PRs #78, #79, #80, #81, and PR #90.

Utils.py + label generation:
- `_generate_extrema_label` accepts `logger: Logger | None`; the
  `LabelGenerator` type signature, `generate_label_data` dispatcher,
  and `QuickAdapterV3.set_freqai_targets` caller propagate the logger.
  `_generate_extrema_label` has no `F821 logger` undefined-name path.
- `register_label_generator` routes the input through
  `_adapt_label_generator`. The adapter detects the canonical
  `(dataframe, params, logger)` shape by a positional parameter named
  `logger` at index 2 (with or without a default); other generators
  with 2 required positional parameters are wrapped via
  `functools.wraps` (preserves `__name__`/`__doc__`/`__wrapped__`) to
  drop the logger argument at dispatch, with defaulted positionals
  after index 1 left at their defaults. `ValueError` is raised at
  registration for `*args`, `**kwargs`, keyword-only `logger`, fewer
  than 2 required positionals, more than 3 required positionals, and 3
  required positionals whose third name is not `logger`.
- `safe_divide` denominator zero-check uses exact-zero
  (`denominator_arr != 0.0`); subnormal and satoshi-scale denominators
  pass the gate, and non-finite division outputs coerce to `fallback`
  via the post-division finite-mask.

Causal label split lookahead:
- `QuickAdapterV3.get_label_horizon_candles` recomputes the horizon
  from the current `label_period_candles` (via
  `get_label_period_candles`); init omits `label_horizon_candles`, so
  HPO period updates propagate to the horizon. The regressor's
  `_optuna_label_params` init likewise omits `label_horizon_candles`.
- `QuickAdapterV3.set_freqai_targets` advances `<label>_known_at_index`
  by the smoothing kernel half-width after smoothing. The
  `Utils.get_smoothing_kernel_half_width(config, *, series_length)`
  helper reuses `get_odd_window`/`get_even_window`/`get_savgol_params`
  shared with `smooth()` and dispatches on kernel routing:
  - `filtfilt`-routed zero-phase kernels (members of
    `SMOOTHING_KERNELS`: `gaussian`, `kaiser`, `kaiser_bessel_derived`,
    `triang`): half-width `effective_window - 1` (forward+backward
    pass extends dependency to the full filter length on both sides).
  - Single-pass centered windows (`smm`, `sma`, `savgol`): half-width
    `effective_window // 2`.
  - `gaussian_filter1d`: `int(4.0 * sigma + 0.5)` matching
    `scipy.ndimage` default truncation.
  The helper returns 0 when `smooth()` itself short-circuits:
  `series_length < max(window_candles, 3)` (top-level no-op) and, for
  the filtfilt/savgol routes, `series_length < effective_window`
  (downstream short-series no-op in `zero_phase_filter` /
  `savgol_filter`). The `series_length` parameter is keyword-only and
  required.

Label-weighting support policy:
- `QuickAdapterRegressorV3._compose_train_weights_with_support` routes
  the zero-pivot case (label-weighting strategy configured but no
  label weights available) through `_apply_support_policy`; the
  support policy governs the contract: `raise` raises, `fallback`
  warns.

Label Optuna selection hardening:
- `_OPTUNA_LABEL_SELECTION_SCHEMA_VERSION` is `2`, co-located with
  `_OPTUNA_LABEL_BEST_PARAMS_SCHEMA_VERSION` in `Utils`. The two
  constants are independent: wire format and selection algorithm
  carry separate version axes.
- `_optuna_label_selection_metadata` rejects non-finite `label_weights`
  / `label_p_order` with `ValueError`; downstream dict equality on the
  selection_metadata is NaN-safe.
- `_validate_optuna_label_best_params` accepts an
  `expected_selection_metadata` keyword. It rejects files that are
  not a `{schema_version, params, selection_metadata}` dict, files
  with mismatched `schema_version`, files missing or with an invalid
  `selection_metadata.schema_version`, and -- when
  `expected_selection_metadata` is provided -- files whose stored
  `selection_metadata` differs from the caller's current view. Legacy
  unversioned best-params files (no `schema_version`) are rejected
  outright. `QuickAdapterRegressorV3.optuna_load_best_params` passes
  its `_optuna_label_selection_metadata()` view for the label
  namespace; `QuickAdapterV3.optuna_load_best_params` omits the
  keyword, since the strategy reads only `label_period_candles`,
  `label_horizon_candles`, and `label_natr_multiplier` from the
  best-params file.
- The study schema-migration branch resets the Optuna label study
  whenever `existing_schema_version` is not an `int` (rejects `bool`
  and other types) or differs from `target_version`; an unversioned
  study (legacy pre-metadata) and a version-mismatched study are
  treated identically. Trials selected under a different metric
  whitelist or weighting scheme cannot be reused.
- `_optuna_label_selection_metadata` includes `label_weights` and
  `label_p_order`; `_calculate_distances` consumes both, so the
  idempotent `set_user_attr` write detects drift on those tunables.
- `_calculate_distances` validates `label_weights` length against the
  original objective count (raises on mismatch) only on the slicing
  path (when `objective_indices is not None` and the count differs).
  The sliced vector falls back to uniform weights only when
  `np.all(sliced == 0.0)` (the user's positive weights all on
  dropped objectives); slices containing negative or non-finite
  values flow through to `_validate_label_weights`.
- `_validate_label_selection_metric` accepts an `aggregate_allowed`
  parameter; cluster/density category callers pass `False`,
  restricting the valid set via the cached
  `_cluster_density_distance_metrics_set()` classmethod
  (SciPy-compatible non-probability metrics). The aggregate metrics
  (`harmonic_mean`, `geometric_mean`, `arithmetic_mean`,
  `quadratic_mean`, `cubic_mean`, `power_mean`, `weighted_sum`) live
  in the cached `_aggregate_distance_metrics_set()` classmethod
  derived by set-algebra from
  `_distance_metrics_set() - _scipy_metrics_set() -
  _probability_distance_metrics_set()`. README cluster/density metric
  rows list the SciPy-compatible set. `compromise_programming`/
  `topsis` accepts the full aggregate set via
  `_calculate_trial_distance_to_ideal`.

Out of scope:
- Two PR #80 findings: `reverse_train_test_order` support-policy
  routing; post-`feature_pipeline.fit_transform` support recheck.

References issues #87, #88, #89.

Closes #86.
README.md
quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py
quickadapter/user_data/strategies/QuickAdapterV3.py
quickadapter/user_data/strategies/Utils.py