株価予測モデルの構築(Youtubeの解説版)


概要

Youtubeで
エンジニアチャンネルさん(https://www.youtube.com/channel/UCMMjv61LfBy5J3AT8Ua0NGQ)
が株価予測をしていくら儲かるかという面白い企画をしていました。

どんなモデルを考えていたのか気になったので、コードを追って解説版を作ることにしました。

株価データ

下記のサイトは銘柄コードを入れるだけでCSVファイルをダウンロードできます。いくつもの企業の株価を10年分とか集めるなら、SeleniumとBeautifulSoupを使ってスクレイピングすればいいのですが、これ系の話は今回置いといて、、
https://kabuoji3.com/stock/

適当な企業の株価データをダウンロードしました。(2019,2020,2021年のデータを1社のみ)

コード

ライブラリ

from glob import glob
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

データの読み込み

今回は、ある階層にあるCSVファイルをglobでとってきて、for文で回して1つのデータフレームにしようと思います。

df_list = sorted(glob('./data/*.csv'), reverse=False)
data = pd.DataFrame()

for i in df_list:
    df = pd.read_csv(i, header=1, encoding='cp932')
    data = data.append(df)

data.set_index('日付', inplace=True)

簡単に解説を。
拡張子csvのファイルのファイル名をリストに格納します。この時、年代順に並べたいのでsortedを使います。
そして空のデータフレームを作成します。ここにfor文で回したデータを追加していきます。

for文の中身について...
dfという変数に1個ずつcsvファイルをデータフレーム化したものを入れます。その後dataに追加していくという感じです。

日付をindexにしたいので、set_indexを使います。inplace=Trueにしないと、設定が保持されません。

インプット・アウトプット用データ作成

max_num = data['始値'].max()
data['始値'] /= max_num
open_price = data['始値']

train = open_price[:len(open_price)*7//11]
test = open_price[len(open_price)*7//11:]
learning_term = 10

def make_data(data, interval=learning_term):
    input_tensor = []
    output_tensor = []
    price_list = list(data)
    for price_idx in range(len(price_list)):
        if price_idx < interval:
            continue
        output_tensor.append(price_list[price_idx])
        learn_price_list = []
        for week_idx in range(interval):
            learn_week_idx = price_idx + week_idx - interval
            learn_price_list.append(price_list[learn_week_idx])
        input_tensor.append(learn_price_list)
    return input_tensor, output_tensor

ここが一番ポイントになってくるでしょう。まずは始値を最大値で規格下します。そしてこの始値をopen_priceという変数に入れます。

今度はopen_priceを分割してtrain, testを作成します。//は切り捨て除算です。割り切れない場合も想定しての//です。

関数の定義で引数に=があります。これはデフォルト値を決定していて、以後関数を使う際はintervalを省略しても10が入るということになります。

問題は関数の中身です。なんかややこしそうですが、前提がわかってしまえば問題なし。
ある日の始値は10日前から前日までの始値によって決まるのでは?としています。

なので10以下の場合はcontinueでスキップするのです。また、learn_week_idxは0~9, 1~10, 2~11, ...
と変化し、ある日から10日前までの始値をリスト化し、input_tensorに追加しています。

モデルの作成と予測結果

x_train, y_train = make_data(train)
x_test, y_test = make_data(test)

lr = LinearRegression(normalize=True)
lr.fit(x_train, y_train)
y_pre = lr.predict(x_test)

y_pre_array = np.array(y_pre)
y_pre_array *= max_num
y_test_array = np.array(y_test)
y_test_array *= max_num

# dpi:インチ単位をピクセル単位にする。初期値=100(10*100=1000ピクセル, 6*100=600ピクセル)
plt.figure(figsize=(10,6), dpi=100)
plt.plot(y_test_array, c='r', label='True')
plt.plot(y_pre_array, c='b', label='Prediction')
plt.title('Prediction of stock price')
plt.xlabel('Count')
plt.ylabel('Open Price')
plt.legend()

この辺は機械学習をかじっていれば、まあわかるので割愛します。

まとめ

以上で解説を終わります。人のコードって読むの大変ですが、エンジニアチャンネルさんが作るのはとても読みやすかったです。。。さすがでした。