Deep Learningを使用して日経平均を予測するというよくある話を試してみた


# 概要
いろんな方々がやっている日経平均予想です。今回はRandomForestとMLPとCNNで試しにやってみました。
一応ディスクレーマーですが、本手法で実際に売買された場合に発生する損失等に関しては私は全く責任を負いません。

# 前置き
## 株に関しての理論的な話
基本的に株というか一般の金融資産というのはランダムウォークでして、一時点前の情報が分かったとしても次の時点の値を予測することは基本的にできません。そんなことができればみんなできているわけで、そんなうまい話があるわけない。

一方で、株の世界にはアノマリーというのがあって、例えば小型株効果とかバリュー株効果というのがあり、理論的には説明できない・理論から外れた一種の揺らぎみたいなものがあることが確認されています。(小型株効果とかバリュー株効果というのは、それまでの資産価格理論であったCAPMでは捉えられないアノマリーとして確認され、Fama-Frenchモデルというモデルが作られました。)

そういうアノマリーというのも、一般に広く知られてしまうとそれが市場に織り込まれてしまうので、アノマリーを使用して利益を上げることが難しくなります。

ディープラーニングや機械学習では、(個人的な理解では)そういったアノマリーを見つけることを目指します。
そのために、これまで使用されていたなかったデータだとか、手法なんかを使ってガサガサと計算します。

まぁ、すごいたくさんのデータを使ったらなんかできんじゃね？みたいな期待も込めてというところでしょうか。

## 実際の株売買に関しての話
一般的な株予測というのは、リターン、つまり前日との変化率を予測します。
つまり、「当日までのデータを使用して、次の日の上昇・下降」を予測します。

ところが、実際問題はそんなに簡単ではなく、

* 当日のデータとは当日の何時に手に入るのか？
* 当日の終値を使用して予測した場合、当日の終値では買えない。
* 当日の終値で買えないので、次の日の寄値で買うことになるが、終値=寄値ではない。
* そもそも寄値では買えない。
* 売買手数料の見合いのリターンを得られるのか？
* 毎日毎日買ったり売ったりというのは、売買手数料がたくさんかかるので、やりたくない。
* 学習とテストデータは、一定の方向性があるような時期だけでなく、いろんな時期を試す必要があります。特にアベノミクス期間だけを使うと、正答率は格段に上がります。

などなどの問題があります。

実際の売買ではこれらの点は予測よりも重要な問題になることが多いので、注意しないといけません。

あとは、海外の株価指標を使用して予測する場合は、「時差」の影響を十分に考慮する必要があります。
これをちゃんと考慮しないと、未来のデータを使用して予測することになってしまいます。

# モデル構築
ここまで前置きで、実際のモデルを作っていきます。

## データと予測対象
データは日経平均構成銘柄の225銘柄(直近構成銘柄)のdaily終値データを使用します。
予測対象は、次の日の日経平均株価の終値が前日と比較して上がるか下がるかです。
つまり、当日対比の終値リターンがプラスかマイナスかを教師データにします。
2000/01/11から2007/12/30までが学習データ、それ以降直近までがテストデータになります。

前置きで示しましたように、終値を使用して終値リターンを予測するのは、実際のトレードでは使えないことを再度確認しておきます。
まぁ、手法の違いで結果に差異がでるのか、を見る感じです。

## モデル別特徴量の構成

### ランダムフォレスト

ランダムフォレストでは、特徴量を横方向に伸ばします。
今回は各銘柄の当日前日終値リターンを列方向に、時点を行方向に持つマトリックスを構築します。

### 多層パーセプトロン(MLP)

ランダムフォレストと同様の特徴量を使用します。

### 畳み込みニューラルネットワーク(CNN)

畳み込みニューラルネットワークでは、画像形式つまり2次元の特徴量マップを生成する必要があるので、チャネルを1として、4次元テンソルとしました。
clm_dim、row_dimはある1時点での2次元画像の列数と行数で、それぞれ業種別銘柄数の最大値、業種数になります。
そこに業種単位で銘柄のリターンを埋め込んでいきます。


```python
    clm_dim = max(industry_count["count"])
    row_dim = len(industry_count)
    l_sample = len(x_train)
    t_sample = len(x_test)
    print row_dim,clm_dim
    x_train_mat = np.zeros((l_sample,1,row_dim,clm_dim),dtype=np.float32)
    x_test_mat = np.zeros((t_sample,1,row_dim,clm_dim),dtype=np.float32)

    for ind in industry_count["ind"]:
        """業種で回す"""
        ind_code_list = ind_data[ind_data["ind"]==ind]["code"]
        len_3 = [i for i,ii in enumerate(industry_count["ind"]) if ii == ind] #行番号

        len_1 = 0 #列番号
        for idx,row in x_train.iterrows():
            len_4 = 0
            for cc in ind_code_list:
                # x_train_mat[len_1,0,len_3,len_4] = 1. if row[str(cc)] > 0 else -1.
                x_train_mat[len_1,0,len_3,len_4] = row[str(cc)]
                len_4 += 1
            len_1 += 1

        len_1 = 0 #列番号
        for idx,row in x_test.iterrows():
            len_4 = 0
            for cc in ind_code_list:
                x_test_mat[len_1,0,len_3,len_4] = row[str(cc)]
                len_4 += 1
            len_1 += 1
```

## モデルパラメータ

### ランダムフォレスト

決定木の数を200としました。

### 多層パーセプトロン(MLP)

3層の多層パーセプトロンで、隠れ層のノード数は1000としています。epochは100です。

### 畳み込みニューラルネットワーク(CNN)

畳み込み→averageプーリングを1回、その後隠れ層1層で、ノード数は1000としています。フィルターサイズは2×3の非対称フィルター、プーリングサイズは1×2、出力チャネルは30。

## 結果

それぞれsklearnのclassification reportとAUCで測定しました。

### ランダムフォレスト

![RF_code.png](https://qiita-image-store.s3.amazonaws.com/0/54539/b058803b-1727-e21a-d504-a7fa13c1d0f6.png "RF_code.png")

### 多層パーセプトロン(MLP)

![MLP_code.png](https://qiita-image-store.s3.amazonaws.com/0/54539/514b6370-c9a2-9f22-475f-caa23fb540aa.png "MLP_code.png")

### 畳み込みニューラルネットワーク(CNN)

![CNN_code.png](https://qiita-image-store.s3.amazonaws.com/0/54539/6b8151b4-702e-ee2e-9047-dd7a73e266d3.png "CNN_code.png")


結果としては、CNNが若干他のものよりも性能がいいといったところです。

CNNの結果での正答率を見てみると、2013年のアベノミクスが最も高いです。
トレンド性がはっきりとあった期間ですので、そうでしょう。

![正答率.png](https://qiita-image-store.s3.amazonaws.com/0/54539/7be29738-49e5-c9f4-4041-dea2d82bae5b.png "正答率.png")


# まとめ
* 前日の価格データだけを使って次の日を予測するのは、基本的に無理だという結果になりました。
* ただ、畳み込みを入れたほうが若干性能は出るのかな？といったところ。
* CNNでも業種で分けたので、疎なデータになってしまっている気がするので、その辺をどうするか。