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.