量化策略开发08:Lasso因子选择 - 因子动物园的断舍离

量化策略开发08:Lasso因子选择 - 因子动物园的断舍离

本文是《量化策略开发》系列的第8篇。我们把31条量价线索交给Lasso筛选,再尝试用分散持仓、排名缓冲、流动性过滤和波动控仓改善净值。两轮实验,一个给出正面结果,一个没有达标,都如实摆出来。

写在前面

上一篇评分卡选股,我们用7个因子给股票打分。实验快结束时发现一个怪现象:单看分箱结果,低波动股票后来表现更好;可几个因子一起进模型后,波动率的系数却反了过来,让部分低波动股票反而被扣分。

问题出在哪?它旁边还站着一个20日振幅。两个因子都在描述价格晃得厉不厉害,提供的信息有很大重叠。模型分不清功劳该给谁,就让它们互相抢了一部分。

两个因子撞车,还能靠人检查。要是31个因子一起上呢?5日、10日、20日动量都在描述涨跌,5日、20日、60日乖离率都在描述价格离均线有多远。信息越堆越多,重复也越来越多。

这篇文章就接着解决这个问题:面对一堆相似、重叠的因子,能不能让模型自己做一次断舍离?

今天研究的主角叫Lasso。它最后的答案是把31个因子删到8个。更意外的是,删掉23个以后,历史复检结果反而比31个全留下更好。

不过先别急着看收益。我们从头把这件事说清楚。

先用一句人话解释:因子、模型和选股是什么关系

因子,就是一条可以计算的股票线索。

比如"过去20日涨了多少"是一个动量因子,"最近交易活不活跃"可以变成换手率因子,"价格最近晃得厉害不厉害"可以变成波动率因子。每只股票每天都有一套因子值,相当于每天都拿到一张体检表。

模型做的事,是把这些线索汇总成一个分数。

分数高,代表模型认为它未来一段时间更可能跑赢其他股票。每月按分数排队,买前20名,就构成了一套最简单的选股规则。

这次的实验链条只有四步:

text 复制代码
31个因子 → 模型给每只股票打分 → 每月买前20名 → 比较两种模型的结果

两种模型,一种叫OLS,一种叫Lasso。它们看到的股票、因子和交易规则完全一样,唯一区别是怎么决定每个因子的权重。

OLS全都留,Lasso会收一笔"系数税"

先想象一个31人的评审团。

OLS的做法是31人全部保留。谁和历史数据更合拍,谁的嗓门就调大一点。问题是,动量、均线、乖离率这些"评审"经常说的是同一句话。重复的声音太多,模型可能把某段历史里的巧合也当成规律。

Lasso多做了一件事:每个因子想占据更大的权重,就要交"系数税"。

它追求的是两件事的平衡:一边要尽量预测准,一边要尽量少用大权重。某个因子如果只能带来一点点额外信息,省下的预测误差还抵不上税款,权重就会被压到0,相当于退出评审团。

税率由一个参数λ控制。λ小,管理宽松,很多因子都能留下;λ大,管理严格,可能连有用的因子也被删掉。所以λ不能拍脑袋,更不能看完最终结果再回去挑一个最赚钱的。

这里还要先统一尺子。动量常用百分之几表示,成交类因子可能是另一个数量级,直接放在一起,模型会受到量纲影响。我们每天把同一因子转换成"相对当天全市场平均水平的标准化距离",就像把不同科目的成绩换到同一把尺子上。最极端的1%也会先压住,避免少数异常值带偏整场考试。

到这里,Lasso的思路就够了:不是寻找听起来最厉害的因子,而是留下那些在其他因子已经存在时,仍能额外提供信息的因子。

实验怎么做:让过去预测未来

这次使用和第07篇相同的全A行情。原始库有5594只股票,剔除ST、*ST和退市整理股后剩5381只,上市约满一年才进入股票池。因子扩展到四组:

因子组 数量 主要内容
动量 5 过去5日至120日涨了多少
波动与形态 12 波动率、振幅、ATR、收益偏度等
成交与流动性 6 换手率、成交量变化、Amihud非流动性等
价格位置 8 乖离率、区间位置、均线比等

训练用的"标准答案",是每只股票随后10日的收益。数据按时间分成三段:

数据段 时间 用途
训练段 2021---2023年 学习因子权重,并选择Lasso的λ
验证段 2024年 检查模型离开训练年份后是否还工作
复检段 2025年1月---2026年10月 第一轮因子实验的只读检查

可以把它理解成上课、模拟考和闭卷考。第一轮实验里,复检段不参与选λ,模型权重也不根据这段答案修改。可是,一旦看过结果再改交易规则,同一份考卷就不能继续冒充新考卷。所以,后文的组合优化只能叫"二次验证",不能叫全新的样本外测试。

λ只在训练段内部选择。每次都用较早的数据训练、较晚的数据检查,顺序不能打乱。否则模型就会偷看未来。组合实验也没有重新选λ,没有改8个因子和股票分数。

