BitMEX自動売買bot(python)でWebSocket高速通信をする(ポジション情報なども取れる!)

※※告知その3!!です!※※

皆様、本当にお待たせしてしまいました。bitmex_websocketを高速化したもののソースコードと、説明記事をアップさせていただきました。

高負荷時のbitmex_websocketの遅延に悩まされている方は、ご一読いただけると幸いです!
いやー、難産だった・・・それでは!

※※告知その2!です!※※

皆様お待たせいたしました!bitmex_websocketの高速化に成功いたしました!
また、以前の告知の内容にも不十分な点ありまして、Cloud9での動作環境が通常のPCよりも低めであることと、BitMEX自体の混雑度にも関連して遅延が発生することがわかってきました。取引量が少ない状況だと、チューンナップしなくてもほぼ遅延はありませんでした。
とはいえ!混雑時でこそ、高速に情報を取得できなければ困りますよね!
公式のbitmex_websocketライブラリに変更を加え、MEX混雑時、Cloud9での動作であってもWEB画面と同等、というよりも一瞬早いんじゃない?というレベルまでの高速化に成功しましたので、ただいま新規にnoteを執筆中です。
近日中にまた無料公開しますので、ご期待ください!

※※告知!告知!※※

AKAGAMI LOUNGEでの報告で、頼りにしていたbitmex_websocketの渡してくる情報が数十秒からひどいときには数分遅い!ということがわかりました。

ほかの汎用的なWebSocketクライアントだと、遅延のほぼない情報が得られるという情報もありましたので、複数視点で検証しつつ、改善版を近日公表します。
それまでは、本当に申し訳ないのですが、こちらのnoteの情報を鵜呑みにされないよう、重々お気をつけて記事をお読みください。
すみませんでした!

※※告知ここまで、です。以下、旧内容をお楽しみください。(使用には気を付けて!)※※

どうもこんばんは。トレード・ド・初心者で先日AKAGAMIさん販売のドテン君でbotトレードを始めたモッチオです。botすごい!地合いあってなのか、AKAGAMIさんのロジックが素晴らしいのか、botの成績はかなりプラスのようです。

botはね・・・フフッ
私はド・初心者だからなのか元からメンタルが弱いのか、誘惑に負けて裁量取引を行って失敗してしまい、マイナスです。いやいや違うんだ、トレーダーのマインドってやつを自分の中で育てていきたくて、あえてやったんだ。この失敗は未来の栄光への一歩なんだ!と自分に言い聞かせています。

そんなことはどうでもいいですね!すみません!

※下記内容はAKAGAMIさん作成のbot「ドテン君」を動かす環境での動作を想定しています。環境の違いで困られた方は、下記のAKAGAMIさんのnoteを参考に、Cloud9の環境を構築してみてください。

今回のやりたいこと「機先を取りたい」

今稼働しているbotでは、ccxtというライブラリを使って、BitMEXとの通信を行っています。BTCの価格や現在もっているポジションの情報を得たり、それらの情報をもとに発注をしたりします。

この処理を繰り返し行って、botは動いていくわけです。
ここで、一つの改善策が頭に浮かんできます。

30秒ごととかじゃなくて、1秒ごととか高い頻度で価格情報の確認や処理を行ったら、早く判断ができて、早くアクションがおこせるんじゃね?

しかし、このアイデアを実行するには、いくつか障害がありました。

解決すべき問題点(REST  APIは高頻度には叩けない!)

ここで使われている情報のやり取りは「HTTPSプロトコル」を使った「REST API」という方式で行われます。
説明すると長くなるので端折りますが、(説明できないわけじゃないんですよ!私、国が認めた情報セキュリティスペシャリストですから!)このHTTPSプロトコルというやつがとっても重たい処理なのです!なのでBitMEXはこのプロトコルを使った「REST API」に使用頻度の制限を課しています。

当社の REST API へのリクエストは、5 分あたり 300 リクエストに制限されています。このカウンタは継続的に補充されます。ログインしていない場合は、レート制限は 5 分あたり 150 リクエストになります。

※「https://www.bitmex.com/app/restAPI」より

5分あたり300回なので、1秒に1回程度しか通信できないということです。
botの処理の中では、複数回の通信を行いますので、実質は10秒に一回程度しか処理を行えません。エラーで落とされてしまいます。

高頻度アクセスの救世主!「WebSocket API」!

そこで登場するのが「WebSocket API」という通信方式です。
「HTTPS」では、情報を取得しようとするたびにリクエストを行う必要がありますが、「WebSocket」方式は一度接続を行ったら、その接続を閉じずに維持し、サーバーとブラウザの相互での通信を続けます。
なんと、その間はリアルタイムで通信し放題!なんどでも最新情報を手に入れることができるんですね!
これが、BitMEXでもちゃんと用意されているんです。使わない手はないですよね!

