{"record":{"id":"297492a83fbb2b5d","repo":"HKUDS/Vibe-Trading","slug":"the-exposure-matrix-is-collinear-with-the-market-f","errorCode":null,"errorMessage":"the exposure matrix is collinear with the market factor or with itself, so the coefficients are not identified","messagePattern":"the exposure matrix is collinear with the market factor or with itself, so the coefficients are not identified","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/factormodel.py","lineNumber":443,"sourceCode":"\n    if market_caps is None:\n        weights = np.ones(len(common))\n    else:\n        caps = pd.Series(market_caps, dtype=float).reindex(common)\n        if caps.isna().any() or (caps <= 0).any():\n            raise ValueError(\n                \"market_caps must be positive and defined for every asset in the \"\n                \"regression\"\n            )\n        weights = np.sqrt(caps.to_numpy(dtype=float))\n\n    sqrt_w = np.sqrt(weights)\n    design_w = design * sqrt_w[:, None]\n    y_w = y * sqrt_w\n\n    rank = np.linalg.matrix_rank(design_w)\n    if rank < design_w.shape[1]:\n        raise ValueError(\n            \"the exposure matrix is collinear with the market factor or with \"\n            \"itself, so the coefficients are not identified\"\n        )\n\n    coefficients, *_ = np.linalg.lstsq(design_w, y_w, rcond=None)\n    fitted = design @ coefficients\n    residuals = y - fitted\n\n    dof = len(common) - design.shape[1]\n    weighted_residuals = y_w - design_w @ coefficients\n    sigma_squared = float(weighted_residuals @ weighted_residuals / dof) if dof > 0 else np.nan\n    try:\n        covariance = sigma_squared * np.linalg.inv(design_w.T @ design_w)\n        standard_errors = np.sqrt(np.diag(covariance))\n    except np.linalg.LinAlgError:  # pragma: no cover - guarded by the rank check\n        standard_errors = np.full(design.shape[1], np.nan)\n\n    with np.errstate(divide=\"ignore\", invalid=\"ignore\"):","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/factormodel.py#L425-L461","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Check rank: np.linalg.matrix_rank(exposures.values) vs exposures.shape[1].","Drop/merge duplicate or constant columns; ensure each factor uses a genuinely distinct characteristic."],"exampleFix":"# before\nfr = cross_sectional_factor_returns(returns, exposures)  # 'size' == 'log_size' duplicate\n# after\nexposures = exposures.drop(columns=[\"log_size\"])\nfr = cross_sectional_factor_returns(returns, exposures)","handlingStrategy":"validation","validationCode":"import numpy as np\nassert np.linalg.matrix_rank(exposures.values) == exposures.shape[1]","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Drop duplicate/constant exposure columns at build time.","Ensure each factor derives from a distinct characteristic."],"tags":["factormodel","collinearity","regression"],"backgroundTag":"multicollinearity-rank-deficiency","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}