見出し画像

【Pythonで仮想通貨自動売買ボット@GMOコイン】 richmanbtcさんのチュートリアルで複数のMLボットの並列運用 とATR指値の最適化(学習済みモデル付き)

こんにちは。richmanbtcさんのチュートリアルベースのボットを現在GMOコインでここ数ヶ月間運用中している、B級ボッターのrog_peterと申します。

この記事ではrichmanbtcさんのチュートリアルベースのボットの並列運用と執行方針であるATR指値の最適化について書いてみたいと思います。

本記事の有料部分には、GMOコイン用のボットの並列運用スクリプトATR指値の最適化を行ったモデルが含まれています。

【追記 2022/04/28】 好評につき値上げさせて頂きました。9000円→9500円
【追記 2022/04/28】 新たに3つのモデルを追加しました。
【追記 2022/05/6】 好評につき値上げさせて頂きました。9500円→10000円
【追記 2022/05/29】50人以上の方がご購入してくださいました。
【追記 2022/05/29】 好評につき値上げさせて頂きました。1000円→12000円

ボットの並列運用

richmanbtcさんのbotを研究するためにFAQを読んでいると、16個の指値に分散させているという文を見つけ、並列化の意義を考え始めました。

ml botは何並列?
16個の指値に分散させてる。

実際、並列運用することで、1)リスク分散2)一部約定の確率を下げる、というメリットがあります。

1)リスク分散
1つのボットで大きいロットを運用すると、もしそのボットが過学習していたりなどしていてあまり良くないボットだった場合、損が拡大する場合もあるかもしれないですし、大損をしないにしても大きな機会損失につながるかもしれません。様々な執行のボットを並列で運用することでこのような損失を抑えることができます。

2)一部約定の確率を下げる
例えばGMOコインでは最小ロットは0.01ですが、ロットを0.1くらいで運用すると一部約定になる場合が増え、バックテストとの乖離が顕著になってきます。これに対してロット0.01(最小ロット)のボットを異なる指値で10個並列で運用すれば(合計ロットは0.1)、各ボットごとに見た場合に一部約定になることはなくなります。(ただし、指値位置がボット同士で近すぎるとボット同士で干渉を起こし、バックテストの結果と乖離する可能性があります。)

これらの並列運用のメリットを活かすためにrichmanbtcさんのチュートリアルベースのbotを様々な指値で並列運用を実行するためのスクリプトを作成しました。

本記事の有料部分で配布する並列運用スクリプトのアーキテクチャは以下のようになっています。

スクリーンショット 2022-03-30 17.29.30

Managerが入力として最大ロットから各ボット毎にロットを割り振り、各ボットが注文IDとポジションIDを管理しながら注文処理をします。これによって異なるATR指値を持つ複数のボットを並列に運用することができます。もちろん紹介する並列運用アーキテクチャは1つのボットでも運用可能です。

ATR指値の最適化

指値の決め方は様々ですが、richmanbtcさんのチュートリアルでは下記の箇所で指値を決めています。具体的には、終値から両側にlimit_price_distだけ離れたところに、買い指値と売り指値を出しています。ここで、limit_price_distは、df['ATR'] * 0.5で定義されています。

# ref. https://github.com/richmanbtc/mlbot_tutorial/blob/master/work/tutorial.ipynb

# ATRで指値距離を計算します
limit_price_dist = df['ATR'] * 0.5
limit_price_dist = np.maximum(1, (limit_price_dist / pips).round().fillna(1)) * pips

# 終値から両側にlimit_price_distだけ離れたところに、買い指値と売り指値を出します
df['buy_price'] = df['cl'] - limit_price_dist
df['sell_price'] = df['cl'] + limit_price_dist


このdf['ATR'] * 0.5の部分の係数の決め方は自由度があり、この値によって執行は大きく変わります。例えば0.5ではなく、1.0に変えたらどうでしょう?約定確率は大きくさがり、取引量は格段に減ります。また0.1に変えたらどうでしょう?こうすると約定確率は1に近づき、取引回数も大幅に増えます。目的変数はこのdf["buy_price"]とdf["sell_price"]によって計算されているため、機械学習(チュートリアルではLightGBM)を行った際の学習のしやすさもこのATRの係数によって大きく変わります。もちろん使用する特徴量によっても最適なATRは変わると思います。