还有一个细节值得说。第一版实验里,2023年12月下旬的一部分训练样本,其"未来10日收益"已经跨进了2024年。复核时发现后,我删掉了训练段末尾10个交易日,交叉验证内部也清掉同样的重叠区,再从头跑。下文所有数字都来自重跑后的版本。

最终,模型保留了8个因子。λ定为0.00744380,之后不再改动。

31个因子,最后留下了哪8个

先说明怎么看表:正号表示这个因子越大,分数越倾向于加分;负号则相反。系数不是收益率,也不能单独理解成因果关系。

保留因子 Lasso系数 直白理解
5日换手率 -0.039372 最近交易越热,越倾向于减分
60日乖离率 -0.028501 相对60日均线涨得越多,越倾向于减分
20日Amihud非流动性 +0.027610 越难大额成交的股票,反而越倾向于加分
5日乖离率 -0.018809 短线相对均线涨得越多,越倾向于减分
120日动量 -0.008866 过去120日涨幅越大,越倾向于减分
20日价格位置 +0.007410 在近20日区间里位置越高,越倾向于加分
10日动量 -0.002663 过去10日涨幅越大,越倾向于减分
60日波动率 -0.000505 中期波动越大,略微减分

被删掉的包括20日动量、60日动量、20日换手率、20日振幅和20日ATR等。这里的"删掉"不等于宣布这些指标从此没用,只是在这31个因子共同进场时,它们提供的额外信息不够多。

比如20日换手率被清零,5日换手率却留下了。这说明在这组数据里,短期交易热度比月度交易热度提供了更多独立信息。换掉一批因子或者换一个市场,留下的名单完全可能变化。

最需要警惕的是Amihud非流动性。它衡量的是:同样一笔成交额,能把价格推动多少。数值越大,通常说明股票越难在不明显影响价格的情况下大额买卖。

模型给这类股票加分,可能是在捕捉长期承担流动性风险后的补偿,也可能只是偏爱了某一阶段表现好的小微盘股票。它既可能是收益来源,也可能成为实盘里最麻烦的成本。

跑起来看看效果:分数怎么变成一笔笔交易

模型训练好以后,每20个交易日给全市场打一次分。信号当天收盘计算,下一交易日收盘买入前20名,等权持有。

买边成本按0.125%,卖边按0.225%,并保守假设每月全部换仓。我们只比较三组:31因子全留下的OLS、删到8因子的Lasso,以及全A等权基准。

下表都是各阶段整段的累计净收益,不是年化收益,也不是预测成功率。

模型 训练段 2024验证段 2025.01---2026.10复检段
全因子OLS +95.9% +44.9% +33.7%
Lasso +103.3% +49.0% +102.2%
全A等权 +19.2% +21.9% +25.6%

训练段里,两种模型差距不大。到了没有参与调参的复检段,Lasso比OLS高了68.5个百分点,也明显超过全A等权。

除了收益,我们还看了IC。IC可以先理解成"排名有没有排对":分数高的股票,后来是不是真的更靠前。它越高越好,但不是命中率,也不是能赚多少钱。复检段里,Lasso的平均IC是0.1125,OLS是0.0911,说明Lasso的改善不只来自前20只股票,整体排序也更好一些。

曲线也得看。Lasso并不是每月稳定赚钱,它在2025年上半年快速拉开差距,随后也出现过明显回撤。复检段从高点到低点的最大跌幅为26.0%,全A等权是19.5%。收益更高,不代表持有过程更舒服。

把手续费和滑点假设全部翻倍后,Lasso复检段收益从102.2%降到87.6%。这说明它没有被这套固定成本假设直接吃掉,但仍不能证明真实大额成交时也能保住同样的结果。

收益更高以后,能不能让净值走得更稳

到这里,第一个问题有了答案:这组实验里,因子做减法确实比全部留下更好。但26.0%的最大回撤也摆在那儿。看累计收益很兴奋,真拿着一条从高点跌去四分之一的曲线,感受完全是另一回事。

这时候问题来了:不改模型,只改怎么买、怎么持有、用多少仓位,能不能让净值稳一点?

我又做了五组对照。Lasso模型、8个因子、股票分数、调仓日期和T+1收盘成交全部冻结,不再重新训练。五组只改组合规则:

  • A组是原样保留。 每月买前20名,并沿用原先"每月全部换仓"的保守扣费方式。
  • B组只改成本口径。 还是前20名,但没有卖出的股票不重复扣费,按实际买卖比例计算成本。
  • C组减少集中和频繁交易。 持有50只;新股票进入前50名才有资格补位,旧持仓只要还在前100名就继续保留。不会因为名次从49变成51,就忙着卖旧买新。
  • D组加一道流动性门槛。 在C组基础上,剔除信号日成交额排名全市场最低20%的股票,先回避最难成交的一部分。
  • E组再控制总仓位。 用目标持仓过去60个交易日的波动估算风险,目标年化波动为12%,最多满仓,剩余资金按现金零收益处理。

B到E都按实际权重变化扣费,买卖边费率不变;A仍保留全换手口径,方便和原结果对齐。这样既能复现旧实验,也能看清每项组合改动本身带来了什么。

