#注意
文中の「全く予測できない場合、50%なので効果はありそうだなという印象です。」が、かなり怪しい(恐らく間違っている)とご指摘を受けたました。皆さんもしっかり勉強しましょう。ページを消してもよいですが、自戒も込めてとりあえず残しておきます(消すかも)。
#はじめに
前日までの株価データを用いて、翌日の株価が上がるか、下がるかを機械学習で判定します。ランダムフォレストを使っただけですが、株価をどう使うかだけでも結構面倒でした。上から順番に追って行けば、実際に分析するときの考慮事項が分かっていくと思います。(注意:プログラム及び分析結果の活用は自己責任でお願いします。)
- 分析する前のデータ準備(一番大事)
- ランダムフォレストとを用いた推定
#株価取得
プログラムの順番に説明をしていきます。最初の株価取得に関しては、Pythonで株価取得とローソク足チャート作成を見てください。
import datetime
import pandas_datareader.data as web
import matplotlib.pyplot as plt
import pandas as pd
#期間の設定
start = datetime.datetime(2011, 1, 1)
end = datetime.datetime(2017, 5, 30)
#株価取得
df = web.DataReader('TM', 'google', start, end)
df = df.loc[:, ['Close']]
#株価の加工
機械学習では、入力$x$と出力$y$を決める必要があります。今回の分析では、連続する$period+1$日の株価を使って、最終日の翌日の株価が上がるかを判定します。単純に$x$を決定するならば、$x=(p_{1},...,p_{period},p_{period+1})$にしたいところですが、$x=(p_{1}/p_{period+1},...,p_{period}/p_{period+1})$にします。これは、年によって同じ値段でも意味合いが異なることが多いからです。特に具体的な根拠はありませんが、このような処理により、性能が上がることはよくあります。$y$は上がっていれば1、下がっていれば$-1$にしました。
#株価のある次の日を返す関数
def GetIndexNextDay(df,day):
for p in range(1,10):
if((day+datetime.timedelta(days=p)) in df.index):
next_day = day+datetime.timedelta(days=p)
return next_day
period = 5
transitions = []
sample = 1600
for i in range(sample):
day = df.index[i]
day_transition = [df['Close'][day]/df['Close'][day]]
next_day = day
for term in range(1,(period+2)):
next_day = GetIndexNextDay(df,next_day)
day_transition.append(df['Close'][next_day]/df['Close'][day])
transitions.append(day_transition)
df2 = (pd.DataFrame(transitions)).T
x = (df2.loc[:period-1,:].values/df2.loc[(period),:].values).T
y = df2.loc[(period+1),:].values/df2.loc[(period),:].values
x = pd.DataFrame(x)
y = pd.DataFrame(y)
f = lambda x : 1 if x > 1.0 else -1
y = y[0].apply(f)
#ランダムフォレスト
ランダムフォレストをざっくり説明すると、単純なルールと一部のデータで作った学習機(決定木)のアンケート結果で最終的な学習結果を出力する学習器です。学習器を特徴づけるパラメータとして、n_estimator(アンケート数)とmax_depth(単純なルールの複雑度)があるのですが、このパラメータの趣旨が分かれば十分です。逆に言うとこれらを調整しないと性能が出ないことが多いです。
#訓練用と評価用データに分類
x_train = x[:train]
x_test = x[train:sample]
y_train = y[:train]
y_test = y[train:sample]
model = RandomForestClassifier()
model.fit(x_train,y_train)
output_train = model.predict(x_train)
accurate_train = y_train-output_train
accurate_train = accurate_train.apply(lambda x : 1 if x == 0 else 0)
output_test = model.predict(x_test)
accurate_test = y_test-output_test
accurate_test = accurate_test.apply(lambda x : 1 if x == 0 else 0)
#評価
作成したデータとランダムフォレストを用いて、株価の上下運動を推定したいと思います。今回最適な学習器を探すために、period,n_estimator,max_depthをグリッドリサーチしました。その結果、最適なパラメータはperiod=2,n_estimator=10,max_depth=100で正解率は58.6%でした。全く予測できない場合、50%なので効果はありそうだなという印象です。
import datetime
import pandas_datareader.data as web
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import sklearn.model_selection as ms
from sklearn.ensemble import RandomForestClassifier
#from sklearn.ensemble import RandomForestClassifier
def GetIndexNextDay(df,day):
for p in range(1,10):
if((day+datetime.timedelta(days=p)) in df.index):
next_day = day+datetime.timedelta(days=p)
return next_day
def GridForest(df,period,n_estimators,max_depth):
transitions = []
sample =1600
train = 1500
for i in range(sample):
day = df.index[i]
day_transition = [df['Close'][day]/df['Close'][day]]
next_day = day
for term in range(1,(period+2)):
next_day = GetIndexNextDay(df,next_day)
day_transition.append(df['Close'][next_day]/df['Close'][day])
transitions.append(day_transition)
#図示
df2 = (pd.DataFrame(transitions)).T
#df2 = df2.T
#df2.plot(legend=False,color = 'black')
x = (df2.loc[:period-1,:].values/df2.loc[(period),:].values).T
y = df2.loc[(period+1),:].values/df2.loc[(period),:].values
x = pd.DataFrame(x)
y = pd.DataFrame(y)
#print(y)
th = 0.000
f = lambda x : 0 if abs(x-1.0) < th else 1 if x > 1.0 else -1
y = y[0].apply(f)
x_train = x[:train]
x_test = x[train:sample]
y_train = y[:train]
y_test = y[train:sample]
model = RandomForestClassifier(n_estimators=n_estimators,max_depth=max_depth)
model.fit(x_train,y_train)
output_train = model.predict(x_train)
accurate_train = y_train-output_train
accurate_train = accurate_train.apply(lambda x : 1 if x == 0 else 0)
output_test = model.predict(x_test)
accurate_test = y_test-output_test
accurate_test = accurate_test.apply(lambda x : 1 if x == 0 else 0)
return accurate_train.sum()/accurate_train.shape[0],accurate_test.sum()/accurate_test.shape[0]
#株価取得
start = datetime.datetime(2011, 1, 1)#株価取得開始日
end = datetime.datetime(2017, 5, 30)#株価取得終了日
df = web.DataReader('TM', 'google', start, end)
df = df.loc[:, ['Close']]
periods = range(1,11)
n_estimators = [10,100]
max_depths = [10,50,100,500]
n = 5
max_accuracy = 0.0
max_para = []
for period in periods:
for n_estimator in n_estimators:
for max_depth in max_depths:
acc_train = 0
acc_test = 0
for i in range(n):
acc1,acc2 = GridForest(df,period,n_estimator,max_depth)
acc_train = acc_train + acc1/n
acc_test = acc_test + acc2/n
print('period',period,'n_estimator',n_estimator,'max_depth',max_depth,'acc_train',acc_train,'acc_test',acc_test)
if(acc_test>max_accuracy):
max_accuracy = acc_test
max_para = [period,n_estimator,max_depth]
print('結果:',max_accuracy,max_para)
#参考
Pythonでデータ分析:ランダムフォレスト
http://tekenuko.hatenablog.com/entry/2016/09/20/222453