HKUDS/Vibe-Trading · error · ValueError

no factor could be built; characteristics carries none of th

Error message

no factor could be built; characteristics carries none of the columns any definition needs: {sorted(characteristics.columns)}

What it means

Each factor definition maps a factor name to the characteristic columns it needs; build_style_exposures raises this when none of the supplied columns match any definition's inputs, so no factor at all could be built. The message lists the frame's columns to aid diagnosis.

Source

Thrown at agent/src/quantlib/factormodel.py:370

            try:
                standardised = standardise_exposures(
                    raw, market_caps=market_caps, winsorise=winsorise
                )
            except ValueError:
                # A single unusable characteristic must not take the whole factor
                # down when the factor has other inputs.
                continue
            parts.append(standardised * sign)

        if not parts:
            continue

        combined = pd.concat(parts, axis=1).mean(axis=1)
        filled[factor] = int(combined.isna().sum())
        columns[factor] = combined.fillna(0.0)

    if not columns:
        raise ValueError(
            "no factor could be built; characteristics carries none of the "
            f"columns any definition needs: {sorted(characteristics.columns)}"
        )
    return pd.DataFrame(columns), filled


def cross_sectional_factor_returns(
    returns: pd.Series,
    exposures: pd.DataFrame,
    market_caps: pd.Series | None = None,
    date: object = None,
) -> FactorReturnFit:
    """Regress one date's asset returns on their exposures.

    The coefficients are that date's factor returns. Weighting is by square-root
    market cap when caps are supplied, which is the standard choice: it respects
    that small-cap residuals are noisier without letting megacaps set the fit.

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Compare sorted(characteristics.columns) (printed in the message) against the keys used in your definitions dict.
  2. Rename columns to the definition's expected names, or supply definitions keyed by the columns you actually have.

Example fix

# before
exposures, _ = build_style_exposures(characteristics, definitions)  # cols are 'btm'
# after
characteristics = characteristics.rename(columns={"btm": "book_to_price"})
exposures, _ = build_style_exposures(characteristics, definitions)
Defensive patterns

Strategy: validation

Validate before calling

needed = {c for recipe in definitions.values() for c in recipe}
assert needed & set(characteristics.columns), sorted(characteristics.columns)

Prevention

When it happens

Trigger: Passing a characteristics frame whose column names (e.g. 'btm', 'pe') match none of the definition keys (e.g. 'book_to_price', 'earnings_yield') — renames, aliases, or a different naming convention.

Common situations: Column renames between pipeline versions, vendor column names vs library-internal names, or passing the wrong frame (returns instead of characteristics).

Related errors


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