
用 Python 揭秘均值回归策略:你的收益从何而来?
量化交易的生死线:用 Python 算出你的最优下注比例
2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含 500 篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!
华尔街的量化团队都信奉同一条铁律:不要押注单一信号,而是把多个「弱信号」融合成一个「强信号」。这正是因子投资(Factor Investing)的核心思想,也是多因子模型能够长期跑赢单指标选股的根本原因。
好消息是,搭建这样一个模型并不需要昂贵的金融终端或天价数据预算。你只需要一台笔记本电脑、一点 pandas 基础,再加上一份干净的价格与基本面数据,就能动手了。这篇文章会带你用 Python 从零构建一个真正可用的多因子排名模型,选用 价值、质量、动量 三大因子,对一篮子股票打分并排名。
学完这篇,你不仅能得到一个可运行的选股模型,还能理解「因子标准化 → 加权合成 → 排序」这套量化选股的通用范式。
如果你只用市盈率(P/E)来排序股票,你大概率会买入一堆「便宜得有理由」的公司:利润率下滑、债务高企、商业模式正在消亡。如果你只用动量来排序,你又很可能在股价见顶回落前一刻追进去。
问题的本质在于:几乎所有单一因子,都存在长时间「失效」的阶段。
解决办法是组合那些彼此相关性不高的因子。一个经典的三因子搭配如下:
把这三者合成为一个综合评分(Composite Score),再对整个股票池排序,得到的名单往往比任何单一因子都更稳健。因为价值、质量、动量通常在不同的市场阶段失效,把它们组合起来,恰恰能让整体表现更平滑。
我们要构建的模型会依次完成:
下面逐步拆解。
首先导入常用库。这里用公开免费的数据接口来获取行情与基本面(例如 yfinance),无需付费即可上手:
import pandas as pd
import numpy as np
import yfinance as yf
# 定义股票池:这里选取跨行业的大盘股作为示例
# 生产环境中可替换为某指数成分股或按行业筛选的动态列表
universe = [
"AAPL", "MSFT", "GOOGL", "AMZN", "NVDA", "META", "TSLA",
"JPM", "V", "UNH", "HD", "PG", "XOM", "CVX", "KO",
"PEP", "COST", "AVGO", "ADBE", "CRM",
]小贴士:股票池从哪来,模型并不关心。你完全可以把它换成某个指数(如标普 500)的成分股,或按市值、行业动态筛选,后续流程一行都不用改。
价值因子回答一个朴素的问题:为每一块钱的盈利和账面价值,你付出的价格合理吗?我们取 市盈率(P/E)和市净率(P/B):
def fetch_value(ticker):
"""获取单只股票的市盈率与市净率"""
info = yf.Ticker(ticker).info
return {
"ticker": ticker,
"pe": info.get("trailingPE"), # 滚动市盈率
"pb": info.get("priceToBook"), # 市净率
}
# 遍历股票池,汇总为 DataFrame
value_df = pd.DataFrame([fetch_value(t) for t in universe])对于价值因子,P/E 和 P/B 越低越好,所以后面在计算 z-score 时要「翻转符号」。
质量因子考察生意本身是否稳健。我们使用 股本回报率(ROE)和负债权益比(D/E):
def fetch_quality(ticker):
"""获取单只股票的 ROE 与负债权益比"""
info = yf.Ticker(ticker).info
return {
"ticker": ticker,
"roe": info.get("returnOnEquity"), # 股本回报率,越高越好
"debt_to_equity": info.get("debtToEquity"), # 负债权益比,越低越好
}
quality_df = pd.DataFrame([fetch_quality(t) for t in universe])规则很直观:ROE 越高越好,负债权益比越低越好,符号处理同样留到 z-score 阶段。
动量因子需要价格历史。我们抓取近一年的日收盘价,同时计算 6 个月和 12 个月两个时间窗口的收益率,混合不同周期有助于平滑短期噪声:
def fetch_momentum(ticker):
"""计算 6 个月与 12 个月动量"""
# 下载近一年日线数据
df = yf.download(ticker, period="1y", progress=False)
close = df["Close"].dropna()
# 12 个月收益率:用最新价 / 一年前的价格 - 1
ret_12m = close.iloc[-1] / close.iloc[0] - 1
# 6 个月收益率:约 126 个交易日
# 数据不足时置为缺失值,避免报错
ret_6m = (close.iloc[-1] / close.iloc[-126] - 1
if len(close) > 126 else np.nan)
return {
"ticker": ticker,
"momentum_6m": float(ret_6m),
"momentum_12m": float(ret_12m),
}
momentum_df = pd.DataFrame([fetch_momentum(t) for t in universe])易错点:这里用「126 个交易日」近似半年,而不是自然日的 180 天。一年约有 252 个交易日,半年因此约为 126 天,直接按自然日切片会取到错误的位置。
原始的 P/E 和 原始的 ROE 处在完全不同的量纲上,直接相加毫无意义。因此在合成之前,必须先把每个因子标准化为 z-score。
z-score 衡量的是「某只股票距离全池均值有多少个标准差」,其定义为:
其中 是该因子在整个股票池中的均值, 是标准差。
# 合并三张因子表
factors_df = (value_df
.merge(quality_df, on="ticker")
.merge(momentum_df, on="ticker"))
def zscore(series):
"""标准化:减去均值再除以标准差"""
return (series - series.mean()) / series.std()
# 价值:P/E、P/B 越低越好,因此取负号翻转
factors_df["value_score"] = -zscore(factors_df["pe"]) - zscore(factors_df["pb"])
# 质量:ROE 越高越好(正号),负债权益比越低越好(负号)
factors_df["quality_score"] = zscore(factors_df["roe"]) - zscore(factors_df["debt_to_equity"])
# 动量:两个周期收益率都是越高越好
factors_df["momentum_score"] = zscore(factors_df["momentum_6m"]) + zscore(factors_df["momentum_12m"])
# 加权合成综合评分:价值 40%、质量 30%、动量 30%
factors_df["composite_score"] = (
0.40 * factors_df["value_score"]
+ 0.30 * factors_df["quality_score"]
+ 0.30 * factors_df["momentum_score"]
)
# 按综合评分从高到低排序
ranked = factors_df.sort_values("composite_score", ascending=False).reset_index(drop=True)
print(ranked[["ticker", "value_score", "quality_score",
"momentum_score", "composite_score"]])关于权重:上面「价值 40%、质量 30%、动量 30%」只是一个起点。你完全可以偏向自己最有把握的因子,或者用历史数据回测几组不同权重,看看哪种组合在你的股票池上表现最好。
一张排序表已经很有用,但图表能让差距一目了然。用一个简单的水平条形图展示综合评分最高的前 10 只股票:
import matplotlib.pyplot as plt
top10 = ranked.head(10)
plt.figure(figsize=(8, 5))
plt.barh(top10["ticker"], top10["composite_score"])
plt.gca().invert_yaxis() # 让评分最高的排在最上方
plt.xlabel("综合评分")
plt.title("多因子综合评分 Top 10")
plt.tight_layout()
plt.show()你还可以用雷达图(Radar Chart)拆解某一只股票在三个因子上的表现,直观判断它是「全面开花」,还是仅靠单一因子拉高了排名——这对于避免踩坑很有帮助。
这个三因子模型刻意做得简单,而这正是「第一版」应有的样子。跑通之后,还有大量可扩展空间:
多因子排名之所以是量化金融中最可靠的思路之一,恰恰在于它不依赖任何单一信号「永远正确」。价值、质量、动量往往在不同时刻失效,把它们组合起来,正好能让整体收益曲线更平滑。
回顾整套方法论,核心只有三步:
我们在这里搭建的只是一个起点,而非成品。换上你自己的股票池、调整因子权重、加入情绪或 ESG 数据,再去回测历史表现——把这套「因子标准化 → 合成 → 排序」的范式吃透,你就掌握了量化选股最通用的一块基石。
2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:
星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!
好文推荐
1. 用 Python 打造股票预测系统:Transformer 模型教程(一)
2. 用 Python 打造股票预测系统:Transformer 模型教程(二)
3. 用 Python 打造股票预测系统:Transformer 模型教程(三)
4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)
6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用
9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解
好书推荐