ARIMA-GARCH 模型(下)

Photo by Isaac Smith on Unsplash

本文重点概要

  • 文章难度:★★★☆☆
  • 以时间序模型预测价格走势
  • 阅读建议:本文会利用时间序列模型进行走势预测,然而不会讲述资料前处理等过程,如果读者对时间序列基本概念不清楚,请先行阅读ARIMA-GARCH 模型(上),如此会更清楚本文所述。

前言

首先,本文会执行模型建置的过程,让读者了解Python套件的应用,但是不会进行上篇文章中提及的任何检定,以避免篇幅冗长;接著,计算预测报酬以及价格;最后将以视觉化方式比对真实历史价格,来检讨ARMA-GARCH模型的预测效果。

Note:本文是利用”ARMA”模型对报酬率建模,而非上篇文章中的”ARIMA”,其差异处仅在ARIMA能够处理非定态数据。上篇使用ARIMA是想尽量让读者了解时间序列的原理,而本文使用ARMA是想让读者多了解一种方法。

编辑环境及模组需求

本文使用 MacOS 并以 Jupyter Notebook作为编辑器

import numpy as np import pandas as pd import matplotlib.pyplot as plt %matplotlib inline import seaborn as sns sns.set() import tejapi tejapi.ApiConfig.api_key = 'Your Key' tejapi.ApiConfig.ignoretz = True

资料库使用

资料导入&模型建置

Step 1. 资料捞取

data = tejapi.get('TWN/APRCD', # 公司交易资料-收盘价             coid= '0050', # 台湾50             mdate={'gte': '2003-01-01', 'lte':'2021-12-31'},             opts={'columns': ['mdate', 'close_d', 'roi']},             chinese_column_name=True,             paginate=True) data['年月日'] = pd.to_datetime(data['年月日']) data = data.set_index('年月日') data = data.rename(columns = {'收盘价(元)':'收盘价', '报酬率%':'日报酬率(%)'})
资料表(一)

Step 2. 资料分割

train_date = data.index.get_level_values('年月日') <= '2020-12-31' train_data = data[train_date].drop(columns = ['收盘价']) test_data = data[~train_date] # 保留test_data收盘价,用来比对模型预测值

Step 3. ARMA参数选择

本文会利用statsmodels进行参数筛选,与上篇利用pmdarima不同。

import statsmodels.api as sm # AIC、BIC准则 sm.tsa.stattools.arma_order_select_ic(train_data, ic=["aic", "bic"])
图(一)

可以看到BIC准则的参数挑选项目为(0,0),这是源于BIC准则会对多解释变数进行较严格的筛选,因此,本文选择(p,q)=(2,2),进行模型建置。

Step 4. ARMA模型建置

from statsmodels.tsa.arima_model import ARMA model = ARMA(train_data, order = (2, 2)) arma = model.fit()  print(arma.summary())
图(二)

Step 5. GARCH模型建置

# 取得ARMA模型的残差项目 arma_resid = list(arma.resid) from arch import arch_model mdl_garch = arch_model(arma_resid, vol = 'GARCH', p = 1, q = 1) garch = mdl_garch.fit() print(garch.summary())
图(三)

模型预测(绘图过程皆详见完整程式码)

在预测的部分,本文会用ARMA模型估计平均,并应用GARCH模型预测波动区间。

Step 1. ARMA预测平均报酬

# len(train_data) = 4333, len(data) = 4577
forecast_mu = arma.predict(start = 4333, end = 4576) 
# 预测函式的end包含当期,所以需进行4577-1=4576。
图(四)

从上图发现经过一段时间后,预测平均报酬会逐渐成为趋近于0的常数,仅在预测初期有较明显的波动。

Step 2. GARCH预测波动度

garch_forecast = [] for i in range(len(test_data)):     train = arma_resid[:-(len(test_data)-i)]     model = arch_model(train, vol = 'GARCH', p = 1, q = 1)     garch_fit = model.fit()     prediction = garch_fit.forecast(horizon=1)     garch_forecast.append(np.sqrt(prediction.variance.values[-1:][0]))

本文此处运用滚动式的方法预测各期的波动度,所以上述程式码是将GARCH模型包在回圈当中,再回传储存值。接下来,先将上述各预测值存入test_data,并计算上下区间,供后续计算。

test_data['ARMA预测报酬(%)'] = list(forecast_mu) test_data['GARCH预测波动度'] = (garch_forecast) test_data['预测区间上限'] = test_data['ARMA预测报酬(%)'] + test_data['GARCH预测波动度'] test_data['预测区间下限'] = test_data['ARMA预测报酬(%)'] - test_data['GARCH预测波动度']
资料表(二)
图(五)

根据上图,大部分的实际报酬率是包含在区间当中的;然而,无法有效预测单一日期波动幅度较大之报酬率。

Step 3. 预测价格

# 本文已经把train_data中的价格删除,所以需重新计算2020-12-30的收盘价 first_price = test_data['收盘价'][0] / (1+test_data['日报酬率(%)'][0]*0.01) # 计算第一期预测 test_data['ARMA预测价格'] = first_price * (1 + test_data['ARMA预测报酬(%)']*0.01) test_data['预测价格区间上限'] = first_price * (1 + test_data['预测区间上限']*0.01) test_data['预测价格区间下限'] = first_price * (1 + test_data['预测区间下限']*0.01) # 计算剩余预测区间 for i in range(1, len(test_data)):         test_data['ARMA预测价格'][i] = test_data['预测价格'][i-1] * (1 + test_data['ARMA预测报酬(%)'][i]*0.01)         test_data['预测价格区间上限'][i] = test_data['预测价格区间上限'][i-1] * (1 + test_data['预测区间上限'][i]*0.01)         test_data['预测价格区间下限'][i] = test_data['预测价格区间下限'][i-1] * (1 + test_data['预测区间下限'][i]*0.01) # 计算区间均价 test_data['预测平均价格'] = (test_data['预测价格区间上限'] + test_data['预测价格区间下限']) / 2
图(六)

上图显示,在预测初期时,预测区间较窄,并且两项价格预测值也与实际价格相去不远;然而,随著预测时间越往后,预测区间扩大、区间均价偏移,导致无法准确判断模型的成效。所以本文接下来会展示时间轴为两个月的预测值。

new_date = test_data.index.get_level_values('年月日') <= '2021-03-01' new_test = test_data[new_date]
图(七)

在两个月的区间中,读者应该可以更清楚地发现,实际价格与预测值间的差异。首先,是两项预测价格与实际价格的关系,可以发现区间均价更贴近实际价格;而在区间预测方面,随著预测区间扩大以及实际价格回落,实际价格的走势才涵盖在预测波动当中。

结论

借由最后的结果,读者应该可以明白此次ARMA-GARCH模型对0050并没有很好的预测效果,尽管在模型配适上有不错的成果。先不论随时间推移而区间扩大,导致失去判断标准这项问题,毕竟时间轴越远,本来就应该进行更保守的估计;单就预测初期的结果,便可以发现实际价格超出模型预测的波动区间,也就代表模型在初期预测便没有足够的可信度,这可能是源于本文并无考量到的外生变量或是季节性因素而造成的。所以,若是读者对这类型的议题有兴趣,请持续关注此平台的文章;另外,欢迎选购 TEJ E Shop中的方案,就能够轻松地对有兴趣的标的,实作走势预测。

完整程式码

延伸阅读

相关连结

返回总览页