HKUDS/Vibe-Trading · error · ValueError
sector {sector!r} has no portfolio return and no benchmark r
Error message
sector {sector!r} has no portfolio return and no benchmark return What it means
A sector exists in the weight universe with zero portfolio weight and no return on either side. Since w_p == 0 the portfolio-return branch falls through, but r_b is also None, leaving no defensible return value, so the function raises rather than inventing 0.0.
Source
Thrown at agent/src/quantlib/attribution.py:276
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,
portfolio_weight=w_p,
benchmark_weight=w_b,View on GitHub (pinned to 80ffdda44c)
Solutions
- Drop sectors with zero weight on both sides and no returns from the inputs before calling
- Extend the returns feed to cover the full benchmark universe
- Explicitly map the sector's return to a value (0.0) upstream if that is the accepted convention
Example fix
# before
pw, bw = {}, {"ghost": 0.0}
brinson_fachler(pw, bw, {}, {})
# after
bw = {s: w for s, w in bw.items() if s in benchmark_returns or s in portfolio_returns}
brinson_fachler(pw, bw, portfolio_returns, benchmark_returns) Defensive patterns
Strategy: validation
Validate before calling
universe = set(portfolio_weights) | set(benchmark_weights)
universe = {s for s in universe if s in portfolio_returns or s in benchmark_returns}
# pass weights/returns restricted to `universe` Type guard
def fully_priced(pw, bw, rp, rb) -> bool:
return all((s in rp or s in rb) for s in set(pw) | set(bw)) Prevention
- Filter the sector universe to priced instruments before attribution
- Monitor returns feeds for coverage gaps on benchmark constituents
When it happens
Trigger: A sector appears in benchmark_weights (weight 0 on the portfolio side) but is absent from both portfolio_returns and benchmark_returns — typically an unpriced instrument in the benchmark universe.
Common situations: Benchmark membership lists including sectors with no return coverage for the period; stale reference data; weekends/holidays where one feed has no rows.
Related errors
- sector {sector!r} has portfolio weight {w_p!r} but no portfo
- 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/82a139d2d19676b2.
Report an issue: GitHub.