指値板を用いたCNNによる価格予想

# はじめに

本記事は、「DeepLOB: Deep Convolutional Neural Networks for Limit Order Books」の紹介です。原論文に加筆、編集を行なっています。

https://arxiv.org/pdf/1808.03668.pdf

論文で紹介しているgithubです。コードは、zcakhaa/DeepLOB-Deep-Convolutional-Neural-Networks-for-Limit-Order-Books/run_train_tensorflow-version2.ipynbからの引用です。

https://github.com/zcakhaa/DeepLOB-Deep-Convolutional-Neural-Networks-for-Limit-Order-Books

# 要約

> 私たちは、現金株式の指値注文帳(LOB)データからの価格変動を予測するための大規模なディープラーニングモデルを開発しています。このアーキテクチャは、畳み込みフィルタを使用して、リミットオーダーブックとLSTMモジュールの空間構造をキャプチャして、より長い時間の依存関係をキャプチャします。提案されたネットワークは、ベンチマークLOBデータセット上の既存のすべての最先端のアルゴリズムを上回っています[1]。より現実的な環境では、ロンドン証券取引所からの1年間の市場相場を使用してモデルをテストし、このモデルはさまざまな商品の非常に安定したサンプル外予測精度を提供します。重要なことに、私たちのモデルはトレーニングセットの一部ではなかった機器にうまく変換され、モデルが普遍的な機能を抽出する能力を示しています。これらの機能をよりよく理解し、「ブラックボックス」モデルを超えて、感度分析を実行してモデル予測の背後にある理論的根拠を理解し、最も関連性の高いLOBのコンポーネントを明らかにします。他の機器にうまく変換される堅牢な機能を抽出する機能は、他の多くのアプリケーションを持つ当社のモデルの重要な特性です。

# データセット
一般公開されているFI-2010データセットを使用します。FI-2010データセットはロンドン証券取引所の5銘柄の1年間の指値板です。また、訓練やバリデーションに使用していない3ヶ月のテスト期間を用いて検証を行なっています。

## 訓練データ

訓練データのラベルは、以下の40項目です。

```console
'PRICE_ASK_0', 'PRICE_ASK_1', 'PRICE_ASK_2', 'PRICE_ASK_3',
'PRICE_ASK_4', 'PRICE_ASK_5', 'PRICE_ASK_6', 'PRICE_ASK_7',
'PRICE_ASK_8', 'PRICE_ASK_9', 'PRICE_BID_0', 'PRICE_BID_1',
'PRICE_BID_2', 'PRICE_BID_3', 'PRICE_BID_4', 'PRICE_BID_5',
'PRICE_BID_6', 'PRICE_BID_7', 'PRICE_BID_8', 'PRICE_BID_9',
'VOLUME_ASK_0', 'VOLUME_ASK_1', 'VOLUME_ASK_2', 'VOLUME_ASK_3',
'VOLUME_ASK_4', 'VOLUME_ASK_5', 'VOLUME_ASK_6', 'VOLUME_ASK_7',
'VOLUME_ASK_8', 'VOLUME_ASK_9', 'VOLUME_BID_0', 'VOLUME_BID_1',
'VOLUME_BID_2', 'VOLUME_BID_3', 'VOLUME_BID_4', 'VOLUME_BID_5',
'VOLUME_BID_6', 'VOLUME_BID_7', 'VOLUME_BID_8', 'VOLUME_BID_9'
```

## 教師データ

1分後、2分後、3分など異なる時間の約定後の価格を5つ用意します。その中から1つ選択し、価格の大きさにより3つにカテゴリ化します。これは、多くの金融データの予測は確率に支配されているので、それに伴う処理です。それらを教師データとします。

# モデル

