ZhuLinsen/daily_stock_analysis · error · ValueError

{alert_type} periods require {required_bars} bars, but at mo

Error message

{alert_type} periods require {required_bars} bars, but at most {MAX_REQUESTED_DAYS} days can be requested

What it means

Raised by _ensure_required_bars_fetchable (src/services/alert_indicators.py:361) during parameter normalization: compute_required_bars (e.g. slow_period + signal_period + 1 for MACD, or period + k_period + d_period + 1 for KDJ) yields a bar count exceeding MAX_REQUESTED_DAYS = 365. Since the evaluator can request at most 365 daily bars, parameter sets needing more history are rejected up front as unfetchable.

Source

Thrown at src/services/alert_indicators.py:361

    triggered = _crossed_threshold(prev_value, curr_value, threshold, direction)
    message = (
        f"{stock_code} CCI{period} {curr_value:.2f} crossed {direction} {threshold:.2f}"
        if triggered
        else f"{stock_code} CCI{period} {curr_value:.2f} did not edge-cross {direction} {threshold:.2f}"
    )
    return IndicatorEvaluation(
        status="triggered" if triggered else "not_triggered",
        observed_value=curr_value,
        threshold=threshold,
        message=message,
        data_timestamp=latest,
    )


def _ensure_required_bars_fetchable(alert_type: str, params: Dict[str, Any]) -> Dict[str, Any]:
    required_bars = compute_required_bars(alert_type, params)
    if required_bars > MAX_REQUESTED_DAYS:
        raise ValueError(
            f"{alert_type} periods require {required_bars} bars, "
            f"but at most {MAX_REQUESTED_DAYS} days can be requested"
        )
    return params


def _direction(value: Any, allowed: frozenset[str], *, default: str) -> str:
    direction = str(value or default).strip().lower()
    if direction not in allowed:
        raise ValueError(f"invalid direction: {direction}")
    return direction


def _int_in_range(value: Any, field_name: str, *, default: int, minimum: int = 2, maximum: int = 250) -> int:
    raw_value = default if value is None or value == "" else value
    try:
        number = int(raw_value)
    except (TypeError, ValueError) as exc:

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Reduce periods so the required bars fit within 365 daily bars (e.g. macd slow_period + signal_period <= 364).
  2. Compute the requirement client-side before submitting: macd => slow+signal+1, kdj => period+k+d+1, ma => window+1, rsi/cci => period+1; keep it <= 365.
  3. If longer history is genuinely needed, raise MAX_REQUESTED_DAYS in alert_indicators.py after confirming the data source can actually serve that many daily bars.

Example fix

// before
{ "alert_type": "kdj_cross", "parameters": { "period": 250, "k_period": 250, "d_period": 250 } } // needs 751 bars

// after
{ "alert_type": "kdj_cross", "parameters": { "period": 9, "k_period": 3, "d_period": 3 } } // needs 16 bars
Defensive patterns

Strategy: validation

Validate before calling

REQUIRED = {
    'ma_price_cross': lambda p: p['window'] + 1,
    'rsi_threshold': lambda p: p['period'] + 1,
    'macd_cross': lambda p: p['slow_period'] + p['signal_period'] + 1,
    'kdj_cross': lambda p: p['period'] + p['k_period'] + p['d_period'] + 1,
    'cci_threshold': lambda p: p['period'] + 1,
}
# fill defaults first, then:
if REQUIRED[alert_type](params) > 365:
    raise ValueError('periods need more than 365 daily bars; reduce them')

Type guard

def periods_fetchable(alert_type: str, p: dict) -> bool:
    return compute_required_bars(alert_type, p) <= 365

Try / catch

try:
    normalize_indicator_parameters(alert_type, params)
except ValueError as e:
    if 'bars, but at most 365 days' in str(e):
        return bad_request('reduce indicator periods: warm-up exceeds 365 daily bars')
    raise

Prevention

When it happens

Trigger: macd_cross with slow_period=250 and signal_period=250 (needs 501 bars); kdj_cross with period=k_period=d_period=250 (needs 751 bars); any combination where the indicator's warm-up window sums past 365 trading days. Each individual field is capped at 250 by _int_in_range, but their sum can exceed 365.

Common situations: Users maximizing periods for 'smoother' signals without realizing warm-up sums; configs migrated from another platform with a higher daily-history cap; defaults are safe (12+26+1, 9+3+3+1) so this only fires with explicit overrides.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/b22dbcdb18b35634. Report an issue: GitHub.