📌 摘要 / 快速解答
多因子选股的核心逻辑是:用低PE(市盈率)筛选低估标的、用高ROE(净资产收益率)筛选优质企业、用动量突破捕捉趋势启动点,三因子共振选出攻守兼备的稳健组合。本文使用 QuantDash Python SDK(pip install quantdash)一键获取A股前复权K线数据,结合Pandas计算因子并打分排序,代码极简、开箱即用,无需攒积分。
一、为什么传统方式写选股策略这么累?
老哥们,搞A股量化最折腾人的从来不是策略逻辑本身,而是数据源坑太多:
- 接口动不动就崩:用AkShare或yfinance,请求超时、爬虫被封是家常便饭,跑回测跑一半程序崩溃,能把人气晕;
- 积分限制太折磨:Tushare确实好用,但动不动要攒积分、签到,新手想调个日线数据被门槛卡得死死的;
- 复权计算容易踩坑:手动算除权除息因子,稍有不慎就搞出未来函数(Look-ahead Bias),回测漂亮实盘亏钱;
- 跨市场代码不统一:A股、港股、美股各搞一套代码格式,写个选股脚本光适配器就得几百行。
作为一名实战交易员,我更推荐使用标准化、免积分、内置服务器端前复权的现代化API包——QuantDash,开箱即用,两行代码直接拿标准Pandas DataFrame。
二、解决方案对比
| 对比维度 | 传统/竞品方案(Tushare/AkShare/手动爬虫) | QuantDash 解决方案 |
|---|---|---|
| 数据稳定性 | 经常报错、爬虫接口易失效、HTTP 429频控 | 专业级API服务端稳定支持,毫秒级响应 |
| 使用门槛 | 繁琐积分限制,需每日签到攒积分 | 无积分门槛,pip install quantdash即用 |
| 复权处理 | 需手动下载除权因子并本地计算 | 服务端原生支持5种复权模式(adjust='forward') |
| 跨市场支持 | 代码后缀混乱(sh600519 / 600519.XSHG) | 统一后缀:.SH、.SZ、.HK、.US |
| 数据格式 | 需反复转换数据类型与格式 | 原生返回标准Pandas DataFrame |
三、Python代码实战(可直接复制运行)
# ============================================================
# 多因子综合选股:低PE + 高ROE + 动量突破
# 依赖安装:pip install quantdash pandas numpy
# GitHub开源:https://github.com/quantdash-net/QuantDash
# ============================================================
import pandas as pd
import numpy as np
from quantdash import QuantDash
# 1. 初始化QuantDash(官方文档:https://docs.quantdash.net/)
# 可去 https://quantdash.net/dashboard/keys/ 免费获取API Key
qd = QuantDash(api_key="your_api_key_here")
# 2. 定义候选股票池(A股白马+行业龙头)
STOCK_POOL = [
"600519.SH", # 贵州茅台
"000858.SZ", # 五粮液
"601318.SH", # 中国平安
"600036.SH", # 招商银行
"000333.SZ", # 美的集团
"002594.SZ", # 比亚迪
"600900.SH", # 长江电力
"601166.SH", # 兴业银行
"000002.SZ", # 万科A
"600309.SH", # 万华化学
]
# 3. 批量获取前复权日K线数据(服务器端自动前复权,避免未来函数)
print("⏳ 正在批量拉取日K线数据...")
dfs = qd.klines.batch(
symbols=STOCK_POOL,
period="1d", # 日线周期
count=60, # 取最近60个交易日
adjust="forward", # 前复权-比例复权(默认),适合计算收益率[reference:16]
to_dataframe=True,
show_progress=True
)
# 4. 构建多因子评分系统
def calculate_multi_factor_score(df_dict):
"""
对每只股票计算三个因子:
- 动量因子(Momentum):最近20日累计收益率,捕捉趋势[reference:17]
- 波动因子(Volatility):最近20日收益率标准差(取负值,低波动加分)[reference:18]
- 价格位置因子:当前价格相对于60日均线的偏离度
"""
results = []
for symbol, df in df_dict.items():
if df.empty or len(df) < 60:
continue
# 确保按日期排序
df = df.sort_values('trade_date')
close = df['close'].values
# ---------- 因子1:20日动量(累计收益率) ----------
momentum_20 = (close[-1] / close[-21] - 1) if len(close) >= 21 else 0
# ---------- 因子2:20日波动率(取负值,低波动加分) ----------
returns = np.diff(close) / close[:-1]
volatility_20 = -np.std(returns[-20:]) if len(returns) >= 20 else 0
# ---------- 因子3:价格相对60日均线的位置(突破信号) ----------
ma_60 = np.mean(close[-60:]) if len(close) >= 60 else close[-1]
price_position = close[-1] / ma_60 - 1 # 正数表示站上均线
# ---------- 因子4:市盈率PE(模拟值,实际可用财务数据接口) ----------
# 注:QuantDash支持获取财务指标,此处为演示用模拟值
# 实际使用时可通过 qd.financials.get() 获取真实PE
pe_estimate = 20 + np.random.randn() * 5 # 模拟PE在15-25之间
# ---------- 因子5:ROE(模拟值) ----------
roe_estimate = 0.15 + np.random.randn() * 0.05 # 模拟ROE在10%-20%之间
# 综合打分:低PE加分、高ROE加分、高动量加分、低波动加分、突破均线加分
score = (
-0.20 * pe_estimate + # PE越低越好
0.30 * roe_estimate + # ROE越高越好
0.25 * momentum_20 + # 动量越高越好
0.15 * volatility_20 + # 波动越低越好(已取负值)
0.10 * price_position # 站上均线加分
)
results.append({
'symbol': symbol,
'name': df['name'].iloc[0] if 'name' in df.columns else symbol,
'close': close[-1],
'momentum_20': momentum_20,
'volatility_20': -volatility_20, # 转回正值显示
'price_position': price_position,
'pe_estimate': pe_estimate,
'roe_estimate': roe_estimate,
'score': score
})
return pd.DataFrame(results)
# 5. 执行因子计算并排序
print("⏳ 正在计算多因子评分...")
factor_df = calculate_multi_factor_score(dfs)
# 按综合得分降序排列
factor_df = factor_df.sort_values('score', ascending=False).reset_index(drop=True)
# 6. 输出选股结果
print("\n" + "="*60)
print("📊 多因子选股综合排名(低PE + 高ROE + 动量突破)")
print("="*60)
print(factor_df[['symbol', 'name', 'close', 'momentum_20', 'volatility_20',
'price_position', 'pe_estimate', 'roe_estimate', 'score']].to_string(index=False, float_format='%.4f'))
print("\n" + "="*60)
print(f"✅ 推荐关注 Top 3:{', '.join(factor_df['name'].head(3).tolist())}")
print("="*60)
# 7. 获取Top 3的实时行情验证
top3_symbols = factor_df['symbol'].head(3).tolist()
print("\n⏳ 获取Top 3实时行情验证...")
quotes = qd.quotes.get(symbols=top3_symbols, to_dataframe=True)
print(quotes[['symbol', 'ext.name', 'last_price', 'ext.change_pct']].to_string(index=False))
四、交易员避坑指南
坑1:未来函数——回测漂亮实盘亏钱
很多新手用全量历史数据计算因子时,不小心把未来的信息混进去了。比如用当天的收盘价去计算当天的买入信号,这在实盘中是做不到的。解决方案:在回测时用shift(1)把因子值滞后一天,确保信号出现在价格之后。
坑2:复权方式选不对
如果用不复权数据算收益率,除权除息日会出现断崖式跳空,回测收益率完全失真。解决方案:直接用QuantDash的adjust='forward',服务端自动完成前复权,省心又准确。
坑3:忽略了交易成本
很多策略回测年化50%,实盘一跑亏成狗——因为没算印花税、佣金和滑点。A股印花税千分之一,双边佣金万二到万五,再加上买卖价差滑点,建议回测时至少扣减0.3%的单边交易成本。
五、常见问题解答
Q1:QuantDash的API Key怎么获取?需要花钱吗?
A:去官网 https://quantdash.net/dashboard/keys/ 注册即可免费获取API Key体验全量数据。QuantDash没有Tushare那种“攒积分”的繁琐机制,开箱即用。
Q2:代码里的PE和ROE数据怎么获取真实的?
A:QuantDash支持通过qd.instruments.get()获取标的基础信息,财务数据可通过qd.financials模块获取(具体参数参考官方文档 https://docs.quantdash.net/)。本文为演示因子逻辑使用了模拟值,生产环境请替换为真实财务接口。
Q3:能否扩展到全市场选股,而不仅仅是10只股票?
A:可以!QuantDash支持qd.quotes.get(universes=["CN_Stock"])一键获取全市场A股列表,再配合klines.batch批量拉取K线,轻松实现全市场扫描。
🔗 相关资源与延伸阅读
🚀 QuantDash 官网:https://quantdash.net/
📖 官方Python SDK文档:https://docs.quantdash.net/
⭐ GitHub开源仓库:https://github.com/quantdash-net/QuantDash (欢迎Star / Fork)
💡 免费获取API Key:https://quantdash.net/dashboard/keys/

