米国株価(個別株)予測!!!(Python初学者)

目次

はじめに
1.ライブラリのインポート
2.データの読み込み
3.データの前処理
4.モデルの学習
5.モデルの評価
6.予測
7.考察

はじめに

最近、米国株に興味を持ち、米国株へ投資したいと思い、(株は素人です。)過去の個別株価を調べた上で株価の予測を行ってみました。Mac mini(intel),Google Colaboratoryを使用しています。

1.ライブラリのインポート

株価の情報取得の為、yahoo finance api2をインストールする。

!pip install yahoo_finance_api2

必要なライブラリをインポートします。

import numpy as np
import pandas as pd
import sys
from yahoo_finance_api2 import share
from yahoo_finance_api2.exceptions import YahooFinanceError
import matplotlib.pyplot as plt
import datetime
from tensorflow import keras
from tensorflow.keras import layers
import math
from sklearn.metrics import mean_squared_error

2.データの読み込み

yahoo finance api2よりShareと言うクラスを呼び出しました。
MSFTの株価のインスタンス取得する。
インスタンスのget_historicalメソッドを利用して、1日間隔で10年分のデータを取得する。
メソッドの実行がエラーの場合、エラーメッセージを発して終了します。

my_share = share.Share('MSFT')
symbol_data = None

try:
    symbol_data = my_share.get_historical(share.PERIOD_TYPE_YEAR,
                                          10,
                                          share.FREQUENCY_TYPE_DAY,
                                          1)
except YahooFinanceError as e:
    print(e.message)
    sys.exit(1)

結果として,データ全体が辞書型で渡されます。また、辞書のキーは、時刻、始値、
高値、低値、終値、取引量があります。辞書のバリューは、リスト型です。
注釈 時刻はエポックミリ秒です。


list(symbol_data.keys())
['timestamp', 'open', 'high', 'low', 'close', 'volume']
type(symbol_data['close'])
list

例として、10日分のデータを記します。

{'close': [284.4700012207031,
  281.7799987792969,
  289.9800109863281,
  277.3500061035156,
  274.7300109863281,
  264.5799865722656,
  269.5],
 'high': [284.94000244140625,
  284.1300048828125,
  290.8800048828125,
  286.3500061035156,
  279.25,
  272.3599853515625,
  273.75],
 'low': [276.2200012207031,
  280.1499938964844,
  276.7300109863281,
  274.3399963378906,
  271.2699890136719,
  263.32000732421875,
  265.07000732421875],
 'open': [277.7099914550781,
  283.9599914550781,
  282.5899963378906,
  285.5400085449219,
  274.80999755859375,
  270.05999755859375,
  271.69000244140625],
 'timestamp': [1651498200000,
  1651584600000,
  1651671000000,
  1651757400000,
  1651843800000,
  1652103000000,
  1652189400000],
 'volume': [35151100,
  25978600,
  33599300,
  43260400,
  37748300,
  47726000,
  39292300]}

データを俯瞰する為にグラフ化します。
横軸:時間 縦軸:終値 です。
注釈 fromtimestamp関数は、エポック秒を入力する為、1/1000にします。

dt = [datetime.datetime.fromtimestamp(ts/1000) for ts in symbol_data['timestamp']]
y = symbol_data['close']
plt.figure(figsize=(16,9))
plt.plot(dt, y, marker='.')

3.データの前処理

DataFrameに終値、時刻を格納する。
利益率にpct_changeを使用し変換する。 Aが当日の株価、Bが前日の株価の時、利益率は(A - B) / Bです。縦軸は利益率です。
標準化の為。訓練、検証、テストデータの性質が違います。(具体例 訓練範囲200ドル以下 検証範囲 200ドル以上〜300ドル以下 テスト範囲 300ドル以上)
注釈 dropnaメソッドを利用して、初日のNaNを削除する。削除しないと学習時に損失関数の値がNaNになります。


df = pd.DataFrame({'y':y}, index= dt)
df = df.pct_change().dropna(how='any')
df.plot(figsize=(16,9))

次に訓練/検証/テストデータに分割します。今回は、検証データとテストデータは120日としています。

