HKUDS/Vibe-Trading · error · ValueError
portfolio_exposures and factor_returns share no factor; expo
Error message
portfolio_exposures and factor_returns share no factor; exposures={sorted(exposures.index)} returns={sorted(returns.index)} What it means
factor_return_attribution multiplies portfolio factor exposures by factor returns element-wise on shared factor names; if the two Series' indexes are disjoint there is no overlap to attribute, and the error names both sets to make the mismatch visible.
Source
Thrown at agent/src/quantlib/factormodel.py:585
portfolio_return: The realised portfolio return being explained.
Returns:
Contribution per factor (exposure times factor return), plus a
``specific`` entry holding the unexplained remainder and a ``total``
entry equal to ``portfolio_return``. The parts sum to the total by
construction: the residual is defined as what is left, never estimated
separately, so no reconciliation gap can appear.
Raises:
ValueError: If the two inputs share no factor.
"""
exposures = pd.Series(portfolio_exposures, dtype=float).drop(
labels=["unmatched_weight"], errors="ignore"
)
returns = pd.Series(factor_returns, dtype=float)
shared = exposures.index.intersection(returns.index)
if shared.empty:
raise ValueError(
"portfolio_exposures and factor_returns share no factor; "
f"exposures={sorted(exposures.index)} returns={sorted(returns.index)}"
)
contributions = exposures.loc[shared] * returns.loc[shared]
explained = float(contributions.sum())
contributions["specific"] = portfolio_return - explained
contributions["total"] = portfolio_return
return contributions
def factor_risk_decomposition(
portfolio_weights: pd.Series | Mapping[str, float],
exposures: pd.DataFrame,
factor_cov: pd.DataFrame,
specific_variances: pd.Series | Mapping[str, float] | None = None,
) -> FactorRiskDecomposition:
"""Decompose portfolio risk into systematic factor and idiosyncratic components.View on GitHub (pinned to 80ffdda44c)
Solutions
- Compare sorted indexes of both Series as the message displays
- Normalize/uppercase factor names on both sides before the call
- Reindex factor_returns to the exposure index after verifying the mapping
Example fix
# before attr = factor_return_attribution(exp, rets) # after rets = rets.rename(dict(zip(rets.index, exp.index))) # fix mapping attr = factor_return_attribution(exp, rets)
Defensive patterns
Strategy: validation
Validate before calling
shared = exposures.index.intersection(returns.index) assert not shared.empty
Try / catch
try:
attr = factor_return_attribution(exp, rets)
except ValueError as e:
if 'share no factor' in str(e):
logger.error('factor taxonomy mismatch: %s', e)
raise Prevention
- Enforce one canonical factor-naming map across the codebase
- Diff sorted factor names in CI for both feeds
When it happens
Trigger: portfolio_exposures indexed by {'value','momentum'} while factor_returns is indexed by {'Value','MOM'} (case/name mismatch), or completely disjoint factor taxonomies.
Common situations: Factor name normalization differs between the risk model and the returns feed; one side uses prefixed names like 'fctr.value'; renaming after a merge dropped shared names.
Related errors
- No matching assets between weights ({sorted(w_series.index)}
- No matching factors between exposures ({sorted(X.columns)})
- No common dates and assets between factor_panel and forward_
- brinson_fachler needs at least one sector
- portfolio and benchmark weights must sum to the same total f
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/c24a666db739440d.
Report an issue: GitHub.