HKUDS/Vibe-Trading · error · ValueError

two valuations share the date

Error message

two valuations share the date {earlier[0]}; a single day can carry only one mark

What it means

This error is raised by _normalize_valuations when two (or more) portfolio valuations in the input resolve to the same calendar date. A single day can carry only one mark because return calculations need an unambiguous ordering of valuations over time; duplicate dates make the interval between them zero-length and the period return undefined.

Solutions

  1. Deduplicate valuations by date before calling the API (keep the last mark per day, e.g. dict(date -> value)).
  2. If two marks on one day are genuinely different (e.g. pre- and post-contribution), decide which one represents the official end-of-day value and drop the other.
  3. If you need to represent a flow on a valuation day, pass it as a flow, not as a second valuation.
  4. Check the input pipeline for duplicate rows or datetime-to-date coercion producing collisions.

Example fix

# before
time_weighted_return(valuations=[
    (date(2024,1,15), 100_000.0),
    (date(2024,1,15), 105_000.0),  # duplicate date -> ValueError
    (date(2024,2,15), 110_000.0),
], flows=[])

# after
merged = {d: v for d, v in [
    (date(2024,1,15), 100_000.0),
    (date(2024,1,15), 105_000.0),  # last wins
    (date(2024,2,15), 110_000.0),
]}
time_weighted_return(valuations=sorted(merged.items()), flows=[])
Defensive patterns

Strategy: validation

Validate before calling

def dedupe_valuations(valuations):
    merged = {}
    for when, value in valuations:
        d = when if isinstance(when, date) else when.date() if hasattr(when, 'date') else parse(when)
        merged[d] = value  # last mark per day wins
    return sorted(merged.items())

Type guard

def has_unique_valuation_dates(valuations) -> bool:
    ds = [normalize_date(v[0]) for v in valuations]
    return len(set(ds)) == len(ds)

Try / catch

try:
    twr = time_weighted_return(valuations, flows)
except ValueError as e:
    if 'share the date' in str(e):
        valuations = dedupe_valuations(valuations)
        twr = time_weighted_return(valuations, flows)
    else:
        raise

Prevention

When it happens

Trigger: Calling time_weighted_return, modified_dietz_return, or money_weighted_return with a valuations list containing two entries whose dates normalize to the same day, e.g. two CashFlow/valuation records on 2024-01-15 (same date given as date vs datetime vs ISO string), or the same date supplied twice from a CSV import.

Common situations: Intraday marks imported as datetimes that collapse to the same date; duplicate rows in a valuation feed; a valuation recorded at midnight boundary; merging two data sources that both contain the period-end mark.

Related errors


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

Appendix: source

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

    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,
    *,
    external_kinds: Iterable[str] | None = None,
    internal_kinds: Iterable[str] | None = None,
) -> tuple[tuple[date, float], ...]:
    """Select the boundary-crossing flows and restate them portfolio-side.

    The returned amounts are the **negation** of ``CashFlow.amount``: the input
    is holder-perspective (a contribution is cash leaving the client, hence
    negative), the output is portfolio-perspective (a contribution is cash
    arriving, hence positive). This is the only place in the module where that

View on GitHub (pinned to 80ffdda44c)