4
5

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 3 years have passed since last update.

深層強化学習(PPO)を用いたシステムトレーディング

Last updated at Posted at 2021-08-18

はじめに

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

rl_flow.png

使用データについて

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

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

以下ソースコード

# PPO1

ppo(Proximal Policy Optimization Algorithms)の特徴は以下の通り

・ 更新前と更新後の方策関数の変化量を抑えるため、クリッピング操作を行う
・ TRPOより、実装が安易
・ 方策ベース手法により、条件付き確率で行動する
・ Actor-Criticモデル

PPOはagentの役割が大きく変容する。agentは経験を溜め、ミニバッチ法により、観測データを抽出し、勾配をParmeter Serverに蓄積させる。

ppo.png

損失関数

L^{CLIP+ VF + S}_t(\theta) = \hat{E_t}[L^{CLIP}_t(\theta) - c_1 L^{VF}_t(\theta) + c_2 S[ \pi_{\theta}] (s_t) ]

方策損失関数

clip手法を用いて、損失関数の更新を大きく変更されることを防いでいる。

L^{CLIP}_t(\theta) = \hat{E_t}[min(r_t(\theta) \hat{A_t},clip(r_t(\theta), 1 - \epsilon, 1 + \epsilon)\hat{A_t})] \\
r_t(\theta) = \frac{\pi_{\theta} (a|s)}{ \pi_{\theta_{old}}(a|s)}

Screen Shot 2021-08-12 at 15.09.47.png

状態価値損失関数

L_t^{\text{VF}} = (V_\theta(s_t) - V_t^{\text{targ}})^2 \\
V_t^{\text{targ}} = \sum_{l=0}^{ \infty } \gamma^l r_{t+1}

GAE2

Advantageを推定する方法として、PPOでは、GAE(HIGH-DIMENSIONAL CONTINUOUS CONTROL USING GENERALIZED ADVANTAGE ESTIMATION)が用いられる。学習を安定化させるために、λ を変更することで、推定量の分散とバイアスを調節をする。

A^{GAE}(s_t,a_t) = \sum_{l=0}^{ \infty } (\gamma \lambda)^l \delta_{t+1} \\
\delta_{t} = r_t + \gamma V(s_{t + 1}) - V(s_{t})

エントロピー損失関数

 S[ \pi_{\theta}] (s_t) = - \sum_{a_t} \pi_{\theta} (a|s) log (\pi_{\theta}(a|s))

ε - greedy法

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

マルチスレッド

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

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

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

multiprosess.png

ループ

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]")
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

マルチスレッド

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]")
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ドル

実装と結果

ソースコードはこちら

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

Unknown.png

ソースコードはこちら

謝意

コードの記述の際に以下のサイトを参考にしました。
大変参考になりました。

第6回 今更だけど基礎から強化学習を勉強する PPO編

【強化学習】実装しながら学ぶPPO【CartPoleで棒立て:1ファイルで完結】

  1. J. Schulman, F. Wolski, P. Dhariwal, A. Radford, O. Klimov "Proximal Policy Optimization Algorithms"(2017) In OpenAI.

  2. J. Schulman, P. Moritz, S. Levine, M. I. Jordan and P. Abbeel "HIGH-DIMENSIONAL CONTINUOUS CONTROL USING GENERALIZED ADVANTAGE ESTIMATION" (2016) Department of Electrical Engineering and Computer Science University of California, Berkeley

4
5
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
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?