「數據遊戲」:使用 ARIMA 算法預測三往後招商銀行收盤價

做者:天瓊,「數據遊戲」優勝隊伍成員html

介紹

本文整理記錄了參與的一次小型數據分析競賽「數據遊戲」,競賽目標是預測2019年5月15日A股閉市時招商銀行600036的股價。算法

主要思路是利用ARIMA算法作時間序列預測。api

使用的數據是公開的數據集 tushare。安全

拿到題目和數據以後,首先結合既往經歷,以爲想要預測準股價,自己是一個不可能的事情,尤爲是A股。微信

影響股價的因素很是複雜並且不透明,以及金融投資領域具備的反身性理論,使得此次預測更多偏向於實驗性質,同時對競爭結果不要有太高的指望。異步

預測得準,是你的運氣;預測的爛,也不會影響你從中學到什麼。學習第一,比賽第二吧。函數

鑑於以上,本次預測只使用了close的時間序列。更多的數據其實並無什麼用。post

首先了解下本文的 ARIMA 建模過程

  1. 獲取時間序列數據 ;
  2. 觀察數據是否爲平穩時間序列;
  3. 對於非平穩時間序列要先進行d階差分運算,轉化爲平穩時間序列;
  4. 對處理獲得的平穩時間序列,求它的階數p,q;
  5. 根據ARIMA算法建模,ARIMA(data, order=(p, d, q))
  6. 模型檢驗和調優
  7. 預測

初學的小夥伴們可能對這個過程並不熟悉,不要緊,先背下來。學習

對Python不熟悉的小夥伴們, 我給你們總結了幾句車軲轆話,你們先強行了解下。測試

  1. 獲取時間序列數據:
data = pd.read_excel(‘600036.xlsx’, index=None)
train = data[‘close’]
  1. 觀察數據是否爲平穩時間序列;對於非平穩時間序列要先進行d階差分運算,轉化爲平穩時間序列;
adf_data = sts.adfuller(train)
diff = train.diff(1)
  1. 對處理獲得的平穩時間序列,求它的階數p, q
sm.tsa.arma_order_select_ic(train, max_ar=8, max_ma=8, ic=[‘aic’, ‘bic’, ‘hqic’])
  1. 根據ARIMA算法建模
ARMAModel = sm.tsa.ARIMA(train, order=(4,1,2)).fit()
  1. 模型檢驗和調優
train_shift = train.shift(1)
pred_recover = predicts.add(train_shift)
np.sqrt( sum( (pred_recover -train) ** 2)/train.size )
  1. 預測
f = ARMAModel.forecast(3)

以上,是本文的核心代碼,你們若是一時看不懂,能夠跳過。
能夠看看下面更詳細的步驟。

導入數據並處理

# 導入必須的模塊
import tushare as ts  #使用的公開的數據
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
import statsmodels.tsa.stattools as sts

import wconfig as wc  #自定義打印輸出控制模塊,與本次競賽無關
wc.dispy() 

# 導入數據,從去年1月開始便可
data = ts.get_hist_data('600036', start='2018-01-08', end='2019-05-10').sort_index()
#data = ts.get_hist_data('600036', start='2018-04-01').sort_index().reset_index()

# 數據安全和源數據備份
#data.to_excel('600036.xlsx')

# 源數據可靠性檢查。12日發現tushare的數據有錯誤,須要手工矯正(13日該數據恢復正常)。
print('tushare中5月10日close股價爲: %.2f元,與實際不符!' % (data['close']['2019-05-10']))
data['close']['2019-05-10']=33.61
print('當日實際收盤價應爲: %.2f元' % data['close']['2019-05-10'])

# 只取 close 字段做爲訓練數據
train = data['close']
train.index = pd.to_datetime(train.index)  # 將字符串索引轉換成時間索引
train.tail()
train.tail().index
tushare中5月10日close股價爲: 33.48元,與實際不符!
當日實際收盤價應爲: 33.61元

