Pythonで株価の上下運動を機械学習で推定(ランダムフォレスト編)

はじめに

 前日までの株価データを用いて、翌日の株価が上がるか、下がるかを機械学習で判定します。ランダムフォレストを使っただけですが、株価をどう使うかだけでも結構面倒でした。上から順番に追って行けば、実際に分析するときの考慮事項が分かっていくと思います。(注意:プログラム及び分析結果の活用は自己責任でお願いします。)

  • 分析する前のデータ準備(一番大事)
  • ランダムフォレストとを用いた推定

株価取得

プログラムの順番に説明をしていきます。最初の株価取得に関しては、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=(p1,...,pperiod,pperiod+1)にしたいところですが、x=(p1/pperiod+1,...,pperiod/pperiod+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