HKUDS/Vibe-Trading · error · ValueError

valuation on {when} must be finite, got {raw_value!r}; a mis

Error message

valuation on {when} must be finite, got {raw_value!r}; a missing mark must be fixed at the source, not carried as NaN

What it means

After a successful float() conversion, _normalize_valuations rejects non-finite values (NaN, +inf, -inf). A NaN mark would silently propagate through the return chain (NaN returns, NaN TWR), and infinities are impossible portfolio values, so the library demands the fix happen at the data source — the message says exactly that.

Source

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

            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)


def external_flows(
    flows: CashFlowSeries | Iterable[CashFlow] | None,
    *,

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Drop or repair NaN marks at the source: df = df.dropna(subset=[value_col]) or re-fetch the price.
  2. Do not use pd.to_numeric(errors='coerce') blindly on data destined for valuations — surface errors instead.
  3. Add assert all(math.isfinite(v) for _, v in marks) in ingestion tests.

Example fix

# before
marks = list(df[['date', 'value']].itertuples(index=False, name=None))  # may contain NaN
twr = time_weighted_return(marks)  # raises on NaN

# after
marks = list(df.dropna(subset=['value'])[['date', 'value']].itertuples(index=False, name=None))
twr = time_weighted_return(marks)
Defensive patterns

Strategy: validation

Validate before calling

import math
clean = [(d, float(v)) for d, v in valuations if math.isfinite(float(v))]

Type guard

def all_values_finite(vals) -> bool:
    return all(math.isfinite(float(v)) for _, v in vals)

Try / catch

try:
    r = time_weighted_return(valuations)
except ValueError as e:
    if 'must be finite' in str(e):
        raise DataQualityError(f'non-finite mark: {e}') from e
    raise

Prevention

When it happens

Trigger: Passing float('nan') as a value (e.g. from pd.to_numeric(errors='coerce') on dirty data, or numpy operations producing NaN), or inf from a division by zero in upstream mark calculations.

Common situations: pandas coercion of empty/parsing-failed cells to NaN; joins introducing NaN for missing dates then itertuples feeding them in; zero-division in a derived per-unit value; SQL NULLs converted to NaN rather than dropped.

Related errors


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