Python 3 / BitMEX の BOT を作ろう CCXT + BOT サンプルコード 〈基礎編〉

Python 3 で BitMEX の Bot を作る〈基礎編〉として、仮想通貨 の BOT 作成を簡単にする CCXT ライブラリの使い方を基礎から解説します。
後半では、全機能を使った実践的な BOT のサンプルコードも掲載しましたのでぜひご覧ください。

●目次
・APIキーの取得
・CCXTの導入
・CCXTの基本操作(その1・その2)
・Tickerの取得
・口座情報の取得
・ポジションの取得
・注文の取得
・発注
・注文のキャンセル
・実践的な「BOT」のサンプルコード
 → サンプルコードは上記すべての機能と実践で必要な全ての機能を実装した
  完全な BOT サンプルです。


●動作環境
・取引所:BitMEX
・言語:Python 3.6.x
・開発環境:AWS Cloud 9

●APIキーの取得
BitMEX で Bot を動作させるには「API キー」が必要になります。BitMEX にログインした状態で上のメニューより「API」を選びます。APIの概要が開けたら「APIキーの管理」を選択しましょう。

APIキーの管理を開くと、APIの作成画面が出てきます。「名前」に任意の名前(何でもいい)を入力し、「キーのアクセス許可」で「注文」を選択し「APIキーを作成」ボタンを押しましょう。するとすぐにAPIキーの発行が完了します。「秘密キー(secret)」は、閉じると二度と見れなくなるので何かにメモするなり保管しておきます。Bot の稼働には「ID」「秘密」の両方が必要となります。

※「注文」を選択してないと Bot からは発注・キャンセルができない。
※「出金」にチェックを入れると API から出金処理が可能になるがセキュリティ上は好ましくないので、通常はチェックを入れないほうが良い。

●CCXT の導入

CCXT https://github.com/ccxt/ccxt

CCXT とは「仮想通貨」専用の API ライブラリです。各取引所の API に対応しており、Bot 制作を容易にしてくれます。BitMEX や Python にも対応しています。とりあえず動作を確認してみたいというビギナーにもおすすめです。


●CCXT のインストール 
(AWS Cloud 9)
ターミナルに pip でインストールする

sudo pip install ccxt

「Successfully installed ccxt」が出ればインストールは完了です。

※ターミナルが見つからない人はウィンドウの「+」ボタンを押して「New Trarminal」を選択すると出てきます。


●CCXT の基本操作   その 1

CCXT を 使用するには、モジュールをインポートする事で使用できます。以下はサンプルコードです。「apiKey(ID)」と「secret(秘密)」はご自分のものに差し替えましょう。
※bitmex.urls['api'] = bitmex.urls['test'] は本番口座の場合は不要です。削除しても構いません。

#coding=utf-8
import ccxt #CCXT をインポートする

def bitmex(): #CCXT を呼び出す関数
   
   bitmex = ccxt.bitmex({

       #APIキーをご自分のものに差し替えてください
       'apiKey': 'Hfh0uk6Ef8tBdbu2jXbymFAF',
       'secret': 'SZYQuGnrPSQkGI7KcsvApy9AIP4z5AwT2clzzPwJHGne1e1B',
       
       })
   
   bitmex.urls['api'] = bitmex.urls['test'] #テスト用 本番口座の場合は不要
   
   return bitmex

#口座情報の取得
balance = bitmex().fetch_balance()
print(balance)


●CCXT の基本操作 その 2  

Bot でよく使う操作を紹介します。覚えるのはこれだけです。

#bitmex() は「その1」の関数です。

#Tickerの取得
ticker = bitmex().fetch_ticker('BTC/USD')

#口座情報の取得
balance = bitmex().fetch_balance()

#ポジションの取得
position = bitmex().private_get_position()

#注文の取得
open_orders = bitmex().fetch_open_orders()

#発注
order = bitmex().create_order('BTC/USD', type='limit', side='buy', amount=10000, price=8000)

#注文のキャンセル
cancel = bitmex().cancel_order('orderID')

●Ticker の取得
fetch_ticker(Symbol)

パラメーター
・Symbol (string): "BTC/USD"

BitMEX から返されるレスポンスは「JSON形式」のデータです。以下「Ticker」のレスポンス。

