
用 Python 揭秘均值回归策略:你的收益从何而来?
2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含 500 篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!
在量化交易的世界里,价格指标固然重要,但成交量往往被誉为"市场的真相"。今天给大家带来一篇来自 Bubble Analytics 的实战策略文章,作者将经典的 Williams %R 指标巧妙地"嫁接"到成交量数据上,构建出一套针对纳斯达克 100 指数 ETF(QQQ)的量化策略。
经过 10 年的历史数据回测,这套策略在 147 笔交易中取得了 347.6% 的累计收益,胜率高达 61.22%。下面我们就用 Python 一步步拆解这套策略的实现思路,文末附完整代码案例,适合正在学习 Python 量化的小伙伴动手实践。
这套策略的核心创意在于:把成交量当作"价格"来计算技术指标。
具体来说,作者将 Williams %R 振荡指标应用在成交量数据上,构建出一个新的指标 Vol %R,用于:
回测环境:
原始的成交量数据只是"成交股数",作者讨论了 3 种调整方法:
本策略选择第三种方式,即美元成交额(Dollar Volume)。
# 计算美元成交额:成交量 × 收盘价
df['Vol'] = df['Volume'] * df['Close']Williams %R 是一个经典的超买超卖指标,公式为:
(最高价 - 当前价) / (最高价 - 最低价) × 100
作者将公式中的"价格"替换为"美元成交额",并用 17 天回看周期,再用 3 日简单移动平均做平滑处理。
def Williams(data, n):
# 在 n 日窗口内取美元成交额的最高值
HiHi = df['Vol'].rolling(n).max()
# 在 n 日窗口内取美元成交额的最低值
LoLo = df['Vol'].rolling(n).min()
# 当前的美元成交额
Close = df['Vol']
# 套用 Williams %R 公式,将其映射到 0-100 区间
will = ((HiHi - Close) / (HiHi - LoLo)) * 100
# 使用 3 日均线进行平滑处理,减少噪音
will1 = will.rolling(3).mean()
return will1
# 调用函数,使用 17 日作为回看周期
df['williams'] = Williams(df, 17)传统 Williams %R 的买入信号是低于 20,但作者发现这种信号过于稀少。于是改用更高阈值的"穿越式"信号:
def signal(data):
# 初始化信号列表,长度与数据帧相同
signal = [0] * len(df)
# 从第 3 行开始遍历,确保有前一日数据可比
for i in range(2, len(df)):
# 买入条件:Vol %R 上穿 50
if (data.williams.iloc[i-1] < 50) and (data.williams.iloc[i] > 50):
signal[i] = 1
# 卖出条件:Vol %R 下穿 90
elif (data.williams.iloc[i-1] > 90) and (data.williams.iloc[i] < 90):
signal[i] = 2
# 其余情况不操作
else:
signal[i] = 0
df["signal"] = signal
# 应用信号函数
signal(df)作者使用 backtesting.py 库执行回测,核心规则如下:
# 定义信号读取函数,供策略类调用
def SIGNAL():
return df.signal
class MyStrat(Strategy):
def init(self):
super().init()
# 将信号注册为指标,方便在 next 中引用
self.signal = self.I(SIGNAL)
def next(self):
super().next()
# 获取当前收盘价作为参考价格
price = self.data.Close[-1]
# 信号为 1,开多仓
if self.signal == 1:
# 如果当前持有空仓或没有持仓,则先平后开
if self.position.is_short or not self.position:
self.position.close()
# 用 99% 资金买入,止盈位为 9%
self.buy(size=0.99, tp=1.09 * price)
# 信号为 2,开空仓
elif self.signal == 2:
# 如果当前持有多仓或没有持仓,则先平后开
if self.position.is_long or not self.position:
self.position.close()
# 用 99% 资金卖空,止盈 2%,止损 2%
self.sell(size=0.99, tp=0.98 * price, sl=1.02 * price)
# 运行回测:传入数据帧、策略类、初始资金等参数
bt = Backtest(df, MyStrat, cash=100_000, margin=1, exclusive_orders=True, commission=0.0005)
stats = bt.run()
print(stats)10 年回测的关键指标如下:
从数据来看,策略虽然跑赢了大多数主动交易方法,但未跑赢简单的买入持有策略(610.55%)。最大回撤接近 -28%,并且在 2018 年附近出现了近 2 年的横盘下行期,实盘体验会比较煎熬。
作者还提供了蒙特卡洛模拟,结果显示如果亏损交易集中在策略初期,账户在第 5 年仍可能处于水下状态。
下面这段代码整合了上述所有步骤,方便大家直接复制运行(需提前安装 yfinance 和 backtesting):
import yfinance as yf
import pandas as pd
from backtesting import Backtest, Strategy
# 步骤 1:下载 QQQ 的 10 年日线数据
df = yf.download('QQQ', period='10y', interval='1d')
df.dropna(inplace=True)
# 步骤 2:计算美元成交额
df['Vol'] = df['Volume'] * df['Close']
# 步骤 3:基于成交量计算 Williams %R 指标
def Williams(data, n):
HiHi = df['Vol'].rolling(n).max()
LoLo = df['Vol'].rolling(n).min()
Close = df['Vol']
will = ((HiHi - Close) / (HiHi - LoLo)) * 100
return will.rolling(3).mean()
df['williams'] = Williams(df, 17)
# 步骤 4:生成交易信号
def signal_fn(data):
sig = [0] * len(data)
for i in range(2, len(data)):
if (data.williams.iloc[i-1] < 50) and (data.williams.iloc[i] > 50):
sig[i] = 1
elif (data.williams.iloc[i-1] > 90) and (data.williams.iloc[i] < 90):
sig[i] = 2
data["signal"] = sig
signal_fn(df)
# 步骤 5:定义策略并运行回测
def SIGNAL():
return df.signal
class MyStrat(Strategy):
def init(self):
self.signal = self.I(SIGNAL)
def next(self):
price = self.data.Close[-1]
if self.signal == 1:
if self.position.is_short or not self.position:
self.position.close()
self.buy(size=0.99, tp=1.09 * price)
elif self.signal == 2:
if self.position.is_long or not self.position:
self.position.close()
self.sell(size=0.99, tp=0.98 * price, sl=1.02 * price)
bt = Backtest(df, MyStrat, cash=100_000, margin=1,
exclusive_orders=True, commission=0.0005)
print(bt.run())通过这篇文章,我们学到了几个非常实用的量化建模思路:
对于学习 Python 量化的同学来说,这是一个非常好的练手项目,建议大家:
量化交易没有"圣杯",但每一次拆解、复现、改进都会让你离市场更近一步。
2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:
星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!
好文推荐
1. 用 Python 打造股票预测系统:Transformer 模型教程(一)
2. 用 Python 打造股票预测系统:Transformer 模型教程(二)
3. 用 Python 打造股票预测系统:Transformer 模型教程(三)
4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)
6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用
9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解
好书推荐