BitMexのBotをVisual Studioで開発する その1 - 環境構築 -
前回はVisual StudioでのPython環境構築とccxtインストール、BitMexのテストネットへの注文を行いました。
実際にbotとして動かす為には注文以外のAPI(関数・メソッド)の使い方を知る必要があります。
しかし、APIの数は約500…
1つ1つ使い方を調べるには多過ぎます。
まずはbotにやらせたい事を把握した上で必要なAPIを探して勉強するというアプローチが早そうです。
今回はコードがメインなので文字ばっかりになります。
目次
botにやらせたい項目
まずはbotにやらせたい事を挙げていきます。
- 現在の価格を取得
- 現在の資産を取得
- 現在の注文情報を取得
- 現在のポジション情報を取得
- 各種指標(RSI、MACD等)を取得
- レバレッジの取得、変更
- 新規注文・キャンセル
- 利確・ロスカットの価格設定(成行/指値)
- 過去のローソク足情報取得
また、今後のコードの前提として以下を実行してbitmexという変数が生成されているものとします。
※C#やJava等の経験をしている為、個人的な好みで文字列は''ではなく""を使う事が多いです。サンプルのコピペだと交じっている事がありますが、見逃してください。
bitmex = ccxt.bitmex({ "apiKey": "APIキー", "secret": "秘密鍵", }) bitmex.urls["api"] = bitmex.urls["test"]
APIの内容は公式のマニュアルを参考にし、実際にPythonでコードを書いてprintでコンソールに表示させてテストネットの内容と比較しながら動作確認しています。
https://github.com/ccxt/ccxt/wiki/Manual
現在の価格を取得
現在価格を取得するにはfetch_tickerを使い、"last"を指定して取得します。
ticker = bitmex.fetch_ticker("BTC/USD") print(ticker["last"]) #最後に取引された価格を表示する
ちなみに、上記で得られたtickerには現在価格以外に過去24時間の以下の情報が含まれます。
{ 'symbol': string # 市場の記号 ('BTC/USD', 'ETH/BTC', ...) 'info': { 取引所APIから返信された生データ }, 'timestamp': int #64-bit 1970/1/1からのUnixタイムスタンプ(ms単位) 'datetime': string, #ISO8601のms単位のタイムスタンプ (UTC +0なので日本時間から-9時間) 'high': float, # 最高値 'low': float, # 最低値 'bid': float, # オーダーブック内の最高のbid値 'bidVolume': float, # オーダーブック内の最高のbid量 'ask': float, # オーダーブック内の最高のask値 'askVolume': float, # オーダーブック内の最高のask量 'vwap': float, # <span>出来高加重平均 (累積売買価格/累積出来高)</span> 'open': float, # 始値 (UTC +0) 'close': float, # 最後に取引された価格 'last': float, # closeと同じ (分かりやすくする為、別名で定義されている) 'previousClose': float, # 前回の期間の終値 'change': float, # 1日の変化の絶対値, `last - open` 'percentage': float, # 1日の変化の相対値[%], `(change/open) * 100` 'average': float, # 平均価格, `(last + open) / 2` 'baseVolume': float, # 過去24時間で取引された基軸通貨(XBT)の量 'quoteVolume': float, # 過去24時間で取引された<span>決済通貨(USD)</span>の量 }
現在の資産を取得
現在資産を取得するにはfetch_balanceを使います。
以下の3種類が取得できます。
- 使用可能なXBT
- 使用中のXBT
- 合計(全資産)のXBT
balancelist = bitmex.fetch_balance() print(balancelist["free"]["BTC"]) #使用可能なXBT print(balancelist["used"]["BTC"]) #使用中のXBT print(balancelist["total"]["BTC"]) #全資産[]内の文字は左右逆でも問題ありません。
現在の注文情報を取得
注文して約定していないものの情報を取得するにはfetch_open_ordersを使います。
複数注文がある場合はそれら全てが取得され、配列として0, 1, 2, 3…として格納されます。
orders = bitmex.fetch_open_orders() print(orders) #全てを表示 print(oresers[0]) #配列の0番目を表示
取得される注文情報の内容は以下のようになっています。
この内容は注文系のAPIの戻り値として共通です。
2018/9/12追記
※typeのストップ指値は"stopLimit"か"StopLimit"じゃないとエラーになるようです!"stoplimit"だとエラーになりました。
{ 'id': '12345-67890:09876/54321', // string 注文ID 'datetime': '2017-08-17 12:42:48.000', // string ISO8601のms単位のタイムスタンプ (UTC +0なので日本時間から-9時間) 'timestamp': 1502962946216, // int 発注時のms単位のUnixタイムスタンプ 'lastTradeTimestamp': 1502962956216, // int 最後に取引された時のms単位のUnixタイムスタンプ 'status': 'open', // string 注文の状態。'open'=注文中, 'closed'=約定, 'canceled'=キャンセルされた 'symbol': 'BTC/USD', // string 市場名 'type': 'limit', // string 注文の種類。'market'=成行, 'limit'=指値, 'stop'=ストップ成行, 'stopLimit'=ストップ指値 'side': 'buy', // string 売買。'buy'=買い(ロング), 'sell'=売り(ショート) 'price': 6310.0, // float 注文価格 'amount': 1000.0, // float 注文量 'filled': 0.0, // float 約定済み量 'remaining': 0.4, // float 未約定残量 'cost': 0.076094524, // float 約定済みの資産価格。'filled' * 'price' 'fee': { // 手数料(有効な場合) 'currency': 'BTC', // string 手数料の単位 'cost': 0.0009, // float 手数料 'rate': 0.002, // float 手数料の利率(有効な場合) }, 'info': { ... }, // 取引所APIから返信された生データ }
以下のように指定すれば各項目毎に取得できます。
for-inを使う事で複数注文があっても全て注文情報を取得できます。
orders = bitmex.fetch_open_orders() for order in orders: print(order["id"]) print(order["status"]) print(order["type"]) print(order["side"]) print(order["amount"])
現在のポジション情報を取得
現在のポジション情報を取得するにはprivate_get_positionを使用します。
これは取得できる情報が多過ぎるので、必要そうな項目をピンポイントで。
なお、BitMexではポジションは1つしか持てませんが、戻り値は配列になっているのでfor-inで要素毎に取り出す書き方をします。
positions = bitmex.private_get_position() for position in positions: print(position["currentQty"]) #現在ポジションの量 print(position["simpleQty"]) #XBT値 print(position["avgEntryPrice"]) #平均参入価格 print(position["markPrice"]) #マーク価格 print(position["marginCallPrice"]) #精算価格 print(position["unrealisedRoePcnt"]) #未実現損益(ROE0.014%) print(position["leverage"]) #レバレッジの倍率
ロングかショートかはcurrentQtyかsimpleQtyの戻り値が+か-かで判断します。-ならショートです。
未実現損益のunrealisedRoePcntは100倍した値が%となります。
各種指標(RSI、MACD等)を取得
残念ながらccxtでは指標を取得できるAPIは用意されていないようです。
過去の足情報を取得して自力で計算するか、指標を計算してくれるライブラリを使うかになります。
これは大変そうなので後日勉強します。
レバレッジの取得、変更
現在のレバレッジは前述のprivate_get_positionの"leverage"で取得できます。
設定する時はprivate_post_position_leverageを使います。
引数に渡す時は文字列ですが、botで実際に動かす時にレバレッジを可変したくなると思うので数値の変数としておいた方が後から楽です。
leverage = 25 bitmex.private_post_position_leverage({"symbol":"XBTUSD", "leverage":str(leverage) }) #レバレッジ25倍に設定
1~100以外を設定するとエラーになったり、意図しない動きとなるのでやめましょう。
一応小数点も大丈夫なようですが、値によってはエラーとなるので整数にしておくのが無難です。
新規注文・キャンセル
注文とキャンセルは別々のAPIがあります。
注文の場合はcreate_orderを使います。
symbol = "BTC/USD" # string 市場名 type = "market" # string limit=指値, market=成行 side = "sell" # string buy=買い, sell=売り amount = 5000 # float 注文量 price = 6600.5 # float 注文価格(指値の場合のみ) marketOrder = bitmex.create_order(symbol, type, side, amount) #成行 print(marketOrder) type = "limit" limitOrder = bitmex.create_order(symbol, type, side, amount, price) #指値 print(limitOrder)
注文に成功した場合は注文情報を取得する時に使ったfetch_open_ordersと同じ項目が返ってきます。
成行と指値で引数を間違えたり、現在価格より高値で指値をロングしたりすると注文が失敗してエラーとなりますので、エラー処理は必ず入れるようにしましょう。
Pythonの場合はエラーが発生しそうな文をtry: - except エラー型 as エラー名:で挟む事でクラッシュせずにプログラムを続行できます。
C#やJava等オブジェクト指向系の言語の経験があると分かると思いますが、try - catch文と同様です。
try: limitOrder = bitmex.create_order(symbol, type, side, amount, price) #ここでエラーが発生するかも print(limitOrder) except Exception as error: print(error) #異常時はエラーメッセージを表示する
注文のキャンセルはcancel_orderを使用します。
引数に注文IDを入れる必要があります。
当然ですが注文が無かったり存在しないIDを入れるとエラーが発生します。
これも成功するとfetch_open_ordersと同じ項目が戻ってきます。
try: limitOrder = bitmex.create_order(symbol, type, side, amount, price) orderId = limitOrder["id"] #注文時のIDを記憶しておく print(limitOrder) except Exception as error: print(error) #異常時はエラーメッセージを表示する try: cancel = bitmex.cancel_order(orderId) #注文時のIDを使う print(cancel) except Exception as error: print(error)
利確・ロスカットの価格設定(成行/指値)
使用するAPIは普通の注文と同じくcreate_orderです。
ロスカットはこの記事が参考になりました。
数量:10000、参入価格6400のロングポジションを持っている前提とし、ストップロスを6200でかけたい場合は以下のようになります。
symbol = "BTC/USD" type = "stop" #ストップロス side = "sell" amount = 10000 price = None #ストップロスの時はNoneを指定する params = { "stopPx" : 6200 } #ストップロスのトリガ価格 try: stopLossOrder = bitmex.create_order(symbol, type, side, amount, price, params) for order in stopLossOrder: print(str(order) + ":" + str(stopLossOrder[str(order)])) except Exception as error: print(error)
利確の場合は通常のcreate_orderで成行/指値注文するのと変わりませんが、指値で利確する場合は
- 売り:現在の価格>利確価格
- 買い:現在の価格<利確価格
となると注文が成行扱いで即注文が確定するので注意しましょう。
過去のローソク足情報取得
ローソク足の情報はfetch_ohlcv(symbol, timeframe, since, limit, params)で取得します。
OHLCVはOpen price(始値)、Highest price(高値)、Lowest price(安値)、Closing price(終値)、Volume(出来高)の頭文字を取ったもので、5つのパラメータが全て取得できます。
引数の意味は以下のようになっています。symbol、timeframe、sinceを設定すればlimit、paramsは設定しなくてもOKです。
- symbol:市場名 "BTC/USD"など
- timeframe:ローソク足の単位 "1m", "5m", "1h", "1d"など
- since:取得開始時刻(Unix時間[ms])
- limit:取得件数 デフォルト=100, MAX=500
- params:その他設定値
過去10分の1分足情報を取得する場合は以下のようにします。
now = datetime.utcnow() # 現在時刻(UTC) unixTime = calendar.timegm(now.utctimetuple()) # Unix時間に変換 since = (unixtime - 60 * 10) * 1000 # 10分前のUnix時間算出→ms単位に変換 ohlcvList = bitmex.fetch_ohlcv("BTC/USD", "1m", since) #10分前からの1分足情報を取得 for ohlcv in ohlcvList: print(ohlcv)
成功した時の戻り値の構造は以下のようになっています。
配列はOHLCVで1つでまとまっているので1つずつ順番に処理する時はfor-inを使いましょう。
[ [ 1504541580000, // UTCタイムスタンプ[ms], integer 4235.4, // (O)pen price 始値, float 4240.6, // (H)ighest price 高値, float 4230.0, // (L)owest price 安値, float 4230.7, // (C)losing price 終値, float 37.72941911 // (V)olume 出来高, float ], [ ... ], ... ]
…ccxtで使用するAPIはこれらを覚えておけば基本的には問題無いでしょう。
後はこれらの組み合わせて、どのようなロジックでポジションを取って利確/ロスカットするかです。
そのタイミングについてはやはりテクニカル指標が重要になってくると思います。
次回は指標を計算する手法について勉強したいと思います。