LoginSignup
17
26

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-09-03

注意

文中の「全く予測できない場合、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

17
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
26