```python
def create_deeplob(T, NF, number_of_lstm):
    input_lmd = Input(shape=(T, NF, 1))
    
    # build the convolutional block
    conv_first1 = Conv2D(32, (1, 2), strides=(1, 2))(input_lmd)
    conv_first1 = keras.layers.LeakyReLU(alpha=0.01)(conv_first1)
    conv_first1 = Conv2D(32, (4, 1), padding='same')(conv_first1)
    conv_first1 = keras.layers.LeakyReLU(alpha=0.01)(conv_first1)
    conv_first1 = Conv2D(32, (4, 1), padding='same')(conv_first1)
    conv_first1 = keras.layers.LeakyReLU(alpha=0.01)(conv_first1)

    conv_first1 = Conv2D(32, (1, 2), strides=(1, 2))(conv_first1)
    conv_first1 = keras.layers.LeakyReLU(alpha=0.01)(conv_first1)
    conv_first1 = Conv2D(32, (4, 1), padding='same')(conv_first1)
    conv_first1 = keras.layers.LeakyReLU(alpha=0.01)(conv_first1)
    conv_first1 = Conv2D(32, (4, 1), padding='same')(conv_first1)
    conv_first1 = keras.layers.LeakyReLU(alpha=0.01)(conv_first1)

    conv_first1 = Conv2D(32, (1, 10))(conv_first1)
    conv_first1 = keras.layers.LeakyReLU(alpha=0.01)(conv_first1)
    conv_first1 = Conv2D(32, (4, 1), padding='same')(conv_first1)
    conv_first1 = keras.layers.LeakyReLU(alpha=0.01)(conv_first1)
    conv_first1 = Conv2D(32, (4, 1), padding='same')(conv_first1)
    conv_first1 = keras.layers.LeakyReLU(alpha=0.01)(conv_first1)
    
    # build the inception module
    convsecond_1 = Conv2D(64, (1, 1), padding='same')(conv_first1)
    convsecond_1 = keras.layers.LeakyReLU(alpha=0.01)(convsecond_1)
    convsecond_1 = Conv2D(64, (3, 1), padding='same')(convsecond_1)
    convsecond_1 = keras.layers.LeakyReLU(alpha=0.01)(convsecond_1)

    convsecond_2 = Conv2D(64, (1, 1), padding='same')(conv_first1)
    convsecond_2 = keras.layers.LeakyReLU(alpha=0.01)(convsecond_2)
    convsecond_2 = Conv2D(64, (5, 1), padding='same')(convsecond_2)
    convsecond_2 = keras.layers.LeakyReLU(alpha=0.01)(convsecond_2)

    convsecond_3 = MaxPooling2D((3, 1), strides=(1, 1), padding='same')(conv_first1)
    convsecond_3 = Conv2D(64, (1, 1), padding='same')(convsecond_3)
    convsecond_3 = keras.layers.LeakyReLU(alpha=0.01)(convsecond_3)
    
    convsecond_output = keras.layers.concatenate([convsecond_1, convsecond_2, convsecond_3], axis=3)
    conv_reshape = Reshape((int(convsecond_output.shape[1]), int(convsecond_output.shape[3])))(convsecond_output)
    conv_reshape = keras.layers.Dropout(0.2, noise_shape=(None, 1, int(conv_reshape.shape[2])))(conv_reshape, training=True)

    # build the last LSTM layer
    conv_lstm = LSTM(number_of_lstm)(conv_reshape)

    # build the output layer
    out = Dense(3, activation='softmax')(conv_lstm)
    model = Model(inputs=input_lmd, outputs=out)
    adam = Adam(lr=0.0001)
    model.compile(optimizer=adam, loss='categorical_crossentropy', metrics=['accuracy'])

    return model
```

![limitorderbook.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/39883ffc-5836-1812-c0f1-2cdd7a03372d.png)

# 結果

下記の結果より、インディケーターを利用した予測より予測精度が高いことがわかります。

![Screen1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/490a2b46-d98b-0788-3adb-892b96d253ea.png)

![Screen Shot 2022-04-21 at 14.29.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/9e732532-6e3a-e232-138e-f3b352df1bd2.png)


# 結論

>本論文では、高頻度の指値注文データを用いて株価の動きを予測する初のハイブリッド型ディープニューラルネットワークを紹介する。従来の手作業で特徴を設計するモデルとは異なり、我々はCNNとInception Moduleを利用して特徴抽出を自動化し、LSTMユニットを利用して時間依存性を捕らえている。
提案手法をFI-2010ベンチマークデータセットにおいて、いくつかのベースライン手法と比較して評価した結果、我々のモデルは短期的な値動きを予測する上で他の手法よりも優れた性能を示すことが分かった。さらに、テスト期間を3ヶ月とし、LSEからの1年間の指値注文データを用いて、我々のモデルの頑健性をテストした。