gs-quant 因子合成实战:用 PCA 与因子分析把 10 个高相关因子压成 3 个
【免费下载链接】gs-quantPython toolkit for quantitative finance项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant
手里攥着 10 个风格因子——市值、估值、动量、波动率、流动性……两两一算相关性,好几位都超过 0.6。直接加权合成,等于对同一个信号重复下注:敞口被放大,风险高度集中,因子库看起来比实际更"厚"。这就是多因子模型里因子合成要解决的问题。gs-quant 工具包可以把风险模型里的因子数据拉成 DataFrame,再配合几行降维代码,把冗余因子压成少数几个正交或近正交的合成因子。
一分钟看懂 PCA 与因子分析:一个压缩照片,一个诊断病因
**PCA(主成分分析)**像给照片压缩体积:不关心细节纹理,只保留轮廓最清晰的主方向,压出来的主成分互相正交,信息密度最高,但你很难说出第一主成分"到底是什么"。
**因子分析(FA)**更像从症状反推病因:一堆症状(原始因子)背后其实只有少数几个病根(潜在因子),FA 的目标就是找出这些病根,并给出每个症状与病根的关联强度(载荷),因子天然带有"可以被命名"的属性,比如"价值风格""动量风格"。
两者输入相同、输出用途不同:要压缩和去噪选 PCA,要解释和命名选 FA。gs-quant 本身负责"取数与度量",分解运算交给 numpy / sklearn 完成,这条分工先记住,下面按四步走。
四步上手:从因子数据到合成因子
第一步:用 FactorRiskModel 取因子暴露
import datetime as dt from gs_quant.models.risk_model import ( FactorRiskModel, DataAssetsRequest, RiskModelUniverseIdentifierRequest, ReturnFormat, ) model = FactorRiskModel.get("MODEL_ID") # 你的风险模型 ID start_date, end_date = dt.date(2022, 1, 1), dt.date(2022, 12, 31) exposure = model.get_universe_exposure( start_date, end_date, assets=DataAssetsRequest(RiskModelUniverseIdentifierRequest.gsid, ["M0Z04P488Y57", "M0Z04P488Y57"]), get_factors_by_name=True, format=ReturnFormat.DATA_FRAME, )返回的exposure是因子暴露 DataFrame。想先确认"到底哪些因子冗余",可以用model.get_factor("Momentum").correlation(model.get_factor("Value"), start_date, end_date)拉两两相关系数序列,相关明显偏高的那几对就是合成对象。
第二步:清洗与标准化
import pandas as pd from gs_quant.timeseries import winsorize factors = exposure # 行=资产×日期,列=因子 # 逐列做 Winsorize 去极端值(默认 2.5 倍标准差截断) clean = factors.apply(lambda col: winsorize(col, limit=2.5), axis=0) # 再做截面 z-score,让不同量纲的因子可比 clean = clean.sub(clean.mean()).div(clean.std())这一步不能省:PCA 对尺度敏感,量纲大的因子会"抢走"主成分的解释方差。
第三步:跑 PCA 得到主成分得分
import numpy as np cov_matrix = clean.cov().values eigvals, eigvecs = np.linalg.eigh(cov_matrix) order = np.argsort(eigvals)[::-1] eigvals, eigvecs = eigvals[order], eigvecs[:, order] n = 3 pca = pd.DataFrame(clean.values @ eigvecs[:, :n], columns=[f"PC{i+1}" for i in range(n)]) print("解释方差占比:", (eigvals[:n] / eigvals.sum()).round(4))eigvals从大到小排列后画个散点(碎石图)就能定 n:特征值跌到 1 以下的主成分基本可以不要。
第四步:用因子分析提取潜在因子
gs-quant 里没有内置的 FA 估计器,分解部分用 sklearn 即可,数据仍来自风险模型:
from sklearn.decomposition import FactorAnalysis fa = FactorAnalysis(n_components=3, random_state=42) fa_scores = fa.fit_transform(clean.values) fa = pd.DataFrame(fa_scores, columns=[f"F{i+1}" for i in range(3)]) loadings = fa.components_.T # 形状: 3 个因子 × 原始因子数loadings每个因子一行,哪一列权重高就说明该因子偏向哪个原始风格——这就是"给病根命名"的依据。
怎么选:PCA 还是 FA
- 只关心压缩、降维、去共线性,不需要解释→ PCA,输出正交、计算快
- 要给合成因子取名字、写进归因报告→ FA,载荷矩阵提供解释抓手
- 原始因子平均相关低于 0.3→ 冗余本身不大,先做因子筛选,合成收益有限
- 因子间相关性高、彼此纠缠→ 合成收益最明显,优先上 PCA
- 最终裁判是验证:拿合成因子对下期收益算 IC / ICIR(
gs_quant.timeseries的correlation、cov都能派上用场),再和"原始因子等权"组合做回测对比,夏普与回撤谁占优一目了然
常见坑与一句解法
- ⚠️ 载荷解释性差、一个因子混了七八个风格 → 对 FA 做 Varimax 旋转后再看载荷
- ⚠️ 合成因子得分隔月漂移,策略跟着乱跳 → 改滚动窗口(如 12 个月)重估,别一次性吃全部历史
- ⚠️ 样本期太短、因子数多,模型过拟合 → 缩短 n、加长窗口,用留一年做样本外检验
- ⚠️ 个别资产因子缺失率很高 → 先按截面中位数填充,再做 Winsorize 与标准化
- ⚠️ 主成分数量拿不准 → 双标准:Kaiser 准则(特征值 > 1)+ 累计解释方差 ≥ 70%
写在最后
因子合成不是"越少越好",而是把冗余压掉、把信号留下:PCA 给你正交与效率,FA 给你解释与命名,用 IC 和回测做最终裁决。仓库里 05_factor_models 示例 和 上传自定义因子模型教程 可以直接照着跑通完整流程,更多 API 见 docs/models.rst。
【免费下载链接】gs-quantPython toolkit for quantitative finance项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考