当前位置:首页>python>不追价格追波动:用 Python 实现「波动率动量」交易策略

不追价格追波动:用 Python 实现「波动率动量」交易策略

  • 2026-10-11 08:17:18
不追价格追波动:用 Python 实现「波动率动量」交易策略

用 Python 揭秘均值回归策略:你的收益从何而来?

2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含 350 篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!

引言

大多数交易系统都聚焦于价格动量:价格上涨时买入,价格下跌时卖空。但今天我们要分享一个有点「反直觉」的思路 —— 不盯价格的方向,而是盯波动率本身的加速度。

核心假设非常简单:

  • • 当波动率正在上升,且价格已经处于上涨趋势中 → 做多
  • • 当波动率正在上升,且价格已经处于下跌趋势中 → 做空
  • • 当波动率动量消失或趋势反转 → 退出

这并不是一个预测模型,而是一个带有波动率确认过滤器的趋势跟随策略。下面我们就用 Python 一步步实现并回测它。

一、策略核心逻辑拆解

1. 计算历史波动率

历史波动率使用收益率的滚动标准差来度量:

import pandas as pdimport talib# 计算每日收益率,缺失值填 0returns = close.pct_change().fillna(0)# 用 TA-Lib 计算滚动标准差作为波动率指标volatility = pd.Series(    talib.STDDEV(        returns.to_numpy(),        timeperiod=vol_window,  # 波动率计算窗口        nbdev=1    ),    index=close.index)

2. 定义波动率动量

波动率动量定义为当前波动率与若干周期前波动率的差值:

# 波动率动量 = 当前波动率 - N 个周期前的波动率vol_momentum = volatility - volatility.shift(vol_momentum_window)

当 vol_momentum > 0 时,说明波动率正在扩张。

3. 趋势过滤器

使用一条简单移动均线(SMA)来判断价格趋势方向:

# 用简单移动均线判断价格趋势price_sma = pd.Series(    talib.SMA(        close.to_numpy(),        timeperiod=price_trend_sma_window    ),    index=close.index)

4. 避免「未来函数」陷阱

为了防止 lookahead bias(前视偏差),所有判断都基于前一根 K 线的数值:

# 全部使用 shift(1) 取上一根 K 线的值,避免未来函数prev_vol_momentum = vol_momentum.shift(1)prev_close = close.shift(1)prev_sma = price_sma.shift(1)

5. 入场与出场条件

# 多头入场:波动率扩张 且 价格在均线之上entries = (    (prev_vol_momentum > 0) &    (prev_close > prev_sma)).fillna(False)# 空头入场:波动率扩张 且 价格在均线之下short_entries = (    (prev_vol_momentum > 0) &    (prev_close < prev_sma)).fillna(False)# 多头出场:波动率动量消失 或 跌破均线exits = (    (prev_vol_momentum <= 0) |    (prev_close < prev_sma)).fillna(False)# 空头出场:波动率动量消失 或 突破均线short_exits = (    (prev_vol_momentum <= 0) |    (prev_close > prev_sma)).fillna(False)

6. ATR 跟踪止损

为了控制风险,策略使用基于 ATR 的跟踪止损:

import numpy as np# 计算平均真实波幅 ATRatr = pd.Series(    talib.ATR(        high.to_numpy(),        low.to_numpy(),        close.to_numpy(),        timeperiod=atr_window    ),    index=close.index)# 把 ATR 转换为百分比形式的止损距离,方便 vectorbt 使用atr_stop_pct = (atr_multiplier * atr / close).shift(1)# 处理可能出现的无穷大数值atr_stop_pct = atr_stop_pct.replace([np.inf, -np.inf], np.nan)

二、完整策略代码(基于 Vectester 框架)

下面是这套波动率动量策略的完整实现,可直接接入 Vectester / vectorbt 进行回测:

