3、单因子构建:基于价格、成交量、财务数据的经典因子构建
好了,咱们进入正题。单因子构建,说白了就是给股票打分。你想想看,我们做量化投资,总不能闭着眼睛瞎买吧?总得有个依据。这个依据,就是因子。
我个人习惯把因子分成三大类:价格类、成交量类、财务类。每一类里又有几个经典门派。今天我就把这些压箱底的东西掏出来,跟你好好聊聊。
核心观点:单因子是策略的砖瓦。砖瓦不结实,楼盖得再高也得塌。所以,因子构建这一步,值得你花80%的精力。
3.1 价格类因子:动量与反转
价格类因子,是最直观的。股票涨了,能不能继续涨?跌了,会不会反弹?这就是动量和反转要回答的问题。
3.1.1 动量因子
动量因子,也叫趋势因子。它的逻辑很简单:过去涨得好的股票,未来一段时间还会涨。我在项目中遇到过不少质疑这个逻辑的人,但说实话,A股市场里,动量效应在短期(比如1-3个月)还是挺明显的。
构建方式:
- 时间窗口:我一般用20个交易日(约1个月)和60个交易日(约1个季度)。
- 计算公式:动量 = 当前收盘价 / N天前的收盘价 - 1
- 注意事项:记得剔除新股和停牌股,不然数据会失真。
# 动量因子计算示例
import pandas as pd
import numpy as np
def calc_momentum(close, window=20):
"""
计算动量因子
close: 收盘价序列
window: 时间窗口
"""
momentum = close / close.shift(window) - 1
return momentum
# 使用示例
df['momentum_20'] = calc_momentum(df['close'], 20)
df['momentum_60'] = calc_momentum(df['close'], 60)
我的小技巧:动量因子不要只用单一窗口。我习惯把20日和60日动量做个等权合成,效果往往比单个窗口更稳定。
3.1.2 反转因子
反转因子,是动量因子的对立面。它认为:涨多了会跌,跌多了会涨。说白了就是均值回归的思想。
我曾经犯过一个错误:直接用过去5日的涨跌幅做反转因子。结果发现,在牛市中这个因子亏得裤衩都不剩。后来我学乖了,反转因子更适合用在短期(1-5天)和超长期(1年以上)。
构建方式:
- 短期反转:用过去5日的累计收益率,取负值。
- 长期反转:用过去250日的累计收益率,取负值。
- 关键点:反转因子需要做市值中性化处理,否则小市值股票会干扰结果。
# 反转因子计算示例
def calc_reversal(close, window=5):
"""
计算反转因子
"""
ret = close / close.shift(window) - 1
reversal = -ret # 取负值,收益率越高,因子值越低
return reversal
df['reversal_5'] = calc_reversal(df['close'], 5)
避坑指南:我曾经用反转因子做日频调仓,结果手续费把收益全吃掉了。反转因子更适合周频或月频调仓,切记!
3.2 成交量类因子:波动率
成交量类因子,反映的是市场情绪和活跃度。波动率是其中最经典的一个。
3.2.1 波动率因子
波动率因子,衡量的是股票价格的不确定性。低波动率的股票,往往有更好的风险调整后收益。这个现象在学术界叫「低波动异象」。
我记得第一次看到这个因子时,觉得不可思议:高风险不是应该带来高收益吗?但数据告诉我,在A股市场,低波动组合确实跑赢了高波动组合。嗯,市场有时候就是这么不讲道理。
构建方式:
- 历史波动率:用过去20日收益率的标准差。
- 已实现波动率:用日内5分钟收益率的平方和开根号。
- 我推荐:用已实现波动率,它对市场变化的反应更快。
# 波动率因子计算示例
def calc_volatility(close, window=20):
"""
计算历史波动率
"""
log_ret = np.log(close / close.shift(1))
volatility = log_ret.rolling(window).std() * np.sqrt(252) # 年化
return volatility
df['volatility_20'] = calc_volatility(df['close'], 20)
注意:波动率因子需要做行业中性化处理。不同行业的波动率水平差异很大,比如科技股天生就比银行股波动大。
3.3 财务类因子:价值与质量
财务类因子,是基本面分析的量化体现。价值因子和质量因子,是我最常用的两个。
3.3.1 价值因子
价值因子的核心思想是:买便宜的股票。但「便宜」怎么定义?不是看股价绝对值,而是看估值指标。
我个人习惯用以下几个指标:
- 市盈率(PE):市值 / 净利润。越低越便宜。
- 市净率(PB):市值 / 净资产。适合银行、地产等重资产行业。
- 股息率:每股分红 / 股价。越高越有安全边际。
构建方式:
# 价值因子计算示例
def calc_value_factors(df):
"""
计算价值因子
df需要包含: market_cap, net_profit, book_value, dividend, close
"""
df['pe'] = df['market_cap'] / df['net_profit']
df['pb'] = df['market_cap'] / df['book_value']
df['dividend_yield'] = df['dividend'] / df['close']
# 合成价值因子:等权合成
df['pe_rank'] = df['pe'].rank(pct=True)
df['pb_rank'] = df['pb'].rank(pct=True)
df['dy_rank'] = df['dividend_yield'].rank(pct=True)
df['value_factor'] = (df['pe_rank'] + df['pb_rank'] + df['dy_rank']) / 3
return df
避坑指南:我曾经直接用原始PE值做因子,结果发现亏损公司的PE是负数,排名全乱了。一定要先做极值处理,把PE限制在[-100, 100]之间。
3.3.2 质量因子
质量因子,衡量的是公司的盈利能力、成长性和财务健康度。说白了,就是找好公司。
我常用的质量指标:
- ROE(净资产收益率):净利润 / 净资产。巴菲特最看重的指标。
- 毛利率:毛利 / 营收。反映公司的定价权。
- 资产负债率:总负债 / 总资产。越低越安全。
- 营收增长率:同比营收增速。反映成长性。
构建方式:
# 质量因子计算示例
def calc_quality_factors(df):
"""
计算质量因子
"""
df['roe'] = df['net_profit'] / df['equity']
df['gross_margin'] = df['gross_profit'] / df['revenue']
df['debt_ratio'] = df['total_liab'] / df['total_assets']
df['revenue_growth'] = df['revenue'] / df['revenue'].shift(4) - 1
# 注意:资产负债率是反向指标
df['debt_ratio_inv'] = 1 - df['debt_ratio']
# 合成质量因子
df['roe_rank'] = df['roe'].rank(pct=True)
df['gm_rank'] = df['gross_margin'].rank(pct=True)
df['dr_rank'] = df['debt_ratio_inv'].rank(pct=True)
df['rg_rank'] = df['revenue_growth'].rank(pct=True)
df['quality_factor'] = (df['roe_rank'] + df['gm_rank'] +
df['dr_rank'] + df['rg_rank']) / 4
return df
重要提醒:财务数据有滞后性。一季报通常在4月底才出完,你如果用3月底的数据做因子,用的其实是去年的年报。一定要做「时点对齐」,避免未来函数。
3.4 本章知识体系
下面这张图,是我梳理的本章知识结构。你可以把它当作一个速查表。
这张图把三大类因子和它们的子因子都列出来了。你写代码的时候,可以对照着看,不容易漏掉关键步骤。
最后说一句:因子构建不是一锤子买卖。我建议你每构建一个因子,都做一遍IC分析(信息系数),看看这个因子到底有没有预测能力。别等到策略亏钱了才回头检查因子。
公众号:蓝海资料掘金营,微信deep3321