#Ticker レスポンス
{
  'symbol': 'BTC/USD', 
  'timestamp': 1522945321870, 
  'datetime': '2018-04-05T16:22:02.870Z', 
  'high': 6935.0, 
  'low': 6555.0, 
  'bid': 6670.0, 
  'bidVolume': None, 
  'ask': 6670.5, 
  'askVolume': None, 
  'vwap': 6744.9076, 
  'open': 6779.0, 
  'close': 6675.5, 
  'last': 6675.5, 
  'previousClose': None, 
  'change': -103.5, 
  'percentage': -1.526773860451394, 
  'average': 6727.25, 
  'baseVolume': 375510.82571484987, 
  'quoteVolume': 2532671971.0, 
  'info': {
      
      'timestamp': '2018-04-06T00:00:00.000Z', 
      'symbol': 'XBTUSD', 
      'open': 6779, 
      'high': 6935, 
      'low': 6555, 
      'close': 6675.5, 
      'trades': 439300, 
      'volume': 2532671971, 
      'vwap': 6744.9076, 
      'lastSize': 550, 
      'turnover': 37551082571485, 
      'homeNotional': 375510.82571484987, 
      'foreignNotional': 2532671971
      
  }
  
}

Bid / Ask の取り出し

#Json の取得(Ticker のレスポンス)
ticker = bitmex().fetch_ticker('BTC/USD')

#Json から要素へアクセス
Bid = ticker['bid']
Ask = ticker['ask']


●口座情報の取得
fetch_balance()

BitMEX から返されるレスポンスは「JSON形式」のデータです。以下「Balance」のレスポンス。

#Balance のレスポンス
{
 'info': [{
   'account': 60135, 
   'currency': 'XBt', 
   'riskLimit': 1000000000000, 
   'prevState': '', 
   'state': '', 
   'action': '', 
   'amount': 249499790, 
   'pendingCredit': 0, 
   'pendingDebit': 0, 
   'confirmedDebit': 0, 
   'prevRealisedPnl': 0, 
   'prevUnrealisedPnl': 0, 
   'grossComm': 0, 
   'grossOpenCost': 0, 
   'grossOpenPremium': 0, 
   'grossExecCost': 0, 
   'grossMarkValue': 0, 
   'riskValue': 0, 
   'taxableMargin': 0, 
   'initMargin': 0, 
   'maintMargin': 0, 
   'sessionMargin': 0, 
   'targetExcessMargin': 0, 
   'varMargin': 0, 
   'realisedPnl': 0, 
   'unrealisedPnl': 0, 
   'indicativeTax': 0, 
   'unrealisedProfit': 0, 
   'syntheticMargin': 0, 
   'walletBalance': 249499790, 
   'marginBalance': 249499790, 
   'marginBalancePcnt': 1, 
   'marginLeverage': 0, 
   'marginUsedPcnt': 0, 
   'excessMargin': 249499790, 
   'excessMarginPcnt': 1, 
   'availableMargin': 249499790, 
   'withdrawableMargin': 249499790, 
   'timestamp': '2018-04-04T11:22:13.845Z', 
   'grossLastValue': 0, 
   'commission': None
   }], 
   'BTC': {
       'free': 2.4949979, 
       'used': 0.0, 
       'total': 2.4949979
       
   }, 'free': {
       'BTC': 2.4949979
       
   }, 'used': {
       'BTC': 0.0
       
   }, 'total': {
       'BTC': 2.4949979
       
   }
   
}

口座情報の取り出し

#Json の取得(balance のレスポンス)
balance = bitmex().fetch_balance()

#Json から要素へアクセス
walletBalance = balance['info'][0]['walletBalance']


●ポジションの取得
private_get_position()


BitMEX から返されるレスポンスは「JSON形式」のデータです。以下「Position」のレスポンス。

