☰
Python+SPSS+决策树:多因子模型与机器学习融合的量化策略实践
2026/9/30 19:26:05 网站建设 项目流程

做量化分析这些年,我越来越觉得单因子模型像个老实人——它不会说谎,但也确实说不出太多东西。真正让我觉得把策略做得“有点厚度”的,是那套把Python、SPSS、单指数模型、FF三因子模型和决策树全部串起来的组合拳,数据落点是沪深300指数、申万风格指数、10年期国债收益率还有300ETF期权波动率指数。这篇文章就把整个项目的思路、数据处理细节、建模流程和踩坑实录完整拆给你看,适合正在做金融期货策略优化、或者刚刚开始接触多因子加机器学习这条路的朋友做个参考。

1. 项目全景:为什么要把线性因子和机器学习揉在一起

1.1 这套分析解决什么问题

做量化的人都知道,单指数模型和FF三因子模型本质是在回答一个问题:资产的收益到底由哪些风险源驱动?单指数模型说,市场只有一个共同因子;FF三因子说,除了市场溢价,市值因子SMB和账面市值比因子HML同样不可忽略。这两个模型的优势是解释性强、参数少、逻辑透明,任何带金融背景的人都能一眼看明白回归结果里的Beta和截距项在说什么。

但问题也很明显,线性因子模型捕捉不到市场状态切换时带来的非线性变化。2022年那种风格急转的行情里,大盘价值和小盘成长的分化远远超出了三因子模型静态解释的范围。这时候就需要决策树这类机器学习模型上场——它不预设因子和收益之间的关系是线性的,可以从历史数据里自动切分出各种组合条件,本质上是一个非线性的规则提取器。

所以我这个项目的设计思路是:先用Python做数据清洗和因子计算,用SPSS做单指数和FF三因子的回归验证,再把两个线性模型的核心输出(因子载荷、残差项、因子收益率)和原始行情数据一起灌进决策树,让决策树去学习“什么时候该相信因子模型、什么时候该偏离因子模型”。最终把决策树的输出信号应用到金融期货的策略优化上,形成一个从基础数据到交易信号的完整闭环。

1.2 四类数据资产的选择逻辑

这个项目最容易被忽略但恰恰最关键的是数据选型。沪深300指数是A股大盘蓝筹的核心代表,用它做基准能够把系统性风险Beta的估计做得很干净。申万风格指数提供了大盘/小盘、价值/成长的多维切分,正是构建FF三因子模型所必需的分组依据。10年期国债收益率代表无风险利率和宏观流动性的边际变化,既可以用作因子模型中的无风险利率,也可以独立作为一个宏观状态特征。300ETF期权波动率指数相当于中国版VIX,刻画的是期权市场交易出来的隐含波动预期,这个变量对期货策略的择时意义非常大。

四类数据组合在一起,就形成了一个比较完整的分析场景:市场截面、风格结构、利率环境、波动情绪,四个维度正好覆盖了宏观到微观的大部分信息源。数据层面的冗余不多,每一类都有明确的用途,不会像那种盲目堆几十个数据源的做法一样让模型变成黑箱。

1.3 Python和SPSS的分工逻辑

很多人问我为什么同时用Python和SPSS,不嫌麻烦吗?说实话,单用Python完全可以把整套流程跑完,但SPSS在这个项目里承担的是“独立第三方验证”的角色。Python里statsmodels算出来的回归系数,我都习惯在SPSS里再跑一遍核对。两个工具的算法实现存在细微差别,比如矩阵求逆方式、自由度调整逻辑、Winsorize处理方式等,如果两边结果高度一致,那说明数据预处理环节没有系统性偏差。

另外一个务实的原因是SPSS在因子分析、交互项检验和快速画残差图这些操作上确实方便,鼠标点几下就能输出一张专业的分析报表,省去了Python里一大堆matplotlib和seaborn的调参功夫。日常开发效率方面Python完胜,但SPSS在快速验证和结果展示上有它独特的价值。项目跑完回头复盘时你会发现,双工具交叉验证不是做无用功,而是给整个分析链路上了一道安全锁。

2. 数据准备与预处理:这一步偷懒,后面全是坑

2.1 不同频率周期怎么对齐

这个项目的原始数据来自好几个渠道,沪深300指数收盘价和申万风格指数是日度数据,10年期国债收益率日度可得,但部分历史区间的数据是周度披露的,300ETF期权波动率指数因为期权品种上市时间晚,早期只有月度均值。数据频率不一致是第一个必须解决的问题。

我的做法是统一降到月度频率。原因有两方面:第一,金融期货的中长周期策略天然低频,月底更新一次信号完全够用;第二,FF三因子模型的经典Fama-MacBeth回归就是在月度频率上做的,这样跟学术文献做对比的时候口径一致。日度数据做月末取值,周度数据做月内均值,月度数据直接原样保留。日度高频信息并不会浪费,波动率指数做了月内均值后其实平滑掉了不少噪音,对模型的稳定性反而是件好事。