看结果前先统一两个词。最大回撤 是净值从历史高点最多跌了多少;夏普比率可以粗略理解为每承担一份波动换来多少收益,不是收益越高,夏普就一定越高。下表仍是二次验证段整段的累计收益,不是年化收益。

组别 累计收益 最大回撤 夏普
A 原始Top 20,全换手成本 102.22% -26.03% 0.98
B 原始Top 20,实际换手成本 106.96% -25.26% 1.01
C 50只+50/100排名缓冲 90.74% -25.08% 1.27
D C+剔除成交额最低20% 51.38% -16.80% 0.89
E D+12%目标波动控仓 21.01% -9.44% 0.83

同期全A等权基准收益为25.65%。

A和B的股票完全一样,差别只在扣费。B的收益多了4.74个百分点,说明原来"每月全部换仓"的成本假设确实偏保守,这不是模型突然变聪明了。

C的夏普最高,但最大回撤仍有25.08%,没有解决最想解决的问题。D和E把回撤压下来了,收益效率也跟着下降。尤其E,通过降低仓位让曲线更平,却也明显削弱了上涨时的参与度。

最后只有第一条达标。E的夏普是0.83,收益是21.01%,另外两条都没过,所以结论只能是:最终方案未达标,停止继续围绕这段数据调参。

两轮实验放在一起,结论是什么

**第一轮能说明的是:**在同一批股票、同一套因子、同一条交易规则下,Lasso这次做减法以后,历史复检表现比全因子OLS更好。因为第一轮复检没有参与选λ,这个结果比"训练集里很厉害"更有分量。

**第二轮能说明的是:**分散、缓冲、流动性过滤和波动控仓各有代价。这次没有找到既满足回撤要求,又不牺牲夏普和基准超额收益的组合方案。更稳不等于更好,更不等于更容易实盘。

**它们都不能说明的是:**明年还能不能翻倍、8个因子是不是永久答案、Lasso是否一定适合期货,以及真实资金能否按回测价格买到这些股票。

实战经验与避坑指南

因子不是越多越好。 相似因子会重复表达同一件事,先把信息重叠问题处理好,再考虑增加复杂度。

标准化不能省。 不同因子先用同一把尺子衡量,Lasso的惩罚才公平。

时间顺序不能乱。 金融训练不能随机把未来分给训练集,连"未来10日标签"跨过分段边界也要清理。

收益高不等于能成交。 尤其涉及小微盘和非流动性时,回测收益、实际冲击成本和策略容量必须一起看。

写在最后

这篇文章其实问了两个问题。第一个问题偏正面:当因子之间大量重复时,31个主动删到8个,第一轮复检的收益和整体排序都超过了全因子OLS。第二个问题没有达标:再想靠组合规则把净值修得更稳,最终方案只降下了回撤,没守住夏普和基准超额收益。

下一篇讲Ridge。Lasso像严格的评审团管理员,把部分因子直接请出场;Ridge则让所有因子留下,但把它们的嗓门一起调小。面对同一批31个因子,我会沿用相同的训练和验证流程;已经看过的复检段,只作二次验证,不再冒充新考卷。

先写到这,有问题欢迎留言交流。


本文基于 ATMQuant 量化交易系统,回测框架已开源至 GitHub 地址 https://github.com/seasonstar/atmquant

本文内容仅供学习交流,不构成任何投资建议。交易有风险,投资需谨慎。


加入「量策堂·AI算法指标策略」

想拿到本篇的31因子清单、LassoCV选参路径、OLS对照明细和五组组合实验记录?加入知识星球,获取研究方案、参数明细和持续复盘。


往期文章回顾

量化策略开发系列

以AI量化为生系列

量化指标解码系列


相关标签 #量化交易 #Lasso #多因子选股 #机器学习 #Python #vnpy

相关推荐
ATMQuant2 小时前
量化指标解码21:逻辑回归支撑阻力 - 让逻辑回归给关键价位打分
python·机器学习·逻辑回归·量化交易·技术指标
q0371803 小时前
深色 IDE 截图 OCR 老是漏字,我加了一层预处理,顺手把识别结果做成了可搜索历史
python
蜗牛8753 小时前
个人机票监控开源项目:6 个平台、10 条通道的自托管方案
python
秋名山小白4 小时前
Playwright UI 自动化测试实战教程
python·ui
AI源码5 小时前
基于 Python 的汽车市场数据分析与可视化平台
python·数据分析·汽车
会飞锦鲤5 小时前
基于 SVM + HOG 的铸造缺陷检测系统
python·opencv·机器学习·计算机视觉·缺陷检测·图像分类·svm
starrysky8106 小时前
zipfile 中文乱码修复:把已知 GBK 旧 ZIP 解压到正确中文目录
python
用户91294046848426 小时前
Codex 从零到能交付(09):让 Codex 写有用的测试,而不是只测成功路径
python
2601_967987376 小时前
Debian如何配置Python调试工具
python·debian·pdb·调试工具·ipdb