{"record":{"id":"bac502642d7b3b45","repo":"HKUDS/Vibe-Trading","slug":"sector-sector-r-has-portfolio-weight-w-p-r-but","errorCode":null,"errorMessage":"sector {sector!r} has portfolio weight {w_p!r} but no portfolio return","messagePattern":"sector (.+?) has portfolio weight (.+?) but no portfolio return","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/attribution.py","lineNumber":274,"sourceCode":"\n    portfolio_total = math.fsum(portfolio_weights.values())\n    benchmark_total = math.fsum(benchmark_weights.values())\n    if abs(portfolio_total - benchmark_total) > weight_sum_tolerance:\n        raise ValueError(\n            \"portfolio and benchmark weights must sum to the same total for the Brinson \"\n            f\"identity to hold; got {portfolio_total!r} and {benchmark_total!r} \"\n            f\"(difference {portfolio_total - benchmark_total!r} exceeds {weight_sum_tolerance!r})\"\n        )\n\n    resolved: list[tuple[str, float, float, float, float]] = []\n    for sector in ordered:\n        w_p = float(portfolio_weights.get(sector, 0.0))\n        w_b = float(benchmark_weights.get(sector, 0.0))\n        r_p = portfolio_returns.get(sector)\n        r_b = benchmark_returns.get(sector)\n        if r_p is None:\n            if w_p != 0.0:\n                raise ValueError(f\"sector {sector!r} has portfolio weight {w_p!r} but no portfolio return\")\n            if r_b is None:\n                raise ValueError(f\"sector {sector!r} has no portfolio return and no benchmark return\")\n            r_p = r_b\n        if r_b is None:\n            if w_b != 0.0:\n                raise ValueError(f\"sector {sector!r} has benchmark weight {w_b!r} but no benchmark return\")\n            r_b = r_p\n        resolved.append((sector, w_p, w_b, float(r_p), float(r_b)))\n\n    total_portfolio_return = math.fsum(w_p * r_p for _, w_p, _, r_p, _ in resolved)\n    total_benchmark_return = math.fsum(w_b * r_b for _, _, w_b, _, r_b in resolved)\n\n    effects: list[SectorEffect] = []\n    for sector, w_p, w_b, r_p, r_b in resolved:\n        active_weight = w_p - w_b\n        effects.append(\n            SectorEffect(\n                sector=sector,","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/attribution.py#L256-L292","documentation":"A sector carries a non-zero portfolio weight but portfolio_returns has no entry for it. Attribution cannot compute a return for a held sector, so the function refuses rather than silently treating it as zero return.","triggerScenarios":"Weight dict includes 'energy' at 5% but the returns mapping lacks 'energy' (data join gap, different sector taxonomies, or missing return for one date).","commonSituations":"Sector naming mismatches ('IT' vs 'Information Technology'); returns fetched for a different date than weights; a newly added holding not yet in the returns feed.","solutions":["Align sector keys between weights and returns (normalize taxonomy/labels)","Fetch returns for the same universe/date as the weights","If the sector genuinely has no return, either drop it from both sides or explicitly supply a return (e.g. 0.0) with a documented rationale"],"exampleFix":"# before\nbrinson_fachler({\"tech\": 1.0}, {\"tech\": 1.0}, {}, {\"tech\": 0.05})\n\n# after\nrp = {s: portfolio_returns.get(s, benchmark_returns[s]) for s in portfolio_weights}\nbrinson_fachler({\"tech\": 1.0}, {\"tech\": 1.0}, rp, {\"tech\": 0.05})","handlingStrategy":"validation","validationCode":"missing = {s for s, w in portfolio_weights.items() if w != 0 and s not in portfolio_returns}\nif missing:\n    raise ValueError(f'portfolios sectors without returns: {sorted(missing)}')","typeGuard":"def returns_cover_weights(weights: Mapping[str, float], returns: Mapping[str, float]) -> bool:\n    return all(w == 0 or s in returns for s, w in weights.items())","tryCatchPattern":null,"preventionTips":["Use one canonical sector taxonomy for weights and returns","Assert key-set coverage between inputs in data-pipeline tests"],"tags":["quantlib","attribution","missing-data"],"backgroundTag":"missing-return-data","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}