5
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Pythonで株価を機械学習

Last updated at Posted at 2020-06-14

#目的

1. githubにコードが上がっているので、どのモデルが一番いいのか教えて欲しい
2. pythonを使用して、株価が次の日上がるか下がるか機械学習させて結果を表示

#準備

  • 本記事はわかりやすいようにjupyterベース(Example.ipynb)でコードを回していきます。jupyterない人も大丈夫! config.iniをいじってコマンドでpython3 main.pyと打てばok

  • コードの中身を詳しく説明しても長ったらしくなるので、簡単に何をしたか説明します

Example.ipynb
from datetime import datetime
from scraiping import Scraiping
from complete import Complete
from train import Tree_updown, Tree_updown_k

start = datetime(2010, 1, 1)
finish = datetime(2020, 5, 17)

target_number = '4751' #(株)サイバーエージェント
prepare_number = ['1309', #上海株式指数・上証50連動型上場投資信託
                '1325', #(NEXT FUNDS)ブラジル株式指数上場投信
                '1326', #SPDRゴールド・シェア
                '1377', #サカタのタネ
                '1465', #ダイワ上場投信-JPX日経400インバース
                '1633', #(NEXT FUNDS)不動産上場投信
                '1678', #(NEXT FUNDS)インド株式指数上場投信
                '1698', #上場インデックスファンド日本高配当
                ]

headers = {'User-Agent' : 'Please write your User-Agent'}

max_depth = 5
test_size = 0.25
k = 2
  • 各モジュールのimport、いつからいつまでのデータをとるか、目的変数(4751)、説明変数(1309, 1325, 1326...)をセットアップしています。

  • headersはこのサイトみてgetしてみてください。<'https://note.nkmk.me/python-beautiful-soup-scraping-yahoo/'> (ちなみになくても大丈夫かも。詳しくはわからないです。)

  • max_depth:機械学習をする際にどのくらいの階層まで掘り下げるか

  • test_size:テスト用と学習用でどのくらいの比率で分けるか

  • k:株価の次の日の値が上下何%で変動するか

#スクレイピング
使用するwebサイト:株式投資メモ

  • スクレイピングで株価の情報をとってきます。
Example.ipynb
stock_numbers = prepare_number.copy()
stock_numbers.insert(0, target_number)
dfs = Scraiping(stock_numbers, start, finish, headers).main()
  • scraping.pyのモジュールに引数をぶちこんでます。
scraping.py
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time

class Scraiping:

    def __init__(self, stock_numbers, start, finish, headers):

        self.sns = stock_numbers
        self.start = start
        self.finish = finish
        self.headers = headers

    def main(self):

        dfs = []
        for sn in self.sns:
            df = self.scra(sn)
            dfs.append(df)
            print('Done : ' + sn)

        return dfs

    def scra(self, sn):


        data = []
        for y in range(self.start.year, self.finish.year+1):

            url = 'https://kabuoji3.com/stock/{}/{}/'.format(int(sn), y)
            soup = BeautifulSoup(requests.get(url, headers = self.headers).content,'html.parser')
            tr = soup.find_all('tr')

            t = 1
            while True:

                try:
                    td_t = [t.text for t in tr[t].find_all('td')]
                    date = pd.to_datetime(td_t[0])
                    if (self.start <= date) and (date <= self.finish):
                        td_t[4] = int(td_t[4])
                        data.append(td_t)
                    elif (date > self.finish):
                        break
                except IndexError:
                    break

                t += 1

            time.sleep(1)



        df = pd.DataFrame(data, columns = ['日付', '始値', '高値', '安値', '終値', '出来高', '終値調整'])
        return df

#機械学習のための前準備

  • 学習させるために前準備をします。とってきたデータをそのまま説明変数にしても意味がないので前日比を出していきます。(※とても重要)
Example.ipynb
complete_df = Complete(dfs, stock_numbers).com()
  • complete.pyのモジュールに先ほど作成したdfsを引数にぶちこんでます。
  • 終値をつかって(次の日)ー(前日)/(前日)をして全てのデータを結合しています。
complete.py
import pandas as pd
import sys

class Complete:

    def __init__(self, dfs, stock_numbers):

        self.dfs = dfs
        self.sns = stock_numbers

    def com(self):

        for i in range(len(self.dfs)):

            df = self.dfs[i][['日付', '終値']]
            num = self.sns[i]
            df = df.rename(columns = {'日付':'Date', '終値':num})

            df[num + '_Diff'] = df[num]/df[num].shift()-1

            if i == 0:
                complete_df = df

            else:
                complete_df = pd.merge(complete_df, df, on = 'Date')


        columns = self.sns.copy()
        columns.insert(0, 'Date')
        complete_isna = complete_df[columns]
        if complete_isna.isna().sum().sum() == 0:
            return complete_df

        else:
            print('Error : There are missing value > Please choose other target_number or prepare_number')
            self.isna(complete_isna)

    def isna(self, complete_isna):
        sys.exit()

ちなみに今回は(いろいろ試したが)欠損値が見つからなかったので、欠損値処理を無視しています。もし欠損値があったら適宜コード改善していこーと!

