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

  1. Drop sectors with zero weight on both sides and no returns from the inputs before calling
  2. Extend the returns feed to cover the full benchmark universe
  3. 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

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


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