因子选股策略
因子选股是量化投资中最主流的策略类型之一,通过多个量化指标(因子)对股票进行评分和排序,选择综合得分最高的股票。
什么是因子?
因子就是能够解释股票收益差异的某种特征变量。例如:
| 因子类别 | 因子名称 | 计算方式 | 逻辑 |
|---|---|---|---|
| 动量因子 | 过去N日收益率 | close.pct_change(N) | 强者恒强 |
| 波动率因子 | 收益率标准差 | returns.rolling(N).std() | 低波动反而长期收益高 |
| 价值因子 | 市盈率 PE | 股价 / 每股收益 | 低估值股票可能被低估 |
| 质量因子 | ROE | 净利润 / 净资产 | 盈利好的公司长期更优 |
| 规模因子 | 市值 | 股价 × 总股本 | 小市值股票长期收益更高 |
| 流动性因子 | 换手率 | 成交量 / 流通股本 | 活跃的股票可能更好 |
单因子测试
import pandas as pd
import numpy as np
def momentum_factor(df, period=20):
"""
动量因子:过去 N 天的累计收益率
"""
return df['close'].pct_change(period)
def volatility_factor(df, period=20):
"""
波动率因子:过去 N 天的收益率标准差
"""
returns = df['close'].pct_change()
return returns.rolling(period).std()
def volume_factor(df, period=20):
"""
成交量因子:过去 N 天的平均成交量变化
"""
return df['volume'].pct_change(period)
多因子打分模型
def multi_factor_score(df):
"""
多因子打分模型
计算多个因子,然后加权合成一个综合得分。
得分越高 = 股票越好
"""
# 1. 计算各因子
df['momentum'] = df['close'].pct_change(20) # 动量因子
df['volatility'] = df['close'].pct_change().rolling(20).std() # 波动率因子
# 2. 因子标准化(排名法)
# 对每个因子进行排名,排名越靠前分数越高
df['momentum_rank'] = df['momentum'].rank(pct=True)
df['volatility_rank'] = (1 - df['volatility']).rank(pct=True) # 低波动更好
# 3. 合成综合得分(等权或自定义权重)
df['score'] = (df['momentum_rank'] * 0.5 +
df['volatility_rank'] * 0.5)
return df
因子选股 + 择时框架
def factor_stock_selection(stock_pool, date, top_n=10):
"""
在每个调仓日,对所有候选股票打分,选择前 N 只
参数:
stock_pool: dict {code: df} 候选股票池
date: 调仓日期
top_n: 选择前多少只
"""
scores = {}
for code, df in stock_pool.items():
if date not in df.index:
continue
# 计算该股票的综合得分
df_scored = multi_factor_score(df)
score = df_scored.loc[date, 'score'] if date in df_scored.index else np.nan
scores[code] = score
# 按得分排序,选前 N 只
scores_series = pd.Series(scores).dropna().sort_values(ascending=False)
selected = scores_series.head(top_n)
return selected
组合回测
def factor_strategy_backtest(stock_pool, rebalance_freq='M', top_n=10,
initial_capital=100000):
"""
因子选股策略回测
参数:
stock_pool: 股票池
rebalance_freq: 调仓频率('M'=每月, 'W'=每周)
top_n: 持仓股票数
"""
# 获取所有调仓日期
first_date = min(df.index[0] for df in stock_pool.values())
last_date = max(df.index[-1] for df in stock_pool.values())
rebalance_dates = pd.date_range(first_date, last_date, freq=rebalance_freq)
portfolio_values = []
current_holdings = {}
capital = initial_capital
for date in rebalance_dates:
# 卖出旧持仓
for code, shares in current_holdings.items():
if date in stock_pool[code].index:
price = stock_pool[code].loc[date, 'close']
capital += shares * price
current_holdings = {}
# 选股
selected = factor_stock_selection(stock_pool, date, top_n)
# 等权买入
per_stock_capital = capital / len(selected)
for code in selected.index:
price = stock_pool[code].loc[date, 'close']
shares = per_stock_capital // price
current_holdings[code] = shares
capital -= shares * price
return portfolio_values
常见因子库总结
| 因子 | 方向 | A 股有效性 |
|---|---|---|
| 动量 | 正向 | ⭐⭐⭐ |
| 低波动 | 低波优先 | ⭐⭐⭐⭐ |
| 小市值 | 小盘优先 | ⭐⭐⭐⭐ |
| 价值(低PE) | 低PE优先 | ⭐⭐⭐ |
| 质量(高ROE) | 高ROE优先 | ⭐⭐⭐⭐ |
| 换手率 | 适度活跃 | ⭐⭐⭐ |
| 分析师预期 | 上调优先 | ⭐⭐⭐⭐ |
| 股东增持 | 增持优先 | ⭐⭐⭐ |
下一步:均值回归策略 →