したがって、ATRの係数をどのように決めるかは執行の肝であり、このrichmanbtcさんのチュートリアルベースのボットを動かす上で最重要な要素の1つになっています。(もちろんATR以外の指標を試すのはありだと思いますが、本記事ではATR指値のみにフォーカスしています。)

実際、界隈で有名な方が予測よりも執行が大事であるということを述べています。

このATR指値の最適化は実際かなり難しく、自由度が高いところ(恣意性も高いところ)です。例えば、訓練する前に最適化するか、訓練中に最適化するか、訓練の結果を見てから最適化するか、など様々なやり方が考えられます。

本記事では、それらの方法を組み合わせて実際にATR最適化を行ったモデル6種類によるバックテスト(累積リターン)の結果をお見せします。これらのモデルではrichmanbtcさんのチュートリアル同様のGMOコインの15分足のデータで、チュートリアルと同じ特徴量を用いてLightGBMで訓練したものとなっております。訓練期間は2021年9月以前のデータテスト期間はそれ以降の期間です。

モデル1

スクリーンショット 2022-03-30 16.46.56

モデル2

スクリーンショット 2022-03-30 16.59.08

モデル3

スクリーンショット 2022-03-30 17.00.03

モデル4

スクリーンショット 2022-04-28 13.11.06

モデル5

スクリーンショット 2022-04-28 13.14.22

モデル6

スクリーンショット 2022-04-28 13.22.58

有料記事部分にて、上記のモデル 1, 2, 3, 4, 5, 6 をチュートリアル同様LightGBMで訓練した学習済みモデルをjoblib.dumpでシリアライズ化された形式で配布させていただきます。上で説明した並列運用スクリプトと組み合わせることでこれらの6つのモデルを並列で運用することができます。配布スクリプトでこれらのモデルですぐに実運用できるようにしてあります

特徴量に関しては非定常量になっているものを省くrichmanbtcさんのnon_stationary_score.ipynbを参照)などをして工夫すればさらに精度が上がると思います。

以上のボットの並列運用スクリプトATRの指値の最適化に関して、興味のある方がいらっしゃれば、ぜひ購入を検討してみてください!

実際の並列運用スクリプトの一部

GMOBotクラス
各ボット用のクラスです。

class GMOBot:
   """
   ボットのクラスです。各ボットごとに注文処理を行い、注文ID、ポジションIDを管理します。
   """

   def __init__(
       self,
       model_buy_path,
       model_sell_path,
       atr_coeff,
       orderId_pkl_path,
       positionId_pkl_path,
       lot=0.01,
       atr_period=14,
   ):
       ***
       
   def **(
       self,
       ***
   ):
       ***
   
   def entry_position(
       self,
       df_features,
       buy_price,
       sell_price,
   ):
       buy_signal, sell_signal = self.get_buysell_signals(df_features)
       buy_size = 0.0
       sell_size = 0.0
       for _, (_, side, size, _) in self.open_positionId_dict.items():
           if side == "BUY":
               buy_size += size
           if side == "SELL":
               sell_size += size

       if buy_signal and buy_size == 0.0:
           args = ("BUY", self.order_lot, buy_price)
           orderId = self.create_limit(*args)
           if orderId != 0:
               self.orderId_dict[orderId] = args
           logger.info(
               f"entry_buy_order : side : {args[0]} size : {args[1]} price : {args[2]}"
           )
       if sell_signal and sell_size == 0.0:
           args = ("SELL", self.order_lot, sell_price)
           orderId = self.create_limit(*args)
           if orderId != 0:
               self.orderId_dict[orderId] = args
           logger.info(
               f"entry_sell_order : side : {args[0]} size : {args[1]} price : {args[2]}"
           )
       return None

   def exit_position(self, buy_price, sell_price):
       if self.open_positionId_dict != {}:
           for open_positionId, (
               _,
               side,
               size,
               _,
           ) in self.open_positionId_dict.items():
               if side == "BUY":
                   args = ("CLOSE-BUY", size, sell_price)
                   orderId = self.close_limit(
                       "SELL", size, sell_price, open_positionId
                   )
                   self.orderId_dict[orderId] = args
                   logger.info(
                       f"exit_buy_position => sell order : size : {size} price : {sell_price}"
                   )
               else:
                   args = ("CLOSE-SELL", size, buy_price)
                   orderId = self.close_limit("BUY", size, buy_price, open_positionId)
                   self.orderId_dict[orderId] = args
                   logger.info(
                       f"exit_sell_position => buy order : size : {size} price : {buy_price}"
                   )

       return None


   def exit_and_entry(self, df_features):
       buy_price, sell_price = self.get_latest_order_price(df_features)
       self.exit_position(buy_price, sell_price)
       self.entry_position(df_features, buy_price, sell_price)

