当前位置:首页>python>用 Python 构建 GJR-GARCH 波动率择时策略:让你的投资更稳健

用 Python 构建 GJR-GARCH 波动率择时策略:让你的投资更稳健

  • 2026-10-14 21:27:42
用 Python 构建 GJR-GARCH 波动率择时策略:让你的投资更稳健

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

引言

在量化投资领域,波动率与市场收益之间的关系是最常被利用的现象之一。当波动率飙升时,市场往往下跌;当市场平静时,上涨趋势更容易持续。但你知道吗?负收益对未来波动率的放大效应,远大于同等幅度的正收益。这种不对称性被称为"杠杆效应"。

今天这篇文章将带你了解如何使用 GJR-GARCH 模型来捕捉这种不对称性,并构建一个实用的"低波动率满仓、高波动率空仓"的择时策略。即使你不打算实盘交易,这套框架也能帮你深入理解波动率建模和回测的核心要点。

什么是 GJR-GARCH 模型?

标准的 GARCH 模型对正向和负向冲击的处理是对称的,而 GJR-GARCH(Glosten-Jagannathan-Runkle GARCH)模型增加了一个不对称项,公式如下:

σ²ₜ = ω + αε²ₜ₋₁ + γε²ₜ₋₁Iₜ₋₁ + βσ²ₜ₋₁

其中 Iₜ₋₁ 是一个指示变量:当 εₜ₋₁ < 0(即出现负收益)时,Iₜ₋₁ = 1,否则为 0。当 γ > 0 时,负向冲击对未来波动率的影响比正向冲击更大。这在股票市场中是被广泛验证的经验事实,因此 GJR-GARCH 特别适合用于风险管理。

策略核心思路

这个策略的思路非常直接:

  1. 1. 使用滚动窗口拟合 GJR-GARCH 模型,估计条件波动率
  2. 2. 根据波动率的历史分位数判断当前是"高波动"还是"低波动"市场状态
  3. 3. 低波动时 100% 持仓,高波动时 0% 持仓(即空仓)

核心假设是:在市场波动剧烈时退出,可以有效降低回撤,同时在平静期捕捉大部分上涨收益。

代码实现详解

下面是策略的核心实现。我们使用 Python 的 arch 库来拟合 GJR-GARCH 模型。

1. 策略配置类

from dataclasses import dataclass@dataclassclass StrategyConfig:    """策略配置参数"""    ticker: str = "SPY"                    # 交易标的    benchmark_ticker: str = "^GSPC"        # 基准指数    start_date: str = "2010-01-01"         # 回测起始日期    end_date: str = "2024-12-31"           # 回测结束日期    garch_window: int = 252                # GARCH 滚动窗口(交易日)    vol_percentile_threshold: float = 70.0 # 波动率分位数阈值    low_vol_weight: float = 1.0            # 低波动时的仓位    high_vol_weight: float = 0.0           # 高波动时的仓位    commission_per_share: float = 0.005    # 每股佣金    min_commission: float = 1.0            # 最低佣金

2. 滚动波动率估计

import numpy as npimport pandas as pdfrom arch import arch_modelclass GJRGARCHEngine:    """GJR-GARCH 波动率引擎"""    def __init__(self, config: StrategyConfig):        self.config = config    def fit_gjr_garch(self, returns: pd.Series) -> dict:        """拟合 GJR-GARCH 模型"""        # 创建 GJR-GARCH(1,1,1) 模型,使用 t 分布捕捉肥尾特性        model = arch_model(            returns * 100,  # 转换为百分比收益率            vol='Garch',            p=1,            # GARCH 项阶数            o=1,            # 不对称项阶数(GJR 的核心)            q=1,            # ARCH 项阶数            dist='t'        # Student-t 分布        )        result = model.fit(disp='off', show_warning=False)        return {            'model': result,            'conditional_vol': result.conditional_volatility / 100,            'params': result.params        }    def compute_rolling_volatility(self, returns: pd.Series) -> pd.Series:        """计算滚动条件波动率"""        n = len(returns)        window = self.config.garch_window        conditional_vol = pd.Series(index=returns.index, dtype=float)        for i in range(window, n):            window_returns = returns.iloc[i-window:i]            try:                result = self.fit_gjr_garch(window_returns)                # 预测未来 1 天的波动率                forecast = result['model'].forecast(horizon=1)                conditional_vol.iloc[i] = np.sqrt(forecast.variance.values[-1, 0])            except:                # 拟合失败时使用简单标准差作为后备                conditional_vol.iloc[i] = window_returns.std()        return conditional_vol

3. 市场状态分类(避免前视偏差)

这是很多回测容易犯错的地方。如果使用整个数据集计算分位数,就会引入"前视偏差"——用未来的信息做过去的决策。正确的做法是使用扩展窗口分位数:

class RegimeClassifier:    """市场状态分类器"""    def __init__(self, config: StrategyConfig):        self.config = config    def classify_regime(self, conditional_vol: pd.Series) -> pd.Series:        """基于扩展窗口分位数进行状态分类"""        # 计算每个时间点相对于历史的分位数排名        expanding_percentile = conditional_vol.expanding(min_periods=20).apply(            lambda x: pd.Series(x).rank(pct=True).iloc[-1] * 100,            raw=False        )        regime = pd.Series(index=conditional_vol.index, dtype=int)        # 0 表示低波动状态,1 表示高波动状态        regime[expanding_percentile <= self.config.vol_percentile_threshold] = 0        regime[expanding_percentile > self.config.vol_percentile_threshold] = 1        return regime

4. 信号生成(注意滞后处理)

信号必须滞后一期,确保我们基于昨天的信息做今天的决策:

class StrategyEngine:    """策略引擎"""    def __init__(self, config: StrategyConfig):        self.config = config    def generate_signals(self, regime: pd.Series) -> pd.Series:        """生成交易信号"""        target_weight = pd.Series(index=regime.index, dtype=float)        target_weight[regime == 0] = self.config.low_vol_weight  # 低波动满仓        target_weight[regime == 1] = self.config.high_vol_weight # 高波动空仓        # 关键:信号滞后一期,避免使用当天信息        signal = target_weight.shift(1)        return signal

回测结果分析

使用 2010-2024 年的 SPY 数据进行回测,结果如下:

指标
策略
基准(买入持有)
总收益
163.83%
352.98%
年化收益
7.24%
11.49%
年化波动率
9.99%
17.21%
夏普比率
0.724
0.668
最大回撤
-18.95%
-33.93%

从绝对收益来看,策略跑输了基准。这不奇怪,因为策略有约 30% 的时间处于空仓状态,错过了一些在高波动期出现的反弹。

但从风险调整后的角度看,结果就不一样了:波动率降低了 42%,最大回撤减少了 44%,夏普比率略高于基准。对于那些无法承受 30% 以上回撤的投资者来说,这种取舍是值得的。

在 15 年间共发生了约 78 次状态切换,平均每年约 5 次,并没有频繁交易的问题。

策略的局限性

  1. 1. 机会成本真实存在:空仓期间会错过市场反弹,而一些涨幅最大的交易日往往出现在高波动时期
  2. 2. 阈值选择具有主观性:70% 分位数是经验选择,不同阈值会产生不同结果
  3. 3. 二元配置过于粗糙:更成熟的做法是根据波动率水平渐进式调整仓位
  4. 4. 计算量较大:滚动拟合 GARCH 模型计算成本较高,实盘需要优化
  5. 5. 未考虑滑点:假设能以收盘价成交,对大资金不现实

可能的改进方向

  1. 1. 采用渐进式仓位调整,而非简单的 0/100% 切换
  2. 2. 结合 VIX 指数进行信号确认
  3. 3. 扩展到多资产组合,使用层次风险平价进行分散
  4. 4. 引入马尔可夫状态切换模型,对状态转换进行概率建模
  5. 5. 增加趋势过滤器,减少震荡市中的假信号

总结

GJR-GARCH 状态切换策略展示了一种基于原理的波动率择时方法。通过尊重波动率冲击的不对称性,并严格避免前视偏差,我们构建了一个能够显著降低风险的策略框架。

它能跑赢买入持有吗?从绝对收益来看,不能。但从回撤调整后的指标来看,它具有竞争力。对于那些在 30% 以上回撤时难以安睡的投资者来说,这种取舍可能正是他们需要的。

这套代码和分析仅供学习参考。过去的表现不代表未来的结果,在实施任何交易策略之前,请进行充分的研究并考虑咨询专业的财务顾问。

参考文章

  1. 1. Building a GJR-GARCH Regime-Switching Strategy in Python:https://medium.com/@NFS303/building-a-gjr-garch-regime-switching-strategy-in-python-f633693b56b5

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

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

  1. 1. 双典系统赋能:获赠《财经数据宝典》与《量化投研宝典》完整文档,凝练多年实战经验,构建系统化知识框架;
  2. 2. 量化因子日更教程(2026重磅新增):每日更新「量化因子专题教程」,配套完整可运行代码与实战案例,深度拆解因子构建、回测与优化全流程;
  3. 3. 量化文章专题教程库:300+篇星球独有高质量教程式文章,系统覆盖策略开发、因子研究、风险管理等核心领域,内容基本每日更新,并配套精选学习资料与实战参考;
  4. 4. PyBroker实战课程:赠送《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 实现指南

好书推荐

最新文章

随机文章