在量化交易这条路上,我见过太多人一上来就写一堆买卖点策略,结果回测曲线倒是好看,一上实盘就翻车。问题几乎都出在同一个地方:你没有搞清楚策略到底赚的是谁的钱。而决定这套逻辑能不能成立的核心,就是你用来筛选股票的因子。我这次专门写给刚入门的朋友,不讲那些复杂的机器学习模型,也不讲期权定价,就讲五个最基础、最容易上手、同时能真正反映市场逻辑的因子。这一篇是上篇,先不讲怎么拼接组合,而是把这五个因子各自是什么、为什么有效、代码怎么写、常见的坑在哪里,全部拆开说清楚。
我尽量用大白话讲,代码也尽量精简。只要你会一点Python,会pandas,跟着敲一遍,你就能在自己的电脑上算出这些因子,并且把它们用于之后的选股和回测。
1. 先搞清楚:量化交易里的“因子”到底是什么
很多新手第一次听到“因子”这个词,容易把它想得很玄。其实因子的本质就是一个可以量化的筛选条件,它用历史数据告诉你,哪些股票更有可能在未来一段时间内跑赢别的股票。你可以把它理解成一个“体检指标”,比如医生判断一个人健不健康,会看血压、心率、血脂,每个指标都有合理区间。金融市场里,因子就是资产的“体检指标”,比如动量、反转、波动率、流动性、质量。
为什么因子能让策略赚钱?因为市场经常会出现一些规律性的偏差。比如某只股票最近涨得猛,未来一段时间继续涨的概率会略高一点点;或者某只股票跌太多了,后面反弹的概率会变大。这些偏差在单个股票上可能很微弱,但当你用因子筛选出一篮子股票,长期反复执行,偏差就会累积成超额收益。这就是因子策略的本质:赚的是市场非完全有效的那部分钱。
这里有个关键点要先说清楚:因子不是万能的,它给的是概率,不是确定性。你筛选出来的十只股票里,可能有六只会涨,四只会跌,但只要这六只的涨幅大于四只的跌幅,整个组合就是赚钱的。所以做因子策略,永远要记得分散,不要押单票。
1.1 因子必须具备的两个属性:预测力与稳定性
不是随便一个指标都能叫因子。判断一个指标能不能用,主要看两件事。
第一是预测力。就是说,过去某个时间点上这个指标低的一组股票,未来收益是不是确实比指标高的那组股票更好(或者相反)。这个可以通过分组回测来验证。第二是稳定性。也就是这个规律不能只在一两年里有效,最好能在不同市场环境(牛市、熊市、震荡市)下都保持大致方向一致。如果某个指标今年有效明年失效,那它作为因子的可靠性就大打折扣。
我见过不少新手自己做研究,发现某个奇特指标在最近三年的数据里回测收益特别高,兴奋得不行。结果一拉长时间,或者换个时间段测试,收益就消失了。这种情况通常不是挖到了宝藏,而是过度拟合了噪音。所以后面写代码的时候,我建议你把数据分成两段:前面一段用来“发现规律”,后面一段用来“验证规律”,千万不要用同一段数据既做研究又做验证。
1.2 一个简单的因子计算框架
我们需要一个统一的数据结构来算所有因子。在Python量化里,最常用的是pandas的DataFrame,每一行代表一只股票在某一天的数据,列包括日期、股票代码、收盘价、成交量、成交额、财务指标等。因子计算本质上就是按股票代码分组,对时间序列做滚动计算。
我习惯先把基础数据准备好,做成一个干净的long format面板数据,然后用groupby加transform或者rolling来算因子。这样写出来的代码不仅简洁,速度也快。后面每个因子的代码示例,我都会基于这个框架来写,这样你看完五个因子,其实也就掌握了一套通用的数据计算套路。
2. 动量因子:强者恒强,最经典的进攻信号
先讲动量因子,因为它是量化里最古老、最广泛使用的因子之一,也是最容易理解的一个。动量因子的核心思想就是:过去一段时间涨得多的股票,未来一段时间倾向于继续涨;过去跌得多的,未来倾向于继续跌。A股里常说的“追涨”,其实就是在交易动量效应,只是散户往往是追在最高点,而量化会把动量当作一个统计规律来用。
2.1 动量效应的来源与解释
动量效应为什么存在?学术界给过很多解释,但最有说服力的有两条。第一是投资者反应不足。市场里的信息传播有快有慢,当一个利好出现时,不是所有人都能立刻意识到,价格会随着信息慢慢扩散而持续上涨。第二是羊群效应。很多人看到股票在涨,会跟风买入,这种跟风行为会让上涨趋势延续一段时间。
但要注意,动量效应并不是永远成立的,它也有失效甚至反转的时候。尤其是当市场风格急剧切换,比如一波热门股突然集体下跌,动量因子会短期回撤得很厉害。所以动量因子的持有周期和观察周期很关键。常见的观察周期有20天、60天、120天,对应的持有周期也不同。国内研究者发现,A股市场短周期动量(比如20天)效果更明显,但也更容易受交易成本和滑点影响。
2.2 Python实现动量因子
用Python算动量因子其实很简单,最常用的动量因子就是过去N个交易日的收益率。
import pandas as pd import numpy as np # 假设df是包含date、code、close(收盘价)三列的面板数据 # 先按股票代码和日期排序 df = df.sort_values(['code', 'date']) # 定义一个计算动量的函数:过去N日收益率 def momentum(df, n=20): # 按股票分组,计算每个股票的收益 df['momentum'] = df.groupby('code')['close'].transform( lambda x: x.pct_change(periods=n) ) return df # 调用示例 df = momentum(df, n=20)这里用的是pct_change(periods=n),意思就是拿今天的收盘价除以20天前的收盘价再减1,得到这20天的累计涨跌幅。注意periods=20在中文语境里容易混淆,它实际上用的是第0天到第20天的收盘价,也就是包含了20个交易日的区间。
实际使用中,我更推荐把动量分成“绝对动量”和“相对动量”。绝对动量就是上面直接算出的收益率,相对动量则是把每只股票的收益率在整个市场里排名,做截面标准化。因为市场大涨的时候,所有股票都有正的绝对动量,但这并不代表每只股票都值得买。相对动量可以过滤掉市场整体波动的干扰。
# 相对动量:截面排名标准化 df['mom_rank'] = df.groupby('date')['momentum'].rank(pct=True)这一步做完后,mom_rank就代表每天每只股票在动量维度上的相对位置,数值在0到1之间,越大说明动量越强。做组合的时候,就可以按这个分数从高到低选股票了。
2.3 动量因子使用中的三个坑
第一,剔除刚上市的新股。新股上市初期波动巨大,动量数据往往失真,建议直接过滤掉上市不满60天的股票。
第二,注意动量因子的“刹车”。连续涨停的股票动量极高,但一旦打开涨停,往往就是大跌的开始。所以动量因子不能机械地用,最好结合价格是否偏离均线过远来判断,比如加上“乖离率”过滤,当乖离率超过某阈值时就剔除。
第三,交易成本是动量策略的隐形杀手。高频调仓的动量策略看起来收益高,但每次换手都要付佣金和冲击成本。新手建议设置一个调仓缓冲带,比如只有当动量排名变化超过20%时才调仓,减少不必要的交易。
3. 反转因子:别追涨,跌多了反而是机会
说完了强者恒强,接着就要讲一个逻辑完全相反的因子——反转因子。反转因子的思想是:过去一段时间跌得多的股票,未来倾向于涨回来;过去涨得太猛的,未来倾向于回调。这看起来跟动量直接矛盾,其实它们各自适应不同的时间尺度。
3.1 反转与动量的差异在哪里
动量因子一般用在相对较短或中等的周期,比如20天到60天,捕捉的是趋势延续。反转因子通常用在更短的周期(比如5天、10天)或者更长的周期(比如250天),捕捉的是过度反应后的价格修复。A股市场的散户占比高,情绪化明显,很多股票短期的暴涨暴跌往往超出基本面,所以短周期反转效应在A股是特别强的。
这里必须强调一下:动量和反转不是“二选一”,而是“看周期”。如果你用5天的涨跌幅做因子,大概率得到的是反转效应;如果用60天的涨跌幅做因子,大概率得到的是动量效应。我自己习惯的做法是:短周期(5-20天)做反转来选“超跌反弹”的标的,中周期(60-120天)做动量来选“趋势强者”,两者结合使用,而不是单押一边。
3.2 反转因子的Python实现
反转因子和动量因子在代码上几乎没有区别,只是方向相反。动量是过去N日收益率越高越好,反转是过去N日收益率越低越好。所以我们可以直接复用动量计算的逻辑,在选股时把排序方向反过来即可。
# 计算过去5日收益率(短期反转因子) def reversal(df, n=5): df = df.sort_values(['code', 'date']) df['reversal'] = df.groupby('code')['close'].transform( lambda x: x.pct_change(periods=n) ) # 反转因子:过去涨幅越小(甚至跌幅越大),未来越有可能上涨 return df # 使用:按reversal从小到大排序,选最小的那一批 df['rev_rank'] = df.groupby('date')['reversal'].rank(ascending=True)这里跟动量有个容易踩坑的地方:计算出来的reversal是负值代表跌。做反转选股时,你以为要选reversal最小的,也就是跌最惨的,但实际操作中要特别注意流动性问题。往往跌最惨的那些股票,可能是出了重大利空,或者流动性枯竭,买进去之后继续跌的概率也不小。所以反转因子不能单独用,至少要叠加一个流动性过滤,或者叠加一个“跌幅不过度”的区间过滤。
3.3 反转因子实操中的注意点
我用反转因子最深的体会是:反转策略最怕的是“价值陷阱”。你以为跌多了会反弹,结果它一路阴跌,根本不给反弹机会。要避免这种情况,最好叠加两个条件:一是股票的基本面没有出现致命问题,比如营业收入和净利润不能连续下滑;二是下跌的过程中成交量要相对放大,说明有资金愿意抄底,而不是无人问津的缩量阴跌。
截面上做反转选股,也不建议选跌幅最大的前1%,而是选跌幅排在前10%到20%之间的股票。这样既捕捉了超跌反弹的机会,又躲开了极端弱势股的下跌惯性。
4. 波动率因子:低波动不是无聊,是稳赚
接下来这个因子,很多人会本能地觉得不对劲:波动大的股票才能赚大钱,波动小的股票涨得慢,怎么会是优质因子?但大量的研究和实盘数据都指向一个反直觉的现象——低波动率股票的组合,长期收益往往好于高波动率股票的组合。这在学术上叫做“低波动异象”。
4.1 低波动异象:为什么高波动股票反而让人失望
高波动股票之所以让人失望,主要有几个原因。第一,波动大的股票往往估值偏高,大量投机资金参与,未来收益被透支。第二,高波动的股票在计算收益率时容易被复利效应拖累——同样年均涨20%,高波动的路径可能是先涨50%再跌20%,而低波动的路径可能是平稳上涨,最后实际收益差异很大。第三,高波动股票回撤太大,实盘中很难拿得住,容易在低点割肉。
低波动因子还有一个操作上的巨大优势:它的换手率低,交易成本小。高波动策略往往需要频繁买卖,每次进出都贡献给券商和交易所,实际到手收益大打折扣。低波动组合则可以拿得比较久,省下不少摩擦成本。
4.2 波动率因子的Python实现
波动率因子最常见的计算方式,是过去N日收益率的标准差。标准差越大,波动越高。
# 计算过去20日收益率的标准差作为波动率 def volatility(df, n=20): df = df.sort_values(['code', 'date']) # 先算每日收益率 df['daily_ret'] = df.groupby('code')['close'].pct_change() # 再用滚动窗口算标准差 df['volatility'] = df.groupby('code')['daily_ret'].transform( lambda x: x.rolling(window=n, min_periods=n).std() ) return df # 使用:按volatility从小到大排序,选波动率最低的 df['vol_rank'] = df.groupby('date')['volatility'].rank(ascending=True)如果你用的是分钟数据或者高频数据,也可以用日内价格振幅来算波动率,但对新手来说,日频收益率标准差已经够了。模拟数据测试时,大概用100只股票,每次选波动率最低的20只,持有一年,就能看出明显差异。
4.3 低波动因子的两个细节问题
第一个细节,是计算波动率时要用对数收益率,而不是简单收益率。简单收益率在股价出现极端值时会造成数据失真,比如一天跌停的股票,收益率为-10%,但第二天反弹时再涨停,简单收益率会造成复利计算偏差。对数收益率可以避免这个问题,代码上把pct_change()换成np.log(close / close.shift(1))就行。
第二个细节,是低波动因子在牛市里会跑输大盘,尤其是一波普涨行情里,高波动的题材股涨得飞起,低波动组合看起来就显得“太稳了”。所以用低波动因子时,心里要清楚它的定位——它不是用来博取市场最高收益的,而是用来控制回撤、平滑曲线的。当行情蒸蒸日上的时候,别随便换掉低波动策略去追热点,可能刚好追在风格切换的顶部。
5. 流动性因子:被忽视的隐形杀手
如果说前四个因子都在回答“买什么”,流动性因子回答的是“买不买得到、卖不卖得掉”。很多新手做回测时从不考虑流动性,结果策略收益曲线很漂亮,实盘一买把价格直接推高,一卖又把价格砸低,收益立刻蒸发一大截。流动性因子正是用来规避这些“看起来很能涨但实际无法交易”的股票。
5.1 为什么流动性直接决定策略能不能落地
流动性可以简单理解成交易的难易程度。流动性好的股票,买卖盘厚实,大资金进出不容易引起价格大幅波动;流动性差的股票,挂单稀疏,可能只有一手买盘,你卖100股就能把价格打下去几个百分点。
对于小资金来说,流动性差的影响可能还不明显,但当你管理的资金上了一个台阶之后,流动性几乎成了第一约束条件。哪怕因子得分再高,如果这只股票一天成交额只有几十万,也不可能重仓买入。所以流动性因子更像一个“门槛”,而不是一个“排名”:先在流动性维度上做过滤,再在剩余的股票里用其他因子排序。
5.2 换手率与成交额:流动性因子的关键指标
流动性最常用的代理指标有两个:换手率和成交额。换手率是成交量除以流通股本,反映的是股票的交易活跃度;成交额是总成交金额,反映的是资金参与的绝对规模。
在A股,我一般会用20日平均成交额作为流动性门槛,比如规定最近20天日均成交额必须大于5000万。注意这只是个示例阈值,你要根据自己的资金规模调整。如果资金量不大,可以放宽到2000万;如果资金量大,可能要提高到1亿以上。
代码实现也不复杂:
# 计算20日平均成交额 df['amount_ma20'] = df.groupby('code')['amount'].transform( lambda x: x.rolling(window=20, min_periods=20).mean() ) # 流动性过滤:只保留日均成交额大于5000万的股票 df_filtered = df[df['amount_ma20'] > 5000_0000]这里要提醒一点,amount字段的数值单位要提前确认好。有些数据源的成交额单位是元,有些是千元,甚至有些是手,单位不统一会导致过滤阈值完全失真。拿到数据后第一步先做单位统一,这是新手最容易忽略的坑。
5.3 极低流动性陷阱的排查方式
我见过一个很典型的例子:某只股票的因子得分常年排在市场前几名,各项指标都非常漂亮,唯一的问题就是日成交额只有几十万。如果你不设流动性过滤,量化模型会反复选到它,但实盘一买入就开始后悔——盘口上根本没人卖给你。
排查方法其实很简单,把你因子得分最高的前50只股票拉出来,逐个看一下20日平均成交额。如果发现一大批股票的日均成交额都在1000万以下,那基本可以断定数据有异常,或者因子在某些边界条件下失效。这时候就该给策略加一道流动性过滤,而且方向不能反——绝不为了买入低流动性股票而降低过滤标准。
6. 质量因子:用财报筛选“好公司”
讲完四个偏交易行为的因子,最后一个要说的是质量因子。质量因子跟高频交易无关,它看的是上市公司的基本面——盈利能力、资产负债结构、现金流状况。它的核心逻辑非常朴素:好公司的股票,长期来看应该跑赢差公司。
6.1 质量因子为什么长期有效
质量因子的有效性,其实来自市场对基本面的定价不充分。很多人买股票只看概念、看消息、看技术图形,对公司财报数据一知半解,这导致那些真正赚钱能力强、财务稳健的公司反而经常被低估。当市场情绪退潮时,资金会重新关注基本面,质量因子的优势就显现出来了。
尤其值得关注的是,质量因子与其他因子的相关性通常较低。意思就是,当动量因子失效的时候,质量因子可能还在工作;当低波动因子在市场极端波动时暂时失灵,质量因子往往能起到稳定器的作用。在组合里加入质量因子,可以有效平滑净值曲线,降低系统性回撤风险。
6.2 质量因子的简化计算
如果用真实的财务数据来构建质量因子,最常用的指标包括ROE(净资产收益率)、毛利率、资产负债率、经营现金流与净利润之比等。不过对新手来说,可以先用一个简化版本:用ROE和毛利率两个指标做基础评分。
# 假设df包含code、date、roe(净资产收益率)、gross_margin(毛利率) # 先对每个指标做截面排名 df['roe_rank'] = df.groupby('date')['roe'].rank(pct=True) df['gm_rank'] = df.groupby('date')['gross_margin'].rank(pct=True) # 质量因子 = 两个排名的简单平均 df['quality'] = (df['roe_rank'] + df['gm_rank']) / 2这里我用的是截面排名而非原始数值,因为不同行业、不同规模的公司在ROE和毛利率上天然存在巨大差异,直接用原始数值比较不同行业的股票会很不公平。如果你希望更精确一点,可以在行业内分别排名,然后再合成因子,这就是所谓的中性化处理,我会在中篇里详细展开。
6.3 质量因子的滞后性与调仓节奏
质量因子的最大缺点是滞后性。财报数据是按季度公布的,而且你拿到的都是上一个报告期的数字。这意味着质量因子的信息时效性远低于交易类因子,市场的认知可能已经发生变化,你却还在基于几个月前的财报做判断。
所以质量因子适合的调仓节奏要慢一些。我一般建议用质量因子选股时,调仓周期至少一个月以上,甚至一个季度调仓一次。太频繁的调仓既增加了交易成本,也浪费了财务数据的特性。
还要注意财报数据的“暴雷”风险。有些公司过去财务数据很好看,但财报公布后突然大幅修正或爆出财务造假,质量因子会瞬间失效。所以即使是质量因子选出来的组合,也要设置一个单票风险上限,避免个别暴雷股拖垮整个组合。
写在最后:上篇结束前,先记住这几件事
五个因子到这里就逐个讲完了。回顾一下:动量因子捕捉趋势延续,反转因子捕捉超跌反弹,波动率因子寻找稳健标的,流动性因子保障交易可行性,质量因子筛选基本面扎实的公司。每一个因子背后,都有一套市场运行逻辑在支撑,都不是凭空捏造的“玄学”。
不过我得再提醒一遍,这一篇讲的是单个因子各自是什么、怎么算。实际做一个策略时,很少有人会只用单一因子,因为单因子的波动太大,而且容易在某段行情里持续失效。真正有效的方法是做因子的组合和高低配,比如用流动性因子做过滤,用动量因子做进攻,用低波动因子和质量因子做防守,再通过一个打分模型把它们融合起来。
这些进阶内容,我打算放在中篇和下篇里详细讲。中篇会讲多因子组合的方法论,包括因子中性化处理、标准化、相关性检验和合成权重;下篇会讲完整的回测框架,包括如何做分组回测、如何计算超额收益、如何处理换手率、如何避免常见回测陷阱。每篇都会继续配Python代码,把你从“知道因子”带到“能跑通一套完整策略”。
最后说一句我的心里话:做量化交易,永远要把风险控制放在收益前面。因子给了你一条大概率成功的路径,但这只是步行的起点。数据质量、交易成本、实盘执行,每一步都可能让收益烟消云散。不要急着上大资金,先把自己的因子库跑熟,把回测框架跑透,再拿小资金去验证半年。这个过程看起来慢,实际上是最快的路。
如果你读完这一篇,能自己算出动量因子,或者用低波动因子筛出一篮子股票去观察,我觉得这篇就没白写。下一篇我们继续。