From: Jérôme Benoit Date: Sat, 27 Dec 2025 14:41:47 +0000 (+0100) Subject: refactor(quickadapter): harmonize error messages X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=a585b30fab263bbbd4a5cad16069407f5db7f225;p=freqai-strategies.git refactor(quickadapter): harmonize error messages Signed-off-by: Jérôme Benoit --- diff --git a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py index e009236..f49dc0e 100644 --- a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py +++ b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py @@ -798,7 +798,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): params = self._optuna_label_params.get(pair) else: raise ValueError( - f"Invalid namespace '{namespace}'. " + f"Invalid namespace {namespace!r}. " f"Supported: {', '.join(QuickAdapterRegressorV3._OPTUNA_NAMESPACES)}" ) return params @@ -814,7 +814,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): self._optuna_label_params[pair] = params else: raise ValueError( - f"Invalid namespace '{namespace}'. " + f"Invalid namespace {namespace!r}. " f"Supported: {', '.join(QuickAdapterRegressorV3._OPTUNA_NAMESPACES)}" ) @@ -825,7 +825,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): value = self._optuna_train_value.get(pair) else: raise ValueError( - f"Invalid namespace '{namespace}'. " + f"Invalid namespace {namespace!r}. " f"Supported: {', '.join(QuickAdapterRegressorV3._OPTUNA_NAMESPACES[:2])}" # Only hp and train ) return value @@ -837,7 +837,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): self._optuna_train_value[pair] = value else: raise ValueError( - f"Invalid namespace '{namespace}'. " + f"Invalid namespace {namespace!r}. " f"Supported: {', '.join(QuickAdapterRegressorV3._OPTUNA_NAMESPACES[:2])}" # Only hp and train ) @@ -846,7 +846,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): values = self._optuna_label_values.get(pair) else: raise ValueError( - f"Invalid namespace '{namespace}'. " + f"Invalid namespace {namespace!r}. " f"Supported: {QuickAdapterRegressorV3._OPTUNA_NAMESPACES[2]}" # Only label ) return values @@ -858,7 +858,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): self._optuna_label_values[pair] = values else: raise ValueError( - f"Invalid namespace '{namespace}'. " + f"Invalid namespace {namespace!r}. " f"Supported: {QuickAdapterRegressorV3._OPTUNA_NAMESPACES[2]}" # Only label ) @@ -1059,7 +1059,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): QuickAdapterRegressorV3._OPTUNA_NAMESPACES[2] }: # Only "label" raise ValueError( - f"Invalid namespace '{namespace}'. " + f"Invalid namespace {namespace!r}. " f"Supported: {QuickAdapterRegressorV3._OPTUNA_NAMESPACES[2]}" # Only label ) if not callable(callback): @@ -1394,7 +1394,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): pred_minima = pred_extrema[pred_extrema < -eps] else: raise ValueError( - f"Invalid extrema_selection '{extrema_selection}'. " + f"Invalid extrema_selection {extrema_selection!r}. " f"Supported: {', '.join(QuickAdapterRegressorV3._EXTREMA_SELECTION_METHODS)}" ) @@ -1489,7 +1489,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): threshold_func = getattr(skimage.filters, f"threshold_{method}") except AttributeError: raise ValueError( - f"Invalid skimage threshold method '{method}'. " + f"Invalid skimage threshold method {method!r}. " f"Supported: {', '.join(QuickAdapterRegressorV3._SKIMAGE_THRESHOLD_METHODS)}" ) @@ -1524,7 +1524,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): return threshold_func(values) except Exception as e: logger.warning( - f"Failed to apply skimage threshold function {threshold_func.__name__} on series {series.name}: {e!r}, falling back to median", + f"Threshold function {threshold_func.__name__} failed on series {series.name}: {e!r}, falling back to median", exc_info=True, ) return np.nanmedian(values) @@ -1586,7 +1586,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): raise ValueError("Invalid weights: must be finite and non-negative") if metric in QuickAdapterRegressorV3._unsupported_cluster_metrics_set(): raise ValueError( - f"Invalid weights: not supported for metric '{metric}'" + f"Invalid weights: not supported for metric {metric!r}" ) matrix = np.asarray(matrix, dtype=np.float64) @@ -1850,7 +1850,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): in QuickAdapterRegressorV3._unsupported_cluster_metrics_set() ): raise ValueError( - f"Invalid label_medoid_metric '{label_medoid_metric}'. " + f"Invalid label_medoid_metric {label_medoid_metric!r}. " f"Unsupported: {', '.join(QuickAdapterRegressorV3._UNSUPPORTED_CLUSTER_METRICS)}" ) p = None @@ -1893,7 +1893,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): in QuickAdapterRegressorV3._unsupported_cluster_metrics_set() ): raise ValueError( - f"Invalid label_kmeans_metric '{label_kmeans_metric}'. " + f"Invalid label_kmeans_metric {label_kmeans_metric!r}. " f"Unsupported: {', '.join(QuickAdapterRegressorV3._UNSUPPORTED_CLUSTER_METRICS)}" ) cdist_kwargs: dict[str, Any] = {} @@ -1971,7 +1971,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): ] else: raise ValueError( - f"Invalid label_kmeans_selection '{label_kmeans_selection}'. " + f"Invalid label_kmeans_selection {label_kmeans_selection!r}. " f"Supported: {', '.join(QuickAdapterRegressorV3._CLUSTER_SELECTION_METHODS)}" ) return trial_distances @@ -1986,7 +1986,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): in QuickAdapterRegressorV3._unsupported_cluster_metrics_set() ): raise ValueError( - f"Invalid label_kmedoids_metric '{label_kmedoids_metric}'. " + f"Invalid label_kmedoids_metric {label_kmedoids_metric!r}. " f"Unsupported: {', '.join(QuickAdapterRegressorV3._UNSUPPORTED_CLUSTER_METRICS)}" ) kmedoids_kwargs: dict[str, Any] = { @@ -2054,7 +2054,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): ] else: raise ValueError( - f"Invalid label_kmedoids_selection '{label_kmedoids_selection}'. " + f"Invalid label_kmedoids_selection {label_kmedoids_selection!r}. " f"Supported: {', '.join(QuickAdapterRegressorV3._CLUSTER_SELECTION_METHODS)}" ) return trial_distances @@ -2123,7 +2123,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): return np.nanmax(neighbor_distances, axis=1) else: raise ValueError( - f"Invalid label metric '{metric}'. Supported: {', '.join(metrics)}" + f"Invalid label metric {metric!r}. Supported: {', '.join(metrics)}" ) def _get_multi_objective_study_best_trial( @@ -2133,7 +2133,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): QuickAdapterRegressorV3._OPTUNA_NAMESPACES[2] }: # Only "label" raise ValueError( - f"Invalid namespace '{namespace}'. " + f"Invalid namespace {namespace!r}. " f"Supported: {QuickAdapterRegressorV3._OPTUNA_NAMESPACES[2]}" # Only label ) n_objectives = len(study.directions) @@ -2150,7 +2150,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): ) # "euclidean" if label_metric not in metrics: raise ValueError( - f"Invalid label_metric '{label_metric}'. Supported: {', '.join(metrics)}" + f"Invalid label_metric {label_metric!r}. Supported: {', '.join(metrics)}" ) best_trials = [ @@ -2275,7 +2275,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): } metric_log_msg = f" using {self.ft_params.get('label_metric', QuickAdapterRegressorV3._SCIPY_METRICS[2])} metric" logger.info( - f"[{pair}] Optuna {namespace} {objective_type} objective hyperopt done{metric_log_msg} ({time_spent:.2f} secs)" + f"[{pair}] Optuna {namespace} {objective_type} objective hyperopt completed{metric_log_msg} ({time_spent:.2f} secs)" ) for key, value in study_best_results.items(): if isinstance(value, list): @@ -2320,7 +2320,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): ) else: raise ValueError( - f"Invalid optuna storage_backend '{storage_backend}'. " + f"Invalid optuna storage_backend {storage_backend!r}. " f"Supported: {', '.join(QuickAdapterRegressorV3._OPTUNA_STORAGE_BACKENDS)}" ) return storage @@ -2352,7 +2352,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): ) else: raise ValueError( - f"Invalid optuna sampler '{sampler}'. " + f"Invalid optuna sampler {sampler!r}. " f"Supported: {', '.join(QuickAdapterRegressorV3._OPTUNA_SAMPLERS)}" ) @@ -2480,7 +2480,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): optuna.delete_study(study_name=study_name, storage=storage) except Exception as e: logger.warning( - f"Optuna study deletion failed for study {study_name}: {e!r}", + f"Optuna study {study_name} deletion failed: {e!r}", exc_info=True, ) diff --git a/quickadapter/user_data/strategies/QuickAdapterV3.py b/quickadapter/user_data/strategies/QuickAdapterV3.py index c87448f..1afed86 100644 --- a/quickadapter/user_data/strategies/QuickAdapterV3.py +++ b/quickadapter/user_data/strategies/QuickAdapterV3.py @@ -728,7 +728,7 @@ class QuickAdapterV3(IStrategy): def get_label_natr_ratio_percent(self, pair: str, percent: float) -> float: if not isinstance(percent, float) or not (0.0 <= percent <= 1.0): raise ValueError( - f"Invalid percent {percent}: must be a float between 0 and 1" + f"Invalid percent {percent!r}: must be a float in range [0, 1]" ) return self.get_label_natr_ratio(pair) * percent @@ -789,7 +789,7 @@ class QuickAdapterV3(IStrategy): or weighting_mmad_scaling_factor <= 0 ): logger.warning( - f"Invalid extrema_weighting mmad_scaling_factor {weighting_mmad_scaling_factor!r}, must be > 0, using default {DEFAULTS_EXTREMA_WEIGHTING['mmad_scaling_factor']!r}" + f"Invalid extrema_weighting mmad_scaling_factor {weighting_mmad_scaling_factor!r}, must be a finite number > 0, using default {DEFAULTS_EXTREMA_WEIGHTING['mmad_scaling_factor']!r}" ) weighting_mmad_scaling_factor = DEFAULTS_EXTREMA_WEIGHTING[ "mmad_scaling_factor" @@ -856,7 +856,7 @@ class QuickAdapterV3(IStrategy): or weighting_sigmoid_scale <= 0 ): logger.warning( - f"Invalid extrema_weighting sigmoid_scale {weighting_sigmoid_scale!r}, must be > 0, using default {DEFAULTS_EXTREMA_WEIGHTING['sigmoid_scale']!r}" + f"Invalid extrema_weighting sigmoid_scale {weighting_sigmoid_scale!r}, must be a finite number > 0, using default {DEFAULTS_EXTREMA_WEIGHTING['sigmoid_scale']!r}" ) weighting_sigmoid_scale = DEFAULTS_EXTREMA_WEIGHTING["sigmoid_scale"] @@ -869,7 +869,7 @@ class QuickAdapterV3(IStrategy): or weighting_softmax_temperature <= 0 ): logger.warning( - f"Invalid extrema_weighting softmax_temperature {weighting_softmax_temperature!r}, must be > 0, using default {DEFAULTS_EXTREMA_WEIGHTING['softmax_temperature']!r}" + f"Invalid extrema_weighting softmax_temperature {weighting_softmax_temperature!r}, must be a finite number > 0, using default {DEFAULTS_EXTREMA_WEIGHTING['softmax_temperature']!r}" ) weighting_softmax_temperature = DEFAULTS_EXTREMA_WEIGHTING[ "softmax_temperature" @@ -1045,7 +1045,7 @@ class QuickAdapterV3(IStrategy): or not np.isfinite(smoothing_sigma) ): logger.warning( - f"Invalid extrema_smoothing sigma {smoothing_sigma!r}, must be a positive finite number, using default {DEFAULTS_EXTREMA_SMOOTHING['sigma']!r}" + f"Invalid extrema_smoothing sigma {smoothing_sigma!r}, must be a finite number > 0, using default {DEFAULTS_EXTREMA_SMOOTHING['sigma']!r}" ) smoothing_sigma = DEFAULTS_EXTREMA_SMOOTHING["sigma"] @@ -1073,7 +1073,7 @@ class QuickAdapterV3(IStrategy): try: return pattern.format(**duration) except (KeyError, ValueError) as e: - raise ValueError(f"Invalid pattern '{pattern}': {e!r}") + raise ValueError(f"Invalid pattern {pattern!r}: {e!r}") def set_freqai_targets( self, dataframe: DataFrame, metadata: dict[str, Any], **kwargs @@ -1412,8 +1412,8 @@ class QuickAdapterV3(IStrategy): trade_price_target_fn = trade_price_target_methods.get(trade_price_target) if trade_price_target_fn is None: raise ValueError( - f"Invalid trade_price_target '{trade_price_target}'. " - f"Supported: {', '.join(sorted(TRADE_PRICE_TARGETS))}" + f"Invalid trade_price_target {trade_price_target!r}. " + f"Supported: {', '.join(TRADE_PRICE_TARGETS)}" ) return trade_price_target_fn() @@ -1442,7 +1442,7 @@ class QuickAdapterV3(IStrategy): ) -> Optional[float]: if not (0.0 <= natr_ratio_percent <= 1.0): raise ValueError( - f"Invalid natr_ratio_percent {natr_ratio_percent}: must be in [0, 1]" + f"Invalid natr_ratio_percent {natr_ratio_percent!r}: must be in range [0, 1]" ) trade_duration_candles = self.get_trade_duration_candles(df, trade) if not QuickAdapterV3.is_trade_duration_valid(trade_duration_candles): @@ -1469,7 +1469,7 @@ class QuickAdapterV3(IStrategy): ) -> Optional[float]: if not (0.0 <= natr_ratio_percent <= 1.0): raise ValueError( - f"Invalid natr_ratio_percent {natr_ratio_percent}: must be in [0, 1]" + f"Invalid natr_ratio_percent {natr_ratio_percent!r}: must be in range [0, 1]" ) trade_duration_candles = self.get_trade_duration_candles(df, trade) if not QuickAdapterV3.is_trade_duration_valid(trade_duration_candles): @@ -1817,7 +1817,7 @@ class QuickAdapterV3(IStrategy): ) else: raise ValueError( - f"Invalid interpolation_direction '{interpolation_direction}'. " + f"Invalid interpolation_direction {interpolation_direction!r}. " f"Supported: {', '.join(QuickAdapterV3._INTERPOLATION_DIRECTIONS)}" ) candle_deviation = ( @@ -1891,7 +1891,7 @@ class QuickAdapterV3(IStrategy): candle_threshold = base_price * (1 - current_deviation) else: raise ValueError( - f"Invalid side '{side}'. Supported: {', '.join(QuickAdapterV3._TRADE_DIRECTIONS)}" + f"Invalid side {side!r}. Supported: {', '.join(QuickAdapterV3._TRADE_DIRECTIONS)}" ) self._candle_threshold_cache[cache_key] = candle_threshold return self._candle_threshold_cache[cache_key] @@ -1999,12 +1999,12 @@ class QuickAdapterV3(IStrategy): if lookback_period > max_lookback_period: lookback_period = max_lookback_period if not isinstance(decay_ratio, (int, float)): - logger.info( + logger.debug( f"[{pair}] Denied {trade_direction} {order}: invalid decay_ratio type" ) return False if not (0.0 < decay_ratio <= 1.0): - logger.info( + logger.debug( f"[{pair}] Denied {trade_direction} {order}: invalid decay_ratio {decay_ratio}, must be in (0, 1]" ) return False @@ -2031,7 +2031,7 @@ class QuickAdapterV3(IStrategy): if side == QuickAdapterV3._TRADE_DIRECTIONS[1]: # "short" trade_direction = QuickAdapterV3._TRADE_DIRECTIONS[0] # "long" if not current_ok: - logger.info( + logger.debug( f"[{pair}] Denied {trade_direction} {order}: rate {format_number(rate)} did not break threshold {format_number(current_threshold)}" ) return False @@ -2073,7 +2073,7 @@ class QuickAdapterV3(IStrategy): side == QuickAdapterV3._TRADE_DIRECTIONS[1] and not (close_k < threshold_k) # "short" ): - logger.info( + logger.debug( f"[{pair}] Denied {trade_direction} {order}: " f"close_k[{-k}] {format_number(close_k)} " f"did not break threshold_k[{-(k + 1)}] {format_number(threshold_k)} " @@ -2474,7 +2474,7 @@ class QuickAdapterV3(IStrategy): return False else: raise ValueError( - f"Invalid trading_mode '{trading_mode}'. " + f"Invalid trading_mode {trading_mode!r}. " f"Supported: {', '.join(QuickAdapterV3._TRADING_MODES)}" ) diff --git a/quickadapter/user_data/strategies/Utils.py b/quickadapter/user_data/strategies/Utils.py index 23fec99..3652ccc 100644 --- a/quickadapter/user_data/strategies/Utils.py +++ b/quickadapter/user_data/strategies/Utils.py @@ -199,7 +199,7 @@ def non_zero_diff(s1: pd.Series, s2: pd.Series) -> pd.Series: @lru_cache(maxsize=8) def get_odd_window(window: int) -> int: if window < 1: - raise ValueError(f"Invalid window {window}: must be > 0") + raise ValueError(f"Invalid window {window!r}: must be > 0") return window if window % 2 == 1 else window + 1 @@ -233,7 +233,7 @@ def _calculate_coeffs( coeffs = sp.signal.windows.triang(M=window, sym=True) else: raise ValueError( - f"Invalid window type '{win_type}'. " + f"Invalid window type {win_type!r}. " f"Supported: {', '.join(SMOOTHING_METHODS[:3])}" ) return coeffs / np.sum(coeffs) @@ -443,7 +443,7 @@ def standardize_weights( else: raise ValueError( - f"Invalid standardization method '{method}'. " + f"Invalid standardization method {method!r}. " f"Supported: {', '.join(STANDARDIZATION_TYPES)}" ) @@ -632,7 +632,7 @@ def normalize_weights( normalized_weights = _normalize_rank(standardized_weights, method=rank_method) else: raise ValueError( - f"Invalid normalization method '{normalization}'. " + f"Invalid normalization method {normalization!r}. " f"Supported: {', '.join(NORMALIZATION_TYPES)}" ) @@ -788,7 +788,7 @@ def calculate_hybrid_extrema_weights( ) else: raise ValueError( - f"Invalid hybrid aggregation method '{aggregation}'. " + f"Invalid hybrid aggregation method {aggregation!r}. " f"Supported: {', '.join(HYBRID_AGGREGATIONS)}" ) @@ -969,7 +969,7 @@ def compute_extrema_weights( ) raise ValueError( - f"Invalid extrema weighting strategy '{strategy}'. " + f"Invalid extrema weighting strategy {strategy!r}. " f"Supported: {', '.join(WEIGHT_STRATEGIES)}" ) @@ -1130,7 +1130,7 @@ def top_change_percent(dataframe: pd.DataFrame, period: int) -> pd.Series: :return: The top change percentage series """ if period < 1: - raise ValueError(f"Invalid period {period}: must be >= 1") + raise ValueError(f"Invalid period {period!r}: must be >= 1") previous_close_top = ( dataframe.get("close").rolling(period, min_periods=period).max().shift(1) @@ -1148,7 +1148,7 @@ def bottom_change_percent(dataframe: pd.DataFrame, period: int) -> pd.Series: :return: The bottom change percentage series """ if period < 1: - raise ValueError(f"Invalid period {period}: must be >= 1") + raise ValueError(f"Invalid period {period!r}: must be >= 1") previous_close_bottom = ( dataframe.get("close").rolling(period, min_periods=period).min().shift(1) @@ -1167,7 +1167,7 @@ def price_retracement_percent(dataframe: pd.DataFrame, period: int) -> pd.Series :return: Retracement percentage series """ if period < 1: - raise ValueError(f"Invalid period {period}: must be >= 1") + raise ValueError(f"Invalid period {period!r}: must be >= 1") previous_close_low = ( dataframe.get("close").rolling(period, min_periods=period).min().shift(1) @@ -1249,7 +1249,7 @@ def _fractal_dimension( ) -> float: """Original fractal dimension computation implementation per Ehlers' paper.""" if period % 2 != 0: - raise ValueError(f"Invalid period {period}: must be even") + raise ValueError(f"Invalid period {period!r}: must be even") half_period = period // 2 @@ -1278,7 +1278,7 @@ def frama(df: pd.DataFrame, period: int = 16, zero_lag: bool = False) -> pd.Seri Original FRAMA implementation per Ehlers' paper with optional zero lag. """ if period % 2 != 0: - raise ValueError(f"Invalid period {period}: must be even") + raise ValueError(f"Invalid period {period!r}: must be even") n = len(df) @@ -1320,7 +1320,7 @@ def smma(series: pd.Series, period: int, zero_lag=False, offset=0) -> pd.Series: https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=173&Name=Moving_Average_-_Smoothed """ if period <= 0: - raise ValueError(f"Invalid period {period}: must be > 0") + raise ValueError(f"Invalid period {period!r}: must be > 0") n = len(series) if n < period: return pd.Series(index=series.index, dtype=float) @@ -1960,7 +1960,7 @@ def get_optuna_callbacks( ] else: raise ValueError( - f"Invalid regressor '{regressor}'. Supported: {', '.join(REGRESSORS)}" + f"Invalid regressor {regressor!r}. Supported: {', '.join(REGRESSORS)}" ) return callbacks @@ -2031,7 +2031,7 @@ def fit_regressor( ) else: raise ValueError( - f"Invalid regressor '{regressor}'. Supported: {', '.join(REGRESSORS)}" + f"Invalid regressor {regressor!r}. Supported: {', '.join(REGRESSORS)}" ) return model @@ -2045,13 +2045,13 @@ def get_optuna_study_model_parameters( ) -> dict[str, Any]: if regressor not in set(REGRESSORS): raise ValueError( - f"Invalid regressor '{regressor}'. Supported: {', '.join(REGRESSORS)}" + f"Invalid regressor {regressor!r}. Supported: {', '.join(REGRESSORS)}" ) if not isinstance(expansion_ratio, (int, float)) or not ( 0.0 <= expansion_ratio <= 1.0 ): raise ValueError( - f"Invalid expansion_ratio {expansion_ratio}: must be a float between 0 and 1" + f"Invalid expansion_ratio {expansion_ratio!r}: must be in range [0, 1]" ) default_ranges: dict[str, tuple[float, float]] = { "n_estimators": (100, 2000),