#Positions のレスポンス
[{
   'account': 60135, 
   'symbol': 'XBTKRW', 
   'currency': 'XBt', 
   'underlying': 'XBT', 
   'quoteCurrency': 'KRW', 
   'commission': 0.00075, 
   'initMarginReq': 0.01, 
   'maintMarginReq': 0.005, 
   'riskLimit': 20000000000, 
   'leverage': 100, 
   'crossMargin': True, 
   'deleveragePercentile': None, 
   'rebalancedPnl': 0, 
   'prevRealisedPnl': 0, 
   'prevUnrealisedPnl': 0, 
   'prevClosePrice': 7279215, 
   'openingTimestamp': '2018-04-06T12:00:00.000Z', 
   'openingQty': 0, 
   'openingCost': 0, 
   'openingComm': 0, 
   'openOrderBuyQty': 0, 
   'openOrderBuyCost': 0, 
   'openOrderBuyPremium': 0, 
   'openOrderSellQty': 0, 
   'openOrderSellCost': 0, 
   'openOrderSellPremium': 0, 
   'execBuyQty': 30000, 
   'execBuyCost': 420870000, 
   'execSellQty': 0, 
   'execSellCost': 0, 
   'execQty': 30000, 
   'execCost': -420870000, 
   'execComm': 315652, 
   'currentTimestamp': '2018-04-06T12:21:19.919Z', 
   'currentQty': 30000, 
   'currentCost': -420870000, 
   'currentComm': 315652, 
   'realisedCost': 0, 
   'unrealisedCost': -420870000, 
   'grossOpenCost': 0, 
   'grossOpenPremium': 0, 
   'grossExecCost': 420870000, 
   'isOpen': True, 
   'markPrice': 7223190, 
   'markValue': -415320000, 
   'riskValue': 415320000, 
   'homeNotional': 4.1532, 
   'foreignNotional': -30000000, 
   'posState': '', 
   'posCost': -420870000, 
   'posCost2': -420870000, 
   'posCross': 0, 
   'posInit': 4208700, 
   'posComm': 318810, 
   'posLoss': 0, 
   'posMargin': 4527510, 
   'posMaint': 2617602, 
   'posAllowance': 0, 
   'taxableMargin': 0, 
   'initMargin': 0, 
   'maintMargin': 10077510, 
   'sessionMargin': 0, 
   'targetExcessMargin': 0, 
   'varMargin': 0, 
   'realisedGrossPnl': 0, 
   'realisedTax': 0, 
   'realisedPnl': -315652, 
   'unrealisedGrossPnl': 5550000, 
   'longBankrupt': 0, 
   'shortBankrupt': 0, 
   'taxBase': 0, 
   'indicativeTaxRate': 0, 
   'indicativeTax': 0, 
   'unrealisedTax': 0, 
   'unrealisedPnl': 5550000, 
   'unrealisedPnlPcnt': 0.0132, 
   'unrealisedRoePcnt': 1.3187, 
   'simpleQty': 4.2087, 
   'simpleCost': 30000000, 
   'simpleValue': 30400000, 
   'simplePnl': 401000, 
   'simplePnlPcnt': 0.0134, 
   'avgCostPrice': 7128000, 
   'avgEntryPrice': 7128000, 
   'breakEvenPrice': 7134000, 
   'marginCallPrice': 4497000, 
   'liquidationPrice': 4497000, 
   'bankruptPrice': 4481000, 
   'timestamp': '2018-04-06T12:21:19.919Z', 
   'lastPrice': 7223190, 
   'lastValue': -415320000
   
}]

ポジション数の取得
保有ポジションの取得は頻繁に使いますので、参考までに実際のコードを紹介しておきます。この関数で現在のポジションがいくつあって、それが買いポジションか?売りポジションか?がわかります。戻り値は (int)

#ポジション数を取得する関数
def getPositions(json_obj, Symbol, label):
    
   try:
       position = json_obj
       pLen = []
       
       #買いのポジション数
       if(label == "BUY"):
       
           for i , p in enumerate(position):
               if(position[i]['symbol'] == Symbol and position[i]['currentQty'] > 0):
                   pLen.append(i)

       #売りのポジション数
       elif (label == "SELL"):
       
           for i , p in enumerate(position):
               if(position[i]['symbol'] == Symbol and position[i]['currentQty'] < 0):
                   pLen.append(i)
   
       Len = len(pLen)
       pLen.clear()
       return Len

   except Exception as e:
       print("Exception => Get Positions: ", label, str(e))
       return 0


#Json の取得(Position のレスポンス)
position = bitmex().private_get_position()

#Json から関数を使って要素にアクセス
BuyCount  = getPositions(position,'XBTUSD', "BUY")
SellCount = getPositions(position,'XBTUSD', "SELL")


●注文の取得
fetch_open_orders()


BitMEX から返されるレスポンスは「JSON形式」のデータです。以下「Orders」のレスポンス。(注文=Pending Orders)

