surga Lab

開発したい!!

【DQN】強化学習でビットコインの価格予想をしてみる

学生時代にもML系について勉強していましたが、久々に1から始めてみようと思い立ちました。
しかし勉強したところで業務に利用することもないし、もちろんお金になりません。

だったら、ビットコインの価格予想で儲けることができればモチベも上がるんじゃないかと。 ということで頑張ってみます。

先に結果を記載しますと、
「一見すると予想はいい感じだけど、いざ取引してみると爆損」です。

一見するといい感じのグラフ

f:id:hisurga:20190430024517p:plain

少しでも興味のある方への手助けになれば幸いですが、私自身かなり浅い所にいるのは自覚しているのでご承知おきください。

使用するもの

今回はkerasとkeras-rlを利用します。なぜなら"私の"学習コストが安いからです。

エージェントにはたくさん学習させますが、私の学習時間は減らしたかったので。
社会人は時間がないのです。勉強しながら働ける職場を熱望します。

また、価格予想には強化学習を利用します。

Keras Keras-rl

Kerasは簡単に機械学習できるライブラリです。
簡単すぎて機械学習についての基礎は、良くも悪くもいらないです。

Keras-rlはそれをさらに簡単にしたライブラリです。(Keras拡張のイメージ)
「なんでもいいから機械学習させてくれ」って方におすすめです。

backendにTensorflowを使うこともできるので、
「Tensorflow? ああ、この前使ったよ」とか言って "AIエンジニア" っぽさをかもせます。

本当はkeras入門的な記事も書きたかったのですが、先駆者が多く、公式ドキュメントも充実していますのでやめました。

keras.io

ビットコイン

ビットコインFXは24時間稼働していますし、ボラティリティが大きいので成果が確認しやすいため、今回の実験対象としました。

私自身は少しだけアカウント取ってみただけでトレード知識はほとんどありません。

環境構築

kerasを用意したところで、まだ実験を始めることはできません。

機械学習には学習させるためのデータが必要です。

データ取得

FX予想に使われるデータとしては、時系列データであるOHLCが一般的ですね。

OHLCは過去のデータを簡単に取得できるので、学習データを集めやすいのが大きいメリットです。

しかし、私は下記仮定を立てました。

  1. 価格情報は学習データの中でユニークな値になりやすいのではないか
  2. ユニークな値で学習するとAgentは容易に学習してしまうのではないか

その結果、OHLCで学習しても使い物にならないのではないか(過学習になりやすいのではないか)と予想を立てました。
※過学習=データの丸暗記みたいなもの。汎用性がない。

対策としてOHLCを値そのままで使わず、増減率にするなどのアイデアもありましたが、今回は板情報から"板の厚み"を学習データにすることを考えました。

そこに価格情報は含めず、「midpriceからx円離れたところの厚み」を利用します。なんとなく汎用性が高まる気がしませんか?

これがいい選択である確信なんてありません。
ですが、何事もトライアル&エラーです。

板情報の取得

bitFlyer APIで板情報を取得してsqliteで保存します。

保存方法はここや

qiita.com

ここを参考にしました。

note.mu

コード全文は上記有料noteを改変したものですので載せるのは控えます。
累積方法は以下のコードを参考にしてください。

# 欲しい深さによって変更
DEPTH = [10, 20, 30, 50, 80, 130, 210, 340, 550, 890, 1440, 2330, 3770, 6100, 9870]
askvol = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
bidvol = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

for i in range(len(DEPTH)):
    for ask in board['asks']:
        if DEPTH[i] < ask['price'] - board['mid_price']:
            break
        else:
            askvol[i] += ask['size']
    
    for bid in board['bids']:
        if DEPTH[i] < board['mid_price'] - bid['price']:
            break
        else:
            bidvol[i] += bid['size']

私はだいたい1週間を1つのファイルに保存していましたが、データベースが大きくなっていくと徐々に処理が重くなっていくので注意です。
そのため、sleepが固定だとデータの取り始めと最後で間隔が異なってしまいます。適当にこんな感じにしておきましょう。

while datetime.datetime.now() < start + datetime.timedelta(seconds=SLEEP):
    sleep(0.01)

ビットフライヤーのメンテ時にも止める処理を入れておきましょう。

計算リソースの確保

効率的にMLを進めるには計算能力のある環境が必要です。

そこで今回はAWSのEC2インスタンス "p2-xlarge"と、AMI "Deep Learning AMI (Ubuntu)" の組み合わせで環境を作ります。

AWSアカウントについての詳細は割愛しますが、インスタンスの作成で下記AMIを選びましょう。

f:id:hisurga:20190426004237p:plain

インスタンスに接続すると「環境を選べ」みたいなメッセージが出ますが、source activate tensorflow_p36を実行しましょう。

p2.xlargeの費用は0.90USD/h(オハイオ/執筆時点)です。
マシンのスペックから考えるとコスパ的には最高なんですが、消し忘れるととても痛いです。

f:id:hisurga:20190427174032p:plain

価格予想で一攫千金を狙うより、素直に節約した方がお金は貯まると思います。

