最早做价值投资的时候我还是个“手动党”。每天打开行情软件按PE从低到高排序再手动把财报里的ROE、毛利率、股息率摘到Excel里用条件格式标色然后挨个看新闻公告生怕漏掉什么“基本面雷”。这套流程在股票数量少、更新频率低的时候勉强能用可一旦把股票池扩到全市场5000多只股票人的精力就成了最大的瓶颈——你能盯住50只票盯不住5000只。更要命的是这种纯人工筛选根本没法回答一个问题这套方法到底靠不靠谱赚了是运气亏了是能力没有任何统计意义上的验证。后来我彻底转向了量化路线核心就是做“量化价值投资多因子模型”。说白了就是把价值投资的逻辑——便宜、优质、有安全边际——变成一串可计算的规则交给程序每天去打分、排序、选股。这个过程不是简单的“写个Python脚本跑一跑”里面涉及到因子定义、数据处理、有效性检验、组合构建、回测风控一整套流程任何一个环节偷懒模型都会在实盘里给你上一课。这篇文章我不打算讲空洞的大道理就把我从零开始搭这套模型的完整过程、关键代码、以及踩过的坑都摆出来给同样想做量化价值研究的人一条能落地的路径。1. 为什么价值投资要走向因子化1.1 手工筛选的边界在哪里手工筛选价值股有三个绕不开的问题第一覆盖面有限。你手动翻F10看财报一周能看100只已经很了不起了但全市场同一时刻可能有几百只股票同时处于“低估值”状态人工覆盖一定会有遗漏而遗漏的里面可能藏着很好的标的。第二标准不统一。每个人对“便宜”的定义不一样有人看市盈率有人看市净率有人看股息率。当你同时考虑五六个指标时如果靠人脑去加权几乎不可能保持一致的逻辑。今天你觉得PE重要明天又觉得PB更重要这种主观漂移会让投资体系变得无法积累。第三无法回测。你没法回答“如果三年前用这套标准选股拿到今天的收益是多少”。没有回测就没有统计意义也就无法区分策略有效性和运气。1.2 因子化是投资逻辑的“翻译工程”量化价值投资多因子模型的本质是把“以低于内在价值的价格买入好公司”这一句话拆解成若干个可计算的维度也就是因子。比如“便宜”可以拆成低市盈率、低市净率、低市销率、高股息率、低企业价值倍数EV/EBITDA。“好公司”可以拆成高ROE、高毛利率、稳定增长、低负债、低波动。每个因子都在捕捉市场某一种定价偏差或者风险溢价。低估值因子赚的更多是均值回归的钱——市场情绪过度悲观把价格打到远低于价值的位置后续价格终会向价值靠拢。质量因子赚的是公司持续创造价值的钱——好学生大概率继续考高分。低波动因子赚的是投资者过度追逐高波动股票、导致低波动股票被低估的钱。把投资逻辑“翻译”成因子之后模型就能在每一期给全市场所有股票打分排序选出得分最高的一组股票形成一个可复现、可验证、可迭代的完整系统。这才是量化的意义——不是取代你的判断能力而是把你的判断力工程化、系统化。1.3 这套模型适合谁如果你想学习量化投资或者已经在做主观价值投资但想引入系统化辅助决策这套模型很适合你。它不要求你精通复杂的机器学习算法核心其实就是统计学和线性代数的组合用Python就能实现。它也不能保证你马上赚钱——任何声称稳赚的量化策略都要打个问号——但它能帮你把投资决策从“拍脑袋”变成“有据可查”。2. 选哪些因子价值因子库的设计逻辑2.1 核心价值因子清单与数学定义价值因子是整套模型的骨架。我常用的因子表如下每个因子都包含定义、计算公式和逻辑解释因子定义计算公式因子逻辑EP盈利收益率净利润 / 总市值PE的倒数数值越大代表相对盈利能力越便宜BP净资产倍率倒数净资产 / 总市值PB的倒数越大代表股价相对每股净资产越便宜SP收入倍率倒数营业收入 / 总市值PS的倒数越大代表销售收入相对估值越便宜FCFP自由现金流收益率自由现金流 / 总市值越大代表每单位市值对应的自由现金流越多DP股息率每股分红 / 股价直接衡量股东现金回报水平EV/EBITDA倒数企业价值倍数倒数EBITDA / 企业价值(EV)排除了资本结构和折旧政策差异后的估值指标这些因子看起来像低PE、低PB的变体但单独用哪一个都有局限性。低PE的股票可能是真正的便宜也可能是因为盈利即将大幅下滑而提前杀估值低PB的股票可能是破净的优质资产也可能是在价值毁灭的夕阳行业。多因子并用本质上就是把不同维度视角的信息综合起来相互校验减少单一指标误判的概率。2.2 辅助因子质量、成长与低波动纯粹的“便宜”很容易踩中价值陷阱——看起来便宜实际上公司基本面在恶化越跌越便宜越便宜越跌。为了解决这个问题我在价值因子之外纳入三个辅助维度质量因子最常用的是ROE和毛利率。ROE反映公司为股东创造回报的能力毛利率反映公司护城河的深浅。高ROE、高毛利与低估值结合才能筛选出“又好又便宜”的标的而不是单纯捡烟蒂。成长因子包括营收增速和净利润增速。价值投资不排斥成长关键是买在增长率被低估时。一个低PE且保持两位数增长的股票比一个低PE但营收连年下滑的股票回撤空间和修复能力完全不一样。低波动因子用过去一年的日收益率标准差来衡量。低波动的股票通常被市场忽视机构资金的容量大净值曲线更平滑能显著改善组合持有体验。很多经典的价值策略里都会加入低波动这个“防守型”因子。这三个辅助因子不喧宾夺主权重低于核心价值因子但缺了它们模型的防御能力会明显不足。2.3 因子方向一致性与预处理多因子模型的第一步就是统一方向。EP、BP这类因子是越大越好所以方向为正。但如果你直接用某个负向因子比如市盈率、市净率就得先把它取倒数或者取负号保证打分时所有因子方向一致。之后要做标准化处理让不同量纲的因子可以在一起比较。最常用的是Z-score标准化import numpy as np def zscore(factor_series): return (factor_series - factor_series.mean()) / factor_series.std()标准化之前必须先剔除极端值因为A股经常出现极端值比如某只股票EP是-500巨额亏损会把整个分布都拉歪。通常用MAD中位数绝对偏差法进行缩尾处理把超出中位数±5倍MAD的值替换为边界值这比普通的3σ缩尾更稳健。3. 数据地基从取数到“可用因子”的完整处理3.1 数据源选择与字段规划因子模型的数据需求非常明确行情数据、财务数据、估值数据、成分股权重数据。我用的数据源主要是Tushare Pro配合A股日线数据和财务报表数据。基础字段包括股票代码、交易日期、收盘价、总市值、流通市值、市盈率、市净率、ROE、毛利率、营业收入、净利润等。有一点必须强调不同字段的更新时间差异很大。行情数据当天可得但财报数据是季频披露的年报截止4月30日、一季报截止4月30日、中报截止8月31日、三季报截止10月31日。使用财务数据时必须做时点对齐——在某一天的截面数据里只能使用已经公告的财报绝不能把未来的财报数据代入过去的截面。3.2 脏数据清洗的标准动作数据质量的优先级永远高于模型复杂度。我的清洗流程是这样的剔除ST、*ST股票。这些股票面临退市风险涨跌幅限制与正常股票不同交易逻辑完全不一样混入样本会严重扭曲因子测试结果。剔除上市不满60个自然日的次新股。新股上市初期的价格波动和估值体系不稳定且经常有连续涨停因子计算很容易失真。剔除停牌股票。停牌期间无法交易如果模型选出来却买不进等于纸上富贵。剔除财务数据缺失或总市值缺失的股票。此外还有一点退市股不能简单一删了之。如果你直接用今天的股票列表去回测三年前的策略就会产生幸存者偏差——那些当年看似满足筛选条件、如今已经退市的股票被系统性剔除了回测收益会虚高。正确的做法是使用“历史时点存在的全部股票”也就是用某一天的交易快照当时的股票池就是当时全市场存续的股票包含后来退市或者ST的品种。3.3 为什么必须做行业市值中性化直接对全市场的EP打分排序结果会非常偏向银行、地产、基建这些传统低估值板块。这没问题因为价值因子天然会暴露在这些行业里。但问题是如果某一年银行股整体大涨你说不清楚收益来自价值因子还是来自行业踏空。为了做归因、控制风险需要把行业和市值的影响从中性化掉。中性化处理分三步一是对因子值关于行业哑变量和市值对数做线性回归二是取残差作为中性化后的因子值三是再对残差做标准化。这样处理之后因子体现的是“行业内相对便宜程度”而不是“哪个行业更便宜”。import statsmodels.api as sm def neutralize(factor, industry_dummies, log_mv): X pd.concat([industry_dummies, log_mv], axis1) X sm.add_constant(X) # 用OLS回归取残差 model sm.OLS(factor.fillna(0), X).fit() resid factor - model.predict(X) return resid这个动作在实战中非常重要。不做中性化的模型资金曲线可能和行业指数的曲线高度重合一旦行业轮动策略就失效。中性化之后的因子更“纯”更接近对选股能力的度量。4. 单因子体检IC分析教你在入场前识别废因子4.1 IC和ICIR因子的“命中率”体检单看一个因子在历史上的收益没有意义必须做统计检验。ICInformation Coefficient信息系数是因子值与下期收益的Spearman秩相关系数衡量因子预测能力的强弱。计算逻辑是把第t期的因子值跟第t1期的收益做秩相关每个调仓周期得到一个IC多个周期的IC构成序列。IC的均值表示因子预测收益的平均能力IC的标准差表示预测能力的稳定性。两者结合就得到ICIRIC的均值除以标准差再乘以调仓频率的开方[ ICIR \frac{mean(IC)}{std(IC)} \times \sqrt{N} ]每期计算IC的Python代码如下from scipy.stats import spearmanr def calculate_ic(factor_df, forward_return_df): # 假设factor_df的行是股票列是日期forward_return_df同理 ic_series {} for date in factor_df.columns: f factor_df[date].dropna() r forward_return_df.loc[f.index, date].dropna() if len(f) 30: ic, _ spearmanr(f, r) ic_series[date] ic return pd.Series(ic_series)经验上看IC均值绝对值在0.03以上ICIR大于0.3IC为正的比例超过55%的因子才具备基本的实战价值。IC高但ICIR过低说明因子表现极不稳定经常大起大落实盘中很难坚持拿住IC虽然不高但很稳定反而更好用。4.2 分组回测看单调性除了IC另一个必做的是分组回测。按月把所有股票按因子值从高到低分成10组计算每组后续一个月的平均收益看收益是否随组别升高而递增。如果出现第10组收益反而最高、或者中间某组断层说明因子的单调性不好选股逻辑存在问题。多空组合收益 最高组收益 - 最低组收益。这个指标剔除了整体市场涨跌的影响是因子纯收益的近似度量。不过A股做空受限多空收益更多是用来检验因子的趋势性不能直接当作可实现的策略收益。实际落地时我们只做多头组合也就是只买入得分最高的那一组或者前10%。4.3 预测期的选择为什么用月度调仓我建议用月频换仓来验证因子。日频换仓的交易成本太高年化换手率会高到无法覆盖收益季频换仓又太长因子信息衰减严重。月度调仓在因子持续性和交易成本之间比较平衡也是国内私募常用频率。这里有一个特别容易踩的坑因子值和收益的“时点对齐”。例如你在4月30日看到年报数据但年报实际可能早在3月就已披露。如果直接用4月30日的截面数据去匹配5月份收益问题不大。但假如你在4月中旬就用“4月底才公布”的财务数据去做决策那就产生前视偏差。实操上为了保守我通常对财报类因子统一做1个月或2个月的滞后处理宁可少赚一点也不要吃到Dirty数据。5. 从单因子到组合多因子合成与选股方案5.1 三种主流合成方法因子检验完成后需要把多个因子合成为一个综合得分。我用过三种方法分别说一下适用场景。等权Z-score法把每个因子的Z-score直接相加不做任何加权。优点是简单透明不容易过拟合缺点是忽略因子质量的差异弱因子会稀释强因子的效果。IC加权法按照每个因子历史上的IC均值占所有因子IC之和的比例加权IC越高的因子权重越大。这比等权更精细代表思路是“过去预测能力强的因子应该配更高的权重”。但缺点是当某个因子IC因偶然因素偏高时容易给过大的权重。最大化ICIR的加权法通过优化算法求解一组权重使合成后因子序列的ICIR最大。这是更进阶的做法目标函数是最大化合成因子的平均IC和稳定性的比值。由于优化过程用的是历史数据很容易过拟合必须配合样本外验证才能确认权重有效。对于个人研究者我建议起步用等权法跑通流程后换成IC加权法作为进阶除非你有足够的样本外数据和经验再考虑优化权重法。5.2 打分选股与权重分配综合得分排序后怎么选股两种常见方式一种是固定数量法比如每月选综合得分排名前30或前50只股票。这种方式直观但股票数量随市场总样本变化遇到极端行情比如全面普跌时前50名的质量可能下滑。另一种是分位数法比如选综合得分前10%的股票。样本多时选出的股票也多样本少时选出的少天然适应市场容量。我实测下来分位数法更适合A股个股数量足够多前10%通常能选出200到500只股票分散化效果好也不容易被单只股票的暴雷拖垮净值。权重分配上最简单的方案是等权重。小资金规模下等权重是性价比最高的选择它不适合做任何优化天然避免了对大市值股票的超配。如果想要更精细可以按综合得分线性加权——得分高的股票权重略高但要注意不要集中度过高。5.3 调仓纪律与交易约束每月固定调仓日执行再平衡。调仓时要把“买不进卖不出”的情况考虑进去涨停的股票买不进跌停的股票卖不出停牌的股票动不了。草率的回测不会处理这些细节实盘中却会让你吃大亏。我的经验是调仓日先检查涨跌停状态剔除无法交易的股票再按目标权重执行买卖。换手率控制也要提前设计。N个月调仓一次的模型换手率如果超过70%/月说明因子组合不够稳定交易成本会侵蚀掉大部分超额收益。一个价值型多因子组合合理的月换手率应该在20%-40%之间。6. 回测框架搭建与风控约束6.1 回测引擎的核心设计回测不是把收益算出来就完事它得模拟真实交易环境。我的回测引擎包含几个必须的模块每日净值计算、涨跌停交易限制、停牌处理、分红送股除权、交易费用扣除。交易费用方面A股现在双边收费大概包括佣金双边万2.5到万3最低5元印花税卖出千1不同时期费率不同过户费十万分之一。再加上滑点影响我倾向于在回测里统一定为双边千分之三的成本。如果模型的月度换手率是30%一个月的摩擦成本就是0.09%一年就是1.08%。别小看这个数字很多因子毛收益很高扣完成本之后优势所剩无几。6.2 评价指标体系策略好不好不能只看年化收益。我常用的评价指标如下表指标含义参考标准年化收益率组合年化复合收益越高越好但要结合风险看超额年化收益跑赢基准指数的部分反映主动管理能力最大回撤历史峰值到谷值的最大跌幅越小越稳卡玛比率年化收益 / 最大回撤越高质量越好年化波动率收益的标准差衡量组合波动性信息比率超额收益均值 / 超额收益标准差IR0.5表现尚可月度换手率调仓时的组合持仓变化比例越低越节约成本回测中我还习惯做分年度绩效统计重点看因子在牛市、熊市、震荡市不同市场环境下的表现。价值因子在牛市中后期往往跑输这是正常的风格轮动不能因为一年跑输就否定整个模型。6.3 风格暴露与集中度监控回测里还需要监控组合相对基准的风格暴露尤其是行业权重偏离度和市值暴露。很多人辛辛苦苦做了中性化处理选股时又忘了做行业中性化约束导致最终组合的行业暴露度很高。我遇到过一种情况模型选出来的股票里银行股占了40%那年银行板块刚好大涨策略收益看起来非常漂亮可一旦风格反转回撤幅度也会远超预期。处理办法是在选股加入行业中性化约束比如限制单一行业占比不超过基准权重的两倍或者直接对综合得分做行业标准化让每个行业选出的股票数量与基准比例接近。这样模型的收益来源更清楚确实是“行业内精选个股”贡献的而非靠赌行业方向。7. 回看不过关的模型过拟合与常见坑7.1 数据前视偏差这是个人做回测最容易犯的错没有之一。常见形式包括用未公布的年报数据参与选股用复权因子引入未来除权信息用当日收盘计算当日买入收益实际次日才能成交。我在前几版回测里就吃过亏当时直接用当期财报计算因子再用当月收益做验证结果IC高得惊人。后来意识到财报数据在报告期末之后还要审计、披露、汇总市场真正拿到信息的时间要比报告期末晚至少几周。把未来数据带入因子等于在市场还没看到信息时就让模型提前“预知”回测结果大幅失真。修正的方法就是前面说过的滞后处理财报类因子统一滞后1到2个月宁可损失一点准确性也要保证没有未来函数。7.2 过拟合的三个征兆多因子模型的过拟合不像机器学习那么隐蔽但有三个明显的征兆一是训练期或全样本期IC显著为正但分年度统计只有一两年表现好其余年份平平甚至为负。这说明模型只是在某一特定市场环境下“记住”了规律没有抓到可泛化逻辑。二是参数敏感性过高。换一个调仓日比如从每月5号改成每月15号、改变某个因子的缩尾倍数结果收益波动巨大。这不是模型稳健而是过拟合在起作用——真正有效的策略应该对微调不敏感。三是因子数量盲目增加。很多新手觉得因子越多越好不断加新的指标回测曲线越来漂亮样本外表现却越来越差。因子不是越多越好相关度高的因子叠加不会带来增量信息只会放大过拟合。7.3 稳健性验证三件套我自己现在做任何模型上线前都要过三道验证第一滚动样本外测试。用过去3年的数据滚动训练权重在之后的1年做样本外验证然后不断向前滚动。这种方法能模拟实际投资中“只能看到历史数据决策未来”的真实状态。第二随机起点测试。改变策略的起始月份看结果是否高度依赖起点。如果从2016年1月和2016年6月分别启动净值曲线差异很大说明策略依赖特定时点的市场状态需要警惕。第三极端行情压力测试。重点看2015年股灾、2018年单边熊市、2020年疫情冲击、2022年成长牛等特殊时期的表现。价值策略天然防御性较强但如果极端行情下回撤超过基准太多说明风控环节出了问题需要回头检查行业暴露和个股集中度。我在实际跑这套流程时最大的体会是好的数据质量 好的因子 好的模型。刚开始做的时候我把大量精力花在调参和因子组合上后来发现收益提升最明显的阶段反而是把数据清洗做扎实、把前视偏差堵死、把交易成本算清楚之后。这些看起来最不起眼的环节恰恰是和别人拉开差距的地方。模型跑通、实盘跟踪之后也别急着躺平。因子会随市场结构变化而衰减过去三年的ICIR未来未必保持每个月我都重新跑一遍单因子体检观察哪些因子IC在衰减哪些因子换手率在升高然后动态调整组合的权重。量化模型不是一个一次性的工程而是一个持续迭代的系统。