HKUDS/Vibe-Trading · error · ValueError
factor_cov matrix must be positive semi-definite
Error message
factor_cov matrix must be positive semi-definite
What it means
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.
Source
Thrown at agent/src/quantlib/factormodel.py:673
unmatched_weight = float(w_series.drop(index=assets, errors="ignore").abs().sum())
w = w_series.loc[assets]
X = exposures.loc[assets]
# Align factors
factors = X.columns.intersection(factor_cov.index).intersection(factor_cov.columns)
if factors.empty:
raise ValueError(
f"No matching factors between exposures ({sorted(X.columns)}) and factor_cov ({sorted(factor_cov.index)})"
)
X = X[factors]
F = factor_cov.loc[factors, factors]
F_mat = F.to_numpy(dtype=float)
if not np.allclose(F_mat, F_mat.T, atol=1e-8):
raise ValueError("factor_cov matrix must be symmetric")
eigvals = np.linalg.eigvalsh(F_mat)
if np.min(eigvals) < -1e-8:
raise ValueError("factor_cov matrix must be positive semi-definite")
# Align specific variances
if specific_variances is not None:
spec_var_s = pd.Series(specific_variances, dtype=float)
if not np.isfinite(spec_var_s.values).all():
raise ValueError("specific_variances contains non-finite values")
d = spec_var_s.reindex(assets, fill_value=0.0).clip(lower=0.0)
else:
d = pd.Series(0.0, index=assets, dtype=float)
# Portfolio factor exposure: x_p = X^T w (K x 1)
x_p = X.T.dot(w)
# Factor variance: x_p^T F x_p
F_x_p = F.dot(x_p)
factor_var = float(np.maximum(0.0, x_p.dot(F_x_p)))
factor_vol = float(np.sqrt(factor_var))
View on GitHub (pinned to 80ffdda44c)
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)
Example fix
# before risk = factor_risk_decomposition(w, X, F) # after from sklearn.covariance import LedoitWolf F = pd.DataFrame(LedoitWolf().fit(R).covariance_, index=F.index, columns=F.columns) risk = factor_risk_decomposition(w, X, F)
Defensive patterns
Strategy: validation
Validate before calling
assert np.linalg.eigvalsh(F.values).min() >= -1e-8
Prevention
- Use shrinkage estimators for high-dimension samples
- Validate PSD in a test whenever the covariance builder changes
When it happens
Trigger: Sample covariance from few observations (n < factors), a hand-edited matrix, or pairwise-complete cov() estimation; eigenvalue like -0.05.
Common situations: Short history with many factors making the sample covariance rank-deficient/negative-definite; blending correlation scenarios without PSD repair.
Related errors
- factor_cov must be a non-empty DataFrame
- factor_cov contains non-finite values
- factor_cov matrix must be symmetric
- holdings is empty
- exposures has no factor columns
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/2494eb7d84c8f373.
Report an issue: GitHub.