@staticmethod
def get_pred_min_max(pred_extrema: pd.Series) -> tuple[pd.Series, pd.Series]:
+ pred_extrema = (
+ pd.to_numeric(pred_extrema, errors="coerce")
+ .where(np.isfinite, np.nan)
+ .dropna()
+ )
+ if pred_extrema.empty:
+ return pd.Series(dtype=float), pd.Series(dtype=float)
n_pred_minima = max(1, sp.signal.find_peaks(-pred_extrema)[0].size)
n_pred_maxima = max(1, sp.signal.find_peaks(pred_extrema)[0].size)
-n_pred_maxima:
]
+ @staticmethod
+ def safe_min_pred(pred_extrema: pd.Series) -> float:
+ try:
+ pred_minimum = pred_extrema.min()
+ except Exception:
+ pred_minimum = None
+ if (
+ pred_minimum is not None
+ and isinstance(pred_minimum, (int, float, np.number))
+ and np.isfinite(pred_minimum)
+ ):
+ return pred_minimum
+ return -2.0
+
+ @staticmethod
+ def safe_max_pred(pred_extrema: pd.Series) -> float:
+ try:
+ pred_maximum = pred_extrema.max()
+ except Exception:
+ pred_maximum = None
+ if (
+ pred_maximum is not None
+ and isinstance(pred_maximum, (int, float, np.number))
+ and np.isfinite(pred_maximum)
+ ):
+ return pred_maximum
+ return 2.0
+
@staticmethod
def soft_extremum_min_max(
pred_extrema: pd.Series, alpha: float
pred_minima, pred_maxima = QuickAdapterRegressorV3.get_pred_min_max(
pred_extrema
)
- return soft_extremum(pred_minima, alpha=-alpha), soft_extremum(
- pred_maxima, alpha=alpha
- )
+ soft_minimum = soft_extremum(pred_minima, alpha=-alpha)
+ if not np.isfinite(soft_minimum):
+ soft_minimum = QuickAdapterRegressorV3.safe_min_pred(pred_extrema)
+ soft_maximum = soft_extremum(pred_maxima, alpha=alpha)
+ if not np.isfinite(soft_maximum):
+ soft_maximum = QuickAdapterRegressorV3.safe_max_pred(pred_extrema)
+ return soft_minimum, soft_maximum
@staticmethod
def skimage_min_max(pred_extrema: pd.Series, method: str) -> tuple[float, float]:
raise ValueError(f"Unknown skimage threshold function: threshold_{method}")
min_val = min_func(pred_minima, threshold_func)
+ if not np.isfinite(min_val):
+ min_val = QuickAdapterRegressorV3.safe_min_pred(pred_extrema)
max_val = max_func(pred_maxima, threshold_func)
+ if not np.isfinite(max_val):
+ max_val = QuickAdapterRegressorV3.safe_max_pred(pred_extrema)
return min_val, max_val
if values.size == 0:
return np.nan
- if values.size == 1 or np.all(np.isclose(values, values[0])):
- return values.mean()
+ if (
+ values.size == 1
+ or np.unique(values).size < 3
+ or np.allclose(values, values[0])
+ ):
+ return np.median(values)
try:
return threshold_func(values)
except Exception as e:
"""
if df.empty:
return False
+ if side not in {"long", "short"}:
+ return False
+ if order not in {"entry", "exit"}:
+ return False
lookback_period = max(0, min(int(lookback_period), len(df) - 1))
if not (0.0 < decay_ratio <= 1.0):
for k in range(1, lookback_period + 1):
close_k = df.iloc[-k].get("close")
- if not np.isfinite(close_k):
+ if not isinstance(close_k, (int, float)) or not np.isfinite(close_k):
return current_ok
decayed_min_natr_ratio_percent = max(
max_natr_ratio_percent=decayed_max_natr_ratio_percent,
candle_idx=-(k + 1),
)
- if not np.isfinite(threshold_k):
+ if not isinstance(threshold_k, (int, float)) or not np.isfinite(
+ threshold_k
+ ):
return current_ok
if (side == "long" and not (close_k > threshold_k)) or (
return np.nan
first_value = values[0]
- if np.all(np.isclose(values, first_value)):
+ if np.allclose(values, first_value):
return (
0.5
if np.isclose(value, first_value)
:return: The rounded value.
:raises ValueError: If step is not a positive integer or value is not finite.
"""
+ if not isinstance(value, (int, float)):
+ raise ValueError("value must be an integer or float")
if not np.isfinite(value):
raise ValueError("value must be finite")
if not isinstance(step, int) or step <= 0: