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中的方案,就能夠輕鬆地對有興趣的標的,實作走勢預測。

完整程式碼

延伸閱讀

相關連結

返回總覽頁
Procesing