もちろん、モデルが複雑でない限りはハイスペックはいらないです。

私は途中からモデルをシンプルにしたので、t2.midiumのCPUで計算させていました。
お手元のmac bookでもいけますが、他の作業ができなくなるのでやめておきましょう。

AWSとローカル間のデータのやり取りはこの記事を参考にしてください。

blog.hisurga.com

実装と実験

とにかく改変 & 改変で作ったので、色々と非効率だったり、コード文法的にレベルは低いかもしれません。ご了承ください。

整備できてなくてお恥ずかしいですが、GitHubならこちら。

github.com

DQNBot

  • Agentには3つの選択肢 "BUY/SELL/STAY(何もしない)" を与える
  • 与える状況は "一定間隔の板の厚さ"
  • BUY -> (STAY) -> SELL もしくはその逆の順番でactionが選択された時、その差額を報酬として与える

実装1

結果1

「Agentはノーポジとガチホを覚えた!!!」

何十万ステップもトレーニングさせると、
「これ取引するより何もしないほうがいいよね」
「最初に買ってガチホが一番だよね」
と考えるようになってしまいました。

逆に学習が少なくしても、ひたすらに負け続けていました。

実装2 LSTMを導入する

モデルを変えてLSTMを導入してみます。
簡単に書くと長期依存に対応することができます。

詳しい解説してくださる方がいらしたので紹介。

qiita.com

「実はこの板の厚みの変化って長期的に見る必要があるんじゃないか」と仮説を立て、LSTMを選定しました。

物は試しです。ネットワーク定義を以下に変えてみます。

# DQNのネットワーク定義
model = Sequential()
model.add(LSTM(units=512, return_sequences=True, input_shape=input_shape))
model.add(Dropout(dropout))
model.add(LSTM(units=512, return_sequences=False))
model.add(Dropout(dropout))
model.add(Dense(units=nb_actions))

結果2

「Agentはノーポジとガチホを覚えた!!!」

変わらん。

反省と仮説

1step(1sec)毎に3択を与えるってAgentにとってめっちゃしんどいんじゃないか?
-> ゲームを攻略できるDQNがあるくらいだからいけるはずだけど...

シンプルにパラメータや考え方が悪いんじゃないか?
-> 技術力不足

一定期間後に上か下か予想させる方が効率よく学習できるんじゃないか?
-> やってみよう

DQNBot_Label

  • Agentには3つの選択肢 "BUY/SELL/STAY(何もしない)" を与える
  • 与える状況は "一定間隔の板の厚さ"
  • BUY or SELLが選択された時、一定期間後のmidpriceとの差額を報酬として与える
  • Labelって名前に特に意味はない

報酬の与え方も少し工夫します。
報酬をシンプルにすることで、汎用性を高めることが目的です。

  • 一定額以上のプラス差額 "報酬1"
  • 一定額未満のプラス差額 "報酬0"
  • マイナス差額 "報酬-1"

実装

結果

trainデータそのままをtestしてたところ、かなり儲けているようです。 (パラメータや学習回数は記録できていません。。)

青がSELL 赤がBUY

f:id:hisurga:20190430023358p:plain

しかし他のデータセットでtestしたところ、惨敗です。

f:id:hisurga:20190430023538p:plain

このモデルでbitFlyerのBotも作ってみましたが、取引の回数が少なく、十分なデータを集められませんでした。(STAYばかり選択される)

反省

  • 汎用性がない どうやら過学習になっているように見受けられる
  • 急騰/急落時にリバを期待して逆張りしていくっぽい
  • もっとガンガン取引してスキャって欲しい

対策

以下の案で考えています。まだ検証は進められていません。

汎用性がない
-> モデルをシンプルにする dropoutを高めに設定する 学習データをもっと増やす

急騰/急落時にリバを期待して逆張りしていくっぽい
-> モデルをシンプルにする

もっとガンガン取引してスキャって欲しい
-> モデルをシンプルにする actionをBUY/SELLの2択にする

まとめ

「DQNBotで儲けられたのか」については、

「今の所、儲けられていません」が回答になります。

バックテストでこの状況では、確実に実環境では大損します。
実環境ではスプレッドなども考慮しなければならず、それを賄えるほどの報酬を安定して出さなければ行けません。

過学習を乗り越えた先に何かがあると信じて、暇を見つけて進めたいと思います。
上手くいった日には、続編として書くかもしれません。

最初のグラフ

最初に載せたグラフはactionをBUY/SELLの2択にして1秒毎1分先の価格予想した結果です。

めちゃくちゃいい感じに伸びていますが、これも過学習でした。

f:id:hisurga:20190430024517p:plain

参考にさせていただいたサイト

DQNで機械学習した人工知能がBitcoinをシストレして月700万円儲けるまでの話(失敗) - Qiita

Deep Q-LearningでFXしてみた

機械学習やディープラーニングでFX予測をする際に超参考になる記事まとめ

AIが学習しすぎる?「過学習」問題とそれを抑制する方法 | AI入門ブログ(人工知能の作り方など人工知能に関する情報を公開)

[Python] Keras-RLで簡単に強化学習(DQN)を試す - Qiita