株価予測WEBアプリを作ってみた(Python,Django,Heroku)

#はじめに


PythonでLSTMモデルを作成し、株価予測アルゴリズムを開発しました。
それをDjangoを用いてWEBアプリ化し、Herokuにデプロイしました。

[株価予測WEBアプリ](https://sm-prediction.herokuapp.com/myapp/templates/nikkei/)

色々と省きますが、WEBアプリができるまでを順に説明したいと思います。

開発環境は以下です。

- Windows8.1 Pro
- Python 3.6.5
- Django 2.0.7

本記事の構成は以下です。

| No.|項目 | 
|:-----------|:------------|
| 1       | PythonによるLSTMモデルの構築と株価予測        | 
| 2    　  | DjangoによるWebアプリケーションの作成      |
| 3       | HerokuへWebアプリケーションをデプロイ        | 

#1.PythonによるLSTMモデルの構築と株価予測 
ここでは、Pythonを用いて、
- 株価データのスクレイピング
- LSTMモデルの定義
- LSTMモデルの学習
- 株価予測
を行っています。

```stock_predict_nikkei.py
#def LSTM(NAME,epoch,s_date,e_date,batch):
from . import brand_list
from . import read_nikkei

def LSTM(input):
        ############
        #---LSTM---#
        ############

        ############
        ###1.importing Libraies and getting stock data
        import pandas as pd

        import datetime as dt
        import numpy as np
        import pandas_datareader.data as web
        import matplotlib.pyplot as plt
        import chainer

        #start = dt.date(2011,1,1)
        #end = dt.date(2018,7,1)
        #start = s_date
        #end = e_date
        start = input[2]
        end = input[3]
        print(start)
        print(end)
        
        #AMZN:Amazon、AAPL:Apple、GOOGL:Google、ACN:ｱｸｾﾝﾁｭｱ、
        #AFL:ｱﾌﾗｯｸ、DIS:ﾃﾞｨｽﾞﾆｰ、FB:Facebook、NVDA:NVIDIA
        #df_org= web.DataReader('NVDA',"morningstar",start,end)
        #df_org= web.DataReader(NAME,"morningstar",start,end)
        #df_org= web.DataReader(input[0],"morningstar",start,end)
        df_org = read_nikkei.get_jstock(input[0],start=pd.Timestamp(start),end=pd.Timestamp(end))
        df_org = df_org.drop('Adj Close',axis=1)
        
        
        #翌日のOpen値入力
        df_add = df_org['Open'].shift(-1)
        #最終日は当日のOpen値入力(これしないとNANになる)
        df_add[len(df_org)-1] = df_add[len(df_org)-2] 

        #翌日のOpen値入力
        df_org = pd.concat([df_org,df_add], axis=1)

        #ｶﾗﾑ更新
        df_org.columns = ['Open', 'High','Low', 'Close', 'Volume','Open-1']

        ############
        #2.setting data

        from sklearn.preprocessing import MinMaxScaler
        x_scaler = MinMaxScaler(feature_range=(0, 1))
        x2_scaler = MinMaxScaler(feature_range=(0, 1))
        x3_scaler = MinMaxScaler(feature_range=(0, 1))
        t_scaler = MinMaxScaler(feature_range=(0, 1))


        df_x = x_scaler.fit_transform(np.array(df_org['Open-1']).reshape(-1,1)).astype(np.float32)
        df_x2 = x2_scaler.fit_transform(np.array(df_org['Close']).reshape(-1,1)).astype(np.float32)
        df_x3 = x3_scaler.fit_transform(np.array(df_org['High']).reshape(-1,1)).astype(np.float32)
        df_t = t_scaler.fit_transform(np.array(df_org['Close']).reshape(-1,1)).astype(np.float32)

        #訓練データと教師データへの分割

        x,x2,x3,t = [],[],[],[]

        N = len(df_org)
        M = 30
        for n in range(M,N):
            _x = df_x[n-M:n]
            _x2 = df_x2[n-M:n]
            _x3 = df_x3[n-M:n]
            _t = df_t[n]
            x.append(_x)
            x2.append(_x2)
            x3.append(_x3)
            t.append(_t)

        #trainﾃﾞｰﾀの割合
        ratio_train_test = input[5]
        #testﾃﾞｰﾀの個数
        n_test = 1
        #testﾃﾞｰﾀ数を個数で決めるか割合で決めるかのSW
        SW = -1

        #例：x[1]は初日からM日分の(標準化された)Closeﾃﾞｰﾀ、t[1]はM+1日目のCloseﾃﾞｰﾀ
        x = np.array(x, dtype = np.float32)
        x2 = np.array(x2, dtype = np.float32)
        x3 = np.array(x3, dtype = np.float32)
        t = np.array(t, dtype = np.float32).reshape(len(t),1)

        #x = np.c_[x,x2]

        if SW == 1:
            n_train = int(len(x) * ratio_train_test)
        elif SW == -1:
            n_train = len(x)-n_test
        else:
            pass

        dataset = list(zip(x, t))
        train, test = chainer.datasets.split_dataset(dataset,n_train)

        ############
        #3.making models
        # chainerと必要なパッケージをインポート
        import chainer.links as L
        import chainer.functions as F
        from chainer import Chain, Variable, datasets, optimizers
        from chainer import report, training
        from chainer.training import extensions
        import chainer.cuda

        # ニューラルネットワークモデルを作成
        class RNN(Chain):
            def __init__(self, n_units, n_output):
                super().__init__()
                with self.init_scope():
                    self.l1 = L.LSTM(None, n_units)
                    self.l2 = L.Linear(None, n_output)

            def reset_state(self):
                self.l1.reset_state()


            def __call__(self, x, t):
                y = self.predict(x)
                loss = F.mean_squared_error(y, t)
                report({'loss':loss},self)
                return loss

            def predict(self, x):
                #if train:
                    #h1 = F.dropout(self.l1(x),ratio = 0.5)
                #else:
                h1 = self.l1(x)
                return self.l2(h1)

        ## LSTMUpdaterを作る。
        class LSTMUpdater(training.StandardUpdater):
            def __init__(self, data_iter, optimizer, device=None):
                super(LSTMUpdater,self).__init__(data_iter, optimizer, device=None)
                self.device = device

            def update_core(self):
                data_iter = self.get_iterator("main")
                optimizer = self.get_optimizer("main")

                batch = data_iter.__next__()
                x_batch, t_batch = chainer.dataset.concat_examples(batch, self.device)

                optimizer.target.reset_state()           #追加
                optimizer.target.cleargrads()
                loss = optimizer.target(x_batch, t_batch)
                loss.backward()
                loss.unchain_backward()                  #追記
                optimizer.update() 

        ############
        #4.setting hyperparameters

        # 乱数のシードを固定 (再現性の確保)
        np.random.seed(1)

        # モデルの宣言
        model = RNN(30, 1)

        # GPU対応
        #chainer.cuda.get_device(0).use()
        #model.to_gpu()                 

        # Optimizer
        optimizer = optimizers.Adam()
        optimizer.setup(model)

        # Iterator
        batchsize = input[4] #ﾐﾆﾊﾞｯﾁのｻｲｽﾞ
        train_iter = chainer.iterators.SerialIterator(train, batchsize)
        test_iter = chainer.iterators.SerialIterator(test, batchsize, repeat=False, shuffle=False)

        # Updater &lt;- LSTM用にカスタマイズ
        updater = LSTMUpdater(train_iter, optimizer,device = -1)

        # Trainerとそのextensions
        #epoch = 100
        epoch = input[1]
        trainer = training.Trainer(updater, (epoch, 'epoch'), out='result')

        # 評価データで評価
        trainer.extend(extensions.Evaluator(test_iter, model,device = -1))

        # 学習結果の途中を表示する
        trainer.extend(extensions.LogReport(trigger=(1, 'epoch')))

        # １エポックごとに、trainデータに対するlossと、testデータに対するlossを出力させる
        trainer.extend(extensions.PrintReport(['epoch', 'main/loss', 'validation/main/loss', 'elapsed_time']), trigger=(1, 'epoch'))

        
        ############
        #5.training

        trainer.run()

        
        ############
        #6.predict stock price

        
        x_,x2_,x3_ = [],[],[]

        _x = df_x[N-M:N]
        _x2 = df_x2[N-M:N]
        _x3 = df_x3[N-M:N]

        x_.append(_x)
        x2_.append(_x2)
        x3_.append(_x3)

        #例：x[1]は初日からM日分の(標準化された)Closeﾃﾞｰﾀ、t[1]はM+1日目のCloseﾃﾞｰﾀ
        x_ = np.array(x_, dtype = np.float32)
        x2_ = np.array(x2_, dtype = np.float32)
        x3_ = np.array(x3_, dtype = np.float32)

        #x_ = np.c_[x_,x2_]
        
        if SW == 1:
            n_train = int(len(x) * ratio_train_test)
        elif SW == -1:
            n_train = len(x)-n_test
        else:
            pass

        train_x, test_x = x[:n_train], x[n_train:]
        train_t, test_t = t[:n_train], t[n_train:]
        
        train_t = t_scaler.inverse_transform(train_t)
        
        #図表示
        plt.clf()
        plt.plot(train_t, label = 'actual', color='blue')  # 実測値
        plt.legend()
        
        plt.savefig('C:\\Users\\Owner\\Desktop\\Django\\mySite\\static\\fig.png')
        
        # 検証データ
        model.reset_state()
        test_y = model.predict(Variable(x_)).data

        test_y = t_scaler.inverse_transform(test_y)
        
        
        test_t = t_scaler.inverse_transform(test_t)

       
        return test_y[0][0],test_t[0][0]
```

#2.DjangoによるWebアプリケーションの作成
編集中

#3.HerokuへWebアプリケーションをデプロイ
編集中

詳細は、今後書いていきます！

質問・ご指摘あればぜひ。

また、株価予測アプリは情報提供を目的としており、株価予測アプリに従って投資した結果に関しましては一切責任をもちません。利用に関しては自己責任でお願いします。