From ceb6a9d7b2faf5a1d259aac8a5e7ddcc3a0433ed Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sat, 13 Dec 2025 03:12:34 +0100 Subject: [PATCH] feat(qav3): add extrema weighting by swing efficiency ratio and speed MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- README.md | 162 +++++++++--------- .../freqaimodels/QuickAdapterRegressorV3.py | 27 ++- .../user_data/strategies/QuickAdapterV3.py | 13 +- quickadapter/user_data/strategies/Utils.py | 96 ++++++++++- 4 files changed, 204 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 601ca1a..5db40ad 100644 --- a/README.md +++ b/README.md @@ -35,87 +35,87 @@ docker compose up -d --build ### Configuration tunables -| Path | Default | Type / Range | Description | -| ---------------------------------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| _Protections_ | | | | -| custom_protections.trade_duration_candles | 72 | int >= 1 | Estimated trade duration in candles. Scales protections stop duration candles and trade limit. | -| custom_protections.lookback_period_fraction | 0.5 | float (0,1] | Fraction of fit_live_predictions_candles used to calculate lookback_period_candles for MaxDrawdown and StoplossGuard protections. | -| custom_protections.cooldown.enabled | true | bool | Enable/disable CooldownPeriod protection. | -| custom_protections.cooldown.stop_duration_candles | 4 | int >= 1 | Number of candles to wait before allowing new trades after a trade is closed. | -| custom_protections.drawdown.enabled | true | bool | Enable/disable MaxDrawdown protection. | -| custom_protections.drawdown.max_allowed_drawdown | 0.2 | float (0,1) | Maximum allowed drawdown. | -| custom_protections.stoploss.enabled | true | bool | Enable/disable StoplossGuard protection. | -| _Leverage_ | | | | -| leverage | proposed_leverage | float [1.0, max_leverage] | Leverage. Fallback to proposed_leverage for the pair. | -| _Exit pricing_ | | | | -| exit_pricing.trade_price_target | `moving_average` | enum {`moving_average`,`interpolation`,`weighted_interpolation`} | Trade NATR computation method. | -| exit_pricing.thresholds_calibration.decline_quantile | 0.90 | float (0,1) | PnL decline quantile threshold. | -| _Reversal confirmation_ | | | | -| reversal_confirmation.lookback_period | 0 | int >= 0 | Prior confirming candles; 0 = none. | -| reversal_confirmation.decay_ratio | 0.5 | float (0,1] | Geometric per-candle volatility adjusted reversal threshold relaxation factor. | -| reversal_confirmation.min_natr_ratio_percent | 0.0095 | float [0,1] | Lower bound fraction for volatility adjusted reversal threshold. | -| reversal_confirmation.max_natr_ratio_percent | 0.075 | float [0,1] | Upper bound fraction (>= lower bound) for volatility adjusted reversal threshold. | -| _Regressor model_ | | | | -| freqai.regressor | `xgboost` | enum {`xgboost`,`lightgbm`} | Machine learning regressor algorithm. | -| _Extrema smoothing_ | | | | -| freqai.extrema_smoothing.method | `gaussian` | enum {`gaussian`,`kaiser`,`triang`,`smm`,`sma`,`savgol`,`nadaraya_watson`} | Extrema smoothing method (`smm`=median, `sma`=mean, `savgol`=Savitzky–Golay, `nadaraya_watson`=Gaussian kernel regression). | -| freqai.extrema_smoothing.window | 5 | int >= 3 | Smoothing window length (candles). | -| freqai.extrema_smoothing.beta | 8.0 | float > 0 | Shape parameter for `kaiser` kernel. | -| freqai.extrema_smoothing.polyorder | 3 | int >= 1 | Polynomial order for `savgol` smoothing. | -| freqai.extrema_smoothing.mode | `mirror` | enum {`mirror`,`constant`,`nearest`,`wrap`,`interp`} | Boundary mode for `savgol` and `nadaraya_watson`. | -| freqai.extrema_smoothing.bandwidth | 1.0 | float > 0 | Gaussian bandwidth for `nadaraya_watson`. | -| _Extrema weighting_ | | | | -| freqai.extrema_weighting.strategy | `none` | enum {`none`,`amplitude`,`amplitude_threshold_ratio`,`volume`} | Extrema weighting source: unweighted (`none`), swing amplitude (`amplitude`), swing amplitude / median volatility-threshold ratio (`amplitude_threshold_ratio`), or swing volume (`volume`). | -| freqai.extrema_weighting.standardization | `none` | enum {`none`,`zscore`,`robust`,`mmad`} | Standardization method applied before normalization. `none`=no standardization, `zscore`=(w-μ)/σ, `robust`=(w-median)/IQR, `mmad`=(w-median)/MAD. | -| freqai.extrema_weighting.robust_quantiles | [0.25, 0.75] | list[float] where 0 <= Q1 < Q3 <= 1 | Quantile range for robust standardization, Q1 and Q3. | -| freqai.extrema_weighting.mmad_scaling_factor | 1.4826 | float > 0 | Scaling factor for MMAD standardization. | -| freqai.extrema_weighting.normalization | `minmax` | enum {`minmax`,`sigmoid`,`softmax`,`l1`,`l2`,`rank`,`none`} | Normalization method for weights. | -| freqai.extrema_weighting.minmax_range | [0.0, 1.0] | list[float] | Target range for `minmax` normalization, min and max. | -| freqai.extrema_weighting.sigmoid_scale | 1.0 | float > 0 | Scale parameter for `sigmoid` normalization, controls steepness. | -| freqai.extrema_weighting.softmax_temperature | 1.0 | float > 0 | Temperature parameter for `softmax` normalization: lower values sharpen distribution, higher values flatten it. | -| freqai.extrema_weighting.rank_method | `average` | enum {`average`,`min`,`max`,`dense`,`ordinal`} | Ranking method for `rank` normalization. | -| freqai.extrema_weighting.gamma | 1.0 | float (0,10] | Contrast exponent applied 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 horizon. | -| freqai.feature_parameters.min_label_period_candles | 12 | int >= 1 | Minimum labeling NATR horizon used for reversals labeling HPO. | -| freqai.feature_parameters.max_label_period_candles | 24 | int >= 1 | Maximum labeling NATR horizon used for reversals labeling HPO. | -| freqai.feature_parameters.label_natr_ratio | min/max midpoint | float > 0 | Zigzag labeling NATR ratio. | -| freqai.feature_parameters.min_label_natr_ratio | 9.0 | float > 0 | Minimum labeling NATR ratio used for reversals labeling HPO. | -| freqai.feature_parameters.max_label_natr_ratio | 12.0 | float > 0 | Maximum labeling NATR ratio 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.feature_parameters.label_metric | `euclidean` | string (supported: `euclidean`,`minkowski`,`cityblock`,`chebyshev`,`mahalanobis`,`seuclidean`,`jensenshannon`,`sqeuclidean`,...) | Metric used in distance calculations to ideal point. | -| freqai.feature_parameters.label_weights | [1/4,1/4,1/4,1/4] | list[float] | Per-objective weights used in distance calculations to ideal point. Objectives: (1) number of detected reversals, (2) median swing amplitude, (3) median swing amplitude / median volatility-threshold ratio, (4) median swing volume. | -| freqai.feature_parameters.label_p_order | `None` | float \| None | p-order used by `minkowski` / `power_mean` (optional). | -| freqai.feature_parameters.label_medoid_metric | `euclidean` | string | Metric used with `medoid`. | -| freqai.feature_parameters.label_kmeans_metric | `euclidean` | string | Metric used for k-means clustering. | -| freqai.feature_parameters.label_kmeans_selection | `min` | enum {`min`,`medoid`} | Strategy to select trial in the best kmeans cluster. | -| freqai.feature_parameters.label_kmedoids_metric | `euclidean` | string | Metric used for k-medoids clustering. | -| freqai.feature_parameters.label_kmedoids_selection | `min` | enum {`min`,`medoid`} | Strategy to select trial in the best k-medoids cluster. | -| freqai.feature_parameters.label_knn_metric | `minkowski` | string | Distance metric for KNN. | -| freqai.feature_parameters.label_knn_p_order | `None` | float \| None | Tunable for KNN neighbor distances aggregation methods: p-order (`knn_power_mean`, default: 1.0) or quantile (`knn_quantile`, default: 0.5). (optional) | -| freqai.feature_parameters.label_knn_n_neighbors | 5 | int >= 1 | Number of neighbors for KNN. | -| _Predictions extrema_ | | | | -| freqai.predictions_extrema.selection_method | `rank` | enum {`rank`,`values`,`partition`} | Extrema selection method. `values` uses reversal values, `rank` uses ranked extrema values, `partition` uses sign-based partitioning. | -| freqai.predictions_extrema.thresholds_smoothing | `mean` | enum {`mean`,`isodata`,`li`,`minimum`,`otsu`,`triangle`,`yen`,`median`,`soft_extremum`} | Thresholding method for prediction thresholds smoothing. | -| freqai.predictions_extrema.thresholds_alpha | 12.0 | float > 0 | Alpha for `soft_extremum`. | -| freqai.predictions_extrema.threshold_outlier | 0.999 | float (0,1) | Quantile threshold for predictions outlier filtering. | -| freqai.predictions_extrema.extrema_fraction | 1.0 | float (0,1] | Fraction of extrema used for thresholds. `1.0` uses all, lower values keep only most significant. Applies to `rank` and `values`; ignored for `partition`. | -| _Optuna / HPO_ | | | | -| freqai.optuna_hyperopt.enabled | true | bool | Enables HPO. | -| freqai.optuna_hyperopt.sampler | `tpe` | enum {`tpe`,`auto`} | HPO sampler algorithm. `tpe` uses [TPESampler](https://optuna.readthedocs.io/en/stable/reference/samplers/generated/optuna.samplers.TPESampler.html) with multivariate and group, `auto` uses [AutoSampler](https://hub.optuna.org/samplers/auto_sampler). | -| freqai.optuna_hyperopt.storage | `file` | enum {`file`,`sqlite`} | HPO storage backend. | -| freqai.optuna_hyperopt.continuous | true | bool | Continuous HPO. | -| freqai.optuna_hyperopt.warm_start | true | bool | Warm start HPO with previous best value(s). | -| 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. | -| freqai.optuna_hyperopt.timeout | 7200 | int >= 0 | HPO wall-clock timeout in seconds. | -| freqai.optuna_hyperopt.label_candles_step | 1 | int >= 1 | Step for Zigzag NATR horizon search space. | -| freqai.optuna_hyperopt.train_candles_step | 10 | int >= 1 | Step for training sets size search space. | -| freqai.optuna_hyperopt.space_reduction | false | bool | Enable/disable HPO search space reduction based on previous best parameters. | -| freqai.optuna_hyperopt.expansion_ratio | 0.4 | float [0,1] | HPO search space expansion ratio. | -| freqai.optuna_hyperopt.min_resource | 3 | int >= 1 | Minimum resource per Hyperband pruner rung. | -| freqai.optuna_hyperopt.seed | 1 | int >= 0 | HPO RNG seed. | +| Path | Default | Type / Range | Description | +| ---------------------------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| _Protections_ | | | | +| custom_protections.trade_duration_candles | 72 | int >= 1 | Estimated trade duration in candles. Scales protections stop duration candles and trade limit. | +| custom_protections.lookback_period_fraction | 0.5 | float (0,1] | Fraction of fit_live_predictions_candles used to calculate lookback_period_candles for MaxDrawdown and StoplossGuard protections. | +| custom_protections.cooldown.enabled | true | bool | Enable/disable CooldownPeriod protection. | +| custom_protections.cooldown.stop_duration_candles | 4 | int >= 1 | Number of candles to wait before allowing new trades after a trade is closed. | +| custom_protections.drawdown.enabled | true | bool | Enable/disable MaxDrawdown protection. | +| custom_protections.drawdown.max_allowed_drawdown | 0.2 | float (0,1) | Maximum allowed drawdown. | +| custom_protections.stoploss.enabled | true | bool | Enable/disable StoplossGuard protection. | +| _Leverage_ | | | | +| leverage | proposed_leverage | float [1.0, max_leverage] | Leverage. Fallback to proposed_leverage for the pair. | +| _Exit pricing_ | | | | +| exit_pricing.trade_price_target | `moving_average` | enum {`moving_average`,`interpolation`,`weighted_interpolation`} | Trade NATR computation method. | +| exit_pricing.thresholds_calibration.decline_quantile | 0.90 | float (0,1) | PnL decline quantile threshold. | +| _Reversal confirmation_ | | | | +| reversal_confirmation.lookback_period | 0 | int >= 0 | Prior confirming candles; 0 = none. | +| reversal_confirmation.decay_ratio | 0.5 | float (0,1] | Geometric per-candle volatility adjusted reversal threshold relaxation factor. | +| reversal_confirmation.min_natr_ratio_percent | 0.0095 | float [0,1] | Lower bound fraction for volatility adjusted reversal threshold. | +| reversal_confirmation.max_natr_ratio_percent | 0.075 | float [0,1] | Upper bound fraction (>= lower bound) for volatility adjusted reversal threshold. | +| _Regressor model_ | | | | +| freqai.regressor | `xgboost` | enum {`xgboost`,`lightgbm`} | Machine learning regressor algorithm. | +| _Extrema smoothing_ | | | | +| freqai.extrema_smoothing.method | `gaussian` | enum {`gaussian`,`kaiser`,`triang`,`smm`,`sma`,`savgol`,`nadaraya_watson`} | Extrema smoothing method (`smm`=median, `sma`=mean, `savgol`=Savitzky–Golay, `nadaraya_watson`=Gaussian kernel regression). | +| freqai.extrema_smoothing.window | 5 | int >= 3 | Smoothing window length (candles). | +| freqai.extrema_smoothing.beta | 8.0 | float > 0 | Shape parameter for `kaiser` kernel. | +| freqai.extrema_smoothing.polyorder | 3 | int >= 1 | Polynomial order for `savgol` smoothing. | +| freqai.extrema_smoothing.mode | `mirror` | enum {`mirror`,`constant`,`nearest`,`wrap`,`interp`} | Boundary mode for `savgol` and `nadaraya_watson`. | +| freqai.extrema_smoothing.bandwidth | 1.0 | float > 0 | Gaussian bandwidth for `nadaraya_watson`. | +| _Extrema weighting_ | | | | +| freqai.extrema_weighting.strategy | `none` | enum {`none`,`amplitude`,`amplitude_threshold_ratio`,`volume`,`speed`,`efficiency_ratio`} | Extrema weighting source: unweighted (`none`), swing amplitude (`amplitude`), swing amplitude / median volatility-threshold ratio (`amplitude_threshold_ratio`), swing volume (`volume`), swing speed (`speed`), or swing efficiency ratio (`efficiency_ratio`). | +| freqai.extrema_weighting.standardization | `none` | enum {`none`,`zscore`,`robust`,`mmad`} | Standardization method applied before normalization. `none`=no standardization, `zscore`=(w-μ)/σ, `robust`=(w-median)/IQR, `mmad`=(w-median)/MAD. | +| freqai.extrema_weighting.robust_quantiles | [0.25, 0.75] | list[float] where 0 <= Q1 < Q3 <= 1 | Quantile range for robust standardization, Q1 and Q3. | +| freqai.extrema_weighting.mmad_scaling_factor | 1.4826 | float > 0 | Scaling factor for MMAD standardization. | +| freqai.extrema_weighting.normalization | `minmax` | enum {`minmax`,`sigmoid`,`softmax`,`l1`,`l2`,`rank`,`none`} | Normalization method for weights. | +| freqai.extrema_weighting.minmax_range | [0.0, 1.0] | list[float] | Target range for `minmax` normalization, min and max. | +| freqai.extrema_weighting.sigmoid_scale | 1.0 | float > 0 | Scale parameter for `sigmoid` normalization, controls steepness. | +| freqai.extrema_weighting.softmax_temperature | 1.0 | float > 0 | Temperature parameter for `softmax` normalization: lower values sharpen distribution, higher values flatten it. | +| freqai.extrema_weighting.rank_method | `average` | enum {`average`,`min`,`max`,`dense`,`ordinal`} | Ranking method for `rank` normalization. | +| freqai.extrema_weighting.gamma | 1.0 | float (0,10] | Contrast exponent applied 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 horizon. | +| freqai.feature_parameters.min_label_period_candles | 12 | int >= 1 | Minimum labeling NATR horizon used for reversals labeling HPO. | +| freqai.feature_parameters.max_label_period_candles | 24 | int >= 1 | Maximum labeling NATR horizon used for reversals labeling HPO. | +| freqai.feature_parameters.label_natr_ratio | min/max midpoint | float > 0 | Zigzag labeling NATR ratio. | +| freqai.feature_parameters.min_label_natr_ratio | 9.0 | float > 0 | Minimum labeling NATR ratio used for reversals labeling HPO. | +| freqai.feature_parameters.max_label_natr_ratio | 12.0 | float > 0 | Maximum labeling NATR ratio 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.feature_parameters.label_metric | `euclidean` | string (supported: `euclidean`,`minkowski`,`cityblock`,`chebyshev`,`mahalanobis`,`seuclidean`,`jensenshannon`,`sqeuclidean`,...) | Metric used in distance calculations to ideal point. | +| freqai.feature_parameters.label_weights | [1/6,1/6,1/6,1/6,1/6,1/6] | list[float] | Per-objective weights used in distance calculations to ideal point. Objectives: (1) number of detected reversals, (2) median swing amplitude, (3) median swing amplitude / median volatility-threshold ratio, (4) median swing volume, (5) median swing speed, (6) median swing efficiency ratio. | +| freqai.feature_parameters.label_p_order | `None` | float \| None | p-order used by `minkowski` / `power_mean` (optional). | +| freqai.feature_parameters.label_medoid_metric | `euclidean` | string | Metric used with `medoid`. | +| freqai.feature_parameters.label_kmeans_metric | `euclidean` | string | Metric used for k-means clustering. | +| freqai.feature_parameters.label_kmeans_selection | `min` | enum {`min`,`medoid`} | Strategy to select trial in the best kmeans cluster. | +| freqai.feature_parameters.label_kmedoids_metric | `euclidean` | string | Metric used for k-medoids clustering. | +| freqai.feature_parameters.label_kmedoids_selection | `min` | enum {`min`,`medoid`} | Strategy to select trial in the best k-medoids cluster. | +| freqai.feature_parameters.label_knn_metric | `minkowski` | string | Distance metric for KNN. | +| freqai.feature_parameters.label_knn_p_order | `None` | float \| None | Tunable for KNN neighbor distances aggregation methods: p-order (`knn_power_mean`, default: 1.0) or quantile (`knn_quantile`, default: 0.5). (optional) | +| freqai.feature_parameters.label_knn_n_neighbors | 5 | int >= 1 | Number of neighbors for KNN. | +| _Predictions extrema_ | | | | +| freqai.predictions_extrema.selection_method | `rank` | enum {`rank`,`values`,`partition`} | Extrema selection method. `values` uses reversal values, `rank` uses ranked extrema values, `partition` uses sign-based partitioning. | +| freqai.predictions_extrema.thresholds_smoothing | `mean` | enum {`mean`,`isodata`,`li`,`minimum`,`otsu`,`triangle`,`yen`,`median`,`soft_extremum`} | Thresholding method for prediction thresholds smoothing. | +| freqai.predictions_extrema.thresholds_alpha | 12.0 | float > 0 | Alpha for `soft_extremum`. | +| freqai.predictions_extrema.threshold_outlier | 0.999 | float (0,1) | Quantile threshold for predictions outlier filtering. | +| freqai.predictions_extrema.extrema_fraction | 1.0 | float (0,1] | Fraction of extrema used for thresholds. `1.0` uses all, lower values keep only most significant. Applies to `rank` and `values`; ignored for `partition`. | +| _Optuna / HPO_ | | | | +| freqai.optuna_hyperopt.enabled | true | bool | Enables HPO. | +| freqai.optuna_hyperopt.sampler | `tpe` | enum {`tpe`,`auto`} | HPO sampler algorithm. `tpe` uses [TPESampler](https://optuna.readthedocs.io/en/stable/reference/samplers/generated/optuna.samplers.TPESampler.html) with multivariate and group, `auto` uses [AutoSampler](https://hub.optuna.org/samplers/auto_sampler). | +| freqai.optuna_hyperopt.storage | `file` | enum {`file`,`sqlite`} | HPO storage backend. | +| freqai.optuna_hyperopt.continuous | true | bool | Continuous HPO. | +| freqai.optuna_hyperopt.warm_start | true | bool | Warm start HPO with previous best value(s). | +| 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. | +| freqai.optuna_hyperopt.timeout | 7200 | int >= 0 | HPO wall-clock timeout in seconds. | +| freqai.optuna_hyperopt.label_candles_step | 1 | int >= 1 | Step for Zigzag NATR horizon search space. | +| freqai.optuna_hyperopt.train_candles_step | 10 | int >= 1 | Step for training sets size search space. | +| freqai.optuna_hyperopt.space_reduction | false | bool | Enable/disable HPO search space reduction based on previous best parameters. | +| freqai.optuna_hyperopt.expansion_ratio | 0.4 | float [0,1] | HPO search space expansion ratio. | +| freqai.optuna_hyperopt.min_resource | 3 | int >= 1 | Minimum resource per Hyperband pruner rung. | +| freqai.optuna_hyperopt.seed | 1 | int >= 0 | HPO RNG seed. | ## ReforceXY diff --git a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py index e8e41c7..43dad13 100644 --- a/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py +++ b/quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py @@ -73,7 +73,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): https://github.com/sponsors/robcaulk """ - version = "3.7.128" + version = "3.7.129" _SQRT_2: Final[float] = np.sqrt(2.0) @@ -374,7 +374,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel): for pair in self.pairs: self._optuna_hp_value[pair] = -1 self._optuna_train_value[pair] = -1 - self._optuna_label_values[pair] = [-1, -1, -1, -1] + self._optuna_label_values[pair] = [-1, -1, -1, -1, -1, -1] self._optuna_hp_params[pair] = ( self.optuna_load_best_params( pair, QuickAdapterRegressorV3._OPTUNA_NAMESPACES[0] @@ -749,6 +749,8 @@ class QuickAdapterRegressorV3(BaseRegressionModel): optuna.study.StudyDirection.MAXIMIZE, optuna.study.StudyDirection.MAXIMIZE, optuna.study.StudyDirection.MAXIMIZE, + optuna.study.StudyDirection.MAXIMIZE, + optuna.study.StudyDirection.MAXIMIZE, ], ), ) @@ -2259,7 +2261,7 @@ def label_objective( max_label_period_candles: int = 24, min_label_natr_ratio: float = 9.0, max_label_natr_ratio: float = 12.0, -) -> tuple[int, float, float, float]: +) -> tuple[int, float, float, float, float, float]: min_label_period_candles, max_label_period_candles, candles_step = ( get_min_max_label_period_candles( fit_live_predictions_candles, @@ -2289,7 +2291,7 @@ def label_objective( ] if df.empty: - return 0, 0.0, 0.0, 0.0 + return 0, 0.0, 0.0, 0.0, 0.0, 0.0 ( _, @@ -2298,6 +2300,9 @@ def label_objective( pivots_amplitudes, pivots_amplitude_threshold_ratios, pivots_volumes, + _, + pivots_speeds, + pivots_efficiency_ratios, ) = zigzag( df, natr_period=label_period_candles, @@ -2307,18 +2312,32 @@ def label_objective( median_amplitude = np.nanmedian(np.asarray(pivots_amplitudes, dtype=float)) if not np.isfinite(median_amplitude): median_amplitude = 0.0 + median_amplitude_threshold_ratio = np.nanmedian( np.asarray(pivots_amplitude_threshold_ratios, dtype=float) ) if not np.isfinite(median_amplitude_threshold_ratio): median_amplitude_threshold_ratio = 0.0 + median_volume = np.nanmedian(np.asarray(pivots_volumes, dtype=float)) if not np.isfinite(median_volume): median_volume = 0.0 + median_speed = np.nanmedian(np.asarray(pivots_speeds, dtype=float)) + if not np.isfinite(median_speed): + median_speed = 0.0 + + median_efficiency_ratio = np.nanmedian( + np.asarray(pivots_efficiency_ratios, dtype=float) + ) + if not np.isfinite(median_efficiency_ratio): + median_efficiency_ratio = 0.0 + return ( len(pivots_values), median_amplitude, median_amplitude_threshold_ratio, median_volume, + median_speed, + median_efficiency_ratio, ) diff --git a/quickadapter/user_data/strategies/QuickAdapterV3.py b/quickadapter/user_data/strategies/QuickAdapterV3.py index 2265dea..e20c041 100644 --- a/quickadapter/user_data/strategies/QuickAdapterV3.py +++ b/quickadapter/user_data/strategies/QuickAdapterV3.py @@ -106,7 +106,7 @@ class QuickAdapterV3(IStrategy): _TRADING_MODES: Final[tuple[TradingMode, ...]] = ("spot", "margin", "futures") def version(self) -> str: - return "3.3.178" + return "3.3.179" timeframe = "5m" @@ -906,6 +906,8 @@ class QuickAdapterV3(IStrategy): amplitudes: list[float], amplitude_threshold_ratios: list[float], volumes: list[float], + speeds: list[float], + efficiency_ratios: list[float], ) -> NDArray[np.floating]: if strategy == WEIGHT_STRATEGIES[1]: # "amplitude" return np.array(amplitudes) @@ -913,6 +915,10 @@ class QuickAdapterV3(IStrategy): return np.array(amplitude_threshold_ratios) if strategy == WEIGHT_STRATEGIES[3]: # "volume" return np.array(volumes) + if strategy == WEIGHT_STRATEGIES[4]: # "speed" + return np.array(speeds) + if strategy == WEIGHT_STRATEGIES[5]: # "efficiency_ratio" + return np.array(efficiency_ratios) return np.array([]) def set_freqai_targets( @@ -928,6 +934,9 @@ class QuickAdapterV3(IStrategy): pivots_amplitudes, pivots_amplitude_threshold_ratios, pivots_volumes, + _, + pivots_speeds, + pivots_efficiency_ratios, ) = zigzag( dataframe, natr_period=label_period_candles, @@ -958,6 +967,8 @@ class QuickAdapterV3(IStrategy): pivots_amplitudes, pivots_amplitude_threshold_ratios, pivots_volumes, + pivots_speeds, + pivots_efficiency_ratios, ) weighted_extrema, _ = get_weighted_extrema( extrema=dataframe[EXTREMA_COLUMN], diff --git a/quickadapter/user_data/strategies/Utils.py b/quickadapter/user_data/strategies/Utils.py index 349ab45..8a9109f 100644 --- a/quickadapter/user_data/strategies/Utils.py +++ b/quickadapter/user_data/strategies/Utils.py @@ -24,12 +24,16 @@ WeightStrategy = Literal[ "amplitude", "amplitude_threshold_ratio", "volume", + "speed", + "efficiency_ratio", ] WEIGHT_STRATEGIES: Final[tuple[WeightStrategy, ...]] = ( "none", "amplitude", "amplitude_threshold_ratio", "volume", + "speed", + "efficiency_ratio", ) EXTREMA_COLUMN: Final = "&s-extrema" @@ -637,7 +641,7 @@ def get_weighted_extrema( extrema: Extrema series indices: Indices of extrema points weights: Raw weights for each extremum - strategy: Weight strategy ("none", "amplitude", "amplitude_threshold_ratio", "volume") + strategy: Weight strategy ("none", "amplitude", "amplitude_threshold_ratio", "volume", "speed", "efficiency_ratio") standardization: Standardization method robust_quantiles: Quantiles for robust standardization mmad_scaling_factor: Scaling factor for MMAD standardization @@ -657,11 +661,16 @@ def get_weighted_extrema( ): # "none" return extrema, default_weights - if strategy in { - WEIGHT_STRATEGIES[1], - WEIGHT_STRATEGIES[2], - WEIGHT_STRATEGIES[3], - }: # "amplitude" / "amplitude_threshold_ratio" / "volume" + if ( + strategy + in { + WEIGHT_STRATEGIES[1], + WEIGHT_STRATEGIES[2], + WEIGHT_STRATEGIES[3], + WEIGHT_STRATEGIES[4], + WEIGHT_STRATEGIES[5], + } + ): # "amplitude" / "amplitude_threshold_ratio" / "volume" / "speed" / "efficiency_ratio" extrema_weights = calculate_extrema_weights( series=extrema, indices=indices, @@ -1099,6 +1108,9 @@ def zigzag( list[float], list[float], list[float], + list[float], + list[float], + list[float], ]: n = len(df) if df.empty or n < natr_period: @@ -1109,6 +1121,9 @@ def zigzag( [], [], [], + [], + [], + [], ) natr_values = (ta.NATR(df, timeperiod=natr_period).bfill() / 100.0).to_numpy() @@ -1129,6 +1144,9 @@ def zigzag( pivots_amplitudes: list[float] = [] pivots_amplitude_threshold_ratios: list[float] = [] pivots_volumes: list[float] = [] + pivots_durations: list[float] = [] + pivots_speeds: list[float] = [] + pivots_efficiency_ratios: list[float] = [] last_pivot_pos: int = -1 candidate_pivot_pos: int = -1 @@ -1216,8 +1234,46 @@ def zigzag( start_pos = min(previous_pos, current_pos) end_pos = max(previous_pos, current_pos) + 1 - volume = np.nansum(volumes[start_pos:end_pos]) - return volume + return np.nansum(volumes[start_pos:end_pos]) + + def calculate_pivot_duration( + *, + previous_pos: int, + current_pos: int, + ) -> float: + if previous_pos < 0 or current_pos < 0: + return np.nan + if previous_pos >= n or current_pos >= n: + return np.nan + + return abs(current_pos - previous_pos) + + def calculate_pivot_efficiency_ratio( + *, + previous_pos: int, + current_pos: int, + ) -> float: + if previous_pos < 0 or current_pos < 0: + return np.nan + if previous_pos >= n or current_pos >= n: + return np.nan + + start_pos = min(previous_pos, current_pos) + end_pos = max(previous_pos, current_pos) + 1 + if (end_pos - start_pos) < 2: + return np.nan + + closes_slice = closes[start_pos:end_pos] + close_diffs = np.diff(closes_slice) + path_length = float(np.nansum(np.abs(close_diffs))) + net_move = float(abs(closes_slice[-1] - closes_slice[0])) + + if not (np.isfinite(path_length) and np.isfinite(net_move)): + return np.nan + if np.isclose(path_length, 0.0): + return np.nan + + return net_move / path_length def add_pivot(pos: int, value: float, direction: TrendDirection): nonlocal last_pivot_pos @@ -1240,14 +1296,32 @@ def zigzag( previous_pos=last_pivot_pos, current_pos=pos, ) + duration = calculate_pivot_duration( + previous_pos=last_pivot_pos, + current_pos=pos, + ) + if np.isfinite(amplitude) and np.isfinite(duration) and duration > 0.0: + speed = amplitude / duration + else: + speed = np.nan + efficiency_ratio = calculate_pivot_efficiency_ratio( + previous_pos=last_pivot_pos, + current_pos=pos, + ) else: amplitude = np.nan amplitude_threshold_ratio = np.nan volume = np.nan + duration = np.nan + speed = np.nan + efficiency_ratio = np.nan pivots_amplitudes.append(amplitude) pivots_amplitude_threshold_ratios.append(amplitude_threshold_ratio) pivots_volumes.append(volume) + pivots_durations.append(duration) + pivots_speeds.append(speed) + pivots_efficiency_ratios.append(efficiency_ratio) last_pivot_pos = pos reset_candidate_pivot() @@ -1373,6 +1447,9 @@ def zigzag( [], [], [], + [], + [], + [], ) for i in range(last_pivot_pos + 1, n): @@ -1410,6 +1487,9 @@ def zigzag( pivots_amplitudes, pivots_amplitude_threshold_ratios, pivots_volumes, + pivots_durations, + pivots_speeds, + pivots_efficiency_ratios, ) -- 2.53.0