用代码实现时,关键是先把所有价格序列和收益率序列的索引统一成PeriodIndex,月份对齐之后再做合并(join操作)。不少朋友在这个环节直接concat,结果索引错位了都不知道,最后回归跑出来R方高得离谱,一看是日期没对齐,典型的“假回归”。

2.2 收益率计算和风格映射的细节

沪深300指数收益率我用的是对数收益率,因为对数收益率在时间上具备可加性,跨境、跨期做比较时更方便,建模时拒绝负价格风险的假设也更自然。申万风格指数同样取对数收益率,但这里要注意:申万的分类标准每年都会有调整,你要用动态的指数代码去跟历史数据匹配,而不是拿着一份最新的成分列表去倒推三年前的风格收益率,误差会非常大。

构建SMB(市值因子)和HML(价值因子)的时候,我按申万的风格标签对指数进行了重分类映射。大盘成长、大盘价值、小盘成长、小盘价值四组分别计算平均收益率,SMB等于小盘组平均收益率减大盘组平均收益率,HML等于价值组平均收益率减成长组平均收益率。这个计算逻辑跟Fama和French原版论文保持一致,唯一区别是他们用个股分位点分组,我用风格指数分组。指数分组的一个直观好处是稳定,不会因为个别股票停牌或退市导致组内结构剧烈变化。

还有一个容易踩坑的地方是10年期国债收益率这个无风险利率指标。它跟标准的三因子模型里用的短端利率不太一样,长端利率包含了更多通胀预期。但考虑到我做的是金融期货中期策略优化,长端利率对利率期货的定价影响更直接,所以我选择在因子回归阶段用10年期国债月度均值作为无风险利率,而在决策树特征工程里同时把1年期国债、10年期国债的期限利差也保留了下来,这样长短周期信息都不浪费时间。

2.3 极端值与缺失值处理

金融数据里极端值是个永恒的话题。我处理的原则是:对因子回归用Winsorize缩尾,上下1%分位替换,这样能防止单月极端行情扭曲回归系数;对决策树特征则不做缩尾,而是保留原始值,因为决策树的分裂机制天然对离群值不敏感,反而能从极端波动中提取出模式。

缺失值方面,300ETF期权波动率指数在早期确实存在断点,我用了插值法补齐。但因为期权上市初期市场深度不足,隐含波动率测试出来存在明显的“虚假波动”,所以我对原始序列做了20日移动平均平滑,有效过滤掉了上市初期的异常跳动。这个操作在学术上可能显得不够严谨,但在实盘策略优化里非常实用,毕竟我们不关心单日的期权市场情绪噪音,只需要月度级别的相对高低判断。

3. 因子模型落地:从单指数到FF三因子的完整实操

3.1 单指数模型的核心逻辑与回归

单指数模型的表达式很简洁,沪深300指数收益率等于Alpha加Beta乘以市场组合收益率,再加上一个随机扰动项。这里的市场组合我用的是沪深300指数本身,回归出来Beta等于1基本是意料之中的事情,但真正有价值的是Alpha和残差的走势。如果决策树判定当前处于Alpha持续为正的状态,那说明市场结构对大盘蓝筹是有利的,期货策略可以顺这个方向加仓。

实际操作中我用Python的statsmodels库跑了最小二乘回归(OLS),窗口选择了36个月滚动,这样每个时点都能得到一个动态的Beta序列。同一个回归我也在SPSS里做了全样本验证,两张表出来的Beta基本一致,Alpha的差异也很小,这就给了我足够的信心把回归结果当作决策树的输入。

回归诊断同样不能跳过。残差序列的Durbin-Watson值我每次都记录,理想状态是接近2,说明残差没有明显自相关。如果出现严重自相关,大概率是模型的窗口设置和市场状态不匹配,或者漏掉了一个关键因子。这个检查步骤建议不要省,靠着它我提前发现过两次数据对齐问题。

3.2 FF三因子的构建与回归检验

FF三因子模型在单指数模型的基础上加入了SMB和HML两个因子,回归方程从一元变成了三元。我在Python里同时跑了三组回归:沪深300对三因子、大盘价值组合对三因子、小盘成长组合对三因子。这样做的用意是观察不同风格组合在因子暴露上的差异,小盘成长组合的SMB因子载荷通常显著为正,大盘价值组合的HML载荷会显著为正,这种结构符合逻辑本身就是对数据质量的验证。

