量化策略开发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