HKUDS/Vibe-Trading · error · ValueError

too many symbols ({len(symbols)}); cap is {_MAX_SYMBOLS}

Error message

too many symbols ({len(symbols)}); cap is {_MAX_SYMBOLS}

What it means

After symbol validation, _run enforces the basket size cap _MAX_SYMBOLS; exceeding it raises with the actual count and the cap value so the caller knows how much to trim.

Source

Thrown at agent/src/tools/portfolio_risk_tool.py:102

    def execute(self, **kwargs: Any) -> str:
        try:
            return self._run(**kwargs)
        except Exception as exc:  # noqa: BLE001 — tool must always return JSON
            logger.warning("portfolio_risk_xray failed: %s", exc)
            return json.dumps(
                {"status": "error", "error": str(exc)}, ensure_ascii=False, allow_nan=False
            )

    # ------------------------------------------------------------------
    def _run(self, **kwargs: Any) -> str:
        symbols = kwargs.get("symbols")
        if not isinstance(symbols, list) or not symbols or not all(
            isinstance(s, str) and s.strip() for s in symbols
        ):
            raise ValueError("symbols must be a non-empty list of strings")
        symbols = [s.strip() for s in symbols]
        if len(symbols) > _MAX_SYMBOLS:
            raise ValueError(f"too many symbols ({len(symbols)}); cap is {_MAX_SYMBOLS}")

        weights = self._parse_weights(kwargs.get("weights"), symbols)
        start_date, end_date = self._parse_dates(kwargs.get("start_date"), kwargs.get("end_date"))
        source = str(kwargs.get("source") or "auto")
        interval = str(kwargs.get("interval") or "1D")

        raw = self._fetch(
            codes=symbols,
            start_date=start_date,
            end_date=end_date,
            source=source,
            interval=interval,
        )
        closes = self._closes_frame(raw, symbols)
        unresolved = raw.get("_unresolved") if isinstance(raw, Mapping) else None

        report = compute_risk_xray(closes, weights)
        envelope = {

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Trim to the cap (prioritize highest-conviction/highest-liquidity names)
  2. Batch into multiple calls and aggregate results if supported
  3. Read the cap from the error message or module constant

Example fix

# before
execute(symbols=all_500_tickers)
# after
execute(symbols=all_500_tickers[:_MAX_SYMBOLS])
Defensive patterns

Strategy: validation

Validate before calling

from agent.src.tools.portfolio_risk_tool import _MAX_SYMBOLS
if len(symbols) > _MAX_SYMBOLS:
    symbols = symbols[:_MAX_SYMBOLS]  # or batch

Type guard

def within_symbol_cap(syms: list[str], cap: int) -> bool:
    return len(syms) <= cap

Try / catch

try:
    out = tool.execute(symbols=symbols)
except ValueError as e:
    if "cap is" in str(e):
        out = tool.execute(symbols=symbols[:_MAX_SYMBOLS])

Prevention

When it happens

Trigger: Passing more than _MAX_SYMBOLS symbols, e.g. an entire index constituent list.

Common situations: Backtesting whole universes instead of baskets, unioning watchlists, or LLMs pasting long ticker lists.

Related errors


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