LoginSignup
4
6

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-07-31

はじめに

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

rl_flow.png

使用データについて

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

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

以下ソースコード

TD学習

TD学習(時間的差分学習: Temporal Difference Learning)とは、代表的な価値ベース手法一つである。逐次的にデータ更新ができるのが特徴。

以下、TD学習での状態価値の更新式。

\Delta V(s) = r + \gamma V(s_{t+1}) - V(s_t)  \\

A2C1

 A2C(Advantage Actor-Critic)とは、A3C(Asynchronous Advantage Actor-Critic)から、非同期の部分を抜いたものであり、A3Cと比べてGPUの負荷が低い。
 Q学習で使用したQ値は、状態価値関数V(s)とアドバンテージ値A(s, a)の2つに分解できる。このアドバンテージ関数とは、ある状態において、ある行動が他の行動に比較しどの程度優れているかを表す。価値関数とは、その状態に優位度を表す。
 つまり、Actorはアドバンテージ値を通しQ値を学ぶ。これによりある行動の評価は、その行動がどれだけ良いかだけでなく、どれだけ良くなるかにも基づいて行われる。アドバンテージ関数の利点は、状態価値関数V(s)をベースラインとして使用することで、政策ネットワークの高い分散を減らし、モデルを安定させる。

A(s_t,a_t) = Q(s_t,a_t) - V(s_t) \\

a2c_frow.png

A3C2

A3Cは並列分散処理を行なってる。処理速度が早い。個別のスレッドが学習した重みはmasterBrainに蓄積される。新しい、学習が始まる時に、各スレッドがmasterBrainから重みをコピーする。

a3c.png

マルチスレッド

マルチスレッドの特徴は以下の通り
・平行処理
・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

損失関数3

損失関数(更新式)は、損失関数(Actor)と損失関数(Critic)に分解できる。

L = L_v + L_π

損失関数(Actor)と損失関数(Critic)は以下の式に分解できる。アドバンテージ値A(s, a)は、一般的なTD学習での状態価値の更新式より、時系列を加味した形式になっている。Hはエントロピー。

L_v = (A(s_t,a_t))^2 \\
L_π = -log(π(a_t,s_t))A(s_t,a_t) - \beta H(π(s_t)) \\
A(s_t,a_t) \approx TD error = r + \gamma V(s_{t+1}) - V(s_t)  \\
H(π(s_t)) = 1/2(log(2π \sigma ^2) + 1) \\
\beta:ハイパーパラメータ、\gamma:割引係数、\sigma:分散

売買ルール

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

実装と結果

ソースコードはこちら

当然の事ながら、A2Cと同じパフォーマンスである。

Unknown.png

ソースコードはこちら


  1. V. Mnih, A. P. Badia, M. Mirza, A. Graves, T. Lillicrap, T. Harley, D. Silver, and K. Kavukcuoglu, “Asynchronous methods for deep rein- forcement learning,” in International conference on machine learning, (2016), pp. 1928–1937. 

  2. V. Mnih, A. P. Badia, M. Mirza, A. Graves, T. P. Lillicrap, T. Harley, D. Silver, K. Kavukcuoglu, "Asynchronous Methods for Deep Reinforcement Learning",(2016), pp. 1602–1783. 

  3. S. Kuutti, R. Bowden, H. Joshi, R.D Temple, and S. Fallah, "End-to-end Reinforcement Learning for Autonomous Longitudinal Control Using Advantage Actor Critic with Temporal Context", IEEE Intelligent Transportation Systems Conference (ITSC),(2019) 

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