问题描述
解决方案
是的,GARCH(广义自回归条件异方差)模型是金融领域预测资产价格波动率最经典且常用的方法之一。它能够很好地捕捉金融时间序列中常见的“波动率聚集”现象(即大波动后往往伴随着大波动,小波动后伴随着小波动)。
在 QMT 平台中,我们可以通过引入 Python 的第三方库 arch 来实现 GARCH 模型。
前置准备:安装 arch 库
QMT 自带的 Python 环境通常不包含 arch 库,你需要手动安装。
- 找到 QMT 安装目录下的 Python 路径(通常在
bin.x64/Lib/site-packages或类似位置)。 - 打开命令行(CMD),使用 QMT 的 pip 安装:
# 示例命令,请根据实际路径修改 D:\QMT\bin.x64\python.exe -m pip install arch
QMT 策略代码实现
以下是一个完整的 QMT 策略代码示例。该策略会获取指定股票的历史收盘价,计算对数收益率,建立 GARCH(1,1) 模型,并预测未来 5 个交易日的波动率。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
try:
from arch import arch_model
except ImportError:
print("错误:未检测到 arch 库。请先在 QMT Python 环境中安装:pip install arch")
def init(ContextInfo):
# 设置要分析的标的(例如:浦发银行)
ContextInfo.target = '600000.SH'
# 设置数据周期
ContextInfo.period = '1d'
# 设置回溯历史数据长度(GARCH模型需要较长的数据来收敛,建议至少1-2年)
ContextInfo.lookback = 500
# 预测未来的天数
ContextInfo.forecast_horizon = 5
def handlebar(ContextInfo):
# 为了演示方便,我们只在最后一根K线(最新时刻)运行预测
if not ContextInfo.is_last_bar():
return
# 1. 获取历史行情数据
# 使用 get_market_data_ex 获取数据,前复权
data_dict = ContextInfo.get_market_data_ex(
['close'],
[ContextInfo.target],
period=ContextInfo.period,
count=ContextInfo.lookback,
dividend_type='front'
)
if ContextInfo.target not in data_dict:
print(f"未获取到 {ContextInfo.target} 的数据")
return
df = data_dict[ContextInfo.target]
# 2. 数据预处理
# 计算对数收益率:ln(Pt / Pt-1)
# 乘以 100 是为了让数值更好收敛(arch库推荐做法)
df['log_return'] = np.log(df['close'] / df['close'].shift(1)) * 100
df.dropna(inplace=True) # 去除计算收益率产生的第一个NaN
returns = df['log_return']
# 3. 构建并拟合 GARCH 模型
# vol='Garch': 使用 GARCH 模型
# p=1, q=1: 标准 GARCH(1,1) 参数
# mean='Constant': 假设均值为常数
# dist='Normal': 假设残差服从正态分布
try:
model = arch_model(returns, vol='Garch', p=1, q=1, mean='Constant', dist='Normal')
res = model.fit(disp='off') # disp='off' 不打印拟合过程中的详细日志
print("=" * 30)
print(f"标的: {ContextInfo.target} GARCH(1,1) 模型拟合完成")
print(res.summary())
# 4. 预测未来波动率
# horizon: 预测步数
forecasts = res.forecast(horizon=ContextInfo.forecast_horizon)
# 获取预测的方差 (Variance)
# forecasts.variance 的最后一行包含基于最新数据的预测
future_variance = forecasts.variance.iloc[-1]
print("-" * 30)
print(f"未来 {ContextInfo.forecast_horizon} 个交易日波动率预测:")
# 将方差转换为标准差(波动率),并还原百分比单位
for i in range(ContextInfo.forecast_horizon):
col_name = f'h.{i+1}'
daily_vol = np.sqrt(future_variance[col_name])
# 年化波动率估算 (假设一年252个交易日)
annual_vol = daily_vol * np.sqrt(252)
print(f"T+{i+1}日: 日波动率={daily_vol:.4f}%, 年化波动率={annual_vol:.4f}%")
print("=" * 30)
except Exception as e:
print(f"GARCH 模型拟合或预测失败: {e}")
代码逻辑详解
-
数据获取 (
get_market_data_ex):- 我们获取了过去 500 个交易日的收盘价。GARCH 模型是基于历史残差来回归的,因此数据量太少会导致模型无法收敛或结果不准确。
- 使用
dividend_type='front'(前复权) 是必须的,因为除权除息导致的价格跳空会被模型误判为剧烈波动。
-
收益率计算:
- 代码使用了对数收益率 (
np.log(close/pre_close))。 - 关键点:我们将收益率乘以了 100。这是使用
arch库时的最佳实践,因为如果收益率数值太小(如 0.0002),优化器可能无法正确找到最优解。
- 代码使用了对数收益率 (
-
模型拟合 (
arch_model):- 我们使用了最通用的 GARCH(1,1) 设定。
p=1(Lag order of the symmetric innovation): 滞后残差平方项。q=1(Lag order of lagged volatility): 滞后条件方差项。
-
预测 (
forecast):res.forecast(horizon=N)会基于当前最新的信息集,向后迭代预测 N 步。- 模型输出的是方差 (Variance)。我们需要对其开根号得到标准差 (Standard Deviation),即我们常说的波动率。
常见问题 (Q&A)
Q1: 为什么运行代码报错 No module named 'arch'?
A: QMT 的 Python 环境是独立的。你必须使用 QMT 安装目录下的 pip 来安装这个库,而不是系统全局的 pip。请参考“前置准备”部分的说明。
Q2: 预测出来的波动率数值代表什么?
A: 在代码中,由于我们在输入数据时乘以了 100,输出的日波动率单位是百分比(%)。例如输出 1.5,代表预测该股票下一日的涨跌幅标准差为 1.5%。年化波动率则是将其乘以 $\sqrt{252}$。
Q3: 除了 GARCH,QMT 还能用简单的方法看波动率吗?
A: 可以。如果不需要预测,只是想看历史波动率,可以直接计算历史收益率的标准差:
# 计算过去20日的历史波动率
hist_vol = returns.rolling(window=20).std() * np.sqrt(252)
GARCH 的优势在于它能根据最近的剧烈波动动态调整预测值,比简单的移动平均标准差反应更灵敏。
Q4: 这个模型可以用来做交易信号吗?
A: 可以。常见的策略包括:
- 期权策略:当 GARCH 预测波动率显著高于当前隐含波动率(IV)时,买入跨式期权(做多波动率)。
- 风控管理:当预测波动率飙升时,降低仓位(VaR计算)。
- 布林带策略:使用 GARCH 预测的动态方差来构建自适应的布林带宽度,而不是固定的 2 倍标准差。