HKUDS/Vibe-Trading · error · ValueError
sector {sector!r} has portfolio weight {w_p!r} but no portfo
Error message
sector {sector!r} has portfolio weight {w_p!r} but no portfolio return What it means
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.
Source
Thrown at agent/src/quantlib/attribution.py:274
portfolio_total = math.fsum(portfolio_weights.values())
benchmark_total = math.fsum(benchmark_weights.values())
if abs(portfolio_total - benchmark_total) > weight_sum_tolerance:
raise ValueError(
"portfolio and benchmark weights must sum to the same total for the Brinson "
f"identity to hold; got {portfolio_total!r} and {benchmark_total!r} "
f"(difference {portfolio_total - benchmark_total!r} exceeds {weight_sum_tolerance!r})"
)
resolved: list[tuple[str, float, float, float, float]] = []
for sector in ordered:
w_p = float(portfolio_weights.get(sector, 0.0))
w_b = float(benchmark_weights.get(sector, 0.0))
r_p = portfolio_returns.get(sector)
r_b = benchmark_returns.get(sector)
if r_p is None:
if w_p != 0.0:
raise ValueError(f"sector {sector!r} has portfolio weight {w_p!r} but no portfolio return")
if r_b is None:
raise ValueError(f"sector {sector!r} has no portfolio return and no benchmark return")
r_p = r_b
if r_b is None:
if w_b != 0.0:
raise ValueError(f"sector {sector!r} has benchmark weight {w_b!r} but no benchmark return")
r_b = r_p
resolved.append((sector, w_p, w_b, float(r_p), float(r_b)))
total_portfolio_return = math.fsum(w_p * r_p for _, w_p, _, r_p, _ in resolved)
total_benchmark_return = math.fsum(w_b * r_b for _, _, w_b, _, r_b in resolved)
effects: list[SectorEffect] = []
for sector, w_p, w_b, r_p, r_b in resolved:
active_weight = w_p - w_b
effects.append(
SectorEffect(
sector=sector,View on GitHub (pinned to 80ffdda44c)
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
Example fix
# before
brinson_fachler({"tech": 1.0}, {"tech": 1.0}, {}, {"tech": 0.05})
# after
rp = {s: portfolio_returns.get(s, benchmark_returns[s]) for s in portfolio_weights}
brinson_fachler({"tech": 1.0}, {"tech": 1.0}, rp, {"tech": 0.05}) Defensive patterns
Strategy: validation
Validate before calling
missing = {s for s, w in portfolio_weights.items() if w != 0 and s not in portfolio_returns}
if missing:
raise ValueError(f'portfolios sectors without returns: {sorted(missing)}') Type guard
def returns_cover_weights(weights: Mapping[str, float], returns: Mapping[str, float]) -> bool:
return all(w == 0 or s in returns for s, w in weights.items()) Prevention
- Use one canonical sector taxonomy for weights and returns
- Assert key-set coverage between inputs in data-pipeline tests
When it happens
Trigger: Weight dict includes 'energy' at 5% but the returns mapping lacks 'energy' (data join gap, different sector taxonomies, or missing return for one date).
Common situations: 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.
Related errors
- sector {sector!r} has no portfolio return and no benchmark r
- brinson_fachler needs at least one sector
- portfolio and benchmark weights must sum to the same total f
- portfolio_exposures and factor_returns share no factor; expo
- no {base}/{self.quote_currency} rate for {day}; pass allow_s
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/bac502642d7b3b45.
Report an issue: GitHub.