HKUDS/Vibe-Trading · error · ValueError

spot_max must be greater than spot_min

Error message

spot_max must be greater than spot_min

What it means

Thrown by _spot_bounds when spot_max is not strictly greater than spot_min — equal bounds or inverted ranges produce an empty/invalid price grid. Both explicit values and pathological leg/strike combinations feeding defaults can surface here.

Source

Thrown at agent/src/tools/options_payoff_tool.py:313

    if not math.isfinite(numeric) or not numeric.is_integer():
        raise ValueError("spot_points must be an integer")
    points = int(numeric)
    if not _MIN_SPOT_POINTS <= points <= _MAX_SPOT_POINTS:
        raise ValueError(f"spot_points must be between {_MIN_SPOT_POINTS} and {_MAX_SPOT_POINTS}")
    return points


def _spot_bounds(kwargs: dict[str, Any], legs: list[OptionLeg], entry_spot: float) -> tuple[float, float]:
    """Resolve explicit chart bounds or safe defaults covering every strike."""
    reference = [entry_spot, *(leg.strike for leg in legs)]
    default_min = max(min(reference) * 0.5, 0.0)
    default_max = max(reference) * 1.5
    spot_min = _optional_float(kwargs, "spot_min", default_min)
    spot_max = _optional_float(kwargs, "spot_max", default_max)
    if spot_min < 0:
        raise ValueError("spot_min must be non-negative")
    if spot_max <= spot_min:
        raise ValueError("spot_max must be greater than spot_min")
    return spot_min, spot_max


def _scenario_ivs(raw: Any, entry_iv: float) -> np.ndarray:
    """Resolve bounded explicit IV scenarios or the skill's five defaults."""
    if raw is None:
        values = [
            entry_iv * 0.5,
            entry_iv * 0.75,
            entry_iv,
            entry_iv * 1.25,
            entry_iv * 1.5,
        ]
    else:
        if not isinstance(raw, list) or not raw:
            raise ValueError("scenario_iv_values must be a non-empty array")
        if len(raw) > _MAX_IV_SCENARIOS:
            raise ValueError(f"scenario_iv_values may contain at most {_MAX_IV_SCENARIOS} entries")

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Ensure spot_max > spot_min (add an epsilon or sanity margin)
  2. Derive bounds from data: spot_min=0.9*min_ref, spot_max=1.1*max_ref, then validate
  3. Omit both to use the tool's safe defaults

Example fix

// before
execute({..., "spot_min": 100, "spot_max": 100})
// after
execute({..., "spot_min": 90.0, "spot_max": 110.0})
Defensive patterns

Strategy: validation

Validate before calling

lo, hi = kwargs.get("spot_min"), kwargs.get("spot_max")
if lo is not None and hi is not None and float(hi) <= float(lo):
    kwargs["spot_min"], kwargs["spot_max"] = None, None  # use defaults

Type guard

def bounds_ordered(lo, hi) -> bool:
    return lo is None or hi is None or float(hi) > float(lo)

Try / catch

try:
    execute(kwargs)
except ValueError as e:
    if "greater than spot_min" in str(e):
        kwargs.update(spot_min=None, spot_max=None); execute(kwargs)

Prevention

When it happens

Trigger: spot_min=100, spot_max=100; spot_min=120, spot_max=80; or computed defaults collapsing when all strikes equal entry_spot and multipliers yield equal bounds via bad overrides.

Common situations: Copy-paste errors duplicating one number into both fields; off-by-one rounding making min meet max; automated bound generation without an epsilon check.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/7cdc85cd3a35b5c6. Report an issue: GitHub.