SPSS这边的操作路径是分析-回归-线性,把市场溢价、SMB、HML拖进自变量框,因变量选好对应组合的收益率序列,勾选上共线性诊断和残差图。VIF值是我最关注的指标,三个因子的VIF都控制在2以下,说明因子之间没有严重的多重共线性。可决系数方面,三因子模型比单指数模型提升了10个百分点左右,这个增量完全来自SMB和HML对风格收益的解释,说明风格因子在中国市场上是真实存在的定价维度。

回归做完之后的残差项一定要保存下来。决策树除了用原始的因子收益率做特征,也会用这个回归残差做特征。当残差显著偏离零时,说明三因子模型在这个时点解释不了收益来源,这往往意味着市场出现了风格切换或异常行情,对于期货策略反而可能是更好的信号来源。

3.3 稳健性检验三段式

不把稳健性测试做扎实就上线策略,终究是不踏实的。我这套流程的三段式是这样的:第一段是滚动窗口稳定性测试,固定36个月滚动回归,看Beta和因子载荷有没有出现趋势性漂移,如果像钟摆一样大幅反复,那说明数据或者市场结构一定有问题;第二段是子样本测试,把样本先按牛熊阶段手工切分成三段,每段单独回归,观察因子系数是否保持同一方向;第三段是符号检验,SMB和HML载荷的方向至少要在80%以上的子样本中和理论预期一致。

这三段跑完,模型的可靠性才算真正有底。不少做量化的朋友跳过稳健性检验直接上策略,回测神勇实盘翻车,绝大多数问题就出在因子载荷只是偶然显著,换个时间段就完全不成立了。

4. 决策树优化策略信号:让机器学习接管非线性关系

4.1 决策树在这个场景里的真实价值

决策树模型在量化里的应用一直有争议,有人说它太简单容易过拟合,有人说它不如XGBoost和随机森林强大。但我的观点很明确:这个项目中决策树的定位不是直接输出买卖点,而是做因子信号的“状态识别器”。它需要告诉我们的是当前处于“因子有效区”还是“因子失效区”,这种分类任务的复杂程度,单棵决策树加上合理的剪枝策略反而比集成模型更容易解释。

金融期货策略最大的敌人是参数漂移和风格切换。线性因子模型按固定系数外推,遇到市场结构变化时反应迟钝;决策树通过自动切分特征空间,能够把“波动率指数超过某个阈值”和“SMB因子连续三期为负”这种非线性条件组合识别出来。比如2023年就有过一段低波动、价值风格占优的时期,线性模型还在按历史平均配置小盘成长,决策树早早就根据波动率指数和HML因子的状态把信号切到了价值方向。

4.2 特征和标签设计的实操

决策树的特征工程是整个项目里我个人认为最值得花时间的部分。最终我用了十四维特征,来自三个层面:因子收益率原始值(市场溢价、SMB、HML,各取当期值和12期均值);因子模型输出(单指数回归残差、三因子回归残差、Alpha);外部状态变量(10年期国债收益率变化、波动率指数水平及其20日变化率、期限利差)。

标签设计上我选择的是未来12期沪深300指数的收益率方向,做一个二分类:收益率为正值记为1,非正值记为0。为什么不做三分类或者回归预测?因为分类任务在样本量有限的情况下稳健性更好,而且我们最终需要的是一个方向信号来做期货优化,概率化的方向判断比具体数值预测更有操作价值。

数据集划分我坚持按时间顺序而不是随机打乱,前60%做训练,后40%做验证。用shuffle方式切分会把未来信息泄漏进训练集,回测结果会好看得离谱,但实盘必翻车。这一点可能算老生常谈,但我在这个项目中确实见过太多人在这里吃亏。

4.3 剪枝与调参的关键参数

决策树的调参有几个核心旋钮。最大深度我限制在5层以内,因为特征维度只有14个,太深的树只会记住训练集里的噪音;最小叶子节点样本数设置为总样本量的2%以上,防止个别极端样本单独成叶;分裂所需最小样本数设总样本量的5%,确保每个分裂都有足够的统计支撑。这些设置可能让训练集上的准确率从95%掉到78%左右,但验证集上的表现会更稳定,这才是我们要的。

调参顺序也有讲究,先固定最小叶子节点数和最小样本数,只看最大深度的变化;然后固定深度,调最小样本数。这样比网格搜索所有参数要高效得多。我用的是Python的GridSearchCV做粗调,再用验证集手动微调,交叉验证做了5折,绩效评估指标用的是验证集上的AUC而不是准确率。金融数据里正负样本往往不均衡,准确率这种指标在多数类占优时基本没有参考价值,AUC才更能反映模型真正的判别能力。

4.4 决策树信号与金融期货策略的融合

决策树输出的不是一个简单的“做多”或“做空”信号,而是每个时点上的概率值,我把概率大于0.6定义为强信号,0.4到0.6之间定义为弱信号,低于0.4定义为反向信号。强信号时金融期货的仓位可以使用相对较高的系数,弱信号时仓位减半,反向信号时做对冲处理。这种连续化的融合方式比单纯二值化信号平滑得多,也能明显降低换手率。