檢驗時間序列的穩定性

ARIMA算法要求時間序列穩定,因此在建模以前,要先檢驗時間序列的穩定性。
adfuller就是用來幹這個的。
adfuller 全稱 Augmented Dickey–Fuller test, 即擴展迪基-福勒檢驗,用來測試平穩性。

先作一個解釋器, 讓 adfuller 的輸出結果更易讀易理解:

def tagADF(t):
    result = pd.DataFrame(index=["Test Statistic Value", "p-value", "Lags Used", 
                                 "Number of Observations Used", 
                                 "Critical Value(1%)", "Critical Value(5%)", "Critical Value(10%)"],
                          columns=['value']
    )
    result['value']['Test Statistic Value']=t[0]
    result['value']['p-value']=t[1]
    result['value']['Lags Used']=t[2]
    result['value']['Number of Observations Used'] = t[3]
    result['value']['Critical Value(1%)']=t[4]['1%']
    result['value']['Critical Value(5%)']=t[4]['5%']
    result['value']['Critical Value(10%)']=t[4]['10%']
    print('t is:', t)
    return result

adfuller檢驗是檢查時間序列平穩性的統計測試之一。 這裏的零假設是:序列 train 是非平穩的。
測試結果包括測試統計和差別置信水平的一些關鍵值。 若是測試結果中的 P-value 小於臨界值,咱們能夠拒絕原假設並說該序列是平穩的。

adf_data = sts.adfuller(train)
tagADF(adf_data)

檢驗結果顯示,p-value=0.414, 遠遠大於5%的臨界值,說明零假設是成立的,即序列 train 是非平穩的。

t is: (-1.7325346908056185, 0.4144323576685054, 0, 322, {‘1%’: -3.4508226600665037, ‘5%’: -2.870558121868621, ‘10%’: -2.571574731684734}, 523.9067372199033)

時間序列平穩化

爲了讓時間序列平穩,須要對 train 序列作差分運算:

# df.diff 差分運算,默認是後一行減前一行
# http://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.diff.html?highlight=diff#pandas.DataFrame.diff
diff = train.diff(1).dropna()  # df.dropna 刪除有缺失值的行
plt.figure(figsize=(11,6))  # 指定顯示大小
plt.plot(diff, label='Diff')  # 繪製趨勢圖
plt.legend(loc=0)  # 顯示圖例,loc指定圖例位置,0爲最佳位置。

關於時間序列穩定性的判斷標準,可參考這篇博客:https://blog.csdn.net/u012735...

檢驗差分數據的平穩性

檢驗差分後數據的平穩性,和第一次驗證方法相同

adf_Data1 = sts.adfuller(diff)
tagADF(adf_Data1)  # p-value很小,零假設不成立,所以,diff數據序列符合平穩性要求。

肯定ARIMA的階數p,q

在這裏,先簡單解釋下本身對 ARIMA 算法的理解。不正確的地方請你們指點。

ARIMA算法認爲時間序列上 t 時刻的值由2部分構成,第一部分是由以前p項歷史值決定的,好比15日的收盤價是12日,13日,14日的收盤價的線性迴歸,用AR§表示。

可是這個線性迴歸的輸出值確定和15日的實際收盤價有一個偏差,假設是e。

因此第二部分就是若是表示這個 e 。

這個e 能夠認爲是以前q項偏差的線性迴歸,用MA(q)表示。

兩部分合起來就是ARMA。

而後爲了讓時間序列平穩,再加個d 階差分操做功能,就變成了ARIMA算法。

因此在使用 ARIMA 算法以前,須要先肯定(p, d, q)的值。

肯定ARIMA的階數p,q

# ARMA(p,q)是AR(p)和MA(q)模型的組合,關於p和q的選擇,一種方法是觀察自相關圖ACF和偏相關圖PACF, 
# 另外一種方法是經過藉助AIC、BIC等統計量自動肯定。 
ic = sm.tsa.arma_order_select_ic(
    train, 
    max_ar=8, 
    max_ma=8, 
    ic=['aic', 'bic', 'hqic']
)
ic

