HKUDS/Vibe-Trading · error · ValueError

valuation on {when} must be numeric, got {raw_value!r}

Error message

valuation on {when} must be numeric, got {raw_value!r}

What it means

Each valuation value is coerced with float(); if that raises TypeError/ValueError the item is not numeric and _normalize_valuations reports the date and the offending value. This catches strings like 'N/A', None, Decimal-with-comma formats, or objects without __float__ before they poison the return computation.

Source

Thrown at agent/src/quantlib/performance.py:332

                raise ValueError(
                    f"valuations[{index}] must have exactly two elements "
                    f"(date, value), got {len(pair)}"
                )
            raw_items.append((pair[0], pair[1]))

    if len(raw_items) < 2:
        raise ValueError(
            "a return needs an opening and a closing valuation; got "
            f"{len(raw_items)}"
        )

    resolved: list[tuple[date, float]] = []
    for raw_date, raw_value in raw_items:
        when = normalize_date(raw_date, field_name="valuation date")
        try:
            value = float(raw_value)
        except (TypeError, ValueError) as exc:
            raise ValueError(
                f"valuation on {when} must be numeric, got {raw_value!r}"
            ) from exc
        if not math.isfinite(value):
            raise ValueError(
                f"valuation on {when} must be finite, got {raw_value!r}; a "
                "missing mark must be fixed at the source, not carried as NaN"
            )
        resolved.append((when, value))

    resolved.sort(key=lambda item: item[0])
    for earlier, later in zip(resolved, resolved[1:], strict=False):
        if earlier[0] == later[0]:
            raise ValueError(
                f"two valuations share the date {earlier[0]}; a single day can "
                "carry only one mark"
            )
    return tuple(resolved)

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Clean values before calling: coerce with pd.to_numeric(errors='coerce') and then handle NaN deliberately (note NaN is also rejected downstream as non-finite).
  2. Represent missing marks by omitting the date entirely rather than a placeholder.
  3. For strings, strip separators: float(s.replace(',', '')).

Example fix

# before
twr = time_weighted_return([('2024-01-01', '1,050.00'), ('2024-12-31', '1,100.00')])  # raises

# after
clean = [(d, float(str(v).replace(',', ''))) for d, v in marks]
twr = time_weighted_return(clean)
Defensive patterns

Strategy: validation

Validate before calling

clean = []
for d, v in valuations:
    try:
        clean.append((d, float(v)))
    except (TypeError, ValueError):
        continue  # or raise with contract/account context

Type guard

def all_values_numeric(vals) -> bool:
    try:
        return all(float(v) is not None for _, v in vals)
    except (TypeError, ValueError):
        return False

Try / catch

try:
    r = time_weighted_return(valuations)
except ValueError as e:
    if 'must be numeric' in str(e):
        raise MarkDataError(str(e)) from e
    raise

Prevention

When it happens

Trigger: Passing [('2024-01-01', 'N/A'), ...], a value of None from a sparse DB column, or a string '1,050.00' with a thousands separator — float() rejects all of these.

Common situations: CSV import with empty cells becoming None or ''; locale-formatted numbers; ORM models returning Decimal is fine but custom Money objects without __float__ are not; mixed dtype object columns in pandas.

Related errors


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