| freqai.label_pipeline.sigmoid_scale | 1.0 | float > 0 | Scale parameter for `sigmoid` normalization, controls steepness. |
| freqai.label_pipeline.gamma | 1.0 | float (0,10] | Contrast exponent applied to labels after normalization: >1 emphasizes extrema, values between 0 and 1 soften. |
| _Feature parameters_ | | | |
-| freqai.feature_parameters.label_period_candles | min/max midpoint | int >= 1 | Zigzag labeling NATR period. When label HPO is enabled, backtest and hyperopt retain the period produced for each simulated candle when calculating strategy NATR. |
+| freqai.feature_parameters.label_period_candles | min/max midpoint | int >= 1 | Zigzag labeling NATR period. |
| freqai.feature_parameters.label_horizon_candles | `label_period_candles` | int >= 1 | Number of candles after a label row before the label is considered known by causal split guards. Recommended: cover the zigzag pivot confirmation lag (the smoothing kernel half-width is added automatically by `set_freqai_targets`). Used by causal split guards and `<label>_known_at_lookahead` metadata. When unset, falls back to `label_period_candles`. |
| freqai.feature_parameters.causal_mode | true | bool | Causal split guard toggle. When `true` (default): rejects `data_split_parameters.shuffle=true`, `shuffle_after_split=true`, `reverse_train_test_order=true`; for `timeseries_split` auto-sets `gap=label_horizon_candles` when unset/`0` (rejects explicit `gap<label_horizon_candles`); for `train_test_split` drops train rows where position `>=first_test_position-label_horizon_candles`; with `<label>_known_at_lookahead` columns, additionally drops rows where `local_position + row-wise max(<label>_known_at_lookahead) >= first_test_position`. `false` is deprecated; acausal baselines only. |
| freqai.feature_parameters.min_label_period_candles | 12 | int >= 1 | Minimum labeling NATR period used for reversals labeling HPO. |
| freqai.feature_parameters.max_label_period_candles | 24 | int >= 1 | Maximum labeling NATR period used for reversals labeling HPO. |
-| freqai.feature_parameters.label_natr_multiplier | min/max midpoint | float > 0 | Zigzag labeling NATR multiplier. When label HPO is enabled, stop-loss, take-profit, and candle-deviation calculations consume the value produced for the current simulated candle. |
+| freqai.feature_parameters.label_natr_multiplier | min/max midpoint | float > 0 | Zigzag labeling NATR multiplier. |
| freqai.feature_parameters.min_label_natr_multiplier | 9.0 | float > 0 | Minimum labeling NATR multiplier used for reversals labeling HPO. |
| freqai.feature_parameters.max_label_natr_multiplier | 12.0 | float > 0 | Maximum labeling NATR multiplier used for reversals labeling HPO. |
| freqai.feature_parameters.label_frequency_candles | `auto` | int >= 2 \| `auto` | Reversals labeling frequency. `auto` = max(2, 2 \* number of whitelisted pairs). |
| freqai.optuna_hyperopt.label_sampler | `auto` | enum {`auto`,`tpe`,`nsgaii`,`nsgaiii`} | HPO sampler algorithm for multi-objective `label` namespace. `nsgaii` uses [NSGAIISampler](https://optuna.readthedocs.io/en/stable/reference/samplers/generated/optuna.samplers.NSGAIISampler.html), `nsgaiii` uses [NSGAIIISampler](https://optuna.readthedocs.io/en/stable/reference/samplers/generated/optuna.samplers.NSGAIIISampler.html). |
| freqai.optuna_hyperopt.storage | `file` | enum {`file`,`sqlite`} | HPO storage backend. |
| freqai.optuna_hyperopt.continuous | true | bool | Continuous HPO. Forced for both namespaces in backtest and hyperopt, resetting the study on each optimization. |
-| freqai.optuna_hyperopt.warm_start | true | bool | Warm start HPO with previous best value(s). Persisted values are loaded and saved only in live and dry-run modes; non-live runs reuse only values produced earlier in the same run. |
+| freqai.optuna_hyperopt.warm_start | true | bool | Warm start HPO with previous best value(s). Persisted values are loaded and saved only in live and dry-run modes; non-live runs reuse only values produced earlier in the same run. |
| freqai.optuna_hyperopt.n_startup_trials | 15 | int >= 0 | HPO startup trials. |
| freqai.optuna_hyperopt.n_trials | 50 | int >= 1 | Maximum HPO trials. |
| freqai.optuna_hyperopt.n_jobs | CPU threads / 4 | int >= 1 | Parallel HPO workers. |
WEIGHT_STRATEGIES,
get_label_column_config,
)
-from pandas import DataFrame, Series, isna
+from pandas import DataFrame, Series, isna, to_numeric
from scipy.stats import pearsonr, t
from technical.pivots_points import pivots_points
get_label_smoothing_config,
get_label_weighting_config,
get_zl_ma_fn,
+ is_finite_number,
label_known_at_lookahead_column_name,
label_weight_column_name,
migrate_config,
@cached_property
def is_trade_runmode(self) -> bool:
- # Mirror the regressor's ``self.live`` gate (runmode in TRADE_MODES):
- # persisted label params are reused only in live and dry-run.
+ # True in live and dry-run (runmode in TRADE_MODES), mirroring the
+ # regressor's ``self.live`` gate.
return self.config.get("runmode") in TRADE_MODES
@property
dataframe["%-hour_of_day"] = (dates.dt.hour + 1) / 25
return dataframe
- @staticmethod
- def _is_finite_number(value: Any) -> bool:
- # Reject bool (int(True) == 1) and non-numeric (str/object) before
- # np.isfinite, which raises on non-numeric input.
- return (
- not isinstance(value, bool)
- and isinstance(value, (int, float, np.integer, np.floating))
- and bool(np.isfinite(value))
- )
-
def get_label_period_candles(
self,
pair: str,
period_series = dataframe.get("label_period_candles")
if period_series is not None and not period_series.empty:
period = period_series.iloc[candle_idx]
- if self._is_finite_number(period) and int(period) > 0:
+ if is_finite_number(period) and int(period) > 0:
return int(period)
period = self._label_params.get(pair, {}).get("label_period_candles")
+ if is_finite_number(period) and int(period) > 0:
+ return int(period)
return int(
- period
- if period is not None
- else self.freqai_info.get("feature_parameters", {}).get(
+ self.freqai_info.get("feature_parameters", {}).get(
"label_period_candles",
self._label_defaults[0],
)
)
def set_label_period_candles(self, pair: str, label_period_candles: Any) -> None:
- if self._is_finite_number(label_period_candles) and int(label_period_candles) > 0:
+ if is_finite_number(label_period_candles) and int(label_period_candles) > 0:
self._label_params[pair]["label_period_candles"] = int(label_period_candles)
def get_label_horizon_candles(self, pair: str) -> int:
multiplier_series = dataframe.get("label_natr_multiplier")
if multiplier_series is not None and not multiplier_series.empty:
multiplier = multiplier_series.iloc[candle_idx]
- if self._is_finite_number(multiplier) and float(multiplier) > 0.0:
+ if is_finite_number(multiplier) and float(multiplier) > 0.0:
return float(multiplier)
multiplier = self._label_params.get(pair, {}).get("label_natr_multiplier")
+ if is_finite_number(multiplier) and float(multiplier) > 0.0:
+ return float(multiplier)
return float(
- multiplier
- if multiplier is not None
- else self.freqai_info.get("feature_parameters", {}).get(
+ self.freqai_info.get("feature_parameters", {}).get(
"label_natr_multiplier", self._label_defaults[1]
)
)
def set_label_natr_multiplier(self, pair: str, label_natr_multiplier: Any) -> None:
- if self._is_finite_number(label_natr_multiplier) and float(label_natr_multiplier) > 0.0:
+ if (
+ is_finite_number(label_natr_multiplier)
+ and float(label_natr_multiplier) > 0.0
+ ):
self._label_params[pair]["label_natr_multiplier"] = float(
label_natr_multiplier
)
# per-row periods within one column is intentional).
dataframe["natr_label_period_candles"] = np.nan
fallback_period = self.get_label_period_candles(pair)
- valid_periods = np.isfinite(label_period_candles_series) & (
- label_period_candles_series >= 1
- )
- periods = label_period_candles_series.where(
- valid_periods, fallback_period
- ).astype(int)
+ numeric_periods = to_numeric(label_period_candles_series, errors="coerce")
+ valid_periods = np.isfinite(numeric_periods) & (numeric_periods >= 1)
+ periods = numeric_periods.where(valid_periods, fallback_period).astype(int)
for period in periods.unique():
period_rows = periods == period
period_natr = ta.NATR(dataframe, timeperiod=int(period))
return _safe_numeric_result(np.asarray(result, dtype=float), numerator, denominator)
+def is_finite_number(value: Any) -> bool:
+ # Reject bool (int(True) == 1) and non-numeric (str/object) before
+ # np.isfinite, which raises on non-numeric input.
+ return (
+ not isinstance(value, bool)
+ and isinstance(value, (int, float, np.integer, np.floating))
+ and bool(np.isfinite(value))
+ )
+
+
@dataclass(frozen=True, slots=True)
class _EnumValidator:
valid_values: tuple[str, ...]
f"label_period_candles={label_period_candles!r} (must be int >= 1)"
)
return None
- if (
- isinstance(label_natr_multiplier, bool)
- or not isinstance(label_natr_multiplier, (int, float, np.integer, np.floating))
- or not np.isfinite(label_natr_multiplier)
- or label_natr_multiplier <= 0
- ):
+ if not is_finite_number(label_natr_multiplier) or label_natr_multiplier <= 0:
if logger is not None:
logger.warning(
f"[{pair}] Ignoring Optuna label best params: invalid "