「WebSocket API」を使うには?

BitMEXのドキュメントページに、このWebSocket APIの使用方法が掲載されています。
https://www.bitmex.com/app/wsAPI

が、読んでも訳がわかりません!何度も読んでいると、「あーなるほど」という気分になってくるかもしれませんが、ちょっと時間と労力が勿体ないです。

ご安心ください。BitMEX提供のWebSocket通信を行ってくれるpythonライブラリがあります!その名も「bitmex_websocket」!

これを利用して情報の取得を行います。

なお、標準ではインストールされていませんので、コンソールから下記のコマンドでインストールしてください。
ライブラリの名前に注意!「bitmex_websocket」ではなく、「bitmex_ws」です。

sudo pip-3.6 install bitmex_ws

いよいよPythonのコードです

それぞれの処理の前に、コメントを記述していますので、処理の詳細に関してはそれをご確認ください。

#!/usr/bin/python3
from datetime import datetime as dt
import time
# WebSocket API を代行してくれるライブラリをインポート
from bitmex_websocket import BitMEXWebsocket

# WebSocket API接続用オブジェクトを生成
ws = BitMEXWebsocket(endpoint="https://www.bitmex.com/api/v1", symbol="XBTUSD", api_key=あなたのAPI_KEY, api_secret=あなたのSECRET_KEY)
# instrumentメソッドを一度呼び出さないとエラーを吐くので追加
ws.get_instrument()

try:
    # Socketの接続が活きている限り処理を続けます
    while ws.ws.sock.connected :
        #################################
        # WebSocket APIを使って情報を取得する
        #################################
        
        now = dt.now().strftime('%Y-%m-%d %H:%M:%S')
        # Ticker情報を取得!
        tick = ws.get_ticker()
        # ポジション情報を取得!(get_position()はありませんが、プロパティ「data」の中にちゃんと入ってるんですね)
        # ただし、ポジションが一つもない場合、'position'というデータ自体がありませんので、そこを考慮します
        if 'position' in ws.data:
             positions = ws.data['position']
        else:
             positions = []
        # 取得したポジション情報には現在保有している全ての通貨セットのポジションが含まれますので、'XBTUSD'でフィルタリングします
        positions = [position for position in positions if position['symbol'] == 'XBTUSD']
        
        # コンソールに表示してみます
        print("%s : ticker : %s" % (now, tick))
        for position in positions:
            print("%s : position : synbol %s : qty %s" % (now, position['symbol'], position['currentQty']))
    
        # 0.1秒ごとに繰り返します!
        time.sleep(0.1)
except:
    ws.exit()

上記コードを「test_websocket.py」などのファイル名で保存し、実行権限を与えて実行すると・・・

うお!いっぱい表示された!!

行先頭で表示されている時間をみると、ちゃんと1秒に10回ほど実行されています。

そして、この頻度で情報を取得し続けても、リクエスト過多でのエラーになりません!
いやーすごいですねWebSocket!

これで、価格などの情報の変化をいち早く察知して、売買アクションを実行できます。

少し解説

コードを実行しただけでは、何をやっているかわかりづらいと思うので、補足の解説をしますね。

# WebSocket API接続用オブジェクトを生成
ws = BitMEXWebsocket(endpoint="https://www.bitmex.com/api/v1", symbol="XBTUSD", api_key=あなたのAPI_KEY, api_secret=あなたのSECRET_KEY)

ここでは、WebSocket経由でBitMEXとの通信を行ってくれるオブジェクトを作り出しています。
api_keyとapi_secretをちゃんと指定しないと、ポジション情報など、アカウント由来のプライベートな情報が取得できないので、注意してください。

# instrumentメソッドを一度呼び出さないとエラーを吐くので追加
ws.get_instrument()

最初に一度だけ、instrumentデータ(取引高とビッド/アスクなどの最新商品情報)の取得処理を呼び出します。
これは、つづく処理の中で使用しているget_ticker()メソッドが、内部的にinstrumentの情報を使用しているためです。具体的には、このメソッドを一度でも動かしておかないと、get_ticker()を呼び出した瞬間にエラーで落ちます。

# Socketの接続が活きている限り処理を続けます
while ws.ws.sock.connected :

WebSocketの接続があることを条件に、while文を回します。
こうすることで、接続できていることのチェックになります。

    # Ticker情報を取得!
   tick = ws.get_ticker()

while文の中でticker情報を取得します!
下記の情報を得ることができます。

{
    "last" : 最終価格,
    "buy"  : Bid価格,
    "sell" : Ask価格,
    "mid"  : BidとAskの平均値    
}

