深層強化学習（GORILA）を用いたシステムトレーディング

# はじめに

　近年、人工知能ブームにより、人工知能を使ったトレーディング手法が盛んである。そこで、今回は深層強化学習を用いたシステムトレーディングを実施した。
　まず、基本的な深層強化学習を用いたトレーディングモデルである。agentの行動として、 BUY、HOLD、SELLの三つの内一つを選択する。環境の戻り値として、状態（今現在保有しているポジションの価格、市場価格、手持ちのキャッシュ）、報酬（手持ちのキャッシュの変化値（含む益も含む））、終了（取引の終了か否か）、情報（ターミナルにディスプレイする情報）を返す。

![rl_flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/f9ed08d7-f612-55bd-fb44-a45950f4079c.png)


## 使用データについて

トレンド傾向の掴みやすさから、yahoo financeからGSPCの日足を使用した。

訓練データの期間：2015/1/1 - 2017/6/30
テストデータの期間：2017/7/1 - 2021/1/1

以下ソースコード

https://github.com/sugiyama404/ReinfoceLearningForTrading/blob/main/get_csv.ipynb

# GORILA[^1]

### DQN[^1]

![Screen Shot 2021-08-06 at 12.02.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/685999f8-164a-51de-66a0-9bac267fbbb1.png)

### GORILA[^1]

![Screen Shot 2021-08-06 at 11.47.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/a7204385-07e4-c250-e088-c512713dc007.png)

GorilaはDQNを並列分散処理させたモデル
Parameter ServerとReplay memoryは全てのスレッドで共有する。そのため、DQNの時よりもReplay memoryのメモリ領域を拡張する必要がある。

# Q学習

```math
Q(s_t,a_t) \leftarrow Q(s_t,a_t) + \eta (R_{t+1} + \gamma \max_a Q(s_{t+1},a_t) -Q(s_t,a_t))
```

Q学習は価値ベース手法の代表的な例の一つ。Q学習は状況に応じて、agentに報酬を最大化するのにどのような行動を取るのが良いかを教えるシステム。

![Q_leaninnng.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/e3c15d80-351d-e9fd-aff7-5502f89930f1.png)


# &epsilon; - greedy法

　行動の初期に、ランダムな行動をし、報酬の局所的な最大化を防ぐ、方法を &epsilon; - greedy 法という。実行回数の増大化に伴い、 &epsilon; が減少し、ランダムな行動の回数が減り、その代わり、学習による行動が増える。

## マルチスレッド

マルチスレッドの特徴は以下の通り
・平行処理
・GILロックを持っているスレッドのみ、実行可能、他のスレッドは待機する。
・cpuのコア数に依存しない。

マルチプロセスの特徴は以下の通り
・並列処理
・メモリーが共有されていないので、値の受け渡しが大変。
・tensowflowだと、modelが動かないことがある。
・cpuのコア一つにプロセスを割り当てする。

cpuバウンドとはcpu内で行なっている数値計算などの処理

![multiprosess.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/556c9590-f14c-d0a4-c98c-7806e69d05c9.png)

### ループ

```python
class Task:
    def __init__(self, name):
        self.name = name
        self.num = 0
        self.sum = 0.0

    def run(self):
        while True:
            sleep(1)
            if self.num == 3:
                break
            print('roop_name  = ' + self.name + '  :num ＝ '+ str(self.num) + '  :sum ＝ '+ str(self.sum))
            self.num += 1
            self.sum += random.random()


name = 'nomal-roop'
start = time.time()

for i in range(4):
    w = Task('roop_point_'+str(i))
    w.run()

end = time.time() - start
arr.append("name:" + name + " process_time:{0}".format(end) + "[s]")
```

```console
roop_name  = roop_point_0  :num ＝ 0  :sum ＝ 0.0
roop_name  = roop_point_0  :num ＝ 1  :sum ＝ 0.642469181212962
roop_name  = roop_point_0  :num ＝ 2  :sum ＝ 1.5964812171373977
roop_name  = roop_point_1  :num ＝ 0  :sum ＝ 0.0
roop_name  = roop_point_1  :num ＝ 1  :sum ＝ 0.8876820994429431
roop_name  = roop_point_1  :num ＝ 2  :sum ＝ 1.627826300716026
roop_name  = roop_point_2  :num ＝ 0  :sum ＝ 0.0
roop_name  = roop_point_2  :num ＝ 1  :sum ＝ 0.03546302344611851
roop_name  = roop_point_2  :num ＝ 2  :sum ＝ 1.0239282875765587
roop_name  = roop_point_3  :num ＝ 0  :sum ＝ 0.0
roop_name  = roop_point_3  :num ＝ 1  :sum ＝ 0.602393530385244
roop_name  = roop_point_3  :num ＝ 2  :sum ＝ 1.555539488491399
```

### マルチスレッド

```python
class Task:
    def __init__(self, name):
        self.name = name
        self.num = 0
        self.sum = 0.0

    def run(self):
        while True:
            sleep(1)
            if self.num == 3:
                break
            print('roop_name  = ' + self.name + '  :num ＝ ' + str(self.num) + '  :sum ＝ ' + str(self.sum))
            self.num += 1
            self.sum += random.random()

name = 'thread-pool'
start = time.time()

thread_num = 4
threads = []
for i in range(thread_num):
    threads.append(Task(name=f'thread_{i}'))

datas = []
with ThreadPoolExecutor(max_workers = thread_num) as executor:
    for task in threads:
        job = lambda: task.run()
        datas.append(executor.submit(job))

end = time.time() - start
arr.append("name:" + name + " process_time:{0}".format(end) + "[s]")
```

```console
roop_name  = thread_0  :num ＝ 0  :sum ＝ 0.0
roop_name  = thread_1  :num ＝ 0  :sum ＝ 0.0
roop_name  = thread_2  :num ＝ 0  :sum ＝ 0.0
roop_name  = thread_3  :num ＝ 0  :sum ＝ 0.0
roop_name  = thread_0  :num ＝ 1  :sum ＝ 0.7829927782861958
roop_name  = thread_2  :num ＝ 1  :sum ＝ 0.7264674393557742
roop_name  = thread_1  :num ＝ 1  :sum ＝ 0.4721450639806136
roop_name  = thread_3  :num ＝ 1  :sum ＝ 0.2746835685320669
roop_name  = thread_0  :num ＝ 2  :sum ＝ 0.8189509274906515
roop_name  = thread_1  :num ＝ 2  :sum ＝ 0.7522106668563098
roop_name  = thread_2  :num ＝ 2  :sum ＝ 1.3346477522815392
roop_name  = thread_3  :num ＝ 2  :sum ＝ 0.33216049073474685
```

# 売買ルール

1.空売りは認めない
2.ポジションを持っている場合、追加注文を出せない。
3.最後のステップでポジションを全て売却する。
4.ポジションは全買い、全売り
5.所持金は1000000ドル

# 実装と結果

ソースコードはこちら

https://github.com/sugiyama404/ReinfoceLearningForTrading/blob/main/GORILA/gorila_test.ipynb

時系列に影響されないために、ReplyMemoryが、時系列データを扱う本論では、裏目に出ている。

![Unknown.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/2692da30-eab6-022f-00a6-c4b3f6c91269.png)


ソースコードはこちら

https://github.com/sugiyama404/ReinfoceLearningForTrading/blob/main/make_data_analysys.ipynb


[^1]: Arun. Nair, et al. "Massively parallel methods for deep reinforcement learning"(2015) In ICML Deep Learning Workshop.