Repository navigation
fix(riskmodel): keep structured covariance on one sample denominator - #2378
Divyansh Barodiya (Divyansh151005) wants to merge 2 commits into
Conversation
A single factor made np.cov return a scalar, so the covariance could not be formed. Residual variance also divided by N while the factor block divided by N - 1, which understated specific risk.
|
Checked the algebra: for the PCA fit, Two wording points:
The |
N - 1 matches numpy.cov. It is not an unbiased specific variance after a k-factor fit, and the diagonal identity holds for PCA only.
|
Thanks for checking the algebra. I pushed a wording follow-up. The docstring and the portfolio README no longer call The one-factor |
|
Verified the wording follow-up at The exact-head risk-model test file passes 7/7, including the PCA diagonal and one-factor PCA/FA shape regressions. I also checked the two-commit diff for whitespace errors; none found. |
Description
StructuredCovEstimatorestimates a statistical factor covarianceTwo moment conventions in
_predictwere inconsistent with that formula.np.covon a single factor returns a scalar.F @ cov_b @ F.Tthen raisesValueError, andreturn_decomposed_components=Truehands the same scalar toexamples/portfolio/prepare_riskdata.pyasfactor_cov. The factor covariance is now always(num_factors, num_factors), including one factor.np.covdivides byN - 1.np.vardivides byN, so residual variance was smaller than the factor block by(N - 1) / N. For a PCA fit the scores are orthogonal to the residuals, and the diagonal of the structured covariance should equal the sample variances of the returns. Residual variance now usesddof=1.Motivation and Context
Fixes #2377
Enhanced indexing reads this decomposition as factor exposures, factor covariance, and specific risk. A one-factor model could not form the covariance, and a shorter window understated idiosyncratic variance (about 5% on 20 observations, about 1.7% on 60). The existing tests compare the estimator to
np.covonly when every component is kept, or within0.1, so neither case was covered.How Has This Been Tested?
pytest qlib/tests/test_all_pipeline.pyunder upper directory ofqlib.test_all_pipeline.pytrains a CSI300 LightGBM workflow and needs the market dataset. These tests use a synthetic return panel.Before the change,
num_factors=1raisedValueError: matmul: Input operand 1 does not have enough dimensions, and the PCA diagonal differed fromnp.covby3.2e-2on a 20×4 panel with 2 factors. After the change that diagonal difference is about1e-15.flake8on the changed Python files, with the ignore list from the developer guide, reported no issues.black -l 120left the files unchanged.Screenshots of Test Results (if appropriate):
tests/test_structured_cov_estimator.py, 7 passed.Types of changes