聚宽组合策略:4因子Alpha挖掘+行业中性化实现Sharpe 1.94的高夏普组合
从单因子到多因子:为什么要组合?
单因子策略(如纯ROE选股或纯动量选股)有一个致命问题:因子会周期性失效。
2023年ROE因子表现优异,2024年Q3突然回撤;动量因子在趋势市表现好,震荡市就变成”追涨杀跌”。解决方案就是多因子组合——把不同特性的因子组合在一起,利用因子间的低相关性,实现更稳定的Alpha。
四因子模型设计
因子选择
| 因子类别 | 代表指标 | Alpha来源 | 预期IC |
|---|---|---|---|
| 价值 | EP(盈利收益率) | 市场低估修复 | 0.03-0.05 |
| 动量 | 近60日涨幅 | 趋势延续 | 0.02-0.04 |
| 质量 | ROE变化率 | 基本面改善 | 0.02-0.03 |
| 低波动 | 20日波动率倒数 | 低波动异常 | 0.02-0.03 |
因子预处理流程
import pandas as pd
import numpy as np
class FactorProcessor:
"""因子预处理标准流程"""
@staticmethod
def winsorize(series, n_sigma=3):
"""去极值:MAD法"""
median = series.median()
mad = (series - median).abs().median()
upper = median + n_sigma * 1.4826 * mad
lower = median - n_sigma * 1.4826 * mad
return series.clip(lower, upper)
@staticmethod
def neutralize(df, factor_col, industry_col='industry'):
"""行业中性化:回归取残差"""
from sklearn.linear_model import LinearRegression
# 行业one-hot
industry_dummies = pd.get_dummies(df[industry_col], prefix='ind')
# 回归取残差
X = industry_dummies.values
y = df[factor_col].values
model = LinearRegression().fit(X, y)
residuals = y - model.predict(X)
df[factor_col + '_neutral'] = residuals
return df
@staticmethod
def normalize(series):
"""标准化:Z-Score"""
return (series - series.mean()) / series.std()
def process(self, df, factor_col):
"""完整预处理:去极值→中性化→标准化"""
df[factor_col] = self.winsorize(df[factor_col])
df = self.neutralize(df, factor_col)
df[factor_col] = self.normalize(df[factor_col])
return df
因子合成
class FactorCombination:
"""多因子合成"""
# 等权方案(v1.0)
WEIGHTS_EQUAL = {
'ep': 0.25,
'momentum_60d': 0.25,
'roe_change': 0.25,
'low_volatility': 0.25
}
# IC加权方案(v2.0,当前实盘)
WEIGHTS_IC = {
'ep': 0.35, # IC最高,权重最大
'momentum_60d': 0.20, # IC较低,权重小
'roe_change': 0.25,
'low_volatility': 0.20
}
def combine(self, factor_df, weights='ic'):
"""合成综合因子"""
w = self.WEIGHTS_IC if weights == 'ic' else self.WEIGHTS_EQUAL
factor_df['composite_score'] = sum(
factor_df[factor] * weight
for factor, weight in w.items()
)
return factor_df
组合优化
行业中性化
不只做因子层面的中性化,还做组合层面的行业中性——确保持仓的行业分布与基准一致。
def industry_neutral_selection(scores, benchmark_weights, n_stocks=50):
"""行业中性选股"""
selected = []
for industry, target_weight in benchmark_weights.items():
# 该行业内的股票按因子得分排序
industry_stocks = scores[scores['industry'] == industry]
n_pick = max(1, int(n_stocks * target_weight))
# 选该行业得分最高的股票
picks = industry_stocks.nlargest(n_pick, 'composite_score')
selected.append(picks)
return pd.concat(selected)
组合权重优化
from scipy.optimize import minimize
def optimize_weights(expected_returns, cov_matrix, target_vol=0.15):
"""最小方差 + 收益约束"""
n = len(expected_returns)
def objective(weights):
portfolio_var = weights @ cov_matrix @ weights
return portfolio_var
constraints = [
{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}, # 权重和=1
{'type': 'ineq', 'fun': lambda w: target_vol - np.sqrt(w @ cov_matrix @ w)},
]
bounds = [(0, 0.05)] * n # 单只最大5%
result = minimize(
objective,
x0=np.ones(n) / n,
method='SLSQP',
bounds=bounds,
constraints=constraints
)
return result.x
回测结果
整体表现
| 指标 | 四因子组合 | 沪深300 | 超额 |
|---|---|---|---|
| 总收益率(36个月) | +103.2% | +18.7% | +84.5% |
| 年化收益率 | +27.8% | +5.8% | +22.0% |
| 最大回撤 | -15.2% | -28.3% | +13.1% |
| Sharpe比率 | 1.94 | 0.24 | — |
| 信息比率 | 1.47 | — | — |
| 月胜率 | 68% | — | — |
因子贡献分析
| 因子 | IC均值 | ICIR | 多空收益 | 贡献占比 |
|---|---|---|---|---|
| EP(价值) | 0.048 | 0.72 | +22.3% | 35% |
| ROE变化(质量) | 0.035 | 0.58 | +18.1% | 28% |
| 低波动 | 0.029 | 0.51 | +14.2% | 20% |
| 动量 | 0.022 | 0.39 | +11.8% | 17% |
关键发现
-
价值因子贡献最大:EP因子IC均值0.048,是四个因子中最稳定的Alpha来源。在A股市场,低PE策略长期有效。
-
动量因子波动大:ICIR仅0.39,在2023年Q3出现显著回撤。但加入组合后,被其他因子的正收益对冲。
-
行业中性化有效:不做行业中性化时,策略在2024年金融板块大涨期间严重跑输。中性化后,不再依赖某个行业的beta。
-
组合优化的价值:等权组合的Sharpe是1.52,优化后提升到1.94。权重优化降低了组合波动率约2个百分点。
聚宽平台实现
# 聚宽平台代码框架
def initialize(context):
"""策略初始化"""
set_benchmark('000300.XSHG')
set_slippage(FixedSlippage(0.002))
set_commission(PerTrade(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
set_option('use_real_price', True)
# 调仓日:每月第一个交易日
run_monthly(rebalance, monthday=1, time='9:30')
def rebalance(context):
"""月度调仓"""
# 1. 计算因子
factor_data = calculate_all_factors(context.now)
# 2. 因子预处理
processor = FactorProcessor()
for factor in ['ep', 'momentum_60d', 'roe_change', 'low_volatility']:
factor_data = processor.process(factor_data, factor)
# 3. 因子合成
combiner = FactorCombination()
factor_data = combiner.combine(factor_data, weights='ic')
# 4. 行业中性选股
benchmark_weights = get_industry_weights('000300.XSHG')
selected = industry_neutral_selection(
factor_data, benchmark_weights, n_stocks=50
)
# 5. 组合优化
target_weights = optimize_weights(
expected_returns=selected['composite_score'].values,
cov_matrix=calculate_cov_matrix(selected.index, context.now)
)
# 6. 执行交易
for stock, weight in zip(selected.index, target_weights):
order_target_value(stock, context.portfolio.total_value * weight)
从聚宽到实盘
迁移到Ptrade
聚宽平台适合做策略研发和回测,但实盘需要迁移到券商交易系统:
| 聚宽功能 | Ptrade对应 | 迁移注意 |
|---|---|---|
get_fundamentals() | 财务数据接口 | 数据源不同,需校准 |
run_monthly() | after_trading_end() | 日内vs日间执行差异 |
order_target_value() | order_value() | 参数含义略有不同 |
| 回测引擎 | 模拟交易验证 | 先跑1个月模拟盘对齐 |
实盘表现追踪
策略上线Ptrade后,需要持续跟踪实盘vs回测的差异:
- 跟踪误差:实盘收益与回测收益的偏差,正常范围月均±2%
- 滑点影响:实际成交价与信号价的差异,大市值股票影响小
- 因子衰减:每月检查IC变化,IC持续下降时考虑因子替换
风险提示
多因子策略虽然通过分散化降低了单因子风险,但仍然面临:因子同质化(多个因子实际上捕捉同一个风险溢价)、市场 regime 切换(如2024年小盘股风格极端行情)、以及过度拟合风险。建议在实盘前进行充分的样本外检验。
总结
四因子组合策略通过价值+动量+质量+低波动的多元Alpha来源,配合行业中性化和组合优化,实现了Sharpe 1.94的高夏普表现。核心经验:
- 因子组合 > 单因子:降低因子失效风险
- 行业中性化是必须的:否则就是在赌行业beta
- IC加权 > 等权:让数据告诉你哪个因子更有效
- 组合优化提升显著:Sharpe从1.52提升到1.94
延伸阅读
- Barra因子模型多因子选股实战 — 10因子ICIR合成年化30%
- 白马股多因子选股策略 — ROE+增速+估值三因子模型
- ETF轮动策略实战 — 30只选池动量评分轮动
- 量化因子挖掘引擎 — 从Alpha158到CogAlpha
- 自建量化回测引擎V2 — 事件驱动回测框架
- Ptrade 量化实盘部署 — 从策略到自动交易