Dự Đoán Giá Chứng Khoán SP500 Sử Dụng LSTM

Lời mở đầu

Ở bài viết này, mình sẽ xây dựng mô hình hơn giản để áp dụng vào tập dữ liệu giá chứng khoáng. Mục tiêu của bài này là chúng ta sẽ dự đoán chỉ số S&P 500 sử dụng LSTM. Các bạn có nhu cầu tìm hiểu thêm về chỉ số sp 500 có thể đọc thêm ở https://vi.wikipedia.org/wiki/S%26P_500. Đây là một ứng dụng nhỏ, không có ý nghĩa nhiều ở thực tế do khi phân tích chứng khoán, ta còn xét thêm rất nhiều yếu tố phụ nữa. Mô hình này thực chất chỉ là một trong những mô hình chơi chơi.

Dẫn nhập

Phân tích dữ liệu

Các bạn có thể download dữ liệu ở https://github.com/AlexBlack2202/alexmodel/blob/master/GSPC.csv

Đầu tiên, như thường lệ, chúng ta sẽ import các thư viện cần thiết để sử dụng.

 1import numpy as np # linear algebra
 2import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
 3
 4from subprocess import check_output
 5from keras.layers.core import Dense, Activation, Dropout
 6from keras.layers.recurrent import LSTM
 7from keras.models import Sequential
 8from sklearn.cross_validation import  train_test_split
 9import time #helper libraries
10from sklearn.preprocessing import MinMaxScaler
11import matplotlib.pyplot as plt
12from numpy import newaxis

Đọc dữ liệu lên:

 1
 2file_name ='GSPC.csv'
 3
 4prices_dataset =  pd.read_csv(file_name, header=0)
 5
 6``
 7
 8Xem kích thước của dữ liệu:
 9
