1. 从零搭建多因子选股的知识地基
很多人一上来就问:有没有现成的多因子选股代码,直接给我跑一遍就行。我特别理解这种心情,毕竟谁不想跳过枯燥的理论直接看结果。但我在实际折腾量化选股的过程中踩过最大的坑,恰恰就是基础不牢——因子含义没搞清、数据口径对不上、行业分类用错版本,最后回测出来的漂亮曲线一到实盘就原形毕露。所以这篇内容我想认真聊聊,用Python做基本面多因子选股之前,到底需要储备哪些知识,这些知识分别解决什么问题,以及它们之间是怎么串起来的。
先把话说在前头:多因子选股不是某个神秘模型,它的本质是用一组可量化的指标,对一篮子股票进行打分排序,然后按规则买入排名靠前的一批、定期换仓。听起来简单,但每一个环节都藏着细节。你需要的知识储备大致分四块:财务与基本面知识、因子理论、数据处理能力、回测与评估方法。这四块缺一块,整个流程就会在某个环节卡住。下面我按自己的理解顺序,一块一块拆开讲,尽量把每个概念都落到“为什么需要它”和“怎么用Python实现”上。
这篇文章适合谁看?如果你已经会一点Python基础语法,能看懂pandas的DataFrame操作,但对“因子”“IC”“分层回测”这些词还比较模糊,那正好。如果你是完全零基础,建议先把pandas的读写、筛选、分组练熟再回来,不然会有点吃力。我不会假设你懂金融,但会假设你愿意动手敲代码。
2. 基本面选股到底在选什么
2.1 基本面分析的三个核心维度
基本面选股,说白了就是不看K线、不看技术指标,而是从公司的经营状况出发判断它值不值得买。传统基本面分析绕不开三个维度:盈利能力、成长能力、财务健康度。这三个维度对应到具体指标上,就是我们后面要用的“因子原料”。
盈利能力回答的是“这家公司赚钱吗、赚得高效吗”。常用的指标有净资产收益率(ROE)、总资产收益率(ROA)、毛利率、净利率。其中ROE是我个人最看重的单一指标,因为它综合反映了公司用股东的钱赚钱的效率。巴菲特那句“如果只能看一个指标,我看ROE”虽然被引用烂了,但道理确实扎实。
成长能力回答的是“这家公司未来能不能赚更多”。常用指标有营业收入同比增长率、净利润同比增长率、经营现金流增长率。这里有个新手常犯的错误:只看增长率高低,不看基数。一家去年净利润100万、今年200万的公司,增长率100%,但绝对体量可能还不如一家从10亿增长到11亿、增长率只有10%的公司。所以成长因子最好和规模因子配合使用。
财务健康度回答的是“这家公司会不会突然暴雷”。常用指标有资产负债率、流动比率、速动比率、利息保障倍数。我见过太多回测里表现优异的股票,最后因为高负债叠加现金流断裂而崩盘。财务健康类因子在打分时往往作为“过滤条件”而非“排序条件”使用,这个思路后面会细讲。
2.2 为什么用Python而不是Excel
有人会问,这些指标财报软件里都有,我拿Excel排个序不就行了?小规模手动选股确实可以,但多因子选股的核心价值在于批量、系统、可复现。当你需要同时处理几千只股票、几十个因子、上百个时间截面时,Excel的透视表和公式会直接卡死,而且每次财报更新都要重做一遍,根本无法形成稳定的策略。
Python的优势在于:pandas能高效处理面板数据(时间×股票×指标的三维结构),numpy能快速做矩阵运算,加上各类数据接口和回测框架,整个流程可以脚本化、自动化。更重要的是,代码是可复现的——你今天跑出来的结果,三个月后换台电脑还能跑出一模一样的,这对策略验证至关重要。
2.3 数据从哪来:常见数据源类型盘点
做基本面选股,数据是命根子。数据源大致分三类:行情数据、财务数据、辅助数据。行情数据包括日线、周线的开高低收、成交量、复权因子;财务数据包括三大报表(资产负债表、利润表、现金流量表)以及衍生指标;辅助数据包括行业分类、指数成分、停复牌信息、分红送转等。
这里我要强调一个新手极易忽略的点:财务数据的“报告期”和“公告期”是两回事。一家公司2023年年报的报告期是2023年12月31日,但公告日期可能是2024年3月甚至4月。如果你在2024年1月就用这份年报数据选股,那就是典型的“未来函数”,回测收益会虚高得离谱。正确的做法是:用公告日期作为数据可用的时间点,而不是报告期。这个坑我在早期回测里踩过,当时年化收益虚高了将近一倍,排查了两天才找到原因。
3. 因子理论:多因子模型的底层逻辑
3.1 什么是因子,什么样的指标能成为因子
因子这个词听起来学术,其实就是一个“能区分股票好坏的特征”。但不是随便一个指标都能当因子,一个合格的因子需要满足几个条件:有明确的经济含义、数据可获取且质量稳定、在历史上对收益有一定区分度、与其他因子相关性不过高。
举个反例:股票代码的尾号。这个指标数据肯定能拿到,但它没有任何经济含义,历史上也不该有区分度(如果有,那纯属巧合)。再比如“公司名字笔画数”,这种就是典型的伪因子。真正合格的因子,比如ROE,背后有“盈利能力强的公司长期跑赢”这个经济逻辑支撑。
因子按方向分两类:正向因子(值越大越好,如ROE)和反向因子(值越小越好,如市盈率PE、资产负债率)。在合成综合得分前,必须统一方向,否则排序会乱套。这个处理叫“因子方向调整”,后面实操部分会讲具体怎么做。
3.2 单因子到多因子:为什么要组合
单因子选股的问题在于“偏科”。只看ROE,可能选出一堆高杠杆的银行和地产;只看成长率,可能选出一堆估值离谱的题材股。每个因子都有它的盲区,组合起来才能互相补位。
多因子组合的核心思想是:把多个维度的信息压缩成一个综合得分,用这个得分排序选股。组合方式有很多种,最简单的等权打分,复杂一点的用回归确定权重,再复杂的有机器学习方法。对新手来说,我强烈建议从等权打分开始,先把流程跑通,再考虑优化权重。一上来就上机器学习,很容易陷入“调参调出漂亮回测但不知道为啥赚钱”的陷阱。
3.3 因子有效性怎么衡量:IC、IR与分层测试
因子选出来不能直接用,得先验证它有没有效。最常用的三个工具是IC值、IR值和分层回测。
IC(Information Coefficient)是因子值与下期收益的相关系数。如果IC长期为正且绝对值较大,说明这个因子对未来收益有预测力。一般IC绝对值超过0.03就算有一定效果,超过0.05算不错。注意IC有正负之分,正向因子的IC应该为正。
IR(Information Ratio)是IC的均值除以IC的标准差,衡量的是因子预测能力的稳定性。IC均值高但波动大,IR就低,说明这个因子时灵时不灵,实盘风险大。我个人更看重IR,因为稳定性比爆发力重要。
分层回测是把股票按因子值分成若干组(通常5组或10组),看各组的历史收益是否单调。如果第一组到第五组收益依次递增,说明因子区分度好;如果中间乱序,说明因子可能只在极端值有效。这个测试比单纯看IC更直观,强烈建议每个因子都跑一遍。
4. 数据处理能力:Python实操的地基
4.1 pandas面板数据处理的核心操作
多因子选股的数据结构是典型的“面板数据”:行是时间,列是股票,值是因子。或者反过来,用MultiIndex(日期,股票代码)作为索引。我习惯用后者,因为查询和分组更方便。
几个必须练熟的操作:pivot和pivot_table(长表转宽表)、groupby(按日期或股票分组计算)、rolling(滚动窗口计算,比如过去20日波动率)、shift(时间平移,用于计算下期收益)、rank(截面排序,因子标准化常用)。这几个操作练熟,80%的数据处理需求都能搞定。
举个实际场景:你要计算每只股票在每个交易日的ROE排名。数据是长表格式(日期、代码、ROE),你需要先按日期groupby,然后对ROE做rank,得到每只股票在当天的排名。这个操作一行代码就能搞定,但如果不熟悉groupby的用法,可能会写一堆循环,慢得让人抓狂。
4.2 缺失值、异常值与停牌处理
真实数据永远不干净。财务数据有缺失(新股没历史数据、某些行业不适用某些指标),行情数据有异常(涨跌停、停牌、一字板),这些都会影响因子计算和回测结果。
缺失值的处理原则是:能补则补,不能补则剔除,绝不瞎填。财务指标的缺失,如果是报告期未到,可以向前填充(用上一期数据);如果是公司本身不披露,那就只能剔除。用0填充是大忌,因为0在因子排序里可能排到极端位置,造成误选。
异常值处理常用中位数绝对偏差(MAD)法或分位数截断(Winsorize)。比如把因子值限制在1%和99%分位数之间,防止极端值主导排序。这个步骤叫“去极值”,是多因子流程的标准环节。
停牌股的处理也很关键。停牌期间股票无法交易,如果回测时还把它算进持仓,收益就是假的。常见做法是:停牌当日不调仓,或者把停牌股从候选池中剔除。这个细节看似小,但对回测真实性影响很大。
4.3 行业中性化:为什么必须做
这是新手最容易忽略、但老手最看重的一步。行业中性化的意思是:在比较股票因子值时,要在同行业内部比较,而不是全市场一起比。
为什么?因为不同行业的财务特征差异巨大。银行股的资产负债率天然在90%以上,而软件公司的资产负债率可能只有20%。如果你在全市场按资产负债率排序选低负债股,那选出来的全是软件、传媒类公司,行业集中度极高。一旦这个行业整体回调,组合就会惨不忍睹。
行业中性化的做法通常是:对每个因子,在每个行业内做标准化(减去行业均值、除以行业标准差),或者用回归剔除行业影响。这样处理后,因子值反映的是“在同行业中的相对水平”,选股会更均衡。我实测下来,加了行业中性化之后,组合的行业分布明显改善,回撤也小了不少。
5. 实操流程:从数据到因子得分的完整链路
5.1 数据获取与清洗的代码框架
假设你已经有了数据接口(具体接口因平台而异,这里只讲通用框架),第一步是把原始数据整理成标准格式。我习惯建三个DataFrame:行情表(日期、代码、收盘价、复权因子、成交量)、财务表(代码、公告日期、报告期、各财务指标)、辅助表(代码、行业分类、上市日期、是否ST)。
清洗的关键步骤包括:统一股票代码格式(有的接口带后缀,有的不带)、统一日期格式(都转成datetime)、剔除ST股和上市不满一定天数的次新股(次新股财务数据不稳定,且常有炒作)。这些过滤条件看起来简单,但每一条都会显著影响最终结果,建议在代码里做成可配置的参数,方便对比测试。
5.2 因子计算:以ROE和营收增长为例
ROE的计算公式是净利润除以净资产。但这里有个细节:用哪个净利润?用归母净利润还是净利润总额?用哪个净资产?用期初、期末还是平均?不同口径算出来的ROE差异不小。我一般用归母净利润除以期初期末平均净资产,这个口径相对稳健。
营收增长率的计算要注意同比和环比的区分。同比是跟去年同期比,能消除季节性影响;环比是跟上一季度比,反映短期变化。基本面选股通常用同比,因为季节性对很多行业影响很大。
计算完原始因子值后,还要做前面提到的去极值、标准化、方向调整、行业中性化四步处理。这四步的顺序有讲究:先去极值再标准化,因为极端值会拉偏均值和标准差;方向调整可以在标准化之前或之后,但要在合成得分之前;行业中性化通常放在最后。
5.3 因子合成与打分排序
处理完所有单因子后,把它们合成一个综合得分。等权合成的做法是:每个因子标准化后,正向因子直接加,反向因子取负号后加,然后求平均。这样得到的综合得分,值越大代表综合表现越好。
合成之后按日期做截面排序,选出排名前N%的股票作为持仓。N的选择是个权衡:太小则持仓集中、波动大;太大则接近指数、超额收益少。常见做法是选前10%到20%,或者固定选前30到50只。我一般选前10%,但会加一个“单行业不超过20%”的约束,防止行业过度集中。
5.4 回测框架的搭建要点
回测框架不用自己从零写,但核心逻辑要清楚。一个基本的回测循环是:每个调仓日,根据当日可用的因子数据计算得分、选出持仓、按收盘价买入;持有到下一个调仓日,计算期间收益、换仓。关键点有三个:调仓频率(月度、季度还是周度)、交易成本(印花税、佣金、冲击成本)、数据可用性(严格用公告日期过滤)。
调仓频率我建议从月度开始。季度太慢,因子变化反映不及时;周度太快,交易成本吃掉太多收益,而且财务数据本身是季度更新的,周度调仓意义不大。交易成本一定要算,哪怕只是简单地扣千分之一,也能让回测结果更接近现实。我见过太多回测年化30%但没扣成本的策略,实盘一扣成本直接腰斩。
6. 常见问题与排查技巧实录
6.1 回测收益虚高的五大元凶
回测收益虚高是新手最常遇到的问题,我总结了五个主要原因,按出现频率排序:
| 问题类型 | 具体表现 | 排查方法 |
|---|---|---|
| 未来函数 | 用了尚未公告的财务数据 | 检查数据是否按公告日期过滤 |
| 幸存者偏差 | 只用了当前还在上市的股票 | 确认股票池包含已退市股票 |
| 未扣成本 | 回测收益未扣除交易费用 | 加入印花税和佣金计算 |
| 复权错误 | 用未复权价格计算收益 | 确认使用后复权价格 |
| 停牌未处理 | 停牌股仍按最后价格计算 | 剔除停牌日或标记不可交易 |
未来函数是最隐蔽也最致命的。除了财务数据的公告日期问题,还有一种情况是用了“当日收盘后才知道”的数据做当日交易决策。比如你用当日收盘价计算因子,又用当日收盘价买入,这在现实中是不可能的——你收盘时才知道价格,怎么可能同时完成买入?正确的做法是用T日数据计算因子,T+1日开盘或收盘买入。
6.2 因子失效了怎么办
因子失效是常态,没有哪个因子能永远有效。当你发现某个因子近期IC持续为负或接近零,先别急着扔掉,按以下步骤排查:先看是不是市场风格切换(比如价值因子在成长风格年份会失效)、再看是不是因子拥挤(用的人太多导致收益被抹平)、最后看是不是数据问题(接口变更、口径调整)。
如果确认是因子本身失效,处理方式有三种:降低权重、暂时剔除、寻找替代因子。我倾向于降低权重而非直接剔除,因为因子失效往往是周期性的,过一段时间可能又有效了。直接剔除容易在底部割肉,得不偿失。
6.3 实操避坑清单
最后整理一份我踩过坑之后总结的清单,都是血泪教训:
- 股票代码格式不统一:不同数据源的代码格式可能不同,合并前务必统一,否则会出现大量匹配失败。
- 行业分类版本混乱:行业分类标准有多个版本,且每年调整,回测时要确保用的是当时有效的分类,不能用最新分类回溯历史。
- 复权方式选错:计算收益率必须用后复权价格,前复权价格会随时间变化,导致历史收益计算错误。
- 调仓日选在财报密集期:财报密集期数据更新频繁,容易造成数据不一致,建议避开或做好数据版本管理。
- 忽略涨跌停无法成交:涨停买不进、跌停卖不出,回测时要模拟这个限制,否则收益会虚高。
- 因子标准化用了全样本:标准化应该用截面数据(同一天的所有股票),不能用全历史数据,否则会引入未来信息。
这些坑我基本都踩过一遍,每一个都花了少则半天多则几天才排查出来。写在这里是希望你能少走弯路,把时间花在策略优化上,而不是debug上。
7. 知识储备的进阶路线
7.1 从等权到加权:因子权重的确定方法
等权合成是起点,但不是终点。当你对因子有了感觉之后,可以尝试更精细的权重方法。常见的有IC加权(用历史IC均值作为权重)、IC_IR加权(用IC的IR作为权重)、最大化IC_IR的优化方法。这些方法的核心思想都是“让历史上预测能力强的因子获得更高权重”。
但我要泼一盆冷水:加权方法在样本外往往不如等权。因为历史IC高的因子,未来不一定继续高,过度拟合历史权重的结果就是样本外表现差。我实测下来,等权或者简单的手动权重(比如给ROE和成长各30%,其余分配40%)反而更稳健。所以别迷信复杂方法,简单有效才是王道。
7.2 风险模型与组合优化入门
当你管理的资金规模变大,或者对回撤控制要求变高时,就需要引入风险模型。风险模型的作用是控制组合对各类风险因子的暴露,比如市值、估值、动量、波动率等。常见做法是用风险模型计算组合的风险暴露,然后在优化器中加入约束,限制某些暴露不超过阈值。
组合优化听起来高大上,但核心就是“在预期收益最大化和风险约束之间找平衡”。对个人投资者来说,简单的约束(如单股权重上限、单行业权重上限)就能解决大部分问题,不必一上来就上复杂的优化器。
7.3 持续学习的方向建议
多因子选股是个需要持续积累的领域。我的建议是:先把一个完整的流程跑通,再逐步优化每个环节。不要一开始就追求完美,先有一个能跑、能出结果的版本,哪怕粗糙。然后针对每个环节(数据清洗、因子计算、合成、回测)逐个优化,每次只改一个变量,观察结果变化。
学习资源方面,与其看太多理论书,不如多动手复现经典因子。把ROE、PB、动量这些基础因子自己算一遍、测一遍,比看十篇论文都管用。遇到不懂的概念,先查清楚定义,再想它在选股中解决什么问题,最后用代码实现一遍。这个“定义-问题-实现”的循环,是我认为最高效的学习路径。
我个人在实际操作中的体会是,多因子选股最难的不是写代码,而是保持纪律。回测跑出漂亮结果时容易过度自信,实盘亏损时容易怀疑策略。真正能长期赚钱的,往往是那些逻辑清晰、执行稳定、不频繁折腾的策略。把知识地基打牢,把流程跑顺,剩下的就是耐心和坚持了。