Googleアカウントさえ持っていれば環境構築の手間なく手軽に使える「Google Colaboratory」を用いて、複数の機械学習手法による株値動きの予測を簡単に行ってみました。
株価のデータセットは無料で手に入れられるものが少なく、各証券会社などから有料で利用できるものが多いですが、今回は「yahoo_finance_api2」から株価を取得して手軽に無料で行っていきます。
前提知識:
- Python調べながら分かる
機械学習による株価予測概要
- データセット
- yahoo_finance_ap2から取得
- 株価の情報と出来高を用いる
- 超短期投資(デイトレ)を仮定
- 実際に利益を見込めなければ作る面白みがない、そこで、前日の株情報から翌営業日の終値を予想して、翌日の始値によって取引の判断を行うことを仮定した予測を行う
回帰(値の予測)
重回帰分析(線形回帰)を用いて株価の予測を行います。端的に言うと、取得した情報から得られる複数の関連情報から、目的の株価を予測する方法です。
- 始値
- 終値
- 出来高
- 高値と安値の差額
分類(上がる or 下がる)
上記に加えて、サポートベクターマシン(SVM)による分類予測も行います。
ただこの方法では、ほとんど値動きのない日への効果が薄いので、今回はあくまで機械学習の実装テストくらいの意味で行います。
主に用いるライブラリ:
最終的に取得したデータの一部を切り出した「テストデータ」から、実用精度を評価します。- pandas(データ加工)
- seaborn(グラフ表示)
- scikit-learn(機械学習)
yahoo_finance_api2からデータ取得
まず、日本株の株価データ取得のためのライブラリをインストール
!pip install yahoo_finance_api2
その他、日本株を証券コードを変えることで好きに設定可能。データはならべく多く所得したいところだが、上場した年より前のデータを取得するとおかしなことになる。
import sys
from yahoo_finance_api2 import share
from yahoo_finance_api2.exceptions import YahooFinanceError
#「mykey」に日本株の銘柄コードを入力することで予測する株を変更
mykey = 2702
company_name = "日本マクドナルドホールディングス" # 特に名前を入れる意味はない
#20年間1日刻みデータの所得
company_code = str(mykey) + '.T'
my_share = share.Share(company_code)
symbol_data = None
try:
symbol_data = my_share.get_historical(
share.PERIOD_TYPE_YEAR, 20,
share.FREQUENCY_TYPE_DAY, 1
)
except YahooFinanceError as e:
print(e.message)
sys.exit(1)
import pandas as pd
df = pd.DataFrame({'日付': symbol_data['timestamp'], '始値' : symbol_data['open'],
'高値' : symbol_data['high'], '安値' : symbol_data['low'],
'終値' : symbol_data['close'], '出来高' : symbol_data['volume']})
#時間表示変換(ミリ秒→日本標準時)
df['日付'] = pd.to_datetime(df['日付'], unit="ms")
df['日付'] = pd.DatetimeIndex(df['日付']).tz_localize("UTC").tz_convert("Asia/Tokyo")
データの編集・考察
上記概要でも述べた通り4つの項目(説明変数:始値,終値,出来高,値動き幅)と正解ラベル(目的変数:翌終値)をデータフレームに整えていく。
# 高値と安値の差額列作成
df['値動き幅'] = df['高値'] - df['安値']
# 正解ラベルの追加(翌高値)
df2 = df['終値']
df2 = df2.drop(df2.index[0])
df2 = df2.reset_index()
df2 = df2.drop('index', axis=1)
df2 = df2.set_axis(['翌終値'], axis=1)
# データ結合
df = pd.concat((df, df2), axis=1)
# 学習に不要な項目削除
df = df.drop(['高値', '安値'], axis=1)
df = df.drop(df.index[len(df.index)-1]) # 最終データには正解データがない
欠損データの補完
欠損データの確認
df.isnull().sum()
- 欠損値を含む行/列を取り除く
- 欠損値を何らかの値で補完する
- 欠損値を無視する
今回はデータを補完。欠損データが存在しない場合も、とりあえずやっておけば間違いない。様々手法があるが、ここでは最頻値での補完を用いる。
from sklearn.impute import SimpleImputer
import numpy as np
#最頻値での補完
imp_mean = SimpleImputer(missing_values=np.nan, strategy='most_frequent', fill_value=None)
imp_mean.fit(df)
#補完
result_mean = imp_mean.transform(df)
df = pd.DataFrame(result_mean, columns=df.columns)
データの可視化
import seaborn as sns
sns.pairplot(df, hue="翌終値")