#注文のレスポンス
[{
   'info': 
   {
       'orderID': 'de0d23f6-780a-64c9-4f19-dc649085d78f', 
       'clOrdID': '', 
       'clOrdLinkID': '', 
       'account': 60135, 
       'symbol': 'XBTKRW', 
       'side': 'Sell', 
       'simpleOrderQty': None, 
       'orderQty': 30000, 
       'price': 8225000, 
       'displayQty': None, 
       'stopPx': None, 
       'pegOffsetValue': None, 
       'pegPriceType': '', 
       'currency': 'KRW', 
       'settlCurrency': 'XBt', 
       'ordType': 'Limit', 
       'timeInForce': 'GoodTillCancel', 
       'execInst': '', 
       'contingencyType': '', 
       'exDestination': 'XBME', 
       'ordStatus': 'New', 
       'triggered': '', 
       'workingIndicator': True,
       'ordRejReason': '', 
       'simpleLeavesQty': 3.6474, 
       'leavesQty': 30000, 
       'simpleCumQty': 0, 
       'cumQty': 0, 
       'avgPx': None, 
       'multiLegReportingType': 'SingleSecurity', 
       'text': 'Submission from testnet.bitmex.com', 
       'transactTime': '2018-04-06T12:55:24.327Z', 
       'timestamp': '2018-04-06T12:55:24.327Z'
   }, 
   'id': 'de0d23f6-780a-64c9-4f19-dc649085d78f', 
   'timestamp': 1523019324327, 
   'datetime': '2018-04-06T12:55:24.327Z', 
   'symbol': 'BTC/KRW', 
   'type': 'limit', 
   'side': 'sell', 
   'price': 8225000.0, 
   'amount': 30000.0, 
   'cost': 0.0, 
   'filled': 0.0, 
   'remaining': 30000.0, 
   'status': 'open', 
   'fee': None
   
}]

保有中のオーダー数(注文数)を取得
注文数は頻繁に使いますので、参考までに実際のコードを紹介しておきます。戻り値は (int)

#注文数を取得する関数
def getOrdersCount(json_obj, Symbol):
     
   try:
       orders = json_obj
       oLen = []
       Len=0

       for i , o in enumerate(orders):
           if(orders[i]['info']['symbol'] == Symbol):
               oLen.append(i)

       Len = len(oLen)
       oLen.clear()
       return Len

   except Exception as e:
       print("Exception => Get Pending Orders Count: ", str(e))
       return 0

#Json の取得(Orders のレスポンス)
orders = bitmex().fetch_open_orders()

#Json から関数を使って要素にアクセス
orderCount = getOrdersCount(orders, 'XBTUSD')


発注(新規注文)
create_order(Symbol, Type, Side, Amount, Price)

パラメーター
・Symbol (string): "BTC/USD"
・Type (string): "limit"(指値注文)/ "market"(成行注文)
・Side (string):"buy" (買い)  /  "sell" (売り)
・Amount (float / int): オーダー枚数(数量)
・Price (float / int): オーダー価格(指値)

発注に成功すると以下のレスポンスが返ります。形式は「JSON形式」。

#新規発注のレスポンス(成功時)
{
   'info': 
   {
       'orderID': 'e31e66c4-a5fd-3e6d-0b6a-381d1a6b98b9', 
       'clOrdID': '', 
       'clOrdLinkID': '', 
       'account': 60135, 
       'symbol': 'XBTKRW', 
       'side': 'Sell', 
       'simpleOrderQty': None, 
       'orderQty': 30000, 
       'price': 8225000, 
       'displayQty': None, 
       'stopPx': None, 
       'pegOffsetValue': None, 
       'pegPriceType': '', 
       'currency': 'KRW', 
       'settlCurrency': 'XBt', 
       'ordType': 'Limit', 
       'timeInForce': 'GoodTillCancel', 
       'execInst': '', 
       'contingencyType': '', 
       'exDestination': 'XBME', 
       'ordStatus': 'New', 
       'triggered': '', 
       'workingIndicator': True, 
       'ordRejReason': '', 
       'simpleLeavesQty': 3.6474, 
       'leavesQty': 30000, 
       'simpleCumQty': 0, 
       'cumQty': 0, 
       'avgPx': None, 
       'multiLegReportingType': 'SingleSecurity', 
       'text': 'Submitted via API.', 
       'transactTime': '2018-04-06T13:43:04.066Z', 
       'timestamp': '2018-04-06T13:43:04.066Z'
       
   }, 
   'id': 'e31e66c4-a5fd-3e6d-0b6a-381d1a6b98b9', 
   'timestamp': 1523022184066, 
   'datetime': '2018-04-06T13:43:04.660Z', 
   'symbol': 'BTC/KRW', 
   'type': 'limit', 
   'side': 'sell', 
   'price': 8225000.0, 
   'amount': 30000.0, 
   'cost': 0.0, 
   'filled': 0.0, 
   'remaining': 30000.0, 
   'status': 'open', 
   'fee': None
   
}