つぎはポジション情報です。
この情報はAPIキーを指定しているとき、ポジションが存在するときにしか取得できないので、ご注意ください。

    # ポジション情報を取得!(get_position()はありませんが、プロパティ「data」の中にちゃんと入ってるんですね)
    # ただし、ポジションが一つもない場合、'position'というデータ自体がありませんので、そこを考慮します
    if 'position' in ws.data:
        positions = ws.data['position']
    else:
        positions = []
    # 取得したポジション情報には現在保有している全ての通貨セットのポジションが含まれますので、'XBTUSD'でフィルタリングします
    positions = [position for position in positions if position['symbol'] == 'XBTUSD']

コメントでほとんどのことを解説していますが、まず、ポジションデータはWebSocket通信用オブジェクトの「data」プロパティから取得します。
get_position()などのメソッドが用意されていないので、私などは取得できないのかと思ってしまいましたが、しっかりと取得する方法がありました。

まず、ポジションがない場合は、このdataプロパティにもポジション情報が存在しないので、エラー回避のためにチェックしています。存在する場合はデータをposition変数に格納、存在しない場合は空の配列を設定しています。

つづいて、このポジション情報には「XBTUSD」以外の取引ペアのポジションが含まれている場合があるので、「XBTUSD」のみのデータとなるように操作しています。

その他の取得できる情報

BitMEXWebsocketには、その他にもたくさんの情報を取得できるメソッドが用意されています。

market_depth()        # フルレベル 2 のオーダーブック
recent_trades()       # 取引のライブ情報
funds()               # (APIKEY指定必須)最新アカウント残高と証拠金必要額に関する最新情報
open_orders(orderid)  # 指定したオーダーIDの最新情報

特に、まだ未検証ですが、open_orders()はオーダーが通ったかどうかの確認にも使えるかもしれません。

また、dataプロパティには提供される情報の全てが含まれるようですので、BitMEXのAPI解説ページにある下記の情報も取得できるでしょう。
※すみませんが、こちらは未検証です。

"announcement",// サイトのお知らせ
"chat",        // Trollbox チャット
"connected",   // 接続ユーザー/ボットの統計
"funding",     // 最新のスワップ資金調達率。 資金調達間隔ごとに送信 (通常8時間)
"instrument",  // 取引高とビッド/アスクなどの最新商品情報
"insurance",   // デイリーの保険基金に関する最新情報
"liquidation", // ブックに記入される清算注文
"orderBookL2", // フルレベル 2 のオーダーブック
"orderBook10", // 従来のフルブックプッシュを使用する上位 10 レベル
"publicNotifications", // システム全体への通知 (短期公開メッセージ用)
"quote",       // ブックの上位レベル
"quoteBin1m",  // 1 分足クォートビン
"quoteBin5m",  // 5 分足クォートビン
"quoteBin1h",  // 1 時間足クォートビン
"quoteBin1d",  // 1 日足クォートビン
"settlement",  // 決済
"trade",       // ライブ取引
"tradeBin1m",  // 1 分足取引ビン
"tradeBin5m",  // 5 分足取引ビン
"tradeBin1h",  // 1 時間足取引ビン
"tradeBin1d",  // 1 日足取引ビン

次のトピックには認証が必要です。
"affiliate",   // アフィリエイトの状況 (合計紹介ユーザーや支払い率等)
"execution",   // 個別執行 (注文ごとに複数の可能性)
"order",       // リアルタイムでの注文の最新状況
"margin",      // 最新アカウント残高と証拠金必要額に関する最新情報
"position",    // ポジションに関する最新情報
"privateNotifications", // 個人的通知 - 現在未使用
"transact"     // 入金/出金に関する最新情報
"wallet"       // ビットコインアドレス残高データ (合計入金・出金額等)

botへの組み込み

さて、いよいよ自動取引botへの組み込み・・・と行きたいところですが、pythonですでに自動取引botを作成されている方は、ここまでに紹介した情報で十分、活用できることと思います。

AKAGAMIさん作成のbotに上記のwebsocket通信を適用したバージョンは、近日にドテン君を購入された方々が参加できる「AKAGAMI LOUNGE」にて公開させていただこうと思っています。

5万円と高額ですが、このドテン君とAKAGAMI LOUNGEへの参加の価値を見出せる方々が集まったコミュニティです。日々濃密な議論が交わされており、非常に勉強になります。
まだ購入されていない方は、是非購入なさって、ドテン君を入手し、「AKAGAMI LOUNGE」にてお会いしましょう。

なお、私のコミュニティでの名前も「モッチオ」となっております。
当noteに関するご質問やコード提供など、できる限り対応させていただきますので、是非声をおかけください。

長文にも関わらず、最後まで読んでいただき、ありがとうございました!!


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