10```python
11print(prices_dataset.shape)
1(17114, 7)

Kết quả là ta có 17114 ngàn dòng và 7 cột. Thử show 10 row đầu tiên của dữ liệu lên xem như thế nào.

1print(prices_dataset.head())
1         Date       Open       High        Low      Close  Adj Close   Volume
20  1950-11-09  19.790001  19.790001  19.790001  19.790001  19.790001  1760000
31  1950-11-10  19.940001  19.940001  19.940001  19.940001  19.940001  1640000
42  1950-11-13  20.010000  20.010000  20.010000  20.010000  20.010000  1630000
53  1950-11-14  19.860001  19.860001  19.860001  19.860001  19.860001  1780000
64  1950-11-15  19.820000  19.820000  19.820000  19.820000  19.820000  1620000

Cột đầu tiên là ngày, sau đó là giá mở cửa, giá giao dịch cao nhất, giá giao dịch thấp nhât, giá đóng cử, giá đóng cửa đã điều chỉnh, khối lượng giao dịch.

Plot đồ thị của mã SP500 lên:

 1import matplotlib.pyplot as plt
 2
 3plt.plot(prices_dataset.Open.values, color='red', label='open')
 4plt.plot(prices_dataset.Close.values, color='green', label='close')
 5plt.plot(prices_dataset.Low.values, color='blue', label='low')
 6plt.plot(prices_dataset.High.values, color='black', label='high')
 7plt.title('stock price')
 8plt.xlabel('time [days]')
 9plt.ylabel('price')
10plt.legend(loc='best')
11plt.show()

Hình ảnh đừng đồ thị của chỉ số sp 500

Hình với số lượng hơi nhiều nên khó phân biệt được giá trị của dữ liệu, chúng ta thử show đồ thị của 50 ngày cuối cùng trong dữ liệu.

 1prices_dataset_tail_50 = prices_dataset.tail(50)
 2
 3plt.plot(prices_dataset_tail_50.Open.values, color='red', label='open')
 4plt.plot(prices_dataset_tail_50.Close.values, color='green', label='close')
 5plt.plot(prices_dataset_tail_50.Low.values, color='blue', label='low')
 6plt.plot(prices_dataset_tail_50.High.values, color='black', label='high')
 7plt.title('stock price')
 8plt.xlabel('time [days]')
 9plt.ylabel('price')
10plt.legend(loc='best')
11plt.show()

Hình ảnh đừng đồ thị của chỉ số sp 500

Hình ảnh trông khá rõ ràng và trực quan hơn rất nhiều.

Chúng ta sẽ bỏ đi cột DATE,Adj Close,Volume đi. Các cột đó không cần thiết cho quá trình dự đoán.

1
2prices_dataset_dropout = prices_dataset.drop(['Date','Adj Close','Volume'], 1)

Scale dữ liệu

Khi sử dụng ANN, chúng ta thông thường sẽ scale dữ liệu input về đoạn [-1,1]. Trong python, thư viện sklearn đã hỗ trợ cho chúng ta sẵn các hàm scale dữ liệu cần thiết.

 1# Scale data
 2def normalize_data(df):
 3   min_max_scaler = MinMaxScaler()
 4   df['Open'] = min_max_scaler.fit_transform(df.Open.values.reshape(-1,1))
 5   df['High'] = min_max_scaler.fit_transform(df.High.values.reshape(-1,1))
 6   df['Low'] = min_max_scaler.fit_transform(df.Low.values.reshape(-1,1))
 7   df['Close'] = min_max_scaler.fit_transform(df.Close.values.reshape(-1,1))
 8   return df
 9
10prices_dataset_norm = normalize_data(prices_dataset_dropout)

Phân chia tập train và test.

Chúng ta sẽ chia dữ liệu thành 2 phần với 80% là train và 20% còn lại là test. Chọn seq_len=20, các bạn có thể test với các seq len khác, và sau đó chuyển dữ liệu về dạng numpy array để dễ dàng thực hiện các phép chuyển đổi.

 1
 2def generate_data(stock_ds, seq_len):
 3   data_raw = stock_ds.as_matrix()
 4   data = []
 5
 6   # create all possible sequences of length seq_len
 7   for index in range(len(data_raw) - seq_len):
 8       data.append(data_raw[index: index + seq_len])
 9   return data
10
11#data as numpy array
12def generate_train_test(data_ds,split_percent=0.8):
13   print(len(data_ds))
14   data = np.asarray(data_ds)
15
16   data_size = len(data)
17   train_end = int(np.floor(split_percent*data_size))
18
19   x_train = data[:train_end,:-1,:]
20   y_train = data[:train_end,-1,:]
21
22
23
24   x_test = data[train_end:,:-1,:]
25   y_test = data[train_end:,-1,:]
26
27   return [x_train, y_train, x_test, y_test]
28
29
30
31seq_len = 20 # choose sequence length
32
33seq_prices_dataset = generate_data(prices_dataset_norm,seq_len)
34
35x_train, y_train, x_test, y_test = generate_train_test(seq_prices_dataset, 0.8)
36
37print('x_train.shape = ',x_train.shape)
38print('y_train.shape = ', y_train.shape)
39print('x_test.shape = ', x_test.shape)
40print('y_test.shape = ',y_test.shape)

Kết quả:

1x_train.shape =  (13675, 19, 4)
2y_train.shape =  (13675, 4)
3x_test.shape =  (3419, 19, 4)
4y_test.shape =  (3419, 4)

Xây dựng mô hình sử dụng keras

Ở đây mình sử dụng keras xây dựng mô hình ANN. Mô hình của mình xây dựng gồm:

 1model = Sequential()
 2
 3model.add(LSTM(
 4   input_dim=4,
 5   output_dim=50,
 6   return_sequences=True))
 7model.add(Dropout(0.2))
 8
 9model.add(LSTM(
10   100,
11   return_sequences=False))
12model.add(Dropout(0.2))
13
14model.add(Dense(
15   output_dim=4))
16model.add(Activation('linear'))
17
18
19
20model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy'])
21checkpoint = ModelCheckpoint(filepath='sp500_stockperdict.h5', verbose=1, save_best_only=True)
22hist = model.fit(x_train, y_train, epochs=300, batch_size=128, verbose=1, callbacks=[checkpoint], validation_split=0.2)

Sau một thời gian chạy, mình cũng thu được model. Các bạn quan tâm có thể download model của mình huấn luyện được tại https://drive.google.com/open?id=1ImHQM9yWmOjpF5tjmSI9oqAi5BORa9Rs . Tiến hành plot dữ liệu tập test lên xem kết quả như thế nào.

 1
 2model =load_model('sp500_stockperdict.h5')
 3
 4
 5y_hat = model.predict(x_test)
 6
 7ft = 3 # 0 = open, 1 = highest, 2 =lowest , 3 = close
 8
 9plt.plot( y_test[:,ft], color='blue', label='target')
10
11plt.plot( y_hat[:,ft], color='red', label='prediction')
12
13plt.title('future stock prices')
14plt.xlabel('time [days]')
15plt.ylabel('normalized price')
16plt.legend(loc='best')
17
18plt.show()
19
20from sklearn.metrics import mean_squared_error
21
22# 0 = open, 1 = highest, 2 =lowest , 3 = close
23print("open error: ")
24print(mean_squared_error(y_test[:,0], y_hat[ :,0]))
25
26print("highest error: ")
27print(mean_squared_error(y_test[:,1], y_hat[ :,1]))
28
29print("lowest error: ")
30print(mean_squared_error(y_test[:,2], y_hat[ :,2]))
31
32print("close error: ")
33print(mean_squared_error(y_test[:,3], y_hat[ :,3]))

hình chứng khoán

1open error:
20.0009739211460315127
3highest error:
40.0010539412808401607
5lowest error:
60.0010066509540756113
7close error:
80.0010840500965408758

Hiện đã có bản tensorflow 2 có tích hợp keras, mình update lại code

  1
  2from re import T
  3import numpy as np
  4# linear algebra
  5import pandas as pd
  6from tensorflow.keras.models import Sequential
  7from tensorflow.keras.layers import Dense
  8from tensorflow.keras.layers import LSTM
  9from sklearn.preprocessing import MinMaxScaler
 10import tensorflow as tf
 11import joblib
 12
 13import matplotlib
 14matplotlib.use('TkAgg')
 15import matplotlib.pyplot as plt
 16
 17
 18file_name ='GSPC.csv'
 19
 20
 21prices_dataset =  pd.read_csv(file_name, header=0)
 22
 23
 24# prices_dataset_dropout = prices_dataset.drop(['Date','Adj Close','Volume'], 1)
 25prices_dataset_dropout=prices_dataset.reset_index()['Close']
 26
 27
 28scaler=MinMaxScaler(feature_range=(0,1))
 29prices_dataset_norm=scaler.fit_transform(np.array(prices_dataset_dropout).reshape(-1,1))
 30joblib.dump(scaler, 'scaler.alex')
 31
 32
 33print(prices_dataset_norm[:10])
 34
 35
 36def generate_data(stock_ds, seq_len,predict_next_t):
 37   dataX, dataY = [], []
 38   for i in range(len(stock_ds)-seq_len-1):
 39       dataX.append(stock_ds[i:(i+seq_len)])
 40       dataY.append(stock_ds[i + seq_len+predict_next_t])
 41   return np.array(dataX), np.array(dataY)
 42
 43#data as numpy array
 44def generate_train_test(data_x,data_y,split_percent=0.8):
 45
 46   train_end = int(np.floor(split_percent*data_x.shape[0]))
 47
 48   x_train,x_test=data_x[:train_end,:],data_x[train_end:,:]
 49   y_train,y_test = data_y[:train_end],data_y[train_end:]
 50   return x_train,y_train,x_test,y_test
 51
 52
 53
 54seq_len = 100 # choose sequence length
 55predict_next_t = 1 # 0 is next date, 1 is 2 next date
 56
 57data_x, data_y = generate_data(prices_dataset_norm,seq_len,predict_next_t)
 58
 59x_train,y_train,x_test,y_test = generate_train_test(data_x,data_y, 0.8)
 60
 61
 62x_train =x_train.reshape(x_train.shape[0],x_train.shape[1] , 1)
 63x_test = x_test.reshape(x_test.shape[0],x_test.shape[1] , 1)
 64print('x_train.shape = ',x_train.shape)
 65print('y_train.shape = ', y_train.shape)
 66print('x_test.shape = ', x_test.shape)
 67print('y_test.shape = ',y_test.shape)
 68
 69
 70
 71model = Sequential()
 72
 73   # input_dim=4,
 74   # output_dim=50,
 75model.add(LSTM(units=100,input_shape=x_train.shape[1:],
 76   return_sequences=True))
 77
 78model.add(LSTM(
 79   100,
 80   return_sequences=False))
 81model.add(Dense(1))
 82
 83
 84model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy'])
 85checkpoint = tf.keras.callbacks.ModelCheckpoint(filepath='my_model_stock.h5', verbose=1, save_best_only=True)
 86hist = model.fit(x_train, y_train, epochs=3, batch_size=64, verbose=1, callbacks=[checkpoint], validation_split=0.2)
 87
 88from tensorflow.keras.models import load_model
 89print('load model')
 90model =load_model('my_model_stock.h5')
 91
 92print('predict')
 93y_test = y_test.reshape(y_test.shape[0])
 94# train_predict=model.predict(x_train)
 95test_predict=model.predict(x_test)
 96print('invert')
 97print(y_test.shape)
 98# train_predict=scaler.inverse_transform(train_predict)
 99
100# scaler = joblib.load('scaler.alex')
101
102y_hat=scaler.inverse_transform(test_predict)
103y_test=scaler.inverse_transform(y_test.reshape(-1, 1))
104print(y_hat.shape)
105# y_hat = model.predict(x_test)
106# import matplotlib
107# matplotlib.use('GTKAgg')
108# print('plot')
109
110plt.plot( y_test, color='blue', label='target')
111
112plt.plot( y_hat, color='red', label='prediction')
113print('plot complete')
114plt.title('future stock prices')
115plt.xlabel('time [days]')
116plt.ylabel('normalized price')
117plt.legend(loc='best')
118print('plot show')
119plt.savefig("mygraph.png")
120plt.show()

Kết quả của mô hình trông khá tốt, về hình dạng thì khá tương đồng với kết quả. Chúng ta có thể cải tiến model bằng cách nâng số lượng layer/ hidden node.

Cảm ơn các bạn đã theo dõi. Hẹn gặp bạn ở các bài viết tiếp theo.

Comments