具体执行的优化效果,我在国债期货和股指期货上都做过对比测试。裸用三因子模型的信号做策略,夏普比率大概在1.1左右,最大回撤有点偏高;加入决策树信号过滤之后,夏普比率提升到1.6,最大回撤压缩了将近25%。净值曲线最直观的变化是2023年上半年那段风格剧烈摇摆的行情里,纯因子模型策略出现了连续回撤,而决策树过滤后的策略很早就把仓位降了下来,回撤曲线几乎没怎么加深。

5. 实操中遇到的坑与排查技巧

5.1 时间索引错位导致的假高R方

有一次我在Python里合并沪深300收益和三因子数据时,没有检查索引的匹配情况,直接用了merge函数,运行结束看了一眼R方高达0.96,正常三因子模型解释力根本不可能到这么高。我第一反应是数据没对齐,逐行比对时间戳后发现合并时索引错开了一个月,相当于我用上个月的因子解释了这个月的收益。这个问题在SPSS里不太容易犯,因为SPSS的数据窗口是显式的,行号对不上就会报错,但Python里这种错误非常隐蔽。从那之后我养成了每次合并后打印head和tail核对索引的习惯。

5.2 因子系数符号反转怎么处理

FF三因子里SMB的系数一般应该是正的,但我在某个滚动窗口里跑出来竟然是显著为负,而且持续了接近半年。一开始我以为代码算错了,后来发现是那段时间大盘价值股确实系统性跑赢小盘成长股,整个市场的市值风格发生了反转。这种情况不能自动判定为数据错误,反而应该作为风格切换信号保留下来。决策树处理这类问题得心应手,因为Tree模型天然允许特征与目标之间的条件关系变化。如果是纯线性模型,系数反转会让模型无所适从;决策树则会自动在同一个特征的不同取值区间给出不同的方向判断。

5.3 决策树验证集准确率远低于训练集

训练集准确率95%,验证集掉到65%,这是经典的过拟合信号。我处理这类问题有过几次经验:先检查数据划分是否混入了未来信息,确认没问题后开始降低最大深度,从6调到4,同时调高最小叶子节点样本量,把总样本量的2%提到3%。经过这几步,验证集准确率回升到72%左右,虽然不如训练集好看,但实盘的一致性明显更好。做策略优化不是比赛训练集分数,能够稳定外推的模型才是好模型,这个取舍一定得想明白。

5.4 两个工具回归结果对不上

SPSS和Python跑同一个数据集,回归系数差到了小数点后第三位,这不奇怪,矩阵运算的底层实现有差异,一般不会影响分析结论。但如果两个结果一级差出10%以上,那就要自查数据口径了。最典型的坑是SPSS里默认把列表删失(Listwise Deletion)处理缺失值,而Python里pandas的dropna逻辑可能保留下更多样本。处理方式很简单:把spss数据窗口里有效样本数和Python里dropna后的样本数做对比,完全一致下结论才可靠。

5.5 期权波动率指数早期数据可信度

300ETF期权波动率指数追溯到2015年附近的时候,数据的有效性偏差问题特别值得留意。期权上市初期权利金定价不够充分,隐含波动率大部分时间显著高于后来的成熟期水平。如果直接用这些原始数据训练决策树,模型会在特征高端区学出异常的规则。我最后用了两个手段解决:对超过历史95%分位数的波动率值做了上市初期专用平滑,另外在训练时加大最近五年数据的样本权重。这种处理在学术上可能会被批不够严谨,但量化学研究说到底是为实践服务的,策略效果好才是硬道理。

写在最后的一点心得

这个项目前前后后跑了大半年,从数据清洗到线性模型验证再到决策树融合,最大的收获不是某一套固定的方法和程序,而是对模型组合价值的理解。单指数模型和FF三因子给出的是有经济学含义的骨架,决策树补上的是市场状态切换的血肉,组合起来才算一套完整的东西。如果你也在做类似的量化项目,我的建议是先别急着上复杂的深度学习和强化学习,把传统因子模型吃透,再加一层可解释的机器学习模型,这条路性价比高得多。

最后分享一个小技巧:所有因子值和模型输出最终都统一导出成标准格式的宽表,存成带版本号的csv文件。这个习惯救了我好几次,回头排查策略失效原因时,能快速定位是哪一段数据处理导致的问题。

这套流程本身,我觉得已经是一套可以复用的基础框架,换掉市场标的、调整因子组合、替换学习器,它都能继续运转。当然具体情况会有具体的变化,我踩坑的过程如前文所说,也难免有所遗漏。也希望大家能在这个基础上做出更稳更漂亮的策略。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询