### Configuration tunables
-| Path | Default | Type / Range | Description |
-| ---------------------------------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| _Protections_ | | | |
-| estimated_trade_duration_candles | 48 | int >= 1 | Heuristic for StoplossGuard tuning. |
-| _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 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.2 | 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`} | Extrema smoothing kernel (smm=simple moving median, sma=simple moving average). |
-| freqai.extrema_smoothing.window | 5 | int >= 3 | Window size for extrema smoothing. |
-| freqai.extrema_smoothing.beta | 8.0 | float > 0 | Kaiser kernel shape parameter. |
-| _Extrema weighting_ | | | |
-| freqai.extrema_weighting.strategy | `none` | enum {`none`,`threshold`} | Weighting strategy applied before smoothing. |
-| freqai.extrema_weighting.normalization | `minmax` | enum {`minmax`,`zscore`,`l1`,`l2`,`robust`,`softmax`,`tanh`,`rank`,`none`} | Normalization method for weights. |
-| freqai.extrema_weighting.gamma | 1.0 | float (0,10] | Contrast exponent applied after normalization (>1 emphasizes extremes, 0<gamma<1 softens). |
-| 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.tanh_scale | 1.0 | float > 0 | Scale parameter for tanh normalization. |
-| freqai.extrema_weighting.tanh_gain | 1.0 | float > 0 | Gain parameter for tanh normalization. |
-| freqai.extrema_weighting.robust_quantiles | [0.25, 0.75] | list[float] where 0 <= q_low < q_high <= 1 | Quantile range for robust normalization. |
-| freqai.extrema_weighting.rank_method | `average` | enum {`average`,`min`,`max`,`dense`,`ordinal`} | Ranking method for rank normalization. |
-| _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 | [0.5,0.5] | list[float] | Per-objective weights used in distance calculations to ideal point. First objective is the median threshold of Zigzag reversals (reversals quality). Second objective is the number of detected reversals. |
-| freqai.feature_parameters.label_p_order | `None` | float | p-order used by Minkowski / power-mean calculations (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 | p-order for KNN Minkowski metric distance. (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 {`values`,`rank`,`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`,`median`,`isodata`,`li`,`minimum`,`otsu`,`triangle`,`yen`,`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. |
-| _Optuna / HPO_ | | | |
-| freqai.optuna_hyperopt.enabled | true | bool | Enables HPO. |
-| freqai.optuna_hyperopt.sampler | `tpe` | enum {`tpe`,`auto`} | HPO sampler algorithm. `tpe` uses TPESampler with multivariate and group, `auto` uses AutoSampler. |
-| 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_ | | | |
+| estimated_trade_duration_candles | 48 | int >= 1 | Heuristic for StoplossGuard tuning. |
+| _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 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.2 | 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`} | Extrema smoothing kernel (smm=simple moving median, sma=simple moving average). |
+| freqai.extrema_smoothing.window | 5 | int >= 3 | Window size for extrema smoothing. |
+| freqai.extrema_smoothing.beta | 8.0 | float > 0 | Kaiser kernel shape parameter. |
+| _Extrema weighting_ | | | |
+| freqai.extrema_weighting.strategy | `none` | enum {`none`,`amplitude`,`amplitude_excess`} | Extrema weighting source: unweighted (`none`), swing amplitude (`amplitude`), or volatility-adjusted swing amplitude (`amplitude_excess`). |
+| freqai.extrema_weighting.normalization | `minmax` | enum {`minmax`,`zscore`,`l1`,`l2`,`robust`,`softmax`,`tanh`,`rank`,`none`} | Normalization method for weights. |
+| freqai.extrema_weighting.gamma | 1.0 | float (0,10] | Contrast exponent applied after normalization (>1 emphasizes extremes, 0<gamma<1 softens). |
+| 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.tanh_scale | 1.0 | float > 0 | Scale parameter for tanh normalization. |
+| freqai.extrema_weighting.tanh_gain | 1.0 | float > 0 | Gain parameter for tanh normalization. |
+| freqai.extrema_weighting.robust_quantiles | [0.25, 0.75] | list[float] where 0 <= q_low < q_high <= 1 | Quantile range for robust normalization. |
+| freqai.extrema_weighting.rank_method | `average` | enum {`average`,`min`,`max`,`dense`,`ordinal`} | Ranking method for rank normalization. |
+| _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 | [0.5,0.5] | list[float] | Per-objective weights used in distance calculations to ideal point. First objective is the median swing amplitude of Zigzag reversals (reversals quality). Second objective is the number of detected reversals. |
+| freqai.feature_parameters.label_p_order | `None` | float | p-order used by Minkowski / power-mean calculations (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 | p-order for KNN Minkowski metric distance. (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 {`values`,`rank`,`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`,`median`,`isodata`,`li`,`minimum`,`otsu`,`triangle`,`yen`,`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. |
+| _Optuna / HPO_ | | | |
+| freqai.optuna_hyperopt.enabled | true | bool | Enables HPO. |
+| freqai.optuna_hyperopt.sampler | `tpe` | enum {`tpe`,`auto`} | HPO sampler algorithm. `tpe` uses TPESampler with multivariate and group, `auto` uses AutoSampler. |
+| 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
from sklearn_extra.cluster import KMedoids
from Utils import (
+ REGRESSORS,
+ Regressor,
calculate_min_extrema,
calculate_n_extrema,
fit_regressor,
https://github.com/sponsors/robcaulk
"""
- version = "3.7.122"
+ version = "3.7.123"
_SQRT_2: Final[float] = np.sqrt(2.0)
self.set_optuna_label_candle(pair)
self._optuna_label_candles[pair] = 0
+ self.regressor: Regressor = self.freqai_info.get("regressor", REGRESSORS[0])
+ if self.regressor not in REGRESSORS:
+ self.regressor = REGRESSORS[0]
+ self.freqai_info["regressor"] = self.regressor
logger.info(
- f"Initialized {self.__class__.__name__} {self.freqai_info.get('regressor', 'xgboost')} regressor model version {self.version}"
+ f"Initialized {self.__class__.__name__} {self.regressor} regressor model version {self.version}"
)
def get_optuna_params(self, pair: str, namespace: str) -> dict[str, Any]:
namespace=QuickAdapterRegressorV3._OPTUNA_NAMESPACES[0], # "hp"
objective=lambda trial: hp_objective(
trial,
- str(self.freqai_info.get("regressor", "xgboost")),
+ self.regressor,
X,
y,
train_weights,
namespace=QuickAdapterRegressorV3._OPTUNA_NAMESPACES[1], # "train"
objective=lambda trial: train_objective(
trial,
- str(self.freqai_info.get("regressor", "xgboost")),
+ self.regressor,
X,
y,
train_weights,
)
model = fit_regressor(
- regressor=str(self.freqai_info.get("regressor", "xgboost")),
+ regressor=self.regressor,
X=X,
y=y,
train_weights=train_weights,
def train_objective(
trial: optuna.trial.Trial,
- regressor: str,
+ regressor: Regressor,
X: pd.DataFrame,
y: pd.DataFrame,
train_weights: NDArray[np.floating],
def hp_objective(
trial: optuna.trial.Trial,
- regressor: str,
+ regressor: Regressor,
X: pd.DataFrame,
y: pd.DataFrame,
train_weights: NDArray[np.floating],
if df.empty:
return -np.inf, 0
- _, pivots_values, _, pivots_thresholds = zigzag(
+ _, pivots_values, _, pivots_amplitudes, _ = zigzag(
df,
natr_period=label_period_candles,
natr_ratio=label_natr_ratio,
)
- return np.median(pivots_thresholds), len(pivots_values)
+ median_amplitude = np.nanmedian(pivots_amplitudes)
+ if not np.isfinite(median_amplitude):
+ median_amplitude = 0.0
+ return median_amplitude, len(pivots_values)
_TRADING_MODES: Final[tuple[TradingMode, ...]] = ("spot", "margin", "futures")
def version(self) -> str:
- return "3.3.172"
+ return "3.3.173"
timeframe = "5m"
@staticmethod
@lru_cache(maxsize=128)
- def td_format(
+ def _td_format(
delta: datetime.timedelta, pattern: str = "{sign}{d}:{h:02d}:{m:02d}:{s:02d}"
) -> str:
negative_duration = delta.total_seconds() < 0
except (KeyError, ValueError) as e:
raise ValueError(f"Invalid pattern '{pattern}': {repr(e)}")
+ @staticmethod
+ def _get_weights(
+ strategy: str, amplitudes: list[float], amplitude_excesses: list[float]
+ ) -> list[float]:
+ if not isinstance(strategy, str):
+ return []
+ strategy = strategy.lower().strip()
+ if strategy == "amplitude_excess":
+ return (
+ amplitude_excesses
+ if len(amplitude_excesses) == len(amplitudes)
+ else amplitudes
+ )
+ if strategy == "amplitude":
+ return amplitudes
+ return []
+
def set_freqai_targets(
self, dataframe: DataFrame, metadata: dict[str, Any], **kwargs
) -> DataFrame:
pair = str(metadata.get("pair"))
label_period_candles = self.get_label_period_candles(pair)
label_natr_ratio = self.get_label_natr_ratio(pair)
- pivots_indices, _, pivots_directions, pivots_thresholds = zigzag(
+ (
+ pivots_indices,
+ _,
+ pivots_directions,
+ pivots_amplitudes,
+ pivots_amplitude_excesses,
+ ) = zigzag(
dataframe,
natr_period=label_period_candles,
natr_ratio=label_natr_ratio,
dataframe[EXTREMA_COLUMN] = 0
if len(pivots_indices) == 0:
logger.warning(
- f"{pair}: no extrema to label (label_period={QuickAdapterV3.td_format(label_period)} / {label_period_candles=} / {label_natr_ratio=:.2f})"
+ f"{pair}: no extrema to label (label_period={QuickAdapterV3._td_format(label_period)} / {label_period_candles=} / {label_natr_ratio=:.2f})"
)
else:
dataframe.loc[pivots_indices, EXTREMA_COLUMN] = pivots_directions
dataframe[EXTREMA_COLUMN] == TrendDirection.UP, 1, 0
)
logger.info(
- f"{pair}: labeled {len(pivots_indices)} extrema (label_period={QuickAdapterV3.td_format(label_period)} / {label_period_candles=} / {label_natr_ratio=:.2f})"
+ f"{pair}: labeled {len(pivots_indices)} extrema (label_period={QuickAdapterV3._td_format(label_period)} / {label_period_candles=} / {label_natr_ratio=:.2f})"
)
extrema_smoothing = self.freqai_info.get("extrema_smoothing", {})
extrema_weighting, pair
)
+ pivot_weights = QuickAdapterV3._get_weights(
+ extrema_weighting_params["strategy"],
+ pivots_amplitudes,
+ pivots_amplitude_excesses,
+ )
weighted_extrema, _ = get_weighted_extrema(
extrema=dataframe[EXTREMA_COLUMN],
indices=pivots_indices,
- weights=np.array(pivots_thresholds),
+ weights=np.array(pivot_weights),
strategy=extrema_weighting_params["strategy"],
normalization=extrema_weighting_params["normalization"],
gamma=extrema_weighting_params["gamma"],
T = TypeVar("T", pd.Series, float)
-WeightStrategy = Literal["none", "threshold"]
-WEIGHT_STRATEGIES: Final[tuple[WeightStrategy, ...]] = ("none", "threshold")
+WeightStrategy = Literal["none", "amplitude", "amplitude_excess"]
+WEIGHT_STRATEGIES: Final[tuple[WeightStrategy, ...]] = (
+ "none",
+ "amplitude",
+ "amplitude_excess",
+)
NormalizationType = Literal[
"minmax", "zscore", "l1", "l2", "robust", "softmax", "tanh", "rank", "none"
): # "none"
return extrema, default_weights
- if strategy == WEIGHT_STRATEGIES[1]: # "threshold"
+ if strategy in (
+ WEIGHT_STRATEGIES[1],
+ WEIGHT_STRATEGIES[2],
+ ): # "amplitude" or "amplitude_excess"
extrema_weights = calculate_extrema_weights(
series=extrema,
indices=indices,
df: pd.DataFrame,
natr_period: int = 14,
natr_ratio: float = 9.0,
-) -> tuple[list[int], list[float], list[TrendDirection], list[float]]:
+) -> tuple[list[int], list[float], list[TrendDirection], list[float], list[float]]:
n = len(df)
if df.empty or n < natr_period:
- return [], [], [], []
+ return [], [], [], [], []
natr_values = (ta.NATR(df, timeperiod=natr_period).bfill() / 100.0).to_numpy()
pivots_indices: list[int] = []
pivots_values: list[float] = []
pivots_directions: list[TrendDirection] = []
- pivots_thresholds: list[float] = []
+ pivots_amplitudes: list[float] = []
+ pivots_amplitude_excesses: list[float] = []
last_pivot_pos: int = -1
candidate_pivot_pos: int = -1
pivots_indices.append(indices[pos])
pivots_values.append(value)
pivots_directions.append(direction)
- pivots_thresholds.append(thresholds[pos])
+ if len(pivots_values) > 1:
+ prev_value = pivots_values[-2]
+ if np.isclose(prev_value, 0.0):
+ amplitude = np.nan
+ else:
+ amplitude = abs(value - prev_value) / abs(prev_value)
+ threshold_current = thresholds[pos]
+ if (
+ np.isfinite(threshold_current)
+ and threshold_current > 0
+ and np.isfinite(amplitude)
+ ):
+ amplitude_excess = amplitude / threshold_current
+ else:
+ amplitude_excess = np.nan
+ else:
+ amplitude = np.nan
+ amplitude_excess = np.nan
+ pivots_amplitudes.append(amplitude)
+ pivots_amplitude_excesses.append(amplitude_excess)
last_pivot_pos = pos
reset_candidate_pivot()
state = TrendDirection.UP
break
else:
- return [], [], [], []
+ return [], [], [], [], []
for i in range(last_pivot_pos + 1, n):
current_high = highs[i]
)
state = TrendDirection.UP
- return pivots_indices, pivots_values, pivots_directions, pivots_thresholds
+ return (
+ pivots_indices,
+ pivots_values,
+ pivots_directions,
+ pivots_amplitudes,
+ pivots_amplitude_excesses,
+ )
-regressors = {"xgboost", "lightgbm"}
+Regressor = Literal["xgboost", "lightgbm"]
+REGRESSORS: Final[tuple[Regressor, ...]] = ("xgboost", "lightgbm")
def get_optuna_callbacks(
- trial: optuna.trial.Trial, regressor: str
+ trial: optuna.trial.Trial, regressor: Regressor
) -> list[Callable[[optuna.trial.Trial, str], None]]:
- if regressor == "xgboost":
+ if regressor == REGRESSORS[0]: # "xgboost"
callbacks = [
optuna.integration.XGBoostPruningCallback(trial, "validation_0-rmse")
]
- elif regressor == "lightgbm":
+ elif regressor == REGRESSORS[1]: # "lightgbm"
callbacks = [optuna.integration.LightGBMPruningCallback(trial, "rmse")]
else:
raise ValueError(
- f"Unsupported regressor model: {regressor} (supported: {', '.join(regressors)})"
+ f"Unsupported regressor model: {regressor} (supported: {', '.join(REGRESSORS)})"
)
return callbacks
def fit_regressor(
- regressor: str,
+ regressor: Regressor,
X: pd.DataFrame,
y: pd.DataFrame,
train_weights: NDArray[np.floating],
callbacks: Optional[list[Callable[[optuna.trial.Trial, str], None]]] = None,
trial: Optional[optuna.trial.Trial] = None,
) -> Any:
- if regressor == "xgboost":
+ if regressor == REGRESSORS[0]: # "xgboost"
from xgboost import XGBRegressor
model_training_parameters.setdefault("random_state", 1)
sample_weight_eval_set=eval_weights,
xgb_model=init_model,
)
- elif regressor == "lightgbm":
+ elif regressor == REGRESSORS[1]: # "lightgbm"
from lightgbm import LGBMRegressor
model_training_parameters.setdefault("seed", 1)
)
else:
raise ValueError(
- f"Unsupported regressor model: {regressor} (supported: {', '.join(regressors)})"
+ f"Unsupported regressor model: {regressor} (supported: {', '.join(REGRESSORS)})"
)
return model
def get_optuna_study_model_parameters(
trial: optuna.trial.Trial,
- regressor: str,
+ regressor: Regressor,
model_training_best_parameters: dict[str, Any],
space_reduction: bool,
expansion_ratio: float,
) -> dict[str, Any]:
- if regressor not in regressors:
+ if regressor not in set(REGRESSORS):
raise ValueError(
- f"Unsupported regressor model: {regressor} (supported: {', '.join(regressors)})"
+ f"Unsupported regressor model: {regressor} (supported: {', '.join(REGRESSORS)})"
)
if not isinstance(expansion_ratio, (int, float)) or not (
0.0 <= expansion_ratio <= 1.0
"reg_lambda", ranges["reg_lambda"][0], ranges["reg_lambda"][1], log=True
),
}
- if regressor == "xgboost":
+ if regressor == REGRESSORS[0]: # "xgboost"
study_model_parameters.update(
{
"max_depth": trial.suggest_int(
),
}
)
- elif regressor == "lightgbm":
+ elif regressor == REGRESSORS[1]: # "lightgbm"
study_model_parameters.update(
{
"num_leaves": trial.suggest_int(