機器學習小項目:從NIFTY指數的當日股價預測股票收盤價格,對比各種模型在時序資料預測的效果
如果你像我一樣涉足股票交易,你可能想知道如何在收盤時判斷股票的走勢——它會在收盤價上方收盤,還是不會?因為确實存在日内模式——人們總是告訴你股票交易活動是“波浪式”的,而且在午餐時間事情往往會放慢一點,并且在接近尾聲的時候可能會發生大的波動。
對于這個項目——(谷歌 Colab 筆記本在後面公開)——我使用 NIFTY 指數(印度),我們正在檢視每分鐘的資料。我們根據開盤價對每個時間序列進行标準化,是以每個點隻是它與開盤價之間的內插補點。印度指數開盤6小時15分鐘左右,意思是應該有375分鐘。我使用了 2018 年至 2019 年的資料,并在資料點少于 372 個(隻有 1 或 2 個)的任何一天删除。那麼問題就變成了——我們需要多少曆史視窗來預測股票的最終走勢?你能在第一個小時之後說出來嗎?或者機器可以在 6.25 小時中的 3 小時後學習模式嗎?
我将嘗試使用 sktime 庫(一個時間序列庫)以及 XGBoost 和 keras-TCN(一個時間卷積網絡庫)為 NIFTY 回答這個問題。我将在這裡重點介紹的是 ROCKET transform 和時間序列分類器。這裡實際上有大量有趣的時間序列分類器,其中許多屬于符号表示類型(将時間序清單示為字母或符号序列,如 DNA)。我發現在這個時間序列中,它們中的大多數都沒有太大的競争力,是以我專注于實際上足夠好用的 2 個,可以在現實生活中部署。
資料
資料來自這個 Kaggle,我們使用 NIFTY,而不是 BANK NIFTY 作為我們選擇的指數。此外,我們隻訓練 2018-2019 年,将這個集合 80/20 劃分,不做任何打亂,這樣我們就可以看到過去訓練的東西如何推廣到未來,即,看看是否有某種概念漂移繼續。
預處理資料——隻需從其餘的值中減去第一個值,使其等于 0,然後删除該列。将第一個 X 小時數作為您的訓練資料。我從 4 小時開始,這意味着 239 個時間點(第 240 個是您要預測的時間點)。然後,通過除以 100 來縮放數字,以獲得大緻在 [0,1] 範圍内的數字。要建立二進制目标變量,隻需将收盤價與開盤價進行比較,如果收盤價更高,我們編碼為 1,否則為 0。此外,您可能想嘗試使用 tsmoothie 的 LOWESS 來平滑時間序列.它在大局中沒有太大變化。這是一個每日時間序列及其平滑版本的圖:
Sktime 分類器要求資料以一種奇怪的格式存儲——一個 Pandas DataFrame,除了每個時間戳的一列(239 個特征,一個形狀數組 (N, 239),你有 1 列,其中每一行或每個元素 該列本身就是一個pandas Series,意思是一個 (N,1) 數組,其中單個特征是 239 個元素系列。
模型選擇
以下是我使用的模型以及它們的配置方式。
ROCKET——這個基于随機卷積核,是以基本上,它就像一個淺層卷積神經網絡,沒有非線性激活、擴張或任何花哨的東西。它被認為是快速和 SOTA 的,而且效果很好。預設情況下,ROCKET 使用 10000 個核心。從技術上講,我使用的是 MINIROCKET,它會生成特征——但是你仍然必須選擇一個分類器來從這些特征中學習。為此,他們推薦Ridge 分類器或邏輯回歸。我發現使用 RidgeCV() 可以獲得不錯的性能,并且它比 LogisticRegressionCV 更快。代碼看起來像這樣:
rocket = MiniRocket(random_state = 2468)
trainx_transform = rocket.fit_transform(Xtrain_sktime)
valx_transform = rocket.transform(Xtest_sktime)
clf = RidgeClassifierCV(alphas = np.logspace(-4,4, num = 100), normalize = True)
clf.fit(trainx_transform, ytrain_sktime)
predicted = clf.predict(valx_transform)
print("Accuracy with Rocket: %2.3f" % accuracy_score(ytest_sktime, predicted))
print("Matthews CC:%2.3f" % matthews_corrcoef(ytest_sktime, predicted))
複制
Time Series Forest——這個很有趣——它不是将每個時間戳作為一個特征并将其扔到基于樹的分類器中,而是擷取時間序列的間隔(模型的 HP 有多少個間隔),并找到一些特殊的特征 像每個人的平均值、偏差和斜率這樣的統計資料,并将它們用作特征。這意味着保留時間戳的順序,而如果您隻是将每個時間戳視為一個獨立的特征,那麼您的算法并不關心它們的排列順序。然後将這些特征交給 DecisionTreeClassifier。代碼如下所示:
steps = [
("extract",RandomIntervalFeatureExtractor(n_intervals = "sqrt",features=[np.mean, np.std, _slope])),("clf", DecisionTreeClassifier())]
time_series_tree = Pipeline(steps)
tsf = TimeSeriesForestClassifier(estimator=time_series_tree,
n_estimators = 100,
criterion = "entropy",
bootstrap=True,
oob_score=True,
random_state = 2222,
n_jobs=-1)
tsf.fit(Xtrain_sktime, ytrain_sktime)
print("Accuracy: ", accuracy_score(ytest_sktime, tsf.predict(Xtest_sktime)))
print("MCC: ", matthews_corrcoef(ytest_sktime, tsf.predict(Xtest_sktime)))
複制
XGBoost——我還用 XGBClassifier() 訓練了一個模型,使用每個時間戳作為一個特征。
TCN——為簡單起見,我使用基于 keras/tensorflow 的庫 keras-tcn。它使用擴張的核心。我沒有更改任何預設設定,隻是確定最後一層使用 log-loss 作為損失函數。代碼如下所示:
i = Input(shape=(trainx.shape[-2], 1))
m = TCN()(i)
m = Dense(1, activation = 'sigmoid')(m)
early_stopping = EarlyStopping(patience = 50, restore_best_weights=True, min_delta = 0.000)
reduceLR = ReduceLROnPlateau(factor = 0.5, patience = 5, min_delta = 0.01)
from keras.optimizers import *
model = Model(inputs=[i], outputs=[m])
model.summary()
model.compile(loss = "binary_crossentropy", optimizer = Adam(lr = 1e-3)
model.reset_states()
model.fit(trainx, trainy,validation_data = (valx, y_test),
shuffle = True,
callbacks = [early_stopping, reduceLR],
batch_size = 64,
epochs = 200)
複制
模型評估 - 4 小時視窗
以下是 4 小時視窗的結果。TCN 花費的時間最長,即使提前停止,并且有超過 90k 的參數。相比之下,ROCKET真的是一眨眼就完成了。
模型評估 - 5 小時視窗
您會期望這會獲得更好的結果,因為不确定性隻是一天中最後 1.25 小時發生的事情。以下是使用相同學習器、相同參數等的結果。
最後本文的隻是對比幾個模型的準确程度,也許可以用在實際的資料中,但是請在使用前進行詳細的驗證。
本文的代碼在這裡:https://colab.research.google.com/drive/11kjEsGcwQwm7lhvx25hU61UaEJ8XdxNw?usp=sharing
作者:Peijin Chen