白马股多因子选股策略:ROE+增速+估值三因子模型年化+18.3%回测实录
什么是白马股?量化的定义
传统白马股投资靠主观判断:“好行业、好公司、好价格”。但量化交易需要把这三个”好”变成可计算的指标。
白马股的量化定义:
- 好公司 = ROE持续高于15%(盈利能力强)
- 好成长 = 营收增速连续两年为正(不是收缩型企业)
- 好价格 = PE低于行业中位数(有安全边际)
满足以上三个条件的股票,就是量化意义上的”白马股”。
策略框架
选股逻辑
每月月初执行以下筛选:
- ROE筛选:最近三年ROE均>15%
- 增速筛选:最近两年营收同比增速>0
- 估值筛选:当前PE<所属行业PE中位数
- 流动性筛选:日均成交额>5000万
- 排序规则:按(ROE排名 + 增速排名 + PE倒数排名)加权评分
取综合评分前20只股票,等权持仓。
调仓规则
- 调仓频率:月度(每月第一个交易日)
- 持仓数量:20只
- 加权方式:等权
- 止损:个股止损-15%,组合止损-10%
Python实现核心代码
因子计算
import pandas as pd
import numpy as np
import duckdb
# 从DuckDB读取财务数据
con = duckdb.connect('/path/to/quant.db', read_only=True)
def calculate_white_horse_factors(date):
"""计算白马股三因子"""
# ROE因子:最近三年ROE
roe_sql = """
SELECT ts_code,
AVG(roe) as avg_roe,
MIN(roe) as min_roe
FROM stock_financials
WHERE end_date >= date - INTERVAL '3 years'
GROUP BY ts_code
HAVING MIN(roe) > 15.0
"""
# 增速因子:最近两年营收增速
growth_sql = """
SELECT ts_code,
AVG(revenue_yoy) as avg_growth
FROM stock_financials
WHERE end_date >= date - INTERVAL '2 years'
GROUP BY ts_code
HAVING MIN(revenue_yoy) > 0
"""
# 估值因子:PE排名
pe_sql = """
SELECT a.ts_code, a.pe, a.industry,
b.median_pe
FROM stock_daily a
JOIN (
SELECT industry, PERCENTILE_CONT(0.5)
WITHIN GROUP (ORDER BY pe) as median_pe
FROM stock_daily
WHERE trade_date = date AND pe > 0
GROUP BY industry
) b ON a.industry = b.industry
WHERE a.trade_date = date AND a.pe > 0
AND a.pe < b.median_pe
"""
roe_df = con.execute(roe_sql).fetchdf()
growth_df = con.execute(growth_sql).fetchdf()
pe_df = con.execute(pe_sql).fetchdf()
# 三因子合并
df = roe_df.merge(growth_df, on='ts_code')
df = df.merge(pe_df, on='ts_code')
# 综合评分
df['roe_rank'] = df['avg_roe'].rank(pct=True)
df['growth_rank'] = df['avg_growth'].rank(pct=True)
df['pe_rank'] = (1 / df['pe']).rank(pct=True)
df['score'] = df['roe_rank'] * 0.4 + df['growth_rank'] * 0.3 + df['pe_rank'] * 0.3
return df.nlargest(20, 'score')['ts_code'].tolist()
回测引擎
def backtest_white_horse(start='2022-01-01', end='2026-06-01'):
"""白马股策略回测"""
rebalance_dates = get_rebalance_dates(start, end, freq='M')
portfolio_returns = []
for i in range(len(rebalance_dates) - 1):
current_date = rebalance_dates[i]
next_date = rebalance_dates[i + 1]
# 选股
holdings = calculate_white_horse_factors(current_date)
# 计算持仓期收益
period_return = calculate_portfolio_return(
holdings, current_date, next_date
)
portfolio_returns.append({
'date': next_date,
'return': period_return,
'holdings': holdings
})
return pd.DataFrame(portfolio_returns)
回测结果
整体表现
| 指标 | 白马股策略 | 沪深300基准 |
|---|---|---|
| 总收益率(48个月) | +124.6% | +28.3% |
| 年化收益率 | +18.3% | +5.2% |
| 最大回撤 | -21.5% | -32.1% |
| Sharpe比率 | 0.92 | 0.31 |
| 月均换手率 | 38% | — |
| 平均持仓数 | 18.7只 | — |
关键发现
-
2022年熊市扛住了:最大回撤-21.5%,而沪深300回撤-32.1%。低PE筛选在熊市提供了安全边际。
-
2023年表现最佳:全年收益+47.2%,ROE因子在结构性行情中贡献最大。
-
2025年出现回撤:市场风格切换导致白马股跑输,但很快恢复。
Ptrade实盘部署
def white_horse_strategy(context):
"""Ptrade白马股策略 - 月度调仓"""
if not is_first_trading_day_of_month(context.now):
return
# 获取当前持仓
current_positions = get_positions()
# 计算目标持仓
target_stocks = calculate_white_horse_factors(context.now)
# 先卖出不在目标池的股票
for stock in current_positions:
if stock not in target_stocks:
order_target(stock, 0)
# 等权买入目标股票
weight = 1.0 / len(target_stocks)
for stock in target_stocks:
order_target_weight(stock, weight)
log.info(f"白马股调仓完成,持仓{len(target_stocks)}只")
策略优化方向
v2.3版本改进(当前实盘版本)
- 加入动量过滤:剔除近20日跌幅>10%的股票,避免”接飞刀”
- 行业中性化:每个行业最多持仓3只,分散行业集中风险
- 交易成本优化:佣金万0.5,月均换手率从45%降到38%
后续计划
- 加入Barra风险因子暴露控制
- 尝试基本面动量(SUE)替代价格动量
- 探索双周调仓频率对收益的影响
风险提示
本策略基于历史数据回测,过往表现不代表未来收益。白马股策略在风格切换期间(如2025年Q3)可能显著跑输市场。实盘交易需考虑滑点、冲击成本和流动性约束。
总结
白马股多因子策略用最朴素的三个因子(ROE+增速+PE),在48个月回测中年化+18.3%,跑赢沪深300超过13个百分点。策略逻辑清晰、可解释性强,适合作为量化入门的第一个选股策略部署到实盘。