第三章:基础因子构建——估值、规模、动量与波动率
各位同学,欢迎来到实战环节。前两章我们聊了量化投资的底层逻辑和数据准备,今天终于要动手了——构建最基础的四大类因子。
说实话,很多新手一上来就追求复杂的机器学习模型,结果往往不如人意。我个人习惯是:先把基础因子做扎实,再谈高阶模型。你想想看,地基不稳,房子再漂亮也是危楼。
3.1 估值因子:PE / PB / PS
估值因子,说白了就是判断股票「贵不贵」的指标。我刚开始做量化时,以为PE越低越好,结果踩了不少坑。
3.1.1 市盈率(PE)
PE = 股价 / 每股收益。这个指标最常用,但要注意:
- 静态PE:用去年年报的EPS,数据滞后
- 滚动PE(TTM):用最近四个季度的EPS,更及时
- 动态PE:用预测的EPS,有主观性
3.1.2 市净率(PB)
PB = 股价 / 每股净资产。这个指标对金融、周期行业特别有效。为什么?因为这些行业的资产容易估值,净资产比较靠谱。
但要注意,PB为负的公司要剔除——净资产为负,说明公司已经资不抵债了,这种股票再便宜也别碰。
3.1.3 市销率(PS)
PS = 股价 / 每股销售收入。这个指标适合:
- 互联网公司(还没盈利,PE无效)
- 零售企业(收入稳定,利润波动大)
- 高成长行业(利润被研发费用侵蚀)
代码实现其实很简单,我们来看一下:
import pandas as pd
import numpy as np
def calc_valuation_factors(df):
"""
df: 包含 price, eps, bvps, sps 的DataFrame
返回:PE, PB, PS 因子
"""
# 剔除异常值
df = df[df['eps'] > 0] # 负PE的公司剔除
df['PE_TTM'] = df['close'] / df['eps_ttm']
df['PB'] = df['close'] / df['bvps']
df['PS'] = df['close'] / df['sps']
# 去极值处理(我习惯用MAD方法)
for factor in ['PE_TTM', 'PB', 'PS']:
median = df[factor].median()
mad = (df[factor] - median).abs().median()
upper = median + 5 * mad
lower = median - 5 * mad
df[factor] = df[factor].clip(lower, upper)
return df[['PE_TTM', 'PB', 'PS']]
3.2 规模因子:市值与流通市值
规模因子是量化投资中最经典的因子之一。Fama-French三因子模型里就有它。说白了就是:小盘股长期跑赢大盘股。
但这里有个细节:用总市值还是流通市值?
| 类型 | 定义 | 适用场景 |
|---|---|---|
| 总市值 | 股价 × 总股本 | 学术研究、全市场选股 |
| 流通市值 | 股价 × 流通股本 | A股实战(考虑限售股) |
我个人建议:在A股市场,优先用流通市值。为什么?因为A股有很多限售股,它们短期内不会在市场上交易,用总市值会高估实际可交易的规模。
嗯,这里要注意:流通市值因子需要取对数,因为市值分布是右偏的。取完对数后,因子值更接近正态分布,模型效果更好。
def calc_size_factors(df):
"""
计算规模因子
"""
df['log_mkt_cap'] = np.log(df['total_market_cap'])
df['log_float_cap'] = np.log(df['float_market_cap'])
# 标准化处理
for factor in ['log_mkt_cap', 'log_float_cap']:
df[factor] = (df[factor] - df[factor].mean()) / df[factor].std()
return df[['log_mkt_cap', 'log_float_cap']]
3.3 动量因子:过去N日收益
动量因子,就是「强者恒强,弱者恒弱」。我见过很多策略,核心逻辑就是买过去涨得好的股票,卖过去跌得惨的股票。
但动量因子有个大坑:反转效应。短期动量(比如1个月)和长期动量(比如12个月)效果不错,但中期(3-6个月)往往会出现反转。
为什么会这样?行为金融学解释是:投资者对信息的反应不足(短期动量),然后过度反应(中期反转),最后慢慢修正(长期动量)。
代码实现:
def calc_momentum_factors(df, price_col='close'):
"""
计算不同周期的动量因子
"""
# 过去5日收益(短期)
df['mom_5d'] = df[price_col].pct_change(5)
# 过去20日收益(约1个月)
df['mom_20d'] = df[price_col].pct_change(20)
# 过去60日收益(约3个月)
df['mom_60d'] = df[price_col].pct_change(60)
# 过去120日收益(约6个月)
df['mom_120d'] = df[price_col].pct_change(120)
# 过去250日收益(约1年)
df['mom_250d'] = df[price_col].pct_change(250)
return df[[col for col in df.columns if col.startswith('mom_')]]
3.4 波动率因子
波动率因子,衡量的是股票价格的不确定性。低波动率股票往往能跑赢高波动率股票——这就是著名的「低波动异象」。
计算波动率的方法有很多,我常用的是:
- 历史波动率:过去N日收益率的标准差
- 已实现波动率:日内高频数据的波动
- GARCH模型:考虑波动率聚集效应
在实战中,历史波动率就够用了。但要注意:波动率有聚集效应——高波动之后往往跟着高波动,低波动之后跟着低波动。所以,直接用原始波动率做因子,会有自相关的问题。
我的处理方法是:对波动率取对数,然后做差分,得到波动率的变化量。这样既能捕捉波动率的变化趋势,又能消除自相关。
def calc_volatility_factors(df, price_col='close'):
"""
计算波动率因子
"""
# 日收益率
df['daily_ret'] = df[price_col].pct_change()
# 过去20日波动率(月波动率)
df['vol_20d'] = df['daily_ret'].rolling(20).std() * np.sqrt(252)
# 过去60日波动率(季波动率)
df['vol_60d'] = df['daily_ret'].rolling(60).std() * np.sqrt(252)
# 波动率变化量
df['vol_change'] = np.log(df['vol_20d']) - np.log(df['vol_60d'].shift(20))
return df[['vol_20d', 'vol_60d', 'vol_change']]
3.5 知识体系总览
说了这么多,我们来画一张图,把今天的内容串起来:
这张图把今天讲的四大因子串起来了。你会发现,每个因子都有自己独特的逻辑和坑。做量化投资,不是堆砌因子,而是理解每个因子背后的经济学含义。
好了,今天的实战内容就到这里。记住:基础因子是量化投资的基石,花时间把它们做扎实,后面的机器学习模型才能发挥真正的威力。