ZhuLinsen/daily_stock_analysis · error · ValueError
{field_name} must be between {minimum:g} and {maximum:g}
Error message
{field_name} must be between {minimum:g} and {maximum:g} What it means
Raised by _float_in_range (src/services/alert_indicators.py:407) when a finite float field is outside its inclusive [minimum, maximum] bounds — used for rsi_threshold's threshold, which must be in [0.0, 100.0] because RSI is a bounded 0-100 oscillator (Wilder's smoothing per _calculate_rsi). The %g format renders bounds without trailing zeros ('0' and '100').
Source
Thrown at src/services/alert_indicators.py:407
try:
number = float(value)
except (TypeError, ValueError) as exc:
raise ValueError(f"invalid {field_name}: {value}") from exc
if not isfinite(number):
raise ValueError(f"{field_name} must be finite")
return number
def _float_in_range(
value: Any,
field_name: str,
*,
minimum: float,
maximum: float,
) -> float:
number = _finite_float(value, field_name)
if number < minimum or number > maximum:
raise ValueError(f"{field_name} must be between {minimum:g} and {maximum:g}")
return number
def _calculate_rsi(close: pd.Series, period: int) -> pd.Series:
delta = close.diff()
gain = delta.where(delta > 0, 0)
loss = -delta.where(delta < 0, 0)
# 使用 Wilder's EMA / SMMA 口径,不使用 rolling SMA。
avg_gain = gain.ewm(alpha=1 / period, adjust=False).mean()
avg_loss = loss.ewm(alpha=1 / period, adjust=False).mean()
rs = avg_gain / avg_loss
return (100 - (100 / (1 + rs))).fillna(50)
def _crossed_threshold(prev_value: float, curr_value: float, threshold: float, direction: str) -> bool:
if direction == "above":
return prev_value <= threshold < curr_value
if direction == "below":View on GitHub (pinned to 5159bd72e8)
Solutions
- Keep rsi_threshold's threshold within 0..100 (typical 70/30).
- Clamp user input client-side: Math.min(100, Math.max(0, v)).
- Remember direction interacts with the value: 'above' 70 for overbought, 'below' 30 for oversold.
Example fix
// before
{ "alert_type": "rsi_threshold", "parameters": { "period": 14, "direction": "above", "threshold": 120 } }
// after
{ "alert_type": "rsi_threshold", "parameters": { "period": 14, "direction": "above", "threshold": 70 } } Defensive patterns
Strategy: validation
Validate before calling
if alert_type == 'rsi_threshold':
t = float(params.get('threshold'))
if not (0.0 <= t <= 100.0):
raise ValueError('RSI threshold must be within 0..100 (try 70 overbought / 30 oversold)')
params['threshold'] = t Type guard
def is_valid_rsi_threshold(v) -> bool:
try:
return 0.0 <= float(v) <= 100.0
except (TypeError, ValueError):
return False Try / catch
try:
normalize_indicator_parameters(alert_type, params)
except ValueError as e:
if 'must be between 0 and 100' in str(e):
params['threshold'] = min(100.0, max(0.0, float(params['threshold'])))
normalize_indicator_parameters(alert_type, params)
else:
raise Prevention
- RSI is bounded 0-100 — never reuse CCI-style (±100+) thresholds on it.
- Clamp in the UI: Math.min(100, Math.max(0, v)).
- Pair direction sensibly: above 70 = overbought, below 30 = oversold.
When it happens
Trigger: rsi_threshold with {"threshold": 120}, {"threshold": -5}, or {"threshold": 100.5}. Threshold has no default for rsi_threshold, so an out-of-range value always comes from the payload; 0 and 100 themselves are accepted.
Common situations: Reusing CCI-style thresholds (±100 and beyond) on RSI; percentile-like inputs (1-99 scale multiplied incorrectly); copy-paste of price levels into an oscillator threshold field.
Related errors
- {field_name} must be between {minimum} and {maximum}
- unsupported alert_type for current EventMonitor runtime: {al
- Event alert rules must be a JSON array
- Event alert rules list must contain only objects; invalid en
- Event alert rule must be an object
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/6c9d3eefb6c76a8f.
Report an issue: GitHub.