import pandas as pdimport numpy as npimport vectorbt as vbtimport talibfrom core.params import ParamDeffrom core.strategy_base import Strategy, StrategyResultclass VolMomentumStrategy(Strategy):    name = "vol_momentum_talib"    # 策略可调参数定义    param_defs = [        ParamDef("vol_window", 30, int, "波动率计算窗口"),        ParamDef("vol_momentum_window", 7, int, "波动率动量窗口"),        ParamDef("price_trend_sma_window", 30, int, "价格趋势 SMA 窗口"),        ParamDef("atr_window", 14, int, "ATR 计算窗口"),        ParamDef("atr_multiplier", 2.0, float, "ATR 跟踪止损倍数"),        ParamDef("long_short", 1, int, "1 = 多空双向,0 = 仅做多"),    ]    def build(self, data: pd.DataFrame, params: dict) -> StrategyResult:        # 提取价格数据        close = data["Close"].astype(float)        high = data["High"].astype(float)        low = data["Low"].astype(float)        # 读取参数        vol_window = int(params["vol_window"])        vol_momentum_window = int(params["vol_momentum_window"])        price_trend_sma_window = int(params["price_trend_sma_window"])        atr_window = int(params["atr_window"])        atr_multiplier = float(params["atr_multiplier"])        long_short = int(params["long_short"])        # 1. 计算收益率与波动率        returns = close.pct_change().fillna(0)        volatility = pd.Series(            talib.STDDEV(returns.to_numpy(), timeperiod=vol_window, nbdev=1),            index=close.index        )        # 2. 计算波动率动量        vol_momentum = volatility - volatility.shift(vol_momentum_window)        # 3. 计算价格 SMA 用于趋势判断        price_sma = pd.Series(            talib.SMA(close.to_numpy(), timeperiod=price_trend_sma_window),            index=close.index        )        # 4. 计算 ATR 用于止损        atr = pd.Series(            talib.ATR(                high.to_numpy(),                low.to_numpy(),                close.to_numpy(),                timeperiod=atr_window            ),            index=close.index        )        # 5. 使用上一根 K 线数据,避免前视偏差        prev_vol_momentum = vol_momentum.shift(1)        prev_close = close.shift(1)        prev_sma = price_sma.shift(1)        # 波动率状态判断        vol_accelerating = prev_vol_momentum > 0   # 波动率正在加速        vol_decelerating = prev_vol_momentum <= 0  # 波动率减速或停滞        # 6. 生成进出场信号        entries = (vol_accelerating & (prev_close > prev_sma)).fillna(False)        exits = (vol_decelerating | (prev_close < prev_sma)).fillna(False)        short_entries = (vol_accelerating & (prev_close < prev_sma)).fillna(False)        short_exits = (vol_decelerating | (prev_close > prev_sma)).fillna(False)        # 7. ATR 百分比止损        atr_stop_pct = (atr_multiplier * atr / close).shift(1)        atr_stop_pct = atr_stop_pct.replace([np.inf, -np.inf], np.nan)        # 8. 根据参数返回多空或仅多头模式        if long_short == 1:            return StrategyResult(                entries=entries,                exits=exits,                signal_kwargs={                    "short_entries": short_entries,                    "short_exits": short_exits,                    "sl_stop": atr_stop_pct,                    "sl_trail": True,    # 启用跟踪止损                    "accumulate": False,                },                pf_kwargs={"direction": "both"},  # 多空双向            )        return StrategyResult(            entries=entries,            exits=exits,            signal_kwargs={                "sl_stop": atr_stop_pct,                "sl_trail": True,                "accumulate": False,            },            pf_kwargs={"direction": "longonly"},  # 仅做多        )

三、回测结果

作者在 ETH-USD 上做了一年期回测,从权益曲线可以看到:

  • • 策略曲线(绿色) 整体走势平稳,波动远小于买入持有
  • • 买入持有(黑色虚线) 收益更高,但中途经历了较大的回撤
  • • 最大回撤 约在 -15% 左右,相比单纯持有 ETH 风险更可控
  • • 月度收益分布显示策略在大部分月份保持盈利,但也有少数月份出现亏损

这说明该策略的核心价值并不是「跑赢买入持有」,而是在控制风险的前提下捕捉波动率扩张阶段的趋势行情。

总结

这是一个干净、可测试的波动率扩张型趋势策略,核心规则可以浓缩成四句话:

波动率上升 + 价格上涨趋势 = 做多波动率上升 + 价格下跌趋势 = 做空波动率动量下降           = 离场ATR 跟踪止损             = 风控

给 Python 量化学习者的几点启发:

  1. 1. 思路比代码重要:换一个维度看市场(比如波动率而非价格),往往能挖出新策略
  2. 2. 避免前视偏差:所有信号都用 shift(1) 取上一根 K 线值,这是回测真实可信的前提
  3. 3. TA-Lib 是好朋友:STDDEV、SMA、ATR 三个指标就能撑起一套完整策略
  4. 4. 风控不能少:ATR 跟踪止损让策略在突发反转时能快速止血
  5. 5. 不要止步于单次回测:要让策略真正可用,还需要做 Walk-Forward 分析、多资产验证以及与基准的对比

如果你也对量化交易感兴趣,不妨把这套代码拿去自己跑一遍,换个标的、调调参数,看看会有什么新发现。

参考文章

加入专注于财经数据与量化投研的知识星球【数据科学实战】,获取本文完整研究解析、代码实现细节。

财经数据与量化投研知识社区

2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:

  1. 1. 双典系统赋能:获赠《财经数据宝典》与《量化投研宝典》完整文档,凝练多年实战经验,构建系统化知识框架;
  2. 2. 量化因子日更教程(2026重磅新增):每日更新「量化因子专题教程」,配套完整可运行代码与实战案例,深度拆解因子构建、回测与优化全流程;
  3. 3. 量化文章专题教程库:300+篇星球独有高质量教程式文章,系统覆盖策略开发、因子研究、风险管理等核心领域,内容基本每日更新,并配套精选学习资料与实战参考;
  4. 4. 量化投研实战课程:赠送《AKQuant-入门及实战》《PyBroker-入门及实战》视频课程,手把手教学,快速掌握量化策略开发技能;
  5. 5. 财经数据支持:定期更新国内外财经数据,为策略研发提供精准、可靠的数据基础;
  6. 6. 顶尖学者与行业专家分享:年度邀请学术界博士与业界资深专家开展前沿论文精讲与实战案例分享,不少于4场,直击研究前沿与产业实践;专家直连答疑:与核心开发者及领域专家实时互动,高效解决投研实战难题;
  7. 7. 专业社群与专属福利:加入高质量交流社群,获取课程折扣及更多独家资源。

星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!

好文推荐

1. 用 Python 打造股票预测系统:Transformer 模型教程(一)

2. 用 Python 打造股票预测系统:Transformer 模型教程(二)

3. 用 Python 打造股票预测系统:Transformer 模型教程(三)

4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)

5. 揭秘隐马尔可夫模型:因子投资的制胜武器

6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用

7. 金融 AI 助手:FinGPT 让你轻松掌握市场分析

8. 量化交易秘籍:为什么专业交易员都在用对数收益率?

9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解

10. 掌握金融波动率模型:完整 Python 实现指南

好书推荐

最新文章

随机文章