創建模型並擬合數據

注意ARIMA的參數中,輸入數據 應該是原始數據 train,ARIMA 會根據 d 的值,對原始數據作 d 階差分運算。
d的含義是,輸入序列須要先通過一個d階的差分,變成一個平穩序列後才能進行數據擬合。

ARMAModel = sm.tsa.ARIMA(train, order=(4,1,2)).fit()  # order=(p,d,q)
# fittedvalues和diff對比
plt.figure(figsize=(11, 6))
plt.plot(diff, 'r', label='Orig')
plt.plot(ARMAModel.fittedvalues, 'g',label='ARMA Model')
plt.legend()

模型評估和調優

# 樣本內預測
predicts = ARMAModel.predict()

# 由於預測數據是根據差分值算的,因此要對它一階差分還原
train_shift = train.shift(1)  # shift是指series日後平移1個時刻
pred_recover = predicts.add(train_shift).dropna()  #這裏add是指兩列相加,按index對齊

# 模型評價指標 1:計算 score
delta = ARMAModel.fittedvalues - diff
score = 1 - delta.var()/train.var()
print('score:\n', score)

# 模型評價指標 2:使用均方根偏差(RMSE)來評估模型樣本內擬合的好壞。
#利用該準則進行判別時,須要剔除「非預測」數據的影響。
train_vs = train[pred_recover.index]  # 過濾沒有預測的記錄
plt.figure(figsize=(11, 6))
train_vs.plot(label='Original')
pred_recover.plot(label='Predict')
plt.legend(loc='best')
plt.title('RMSE: %.4f'% np.sqrt(sum((pred_recover-train_vs)**2)/train_vs.size))
plt.show()

# 局部數據觀察
train_t = train_vs.tail(15)
pred_t = pred_recover.tail(15)
plt.figure(figsize=(11, 6))
train_t.plot(label='Original')
pred_t.plot(label='Predict')
plt.legend(loc='best')
plt.title('RMSE: %.4f'% np.sqrt(sum((pred_t-train_t)**2)/train_t.size))
plt.show()

預測目標

使用 forecast 對樣本外的時間序列進行預測。

關於foreast和predict的區別:
predict 能夠對樣本內和樣本外的進行預測,結果是同樣的。
舉例說明:forecast(10),表示對將來10個點進行預測,可是能夠用model.fittedvalues查看樣本內點的擬合值;
而predict(start,end)裏面的參數0表示樣本內的第一個數,以此類推。
若是想要預測樣本外的數,須要將start設置爲len(data)+1,即數據長度+1,才表示預測樣本外的第一個數字。
而 forecast函數,是對樣本外的數據進行預測。
可是這兩個函數的預測結果是同樣的。
另外,須要提到的是,ARIMA算法通常只能預測一點點,越長越不許確,即使是簡單的正弦函數也不能準確預測。
# 預測15日close股價,即10日以後的第三個交易日的收盤價
# 可是經過上面的局部數據觀察發現,預測的數據趨勢會延遲1個交易日,因此就取f[0][1]
f = ARMAModel.forecast(3)  # 樣本外預測
print('5月15日close時的股價爲:%.2f 元' % f[0][1])


#----結束-----

5月15日close時的股價爲:33.82 元。

順便安利下此次參與的小競賽「數據遊戲」,是由我的發起、異步社區(郵電出版社)贊助的活動。

整體來講,這個比賽氛圍適中,沒有太大的心理負擔。

因此感受比較適合打算初入的小夥伴,參與以後,這樣的經驗對之後的學習進階和職業發展都有好處。

Ad Time

學習更多數據科學知識請關注微信公衆號:read_csv

參與數據科學活動請加 QQ 羣:759677734

相關文章
相關標籤/搜索