test_size = 120
valid_size = 120
df_train = df.iloc[:-(test_size+valid_size)]
df_valid = df.iloc[-(test_size+valid_size):-test_size]
df_test = df.iloc[-test_size:]
plt.figure(figsize=(16,9))
plt.plot(df_train)
plt.plot(df_valid)
plt.plot(df_test)

データをモデルの入力と予測値に成形する。make_dataset関数を定義します。最初のwindow日数分を差し引きます。その後は、スライスで切り出しています。今回は、windowを7日に設定しています。この関数を訓練/検証/テストデータに対してそれぞれ適用しています。

def make_dataset(df,window):
  X_list,Y_list= [],[]
  for i in range(window,len(df)):
    X_list.append(df.iloc[i-window:i,0].values)
    Y_list.append(df.iloc[i,0])
  X=np.array(X_list)
  Y=np.array(Y_list)
  return np.expand_dims(X,2),Y
window = 7
X_train,Y_train = make_dataset(df_train,window)
X_valid,Y_valid = make_dataset(df_valid,window)
X_test,Y_test = make_dataset(df_test,window)
print(X_train.shape)
print(Y_train.shape)
print(X_valid.shape)
print(Y_valid.shape)
print(X_test.shape)
print(Y_test.shape)
(2267, 7, 1)
(2267,)
(113, 7, 1)
(113,)
(113, 7, 1)
(113,)

4.モデルの学習

1行目ではモデルの基礎を定義します。
入力をwindow日数*特徴量1、出力を1要素とした2層のLSTMモデルを作成する。
デイトレードなので、その日ごとの利益率の振れ幅を知る為、外れ値の予測誤差を許容しないMSEを使用する。Dropoutを指定することで過学習が抑えられる。
損失関数と最適化アルゴリズムを指定してコンパイルする。
訓練データ、検証データを100回学習します。バッチサイズは学習の高速化の為、256に指定しました。verboseはモデルの学習経過を全表記する為、2に指定しました。

model = keras.Sequential()
model.add(layers.LSTM(32, dropout=0.25, input_shape=(window, 1), return_sequences=True))
model.add(layers.LSTM(16, dropout=0.25))
model.add(layers.Dense(1))
# モデルをコンパイルしてください
model.compile(loss='mean_squared_error', optimizer='adam')
# 訓練を行ってください
history = model.fit(X_train,Y_train, validation_data=(X_valid,Y_valid), epochs=100, batch_size=256, verbose=2)
Epoch 1/100
9/9 - 4s - loss: 2.7346e-04 - val_loss: 1.1612e-04 - 4s/epoch - 491ms/step
Epoch 2/100
9/9 - 0s - loss: 2.7330e-04 - val_loss: 1.2560e-04 - 119ms/epoch - 13ms/step
Epoch 3/100
9/9 - 0s - loss: 2.6949e-04 - val_loss: 1.1901e-04 - 119ms/epoch - 13ms/step
Epoch 4/100
9/9 - 0s - loss: 2.6903e-04 - val_loss: 1.1751e-04 - 137ms/epoch - 15ms/step
Epoch 5/100
9/9 - 0s - loss: 2.6879e-04 - val_loss: 1.2062e-04 - 119ms/epoch - 13ms/step
Epoch 6/100
9/9 - 0s - loss: 2.7060e-04 - val_loss: 1.2697e-04 - 113ms/epoch - 13ms/step
Epoch 7/100
9/9 - 0s - loss: 2.7113e-04 - val_loss: 1.2818e-04 - 128ms/epoch - 14ms/step
Epoch 8/100
9/9 - 0s - loss: 2.7090e-04 - val_loss: 1.1580e-04 - 115ms/epoch - 13ms/step
Epoch 9/100
9/9 - 0s - loss: 2.7123e-04 - val_loss: 1.1579e-04 - 119ms/epoch - 13ms/step
Epoch 10/100
9/9 - 0s - loss: 2.7072e-04 - val_loss: 1.2741e-04 - 115ms/epoch - 13ms/step

:

