{"record":{"id":"2494eb7d84c8f373","repo":"HKUDS/Vibe-Trading","slug":"factor-cov-matrix-must-be-positive-semi-definite","errorCode":null,"errorMessage":"factor_cov matrix must be positive semi-definite","messagePattern":"factor_cov matrix must be positive semi-definite","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/factormodel.py","lineNumber":673,"sourceCode":"    unmatched_weight = float(w_series.drop(index=assets, errors=\"ignore\").abs().sum())\n    w = w_series.loc[assets]\n    X = exposures.loc[assets]\n\n    # Align factors\n    factors = X.columns.intersection(factor_cov.index).intersection(factor_cov.columns)\n    if factors.empty:\n        raise ValueError(\n            f\"No matching factors between exposures ({sorted(X.columns)}) and factor_cov ({sorted(factor_cov.index)})\"\n        )\n\n    X = X[factors]\n    F = factor_cov.loc[factors, factors]\n    F_mat = F.to_numpy(dtype=float)\n    if not np.allclose(F_mat, F_mat.T, atol=1e-8):\n        raise ValueError(\"factor_cov matrix must be symmetric\")\n    eigvals = np.linalg.eigvalsh(F_mat)\n    if np.min(eigvals) < -1e-8:\n        raise ValueError(\"factor_cov matrix must be positive semi-definite\")\n\n    # Align specific variances\n    if specific_variances is not None:\n        spec_var_s = pd.Series(specific_variances, dtype=float)\n        if not np.isfinite(spec_var_s.values).all():\n            raise ValueError(\"specific_variances contains non-finite values\")\n        d = spec_var_s.reindex(assets, fill_value=0.0).clip(lower=0.0)\n    else:\n        d = pd.Series(0.0, index=assets, dtype=float)\n\n    # Portfolio factor exposure: x_p = X^T w (K x 1)\n    x_p = X.T.dot(w)\n\n    # Factor variance: x_p^T F x_p\n    F_x_p = F.dot(x_p)\n    factor_var = float(np.maximum(0.0, x_p.dot(F_x_p)))\n    factor_vol = float(np.sqrt(factor_var))\n","sourceCodeStart":655,"sourceCodeEnd":691,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/factormodel.py#L655-L691","documentation":"A valid covariance matrix must be positive semi-definite (min eigenvalue >= -1e-8). If eigvalsh finds a significantly negative eigenvalue, variances computed from it could be negative, so the function rejects it.","triggerScenarios":"Sample covariance from few observations (n < factors), a hand-edited matrix, or pairwise-complete cov() estimation; eigenvalue like -0.05.","commonSituations":"Short history with many factors making the sample covariance rank-deficient/negative-definite; blending correlation scenarios without PSD repair.","solutions":["Apply shrinkage/repair: use Ledoit-Wolf (sklearn) or nearest PSD projection","Increase the estimation sample length relative to factor count","Recompute with a guaranteed-PSD estimator (e.g. correlation x diag scaling)"],"exampleFix":"# before\nrisk = factor_risk_decomposition(w, X, F)\n# after\nfrom sklearn.covariance import LedoitWolf\nF = pd.DataFrame(LedoitWolf().fit(R).covariance_, index=F.index, columns=F.columns)\nrisk = factor_risk_decomposition(w, X, F)","handlingStrategy":"validation","validationCode":"assert np.linalg.eigvalsh(F.values).min() >= -1e-8","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use shrinkage estimators for high-dimension samples","Validate PSD in a test whenever the covariance builder changes"],"tags":["quantlib","factormodel","covariance","psd","eigenvalue"],"backgroundTag":"covariance-not-positive-semidefinite","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}