データを可視化することで、傾向を見たりするらしいが今回はよくわからないのでスルー
訓練/テストデータの分割
データを機械学習の「モデルの訓練用」とモデルの性能を計る「モデルのテスト用」に分ける。
feature = df.loc[:, ["始値", "終値", "出来高", "値動き幅"]] # 説明変数
target = df.loc[:, ["翌終値"]] # 目的変数
#テスト用データを全体の10%とする
x_train, x_test, t_train, t_test = train_test_split(feature, target, test_size=0.1, random_state=1)
変数名 | 説明 |
---|---|
x_train | 訓練用の説明変数 |
t_train | 訓練用の目的変数 |
x_test | 性能テスト用の説明変数(入力) |
t_test | 性能テスト用の目的変数(出力) |
手法1:回帰分析
機械学習自体のモデルの訓練自体は、機械学習用ライブラリである「scikit-learn」を使うことで以下のように簡単に終わる。
#重回帰分析(線形回帰)
from sklearn.linear_model import LinearRegression
LinearRegression = LinearRegression()
#モデルの訓練
LinearRegression.fit(x_train, t_train)
print(LinearRegression.coef_) #求まった重み w
print(LinearRegression.intercept_) #求まったバイアス b
最大値が1で、値が大きいほど与えたデータに対してフィットしているといえるらしい。
print("学習時性能:"+ str(LinearRegression.score(x_train, t_train)))
print("汎化性能 :"+ str(LinearRegression.score(x_test, t_test)))
実用的な精度評価
具体的な株価を1点予測しても、どの程度の誤差が許容可能で利益に繋がるのか考えることに意味がある。
そこで、翌日の始値(翌始値)から相対的に見て、予測した値(翌終値)が上昇するか、下落するかをどの程度予測できているかで評価を行う。
つまり「翌日の始値時点で終値が安くなることが分かっているなら売る、高くなるようなら買う」という判断材料として、どの程度優れているのかという指標で評価してみる。
まず、精度評価に必要なデータフレームを作る。以下はかなり冗長なコードなので最終的に下に示す表のようになっていれば、書き直すことをおすすめする。
df2 = pd.DataFrame(LinearRegression.predict(x_test), columns=t_test.columns) # データセット作成
df2 = df2.set_axis(['翌終値予測値'], axis=1)
df3 = df.drop(['翌終値', '日付', '始値', '終値', '出来高', '値動き幅'] ,axis=1)
df3 = df3.reset_index()
df4 = t_test.join(df3, how='inner')
df4 = df4.reset_index()
df4 = df4.drop(['index', 'level_0'] ,axis=1)
df3 = pd.concat([df4, df2], axis=1)
# 翌日の始値を追加
df4 = t_test
df4 = df4.reset_index()
# 翌日の始値にするためindexを振りなおす
for i in df4.columns:
df4[i] = df4[i] + 1
df4 = df4.set_index('index')
df4 = df4.drop(['翌終値'] ,axis=1)
# 翌日の始値を取得
df2 = df['始値']
df2 = df4.join(df2, how='inner')
df2 = df2.reset_index()
df2 = df2.drop('index', axis=1)
df2 = df2.set_axis(['翌始値'], axis=1)
df2 = pd.concat([df2, df3], axis=1)
# 上昇するとき「1」、下落するとき「-1」、変化なし「0」
df2.loc[df2['翌始値'] == df2['翌終値'], '本結果'] = 0
df2.loc[df2['翌始値'] > df2['翌終値'], '本結果'] = -1
df2.loc[df2['翌始値'] < df2['翌終値'], '本結果'] = 1
df2.loc[df2['翌始値'] == df2['翌終値予測値'], '予測結果'] = 0
df2.loc[df2['翌始値'] > df2['翌終値予測値'], '予測結果'] = -1
df2.loc[df2['翌始値'] < df2['翌終値予測値'], '予測結果'] = 1
df_bool = (df2['本結果'] == df2['予測結果']).sum()
result = (df_bool / (len(df2))) * 100
print('予測精度 : ' + str(result) + '%')
余談:各入力変数の前処理
余談だがモデルの訓練を行う前に前処理を行うことも可能。これによって精度が向上する場合もあるらしい。
以下は、各入力変数ごとに平均が0、分散が1になるように値をスケーリングを行う「標準化」です。
from sklearn.preprocessing import StandardScaler
StandardScaler = StandardScaler()
#データセットの各入力変数ごとの平均と分散の値を計算
StandardScaler.fit(x_train)
print(StandardScaler.mean_) #平均
print(StandardScaler.var_) #分散
#標準化
x_train_scaled = StandardScaler.transform(x_train)
x_test_scaled = StandardScaler.transform(x_test)
手法2:SVMで分類
分類に関してもほとんど手順は同じなので、簡潔にまとめていく。
データの編集
まず、分類の正解データを追加
df2 = df['始値']
df2 = df2.drop(df2.index[0])
df2 = df2.reset_index()
df2 = df2.drop('index', axis=1)
df2 = df2.set_axis(['翌始値'], axis=1)
# データ結合
df = pd.concat((df, df2), axis=1)
df = df.drop(df.index[len(df.index)-1])
# 正解データ追加
df.loc[df['翌始値'] == df['翌終値'], '分類'] = 0
df.loc[df['翌始値'] > df['翌終値'], '分類'] = -1
df.loc[df['翌始値'] < df['翌終値'], '分類'] = 1
モデルの訓練
データセットを「訓練データ」と「テストデータ」に分割。
from sklearn.model_selection import train_test_split
feature = df.loc[:, ['始値', '終値', '出来高', '値動き幅',]]
target = df.loc[:, ['分類']]
#テスト用データを全体の10%とする
x_train, x_test, t_train, t_test = train_test_split(feature, target, test_size=0.1, random_state=1)
from sklearn import svm
svm = svm.SVC(decision_function_shape='ovo')
#モデルの訓練
svm.fit(x_train, t_train)
結果の精度検証
精度検証のためのデータセットを作成
df2 = pd.DataFrame(svm.predict(x_test), columns=t_test.columns)
df2 = df2.set_axis(['予測'], axis=1)
df3 = df.drop(['翌終値', '日付', '始値', '終値', '出来高', '値動き幅', '翌始値', '分類'] ,axis=1)
df3 = df3.reset_index()
df4 = t_test.join(df3, how='inner')
df4 = df4.reset_index()
df4 = df4.drop(['index', 'level_0'] ,axis=1)
df2 = pd.concat([df4, df2], axis=1)
精度の検証
df_bool = (df2["分類"] == df2["予測"]).sum()
result = (df_bool / (len(df2))) * 100
print("分類予測精度:" + str(result) + "%")
まとめ
今回はチュートリアル的に株価予測を行ってみました。
私が行ったときはどちらも精度は50%程度で、人間が勘で当てる場合と変わりありませんでした。
説明変数や目的変数、学習モデル、対象銘柄など工夫できる箇所はたくさんあるので、試してみると面白いかもしれません。
参考:
お疲れさまでした。