HKUDS/Vibe-Trading · error · ValueError
cross-sectional regression needs at least {MIN_CROSS_SECTION
Error message
cross-sectional regression needs at least {MIN_CROSS_SECTION} assets with both a return and full exposures, got {len(common)} What it means
cross_sectional_factor_returns regresses returns on exposures per date; it requires at least MIN_CROSS_SECTION assets that simultaneously have a non-NaN return and a complete (no NaN in any column) exposure row. Below that floor the factor return estimates are too noisy, so it refuses.
Source
Thrown at agent/src/quantlib/factormodel.py:408
returns: Asset returns for the date being explained, indexed by asset.
exposures: Exposure matrix from the *previous* date, rows indexed by
asset. Using the same date's exposures would be a look-ahead: the
characteristic and the return would share information.
market_caps: Market capitalisation by asset for the regression weights.
When None the regression is unweighted.
date: Label recorded on the result. Purely informational.
Returns:
A :class:`FactorReturnFit`.
Raises:
ValueError: If fewer than :data:`MIN_CROSS_SECTION` assets are common to
the inputs, if the design matrix has more columns than rows, or if
the exposures are perfectly collinear.
"""
common = returns.dropna().index.intersection(exposures.dropna(how="any").index)
if len(common) < MIN_CROSS_SECTION:
raise ValueError(
f"cross-sectional regression needs at least {MIN_CROSS_SECTION} assets "
f"with both a return and full exposures, got {len(common)}"
)
y = returns.loc[common].to_numpy(dtype=float)
factor_names = list(exposures.columns)
design = np.column_stack(
[np.ones(len(common)), exposures.loc[common].to_numpy(dtype=float)]
)
names = [MARKET_FACTOR, *factor_names]
if design.shape[1] > design.shape[0]:
raise ValueError(
f"{design.shape[1]} regressors but only {design.shape[0]} assets; "
"the fit would be exactly determined and meaningless"
)
if market_caps is None:View on GitHub (pinned to 80ffdda44c)
Solutions
- Diagnose the intersection: common = returns.dropna().index.intersection(exposures.dropna(how='any').index); print(len(common)).
- Fill or drop sparse exposure columns (build_style_exposures already fills missing cells with 0 and counts them), or restrict the universe to names with full data.
Example fix
# before fr = cross_sectional_factor_returns(returns, exposures_with_nans) # after exposures_filled = exposures_with_nans.fillna(0.0) fr = cross_sectional_factor_returns(returns, exposures_filled)
Defensive patterns
Strategy: validation
Validate before calling
common = returns.dropna().index.intersection(exposures.dropna(how="any").index) assert len(common) >= MIN_CROSS_SECTION, len(common)
Prevention
- Fill missing exposure cells with 0 (and track counts) before regressing.
- Monitor cross-section size per date; skip thin dates.
When it happens
Trigger: An inner join of returns.dropna() and exposures.dropna(how='any') shrinking below the minimum — e.g. one characteristic mostly NaN wipes out complete rows even though returns are fine.
Common situations: Sparse characteristics (analyst estimates coverage), small pilot universes, exposures built with fillna(0) removed by a strictness change, or a date where many tickers lack returns.
Related errors
- a cross-section needs at least {MIN_CROSS_SECTION} finite va
- estimation window needs at least {MIN_ESTIMATION_OBSERVATION
- estimation_window must be at least {MIN_ESTIMATION_OBSERVATI
- winsorise must be in [0, 0.5), got {winsorise}
- market_caps is missing {len(missing)} asset(s) present in va
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/cd6428e97f6b915b.
Report an issue: GitHub.