はじめに
ニューラルネットワークの勉強のため、Kerasを使って米国株時系列データを機械学習させてみました。時系列データに最適と言われるRNN(LSTM)モデルを使いました。
動作環境
動作環境は以下の通りです。
python==3.8.6
numpy==1.19.4
matplotlib==3.3.3
pandas==1.1.4
pandas_datareader==0.9.0
ニューラルネットワークに何を予測させるか
任意の銘柄について、過去20営業日(約1ヶ月)の株価と取引出来高をインプットとして、翌営業日に過去20営業日の高値を更新するかを予測させることを目標にします。
データの準備
今回分析の対象としたのは、S&P500採用銘柄(約500銘柄)の約5年分の日次データ(pandas-DataFrame形式)です。データはpandas-datareaderを使ってダウンロードし、pandas-DataFrameオブジェクトをpickleして保管しました。データ総量は30MB程度ですが、ダウンロードには20分くらいかかりました。
import pandas_datareader as pdr
from pandas import to_pickle
# Read S&P500 tickers
with open('SP500.txt', "r") as f_in: #SP500.txtに米国株のティッカーシンボルを格納
ticker_list = [v.rstrip() for v in f_in.readlines()]
# Get data from Yahoo
pd_data = pdr.get_data_yahoo(ticker_list)
# pickele the pandas-DataFrame object
to_pickle(pd_data, 'pd_data.pkl')
中身はこんな感じです。簡単のため銘柄をAAPL, IBM, MSFT の3つだけにしています。
Attributes Adj Close Close ... Open Volume
Symbols AAPL IBM MSFT AAPL ... MSFT AAPL IBM MSFT
Date ...
2015-12-21 24.859543 107.960846 50.034130 26.832500 ... 54.880001 190362400.0 5617500.0 37246300.0
2015-12-22 24.836382 109.896957 50.508629 26.807501 ... 54.990002 131157600.0 4263800.0 28322200.0
2015-12-23 25.156017 110.382957 50.937519 27.152500 ... 55.700001 130629600.0 5164900.0 27279800.0
2015-12-24 25.021677 110.151917 50.800640 27.007500 ... 55.860001 54281600.0 1495200.0 9558500.0
2015-12-28 24.741419 109.641991 51.056156 26.705000 ... 55.349998 106816800.0 3143400.0 22458300.0
... ... ... ... ... ... ... ... ... ...
2020-12-14 121.779999 123.529999 214.199997 121.779999 ... 213.100006 79184500.0 5050000.0 28798400.0
2020-12-15 127.879997 125.930000 214.130005 127.879997 ... 215.169998 157572300.0 4359600.0 27018100.0
2020-12-16 127.809998 125.550003 219.279999 127.809998 ... 214.750000 98208600.0 4530100.0 35023300.0
2020-12-17 128.699997 125.550003 219.419998 128.699997 ... 219.869995 94215500.0 3787600.0 32473500.0
2020-12-18 127.561699 125.680000 218.440002 127.561699 ... 218.589996 51084709.0 1906637.0 16891181.0
[1259 rows x 18 columns]
学習用データの作成
以下gen_data()は、(ダウンロードした)pandas-DataFrameオブジェクト、サンプル数、時系列データ長(今回は20)を引数として学習用データ(X)と正解データ(t)を作成します。学習用データは、ランダムな銘柄とランダムな開始日から始まる所定期間(20+1営業日)を抽出して正規化します。正解データは、その期間の最後の日の株価が、期間中の最高値の場合は、正解データに1を格納、それ以外の場合は0を格納します。
尚、引数arg1==0(省略時値)の場合、過去sep=250営業日より前の開始日で時系列を抽出し、これを学習用データとします。arg1!=0の場合、過去250営業日以降の開始日で時系列を抽出し、これを評価テスト用データとします。
import numpy as np
import pandas as pd
import random
def gen_data(dataframe, nb_of_samples, length, arg1=0):
ticker_list = df['Close'].columns #dfから銘柄リストを作成
sequences = [] #時系列データを格納
target = [] #正解データを格納
sep = 250 #学習用データと評価テスト用データを分けるセパレーター
max_start_index = len(dataframe) - length - sep
count = 0 #動作には関係ありませんが、最後の日が最高値のサンプル数をカウント
while len(sequences) < nb_of_samples:
if arg1 == 0: #過去sep=250営業日より前の時系列を抽出(学習用データ)
start_index = np.random.choice(max_start_index)
else: #過去sep=250営業日以降の時系列を抽出(評価テスト用データ)
start_index = np.random.choice(sep) + max_start_index
end_index = start_index + length + 1
ticker = random.choice(ticker_list) #ランダムな銘柄を選択
temp_df = dataframe[['Close', 'Volume']].xs(ticker, level='Symbols', axis=1)[start_index:end_index]
#正規化
f = lambda x: (x - np.nanmin(x)) / (np.nanmax(x) - np.nanmin(x))
temp_df = temp_df.apply(f).astype('float32')
if (temp_df.count() == length + 1).all(): #データ中に欠損値がない場合
data_array = temp_df.values
sequences.append(data_array[:-1])
if data_array[-1][0] == 1: #期間中の最後の日が最高値の場合
target.append(1)
count = count + 1
print(count) #進捗確認を兼ねてprint
else:
target.append(0)
#学習用データを作成
X = np.array(sequences).reshape(len(sequences), length, 2) #株価と出来高で2
#正解データを作成
t = np.array(target).reshape(len(target))
return X, t
len_sequence = 20 #時系列データ長
num_training_samples = 10000 #学習データのサンプル数
df = pd.read_pickle('pd_data.pkl') #pickleしたpandas-DataFrameの読み込み
X, t = gen_data(df, num_training_samples, len_sequence)
モデルの作成
簡単なLSTMの学習モデルを作成しました。LSTMについては,以下がとても分かりやすく勉強になりました。
LSTMネットワークの概要
コーディングは以下を参考にさせていただきました。
Kerasで基本的なRNN (LSTM) を試してみる
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import LSTM, Dense
input_dim = 2 #入力データの次元数:株価と出来高で2
output_dim = 1 #出力データの次元数
num_hidden_units = 128 #隠れ層のユニット数
#モデル定義
model = Sequential()
model.add(LSTM(
num_hidden_units,
input_shape=(len_sequence, input_dim),
return_sequences=False))
model.add(Dense(output_dim, activation='sigmoid'))
#モデルをコンパイル
model.compile(loss="binary_crossentropy", optimizer="rmsprop", metrics=["accuracy"])
model.summary()
学習と評価テスト
モデルを作成したら、いよいよ機械学習をさせます。今回は250営業日以前が開始日の10,000サンプルを用いてミニバッチサイズ200、学習エポック数100としました。
評価テストでは250営業日以降のランダム銘柄のランダム開始日1,000サンプルを新たに生成して使用しました。
batch_size = 200 #ミニバッチサイズ
num_of_training_epochs = 100 #学習エポック数
#学習
model.fit(
X, t,
batch_size=batch_size,
epochs=num_of_training_epochs,
validation_split=0.1
)
#評価
X_test, t_test = gen_data(df, 1000, len_sequence, arg1=1) #arg1=1を指定
loss, accuracy = model.evaluate(X_test, t_test)
print("\nloss:{} accuracy:{}".format(loss, accuracy))
結果
結果は以下の通りで、まずまずのaccuracyとなりました。実際この確度で高値更新を予測できたら、億万長者も夢じゃないですね!そううまくはいかないでしょうが。。。
ニューラルネットワークによって高値を更新しそうだと判定されたチャートについて、実際どのような形状のチャートなのか、形状はいわゆるチャート分析における有名形状に合致するのかを確認してみたいと思います。
loss:0.22777067124843597 accuracy:0.8999999761581421