#機械学習(決定木)とその結果

  • ここから目的の機械学習をさせて次の日の予想をしていきます。

##単にupdown

  • これは単に株価の終値が前日より上がったら1、下がったら-1として目的変数にいれていきます。
Example.py
tu = Tree_updown(complete_df, stock_numbers, test_size, max_depth)
X, y = tu.prepare()
tu.predict(X, y)
  • train.pyのモジュールにcomplete_dfやその他もろもろをぶち込んでいます。
  • X, y は説明変数、目的変数となっています。
  • そしてpredictで予想!

###結果###
image.png

###感想###
次の日は下がると予想されました。
正解率はどちらも低くなっていますね...
っていうか、正解率がほぼ50%って笑笑 やった意味がないですね。
学習用正解率、テスト用正解率に差がなく、どちらも1.0に近ければいいモデルができていると思うので、みなさんも是非いろいろパラメータを変えていい結果が出たら教えてください。
説明変数がどれくらい目的変数に寄与しているかはcoefficientの列を見てもらえればわかります。

##2%updown(おまけ)

  • 次に上下何%でupdownするかを考慮します(ここでは2%)
  • 株価の終値が前日より2%上がったら1、下がったら-1、そうでなかったら0として目的変数にいれていきます
Example.ipynb
tu_k = Tree_updown_k(complete_df, stock_numbers, test_size, max_depth, k)
X, y = tu_k.prepare_k()
tu_k.predict(X, y)

###結果###
image.png

###感想###
正解率が上がっているのはconstantの数が多くなっているからですね。
本当に欲しい結果としては、投資する時にupかdownの情報だと思うので、あまりこの結果は意味のないような気がするが、勉強にはなりました。

  • 一応train.pyも載せておきます。
train.py
from sklearn.tree import DecisionTreeClassifier
import sklearn.model_selection
import pandas as pd

class Tree_updown:

    def __init__(self, complete_df, stock_numbers, test_size, max_depth):

        self.c_df = complete_df
        self.sns = stock_numbers
        self.ts = test_size
        self.m = max_depth

        self.diffs = [sn + '_Diff' for sn in self.sns]


    def prepare(self):

        y = self.c_df[self.sns[0]].shift(-1) - self.c_df[self.sns[0]]
        y.loc[y >= 0] = 1
        y.loc[y < 0] = -1
        y = y[1:-1]

        X = self.c_df[self.diffs].iloc[1:-1]

        return X, y

    def predict(self, X, y):

        X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size = self.ts, train_size = 1 - self.ts, random_state = 0)

        model = DecisionTreeClassifier(random_state = 0, max_depth = self.m)
        model.fit(X_train, y_train)

        next_day_diff = self.c_df[self.diffs].iloc[-1]
        next_day_predict = model.predict([next_day_diff])

        print('predict next day : UP') if next_day_predict == 1 else print('predict next day : CONSTANT') if next_day_predict == 0 else print('predict next day : DOWN')

        print('')
        print('テスト用正解率:{}'.format(model.score(X_test, y_test)))
        print('学習用正解率:{}'.format(model.score(X_train, y_train)))

        importance = pd.DataFrame({'feature_names':self.sns, 'coefficient':model.feature_importances_})
        print('')
        print(importance)

        print('')
        print('データ数:{}'.format(len(y)))


class Tree_updown_k(Tree_updown):

    def __init__(self, complete_df, stock_numbers, test_size, max_depth, k):
        super().__init__(complete_df, stock_numbers, test_size, max_depth)
        self.k = k

    def prepare_k(self):

        y = self.c_df[self.sns[0]].shift(-1)/self.c_df[self.sns[0]] - 1
        y.loc[y*100 >= self.k] = 1
        y.loc[y*100 <= -self.k] = -1
        y.loc[(-self.k < y*100) & (y*100 < self.k)] = 0
        y = y[1:-1]

        X = self.c_df[self.diffs].iloc[1:-1]

        return X, y

class Verify(Tree_updown_k):

    def __init__(self, complete_df, stock_numbers, verify_df, test_size, max_depth, k):
        super().__init__(complete_df, stock_numbers, test_size, max_depth, k)
        self.verify_df = verify_df

    def verify(self, X, y):

        X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size = self.ts, train_size = 1 - self.ts, random_state = 0)

        model = DecisionTreeClassifier(random_state = 0, max_depth = self.m)
        model.fit(X_train, y_train)

        y_test = self.verify_df[self.sns[0]].shift(-1)/self.verify_df[self.sns[0]] - 1
        y_test.loc[y_test*100 >= self.k] = 1
        y_test.loc[y_test*100 <= -self.k] = -1
        y_test.loc[(-self.k < y_test*100) & (y_test*100 < self.k)] = 0
        y_test = y_test[1:-1]
        X_test = self.verify_df[self.diffs][1:-1]

        next_day_predict = model.predict(X_test)
        print(next_day_predict)
        print(y_test)

        print(next_day_predict == y_test)

#まとめ

  • 簡単にお金稼ぎなんてできないのかー
  • いいモデルを皆さん探してみてください。そして教えてください。
    株価予測のための機械学習
5
10
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
5
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?