From: Jérôme Benoit Date: Fri, 11 Apr 2025 11:07:44 +0000 (+0200) Subject: perf(qav3): fine tune zigzag params optimisation X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=606ef55d50b2ce59f1998e061a450ed133257beb;p=freqai-strategies.git perf(qav3): fine tune zigzag params optimisation Signed-off-by: Jérôme Benoit --- diff --git a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py index d640b52..f6878e5 100644 --- a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py +++ b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py @@ -44,7 +44,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): https://github.com/sponsors/robcaulk """ - version = "3.7.7" + version = "3.7.8" @cached_property def _optuna_config(self) -> dict: @@ -60,6 +60,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): "n_trials": 36, "timeout": 7200, "candles_step": 10, + "seed": 1, } return { **optuna_default_config, @@ -381,55 +382,58 @@ class QuickAdapterRegressorV3(BaseRegressionModel): ) return min_pred, max_pred - @staticmethod def get_multi_objective_study_best_trial( - namespace: str, study: optuna.study.Study + self, namespace: str, study: optuna.study.Study ) -> Optional[optuna.trial.FrozenTrial]: if not QuickAdapterRegressorV3.optuna_study_has_best_trials(study): return None best_trials = study.best_trials if namespace == "label": peaks_sizes = [trial.values[1] for trial in best_trials] - median_peaks_size = np.median(peaks_sizes) - equal_median_trials = [ + quantile_peaks_size = np.quantile( + peaks_sizes, self.ft_params.get("label_quantile", 0.75) + ) + equal_quantile_peaks_size_trials = [ trial for trial in best_trials - if np.isclose(trial.values[1], median_peaks_size) + if np.isclose(trial.values[1], quantile_peaks_size) ] - if equal_median_trials: - return max(equal_median_trials, key=lambda trial: trial.values[0]) - nearest_above_median = ( + if equal_quantile_peaks_size_trials: + return max( + equal_quantile_peaks_size_trials, key=lambda trial: trial.values[0] + ) + nearest_above_quantile = ( np.inf, -np.inf, None, ) # (trial_peaks_size, trial_peaks_range, trial_index) - nearest_below_median = ( + nearest_below_quantile = ( -np.inf, -np.inf, None, ) # (trial_peaks_size, trial_peaks_range, trial_index) for idx, trial in enumerate(best_trials): peaks_size = trial.values[1] - if peaks_size >= median_peaks_size: - if peaks_size < nearest_above_median[0] or ( - peaks_size == nearest_above_median[0] - and trial.values[0] > nearest_above_median[1] + if peaks_size >= quantile_peaks_size: + if peaks_size < nearest_above_quantile[0] or ( + peaks_size == nearest_above_quantile[0] + and trial.values[0] > nearest_above_quantile[1] ): - nearest_above_median = (peaks_size, trial.values[0], idx) - if peaks_size <= median_peaks_size: - if peaks_size > nearest_below_median[0] or ( - peaks_size == nearest_below_median[0] - and trial.values[0] > nearest_below_median[1] + nearest_above_quantile = (peaks_size, trial.values[0], idx) + if peaks_size <= quantile_peaks_size: + if peaks_size > nearest_below_quantile[0] or ( + peaks_size == nearest_below_quantile[0] + and trial.values[0] > nearest_below_quantile[1] ): - nearest_below_median = (peaks_size, trial.values[0], idx) - if nearest_above_median[2] is None or nearest_below_median[2] is None: + nearest_below_quantile = (peaks_size, trial.values[0], idx) + if nearest_above_quantile[2] is None or nearest_below_quantile[2] is None: return None - above_median_trial = best_trials[nearest_above_median[2]] - below_median_trial = best_trials[nearest_below_median[2]] - if above_median_trial.values[0] >= below_median_trial.values[0]: - return above_median_trial + above_quantile_trial = best_trials[nearest_above_quantile[2]] + below_quantile_trial = best_trials[nearest_below_quantile[2]] + if above_quantile_trial.values[0] >= below_quantile_trial.values[0]: + return above_quantile_trial else: - return below_median_trial + return below_quantile_trial else: raise ValueError(f"Invalid namespace: {namespace}") @@ -491,9 +495,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): **self.get_optuna_params(pair, namespace), } else: - best_trial = QuickAdapterRegressorV3.get_multi_objective_study_best_trial( - "label", study - ) + best_trial = self.get_multi_objective_study_best_trial("label", study) if not best_trial: logger.error( f"Optuna {pair} {namespace} {objective_type} hyperopt failed ({time_spent:.2f} secs): no study best trial found" @@ -555,7 +557,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): return optuna.create_study( study_name=study_name, sampler=optuna.samplers.TPESampler( - multivariate=True, group=True, seed=1 + multivariate=True, group=True, seed=self._optuna_config.get("seed") ), pruner=optuna.pruners.HyperbandPruner(), direction=direction, diff --git a/quickadapter/user_data/strategies/QuickAdapterV3.py b/quickadapter/user_data/strategies/QuickAdapterV3.py index 341d2e1..2719a51 100644 --- a/quickadapter/user_data/strategies/QuickAdapterV3.py +++ b/quickadapter/user_data/strategies/QuickAdapterV3.py @@ -58,7 +58,7 @@ class QuickAdapterV3(IStrategy): INTERFACE_VERSION = 3 def version(self) -> str: - return "3.3.3" + return "3.3.4" timeframe = "5m" @@ -488,8 +488,8 @@ class QuickAdapterV3(IStrategy): return trade_duration_minutes // timeframe_to_minutes(self.timeframe) @staticmethod - def is_trade_duration_valid(trade_duration_candles: int) -> bool: - return not (isna(trade_duration_candles) or trade_duration_candles <= 0) + def is_trade_duration_valid(trade_duration: float) -> bool: + return not (isna(trade_duration) or trade_duration <= 0.0) def get_stoploss_distance( self, df: DataFrame, trade: Trade, current_rate: float