{"record":{"id":"8cbf1b28a1d4eb6c","repo":"HKUDS/Vibe-Trading","slug":"two-valuations-share-the-date-earlier-0-a-sing","errorCode":null,"errorMessage":"two valuations share the date {earlier[0]}; a single day can carry only one mark","messagePattern":"two valuations share the date (.+?); a single day can carry only one mark","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/performance.py","lineNumber":345,"sourceCode":"    for raw_date, raw_value in raw_items:\n        when = normalize_date(raw_date, field_name=\"valuation date\")\n        try:\n            value = float(raw_value)\n        except (TypeError, ValueError) as exc:\n            raise ValueError(\n                f\"valuation on {when} must be numeric, got {raw_value!r}\"\n            ) from exc\n        if not math.isfinite(value):\n            raise ValueError(\n                f\"valuation on {when} must be finite, got {raw_value!r}; a \"\n                \"missing mark must be fixed at the source, not carried as NaN\"\n            )\n        resolved.append((when, value))\n\n    resolved.sort(key=lambda item: item[0])\n    for earlier, later in zip(resolved, resolved[1:], strict=False):\n        if earlier[0] == later[0]:\n            raise ValueError(\n                f\"two valuations share the date {earlier[0]}; a single day can \"\n                \"carry only one mark\"\n            )\n    return tuple(resolved)\n\n\ndef external_flows(\n    flows: CashFlowSeries | Iterable[CashFlow] | None,\n    *,\n    external_kinds: Iterable[str] | None = None,\n    internal_kinds: Iterable[str] | None = None,\n) -> tuple[tuple[date, float], ...]:\n    \"\"\"Select the boundary-crossing flows and restate them portfolio-side.\n\n    The returned amounts are the **negation** of ``CashFlow.amount``: the input\n    is holder-perspective (a contribution is cash leaving the client, hence\n    negative), the output is portfolio-perspective (a contribution is cash\n    arriving, hence positive). This is the only place in the module where that","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/performance.py#L327-L363","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Deduplicate valuations by date before calling the API (keep the last mark per day, e.g. dict(date -> value)).","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.","If you need to represent a flow on a valuation day, pass it as a flow, not as a second valuation.","Check the input pipeline for duplicate rows or datetime-to-date coercion producing collisions."],"exampleFix":"# before\ntime_weighted_return(valuations=[\n    (date(2024,1,15), 100_000.0),\n    (date(2024,1,15), 105_000.0),  # duplicate date -> ValueError\n    (date(2024,2,15), 110_000.0),\n], flows=[])\n\n# after\nmerged = {d: v for d, v in [\n    (date(2024,1,15), 100_000.0),\n    (date(2024,1,15), 105_000.0),  # last wins\n    (date(2024,2,15), 110_000.0),\n]}\ntime_weighted_return(valuations=sorted(merged.items()), flows=[])","handlingStrategy":"validation","validationCode":"def dedupe_valuations(valuations):\n    merged = {}\n    for when, value in valuations:\n        d = when if isinstance(when, date) else when.date() if hasattr(when, 'date') else parse(when)\n        merged[d] = value  # last mark per day wins\n    return sorted(merged.items())","typeGuard":"def has_unique_valuation_dates(valuations) -> bool:\n    ds = [normalize_date(v[0]) for v in valuations]\n    return len(set(ds)) == len(ds)","tryCatchPattern":"try:\n    twr = time_weighted_return(valuations, flows)\nexcept ValueError as e:\n    if 'share the date' in str(e):\n        valuations = dedupe_valuations(valuations)\n        twr = time_weighted_return(valuations, flows)\n    else:\n        raise","preventionTips":["Deduplicate valuation rows by date during ingestion.","Store valuations in a date-keyed mapping so duplicates cannot accumulate.","Warn on intraday marks being collapsed to one date."],"tags":["quantlib","valuations","duplicate-date","input-validation"],"backgroundTag":"duplicate-input-key","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}