Managerクラス
各ボットを管理するクラスです。

class Manager:
   """
   複数のボットを管理するクラスです。ボットごとにロットを自動的に割り当て、各ボットの注文管理をします。
   """
    def __init__(self, **):
       ***
       
    def ***(self, **):
       ****
           
    def exit_and_entry_all_bots(self, df_features):
       for bot_name in self.bot_dict.keys():
           logger.info(f"{bot_name} is starting orders.")
           self.bot_dict[bot_name].exit_and_entry(df_features)
           

有料部分で配布するzipファイルのフォルダ構成

有料部分で配布するzipファイルは下記のような構成になっております。

├── data
│   └── ohlcv_gmo_15m.pkl
├── docker
│   └── Dockerfile
├── features
│   └── features_default.pkl
├── model_buy
│   ├── ****.xz # モデル1
│   ├── ****.xz # モデル2
│   └── ****.xz # モデル3
│   ├── ****.xz # モデル4
│   ├── ****.xz # モデル5
│   └── ****.xz # モデル6
├── model_sell
│   ├── ****.xz # モデル1
│   ├── ****.xz # モデル2
│   └── ****.xz # モデル3
│   ├── ****.xz # モデル4
│   ├── ****.xz # モデル5
│   └── ****.xz # モデル6
├── src
│   ├── config.py
│   ├── gmocoin.py
│   ├── richman_features.py
│   └── start_all_bots.py
├── README.md
├── docker-compose.yml
└── start_allbots_on_mac.sh

動作条件

基本的にGCPやAWS上などのLinuxサーバー上(Ubuntu18.04とUbuntu20.04で動作確認済み)でdockerコンテナを立てた上での運用を前提としていますが、Windows WSL2やMacでもdockerを用いれば動作確認済みとなっております。

動作方法に関しては、GMOコインのapiKeyや最大ロットなどをスクリプトに記入すれば、下記のdocker-composeのコマンドですぐに並列運用開始できるようになっています。

docker-compose up

またこちらのボットはすでにGMOコイン上で長期的な実運用で使用しており、下記の仕様に対応しております。

1. GMOコインのメンテナンス時期(水曜15-16時)に対応
2. GMOコインにおいて朝6時に日付が変わる仕様に対応

有料部分購入の前提条件

Pythondockerをある程度使ったことがあり、かつ、 richmanbtcさんのチュートリアルを再現することができ、内容を理解していることが本有料記事の購入の前提条件となります。(もちろんこれらの前提条件をみたしていなくとも購入可能です。)

下記の記事ではチュートリアルに関して詳細に解説してくださっており、とても参考になります。

注意事項・免責事項

有料部分に掲載する内容は、サーバーやネットワーク環境、相場などにより変化しうるため、将来の利益を保証するものではありません。ご自身の判断と責任の上で、ご購入およびご活用ください。有料部分の共有や転売、転載などは一切これを禁止します。また、プログラムコードを動かすためのサポートは対象外とさせていただきます。

以下には、GMOコイン用のボットの並列運用スクリプトATR指値の最適化を行ったモデルの.xzファイル(上記のモデル1, 2, 3, 4, 5, 6)があります。

ここから先は

969字 / 1ファイル

¥ 12,000

この記事が気に入ったらサポートをしてみませんか?