🚀 AI 一键生成 ptrade 策略代码

PTrade 进阶:如何使用 arch 库构建 GARCH 波动率模型?

ptrade | 发布时间: 2025/12/23
以下内容由 EasyQuant 生成。

问题描述

如何在策略中使用 GARCH 模型预测波动率?

解决方案

在 PTrade 策略中使用 GARCH(广义自回归条件异方差)模型预测波动率,主要依赖于 PTrade 支持的第三方库 arch。GARCH 模型常用于量化交易中的风险管理、期权定价或基于波动率的择时策略。

以下是关于如何在 PTrade 中实现 GARCH 模型的详细说明和完整代码示例。

核心步骤

  1. 获取数据:使用 get_history 获取足够长的历史收盘价数据(通常建议 250 个数据点以上以保证模型收敛)。
  2. 数据预处理:计算对数收益率,并将其放大 100 倍(转换为百分比收益率)。注意:GARCH 优化器在处理非常小的数值(如 0.0001)时容易失败,因此放大数值是必要的技巧。
  3. 模型拟合:使用 arch.arch_model 定义模型(通常为 GARCH(1,1)),并调用 fit() 进行拟合。
  4. 预测:使用 forecast() 方法预测下一期的方差,并转换为波动率。
  5. 交易逻辑:根据预测的波动率制定交易决策(例如:波动率过高时平仓避险,波动率低时持仓)。

完整策略代码

import numpy as np
import pandas as pd
from arch import arch_model

def initialize(context):
    """
    初始化函数
    """
    # 设置标的股票:以上证指数ETF为例,波动率特征较明显
    g.security = '510300.SS'
    set_universe(g.security)
    
    # 设置GARCH模型所需的历史窗口长度,建议至少一年数据
    g.window_size = 500
    
    # 设置波动率阈值,用于触发交易信号
    # 注意:这里的阈值是基于放大100倍后的收益率计算的
    g.vol_threshold = 1.5 
    
    # 设置每天定时运行,避免盘中每分钟计算导致性能问题
    # GARCH计算量较大,建议在盘前或收盘运行
    run_daily(context, trade_logic, time='09:35')

def trade_logic(context):
    """
    每日交易逻辑函数
    """
    security = g.security
    
    # 1. 获取历史收盘价数据
    # 获取过去 g.window_size 天的数据
    hist = get_history(g.window_size, '1d', 'close', security, fq='pre')
    
    # 如果数据长度不足,直接返回
    if len(hist) < g.window_size:
        log.info("历史数据不足,跳过计算")
        return
    
    # 2. 数据预处理
    prices = hist['close'].values
    
    # 计算对数收益率: ln(Pt / Pt-1)
    # 加上 1e-8 防止除零错误(虽然价格通常不为0)
    log_returns = np.diff(np.log(prices))
    
    # 【关键步骤】将收益率放大100倍,转换为百分比收益率
    # 这有助于 GARCH 模型的优化器更好地收敛
    returns = log_returns * 100
    
    # 3. 定义并拟合 GARCH 模型
    try:
        # 定义模型:均值模型为Constant,波动率模型为GARCH,p=1, q=1
        # dist='Normal' 使用正态分布假设
        am = arch_model(returns, mean='Constant', vol='Garch', p=1, q=1, dist='Normal')
        
        # 拟合模型,disp='off' 禁止打印拟合过程中的日志
        res = am.fit(disp='off')
        
        # 4. 预测下一期的波动率
        # horizon=1 表示预测往后一步
        forecasts = res.forecast(horizon=1)
        
        # 获取预测的方差 (Variance)
        # forecasts.variance 是一个 DataFrame,取最后一行第一列
        next_variance = forecasts.variance.iloc[-1, 0]
        
        # 将方差转换为标准差(波动率)
        next_volatility = np.sqrt(next_variance)
        
        log.info("当前预测波动率(百分比): %.4f" % next_volatility)
        
    except Exception as e:
        log.error("GARCH 模型拟合失败: %s" % e)
        return

    # 5. 交易执行逻辑
    # 获取当前持仓
    position = get_position(security).amount
    curr_cash = context.portfolio.cash
    
    # 策略逻辑:
    # 如果预测波动率高于阈值,视为风险过大,清仓观望
    # 如果预测波动率低于阈值,视为行情平稳,全仓买入
    
    if next_volatility > g.vol_threshold:
        if position > 0:
            log.info("波动率过高 (%.4f > %.4f),触发卖出信号" % (next_volatility, g.vol_threshold))
            order_target(security, 0)
    else:
        if position == 0:
            log.info("波动率处于低位 (%.4f <= %.4f),触发买入信号" % (next_volatility, g.vol_threshold))
            order_value(security, curr_cash)

def handle_data(context, data):
    # 必须实现的函数,但在本策略中逻辑已通过 run_daily 处理
    pass

代码关键点解析

  1. 库的导入
    PTrade 环境内置了 arch 库(版本通常为 3.2),直接 from arch import arch_model 即可使用。

  2. 数据缩放 (Scaling)
    代码中 returns = log_returns * 100 是非常重要的一步。如果不放大,收益率数值极小(例如 0.0002),GARCH 的优化算法很难找到最优解,经常会报错或不收敛。

  3. 异常处理
    GARCH 模型拟合是一个数值优化过程,可能会因为数据异常或参数问题导致拟合失败。使用 try...except 块包裹拟合代码,可以防止策略因数学计算错误而意外终止。

  4. 性能优化
    GARCH 计算相对耗时。在 PTrade 中,如果选择分钟级别回测,建议不要在 handle_data(每分钟运行)中直接调用拟合过程,而是使用 run_daily 在每天开盘时计算一次,或者降低计算频率。

  5. 预测结果读取
    res.forecast(horizon=1) 返回的结果包含 variance(方差)。我们需要取最后一行(基于最新数据预测)的值,并开根号得到波动率(标准差)。

常见问题 Q&A

Q: 为什么我的 GARCH 模型总是报错 "Inequality constraints incompatible"?
A: 这通常是因为输入的数据数值太小。请确保你计算的是百分比收益率(即乘以 100),而不是原始的小数收益率。

Q: 如何选择 GARCH 的参数 p 和 q?
A: 在金融时间序列中,GARCH(1,1) 通常足以捕捉大部分波动率聚集特征。如果需要更复杂的模型,可以通过 AIC 或 BIC 准则进行模型选择,但在实盘策略中,简单的 GARCH(1,1) 鲁棒性通常更好。

Q: 这个策略可以直接用于实盘吗?
A: 示例代码仅展示了技术实现。GARCH 预测的是波动率,波动率高并不代表跌,也可能代表暴涨。通常 GARCH 会配合趋势指标(如均线)或用于期权策略(如波动率套利)使用,单独作为择时指标风险较大。

Q: PTrade 回测速度很慢怎么办?
A: 减少 get_history 获取的数据长度(例如从 1000 减到 250),或者减少调仓频率。GARCH 的拟合是计算密集型任务。