発注の方法
発注は取引所の状態によって、必ず成功するとは限りませんので、発注が成功したのか否かを確認すると良いでしょう。

#Buy Order(指値注文)
buy = bitmex().create_order('BTC/USD', type='limit', side='buy', amount=30000, price=8225000)

#Sell Order(指値注文)
sell = bitmex().create_order('BTC/USD', type='limit', side='sell', amount=30000, price=8225000)


●注文のキャンセル
cancel_order(OrderID)

パラメーター
・OrderID(string): アクティブな注文の orderID を指定する

キャンセルに成功すると以下のレスポンスが返ります。形式は「JSON形式」

#注文キャンセルのレスポンス
{
   'info': 
   {
       'orderID': 'e31e66c4-a5fd-3e6d-0b6a-381d1a6b98b9', 
       'clOrdID': '', 
       'clOrdLinkID': '', 
       'account': 60135, 
       'symbol': 'XBTKRW', 
       'side': 'Sell', 
       'simpleOrderQty': None, 
       'orderQty': 30000, 
       'price': 8225000, 
       'displayQty': None, 
       'stopPx': None, 
       'pegOffsetValue': None, 
       'pegPriceType': '', 
       'currency': 'KRW', 
       'settlCurrency': 'XBt', 
       'ordType': 'Limit', 
       'timeInForce': 'GoodTillCancel', 
       'execInst': '', 
       'contingencyType': '', 
       'exDestination': 'XBME', 
       'ordStatus': 'Canceled', 
       'triggered': '', 
       'workingIndicator': False, 
       'ordRejReason': '', 
       'simpleLeavesQty': 0, 
       'leavesQty': 0, 
       'simpleCumQty': 0, 
       'cumQty': 0, 
       'avgPx': None, 
       'multiLegReportingType': 'SingleSecurity', 
       'text': 'Canceled: Canceled via API.\nSubmitted via API.', 
       'transactTime': '2018-04-06T13:43:04.066Z', 
       'timestamp': '2018-04-06T14:30:44.489Z'
       
   }, 
   'id': 'e31e66c4-a5fd-3e6d-0b6a-381d1a6b98b9', 
   'timestamp': 1523025044489, 
   'datetime': '2018-04-06T14:30:44.489Z', 
   'symbol': 'BTC/KRW', 
   'type': 'limit', 
   'side': 'sell', 
   'price': 8225000.0, 
   'amount': 30000.0, 
   'cost': 0.0, 
   'filled': 0.0, 
   'remaining': 30000.0, 
   'status': 'canceled',
   'fee': None
   
}

発注のキャンセル方法

#オーダーidを引数に指定する
Oder_id = 'e31e66c4-a5fd-3e6d-0b6a-381d1a6b98b9'

#発注のキャンセル
res = bitmex().cancel_order(Oder_id)



●サンプルコード
以上で紹介したコードを全て使用したサンプルコードを掲載しておきます。コードが実際にどのようなカタチで使用されているかを確認してみてください。

(ロジックの説明)
以下は、一定時間で発注とキャンセルを繰り返すサンプルロジックです。
各種ステータス表示の最下段に「TickCount」があります。これは Bot のティックカウンターです。5秒間隔でカウントアップされ 25 を過ぎると 0 に戻ります。このカウンターを利用して発注と取り消しを繰り返す事にします。

この続きをみるには

この続き:11,355文字

Python 3 / BitMEX の BOT を作ろう CCXT + BOT サンプルコード 〈基礎編〉

ミラーマン

980円

この記事が気に入ったら、サポートをしてみませんか?気軽にクリエイターを支援できます。

74

ミラーマン

仮想通貨

7つのマガジンに含まれています

コメント2件

ありがとうございます。御礼に購入させていただきました。他のbotのノートを数個購入したんですが動かないし動いても購入してくれない。鍵🔑の発注の時に注文をクリックしてなかっただけなんですがそれに気づかせていただいたので
自分で書くのが手間だったため購入させていただきました。ソースについてはとてもありがたいのですが、クラス分けがされた状態で販売していただけるともっと嬉しかったです。
コメントを投稿するには、 ログイン または 会員登録 をする必要があります。