HKUDS/Vibe-Trading · error · ValueError

the exposure matrix is collinear with the market factor or w

Error message

the exposure matrix is collinear with the market factor or with itself, so the coefficients are not identified

What it means

cross_sectional_factor_returns runs weighted least squares with an intercept; if the exposure matrix (after weighting) has rank below its column count — a constant exposure column is collinear with the intercept, or two factors are perfect multiples — the coefficients are not identified and the library raises instead of relying on lstsq's silent minimum-norm solution.

Source

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

    if market_caps is None:
        weights = np.ones(len(common))
    else:
        caps = pd.Series(market_caps, dtype=float).reindex(common)
        if caps.isna().any() or (caps <= 0).any():
            raise ValueError(
                "market_caps must be positive and defined for every asset in the "
                "regression"
            )
        weights = np.sqrt(caps.to_numpy(dtype=float))

    sqrt_w = np.sqrt(weights)
    design_w = design * sqrt_w[:, None]
    y_w = y * sqrt_w

    rank = np.linalg.matrix_rank(design_w)
    if rank < design_w.shape[1]:
        raise ValueError(
            "the exposure matrix is collinear with the market factor or with "
            "itself, so the coefficients are not identified"
        )

    coefficients, *_ = np.linalg.lstsq(design_w, y_w, rcond=None)
    fitted = design @ coefficients
    residuals = y - fitted

    dof = len(common) - design.shape[1]
    weighted_residuals = y_w - design_w @ coefficients
    sigma_squared = float(weighted_residuals @ weighted_residuals / dof) if dof > 0 else np.nan
    try:
        covariance = sigma_squared * np.linalg.inv(design_w.T @ design_w)
        standard_errors = np.sqrt(np.diag(covariance))
    except np.linalg.LinAlgError:  # pragma: no cover - guarded by the rank check
        standard_errors = np.full(design.shape[1], np.nan)

    with np.errstate(divide="ignore", invalid="ignore"):

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Check rank: np.linalg.matrix_rank(exposures.values) vs exposures.shape[1].
  2. Drop/merge duplicate or constant columns; ensure each factor uses a genuinely distinct characteristic.

Example fix

# before
fr = cross_sectional_factor_returns(returns, exposures)  # 'size' == 'log_size' duplicate
# after
exposures = exposures.drop(columns=["log_size"])
fr = cross_sectional_factor_returns(returns, exposures)
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np
assert np.linalg.matrix_rank(exposures.values) == exposures.shape[1]

Prevention

When it happens

Trigger: A standardisation bug leaving an all-ones/zeros column, two exposure columns built from the same underlying characteristic, or one factor expressed as an exact linear combination of others (e.g. size and log-size with a degenerate range).

Common situations: Definitions accidentally referencing the same column twice, exposures not centred so a nearly-constant column becomes exactly constant after rounding, or duplicated columns after a DataFrame join.

Related errors


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