[Puppeteer] bitmex_websocket対応 python製仮想通貨自動取引bot開発フレームワーク
前回からの経過
前回のnoteにて、bitmexのwebsocketを使って板情報や約定履歴を取得するプログラムに挑戦していました。
モッチオさんのnoteを参考にしてあれやこれやと格闘すること数日。。
最後までインデックスのエラーに悩まされ、とうとうコアロジックを総入れ替え(笑)
まずまずの出来になったので、Puppeteer(傀儡師)にビルトインしました。
巷で人気のMarket Maker Botの実装に必要な基本機能は作り込めたと思っています。
Puppteer(傀儡師)
PuppeteerはMITライセンスです。
誰でも自由に利用することができます。
Puppteerはつぎのgitlabからcloneで取得してください。
(タグ v1.0.1からwebsocket対応です)
Puppeteerフォルダの
- puppets/
- sample1/
- sample1.py
- sample1.json
にwebsocketを使ったサンプルを同梱しています。
websocketを使用したPuppet(傀儡)サンプル
sample1.py
# -*- coding: utf-8 -*-
# ==========================================
# サンプル Puppet (websocket)
# ==========================================
import datetime
from puppeteer import Puppeteer
# ==========================================
# Puppet(傀儡) クラス
# param:
# puppeteer: Puppeteerオブジェクト
# ==========================================
class Puppet(Puppeteer):
_exchange = None # 取引所オブジェクト(ccxt.bitmex)
_logger = None # logger
_config = None # 定義ファイル
_ws = None # websocket
_bitmex = None # ccxt.bimexラッパーオブジェクト
# ==========================================================
# 初期化
# param:
# puppeteer: Puppeteerオブジェクト
# ==========================================================
def __init__(self, Puppeteer):
self._exchange = Puppeteer._exchange
self._logger = Puppeteer._logger
self._config = Puppeteer._config
self._ws = Puppeteer._ws
self._bitmex = Puppeteer._bitmex
# ==========================================================
# 売買実行
# param:
# ticker: Tick情報
# orderbook: 板情報
# position: ポジション情報
# balance: 資産情報
# candle: ローソク足
# ==========================================================
def run(self, ticker, orderbook, position, balance, candle):
# websocketを使う時には、この引数は取得しなくて良い。
self._logger.info('ticker {}, orderbook {}, position {}, balance {}, candle {}'.format(ticker, orderbook, position, balance, candle))
# --------------------------
# ここに処理を記述します
# --------------------------
# ------------------------------------------------------
# orderbookから最新のbid/askを取得する
# ------------------------------------------------------
orderbook = self._ws.orderbook()
bid = orderbook['bids'][0]['price']
ask = orderbook['asks'][0]['price']
# 値チェック
if bid == 0 or ask == 0 or bid == None or ask == None :
self._logger.error('orderbook error: bid={}, ask={}'.format(bid, ask))
return
self._logger.info('bid {}, ask {}'.format(bid,ask))
# ------------------------------------------------------
# ポジションサイズ、参入価格
# ------------------------------------------------------
position = self._ws.position()
if position is not {}:
pos_qty = position['currentQty'] if position['currentQty'] is not None else 0
avg_price = position['avgEntryPrice'] if position['avgEntryPrice'] is not None else 0
self._logger.info('pos {}, avg {}'.format(pos_qty, avg_price))
# ------------------------------------------------------
# オープンオーダー
# ------------------------------------------------------
self._logger.info('open orders = {}'.format(self._ws.open_orders()))
# ------------------------------------------------------
# 資産
# ------------------------------------------------------
self._logger.info('balance {}'.format(self._ws.funds()))
sample1.json
{
"//" : "===============================================",
"//" : " システムで利用",
"//" : "===============================================",
"//" : "取引所のapiKey, secretを設定します",
"APIKEY" : "YOUR_APIKEY",
"SECRET" : "YOUR_SECRET",
"//" : "bitmex取引所で対応する通貨ペア等を記述",
"SYMBOL" : "BTC/USD",
"INFO_SYMBOL" : "XBTUSD",
"COIN_BASE" : "BTC",
"COIN_QUOTE" : "USD",
"//" : "bitmex取引所の価格の最小幅(0.5ドル)",
"PRICE_UNIT" : 0.5,
"//" : "TestNetを使うか?(使う: true, 使わない: false)",
"USE_TESTNET" : true,
"//" : "ticker, orderbook, position, balance, candle のどれを利用するかを指定する。Falseを指定した場合はそのデータは取得しない",
"USE" : {
"TICKER" : false,
"ORDERBOOK" : false,
"POSITION" : false,
"BALANCE" : false,
"CANDLE" : false
},
"//" : "ローソク足の収集定義。",
"CANDLE" : {
"//" : "ローソク足の足幅を設定する。設定値= 1m, 5m, 1h, 1d",
"TIMEFRAME" : "1m",
"//" : "データ取得開始時刻(UNIXTIME:1ミリ秒)、使用しない場合 もしくは自動の場合は null(None) を指定",
"SINCE" : null,
"//" : "取得件数(未指定:100、MAX:500)",
"LIMIT" : null,
"//" : "True(New->Old)、False(Old->New) 未指定時はFlase",
"REVERSE" : false,
"//" : "True(最新の未確定足を含む)、False(含まない) 未指定はTrue",
"PARTIAL" : false
},
"//" : "板情報の収集定義。",
"ORDERBOOK" : {
"//" : "取得件数(未指定:25、MAX:取引所による?)",
"LIMIT" : null
},
"//" : "websocketを使用するかどうかを指定",
"USE_WEBSOCKET" : true,
"//" : "ログレベルを指定。('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG')",
"LOG_LEVEL" : "INFO",
"//" : "インターバル(botの実行周期)を秒で設定",
"INTERVAL" :5,
"//" : "discord通知用URL",
"DISCORD_WEBHOOK_URL" : "",
"//" : "===============================================",
"//" : " ユーザで自由に定義",
"//" : "===============================================",
"//" : "売買するサイズ",
"LOT_SIZE" :50
}
設定説明
ccxtのメソッドは使用しないので、USE設定は
"//" : "ticker, orderbook, position, balance, candle のどれを利用するかを指定する。Falseを指定した場合はそのデータは取得しない",
"USE" : {
"TICKER" : false,
"ORDERBOOK" : false,
"POSITION" : false,
"BALANCE" : false,
"CANDLE" : false
},
のようにすべてfalseとしています。
websocketを有効にするには
"//" : "websocketを使用するかどうかを指定",
"USE_WEBSOCKET" : true,
を指定します。
websocketはPuppetクラスのPuppeteerオブジェクトから
self._ws = Puppeteer._ws
で取得しています。
以降は、この「self._ws」に対して操作を行います。
板情報は
orderbook = self._ws.orderbook()
で取得します。
内容を見るには
bid = orderbook['bids'][0]['price']
ask = orderbook['asks'][0]['price']
のようにすれば、一番狭い幅のbid/askの値が取れます。
対応しているwebsocket通知
今回のwebsocketではbitmexが提供しているデータのうち
execution
order
position
quote
trade
margin
instrument
orderBookL2
を取得しています。
order, orderBookL2の更新にかなり苦労しました。
でも、その甲斐あってパフォーマンスはそれなりに出ていると思います。
orderの更新にはもう少し改善の余地があると思いますが、今後の課題としておきます。
まだHAL900はwebsocket対応機能を使っていませんが、時間を見つけてUpdateしようと思っています。
追記
最新版のPuppeteerはwebsocket-clientを使いますので、
sudo pip3 install websocket-client
でインストールしてください。
もしかしたら、websocket-clientのバージョンが5未満じゃないと動かないかもしれません。
私が動作確認している環境は version 0.47, 0.48 でした。
楽しいbotライフを!
ソフトウェア・エンジニアを40年以上やってます。 「Botを作りたいけど敷居が高い」と思われている方にも「わかる」「できる」を感じてもらえるように頑張ります。 よろしくお願い致します。