Epoch 90/100
9/9 - 0s - loss: 2.6862e-04 - val_loss: 1.2258e-04 - 116ms/epoch - 13ms/step
Epoch 91/100
9/9 - 0s - loss: 2.7067e-04 - val_loss: 1.1762e-04 - 121ms/epoch - 13ms/step
Epoch 92/100
9/9 - 0s - loss: 2.6695e-04 - val_loss: 1.2036e-04 - 114ms/epoch - 13ms/step
Epoch 93/100
9/9 - 0s - loss: 2.6614e-04 - val_loss: 1.1665e-04 - 120ms/epoch - 13ms/step
Epoch 94/100
9/9 - 0s - loss: 2.6976e-04 - val_loss: 1.2020e-04 - 123ms/epoch - 14ms/step
Epoch 95/100
9/9 - 0s - loss: 2.6708e-04 - val_loss: 1.1819e-04 - 117ms/epoch - 13ms/step
Epoch 96/100
9/9 - 0s - loss: 2.6810e-04 - val_loss: 1.1951e-04 - 117ms/epoch - 13ms/step
Epoch 97/100
9/9 - 0s - loss: 2.6772e-04 - val_loss: 1.2321e-04 - 118ms/epoch - 13ms/step
Epoch 98/100
9/9 - 0s - loss: 2.6808e-04 - val_loss: 1.1660e-04 - 116ms/epoch - 13ms/step
Epoch 99/100
9/9 - 0s - loss: 2.6724e-04 - val_loss: 1.2517e-04 - 120ms/epoch - 13ms/step
Epoch 100/100
9/9 - 0s - loss: 2.6873e-04 - val_loss: 1.1878e-04 - 112ms/epoch - 12ms/step

学習曲線を描画しています。学習不足の可能性は否定できないが、過学習ではない。

plt.figure(figsize=(16,9))
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.legend(['train','valid'])
plt.xlabel('epoch')
plt.ylabel('loss')



5.モデルの評価

訓練データ、検証データ、テストデータの予測をします。

pred_train = model.predict(X_train)
pred_valid = model.predict(X_valid)
pred_test = model.predict(X_test)

平方平均2乗誤差を計算する。元データの推移が1%ぐらいの為、テストデータの2%はあまり良い精度ではない。

train_score = math.sqrt(mean_squared_error(Y_train, pred_train))
print('Train Score: %.5f RMSE' % (train_score))
valid_score = math.sqrt(mean_squared_error(Y_valid, pred_valid))
print('Valid Score: %.5f RMSE' % (valid_score))
test_score = math.sqrt(mean_squared_error(Y_test, pred_test))
print('Test Score: %.5f RMSE' % (test_score))
Train Score: 0.01634 RMSE
Valid Score: 0.01090 RMSE
Test Score: 0.02038 RMSE

6.予測

正解データが入っているデータフレームにpred列を作成して、予測結果を格納します。最初のwindowの7日分学習データを用意出来ていないので空白にしてあります。Trueは実データで、Predは予測データです。縦軸は利益率で、横軸は時間軸です。訓練予測、検証予測、テスト予測の確認の結果は正解データに対して予測データが変動(ボラティリティ)にあまり追従していない。変動(ボラティリティ)が大きく変化した場合は、多少の追従が見られます。

df_train.loc[df_train.index[window:],'pred']= pred_train
df_valid.loc[df_valid.index[window:],'pred']= pred_valid
df_test.loc[df_test.index[window:],'pred']= pred_test
plt.figure(figsize=(16,9))
plt.plot(df_train['y'],label='Train True')
plt.plot(df_valid['y'],label='Valid True')
plt.plot(df_test['y'],label='Test True')
plt.plot(df_train['pred'],label='Train Pred')
plt.plot(df_valid['pred'],label='Valid Pred')
plt.plot(df_test['pred'],label='Test Pred')
plt.legend()

7.今後の展望

今回の結果は、株価を予測するには、誤差が大きすぎた。よって、株価を予測する事が難しい。今後の改善点としてはデータ自体window日数*特徴量1の特徴量1の数を、他企業のデータを増やして見る。
前処理しないデータも一緒に入れる。
モデルを回帰モデルから分類モデルへ変えて、利益率自体ではなく利益率をカテゴライズしたものを予測する。例(5%以上)


この記事が気に入ったらサポートをしてみませんか?