ZhuLinsen/daily_stock_analysis · error · ValueError

invalid direction: {direction}

Error message

invalid direction: {direction}

What it means

Raised for PRICE_CROSS rules whose 'direction' field is not 'above' or 'below' (case-insensitive after .lower(), default 'above'). A price-cross alert needs to know whether it fires when price crosses above or below the threshold, so any other word makes the rule unexecutable.

Source

Thrown at src/agent/events.py:497

    if status is not None:
        try:
            AlertStatus(status)
        except ValueError as exc:
            raise ValueError(f"invalid status: {status}") from exc

    ttl_hours = rule.get("ttl_hours")
    if ttl_hours is not None:
        try:
            ttl_value = float(ttl_hours)
        except (TypeError, ValueError) as exc:
            raise ValueError(f"invalid ttl_hours: {ttl_hours}") from exc
        if ttl_value <= 0:
            raise ValueError("ttl_hours must be > 0")

    if alert_type == AlertType.PRICE_CROSS:
        direction = str(rule.get("direction", "above")).lower()
        if direction not in {"above", "below"}:
            raise ValueError(f"invalid direction: {direction}")
        try:
            price = float(rule.get("price"))
        except (TypeError, ValueError) as exc:
            raise ValueError(f"invalid price: {rule.get('price')}") from exc
        if price <= 0:
            raise ValueError("price must be > 0")
    elif alert_type == AlertType.PRICE_CHANGE_PERCENT:
        direction = str(rule.get("direction", "up")).lower()
        if direction not in {"up", "down"}:
            raise ValueError(f"invalid direction: {direction}")
        try:
            change_pct = float(rule.get("change_pct"))
        except (TypeError, ValueError) as exc:
            raise ValueError(f"invalid change_pct: {rule.get('change_pct')}") from exc
        if change_pct <= 0:
            raise ValueError("change_pct must be > 0")
    elif alert_type == AlertType.VOLUME_SPIKE:
        try:

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Set direction to 'above' or 'below' for price_cross rules (or omit it to default to 'above').
  2. When converting rule types between price_cross and price_change_percent, remap direction vocabulary ('up'->'above', 'down'->'below').
  3. Add an enum check in the rule-building UI/LLM extractor for the direction field per alert type.

Example fix

// before
rule = {"stock_code": "600519", "alert_type": "price_cross", "direction": "up", "price": 1800}
# ValueError: invalid direction: up

// after
rule = {"stock_code": "600519", "alert_type": "price_cross", "direction": "above", "price": 1800}
validate_event_alert_rule(rule)
Defensive patterns

Strategy: validation

Validate before calling

def valid_cross_direction(raw) -> bool:
    return str(raw if raw is not None else "above").lower() in {"above", "below"}

Type guard

def is_valid_price_cross_rule(rule: dict) -> bool:
    return (
        str(rule.get("alert_type", "")).lower() == "price_cross"
        and valid_cross_direction(rule.get("direction"))
    )

Try / catch

try:
    validate_event_alert_rule(rule)
except ValueError as e:
    if str(e).startswith("invalid direction"):
        rule["direction"] = "above" if rule.get("direction") in ("up", "higher") else "below"
        validate_event_alert_rule(rule)

Prevention

When it happens

Trigger: validate_event_alert_rule with alert_type='price_cross' and direction set to 'up', 'down', 'higher', '>', 'greater', or '' (empty string fails; only omission is safe and defaults to 'above'). Values are lowercased first, so 'Above' passes.

Common situations: Confusion with PRICE_CHANGE_PERCENT which uses 'up'/'down' vocabulary; LLM or user writing 'crosses above' prose; copying a PRICE_CHANGE_PERCENT rule template and only changing alert_type.

Related errors


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