BitMexの資産監視botに取引通知機能を追加した

B!
スポンサーリンク

前回の記事でBitMexの資産を監視して変化があったタイミングでLINEに通知するbotを開発しました。

 

実際に運用していると資産の変化だけでなく、どんな取引をして資産が変更したかを知りたいという要望がチラホラありました。

そこで、前回開発したbotに取引通知をLINEに通知する機能を実装したいと思います。

 

GitHubはこちら

https://github.com/cryptocat-miner/BitMexLineNotifyBot

目次

ロジック・仕様

  • 取引履歴情報を定期的に監視(資産情報の監視と同じタイミング)
  • 取引履歴情報に変化があったタイミングで更新された取引履歴を通知
  • 成行注文などで取引が複数回になると通知が鬱陶しいので、同じ注文IDの取引は集計して1回で通知する
  • Funding情報も通知する

ロジック自体はシンプルです。

同じIDの注文を集計して平均価格を算出するのが若干面倒でしたが。

 

コード

最初に取引履歴のリストを取得し、後はその先頭の取引情報に変化が無いかを監視しています。

変化があった場合はif trade["id"] == self.latestTradeHistory["id"]:で前回の先頭(最新)取引のIDまでの取引内容をチェックします。

import ccxtWrapper
import LineNotify
import io


class tradeHistory(ccxtWrapper.ccxtWrapper):

    exchange = None
    tradeHistory = None
    latestTradeHistory = None
    tradeHistoryList = list()

    def __init__(self, instance:ccxtWrapper.ccxtWrapper):
        self.exchange = instance
        self.tradeHistory = self.exchange.fetchMyTrades(since=None,limit=50)

    def checkTradeHistory(self):
        tradeHistory = self.exchange.fetchMyTrades(since=None, limit=50)

        if tradeHistory != None:
            if self.latestTradeHistory != None:
                if self.latestTradeHistory["id"] != tradeHistory[0]["id"]:
                    tradeHistoryItem = self.tradeHistoryItem()
                    for trade in tradeHistory:
                        print(trade)
                        print("\r\n\r\n")
                        if trade["id"] == self.latestTradeHistory["id"]:
                            if tradeHistoryItem.amount != 0:
                                tradeHistoryItem.averagePrice = tradeHistoryItem.averagePrice / tradeHistoryItem.amount
                            self.tradeHistoryList.append(tradeHistoryItem)
                            break
                        if tradeHistoryItem.orderId == trade["info"]["orderID"]:
                            tradeHistoryItem.amount += trade["amount"]
                            tradeHistoryItem.averagePrice = tradeHistoryItem.averagePrice + (trade["info"]["lastPx"] * trade["amount"])
                            tradeHistoryItem.feeCost += trade["fee"]["cost"]
                        else:
                            if tradeHistoryItem.orderId != None:
                                if tradeHistoryItem.amount != 0:
                                    tradeHistoryItem.averagePrice = tradeHistoryItem.averagePrice / tradeHistoryItem.amount
                                self.tradeHistoryList.append(tradeHistoryItem)
                                tradeHistoryItem = self.tradeHistoryItem()
                            tradeHistoryItem.execType = trade["info"]["execType"]
                            tradeHistoryItem.orderId = trade["info"]["orderID"]
                            tradeHistoryItem.text = trade["info"]["text"]
                            tradeHistoryItem.amount = trade["amount"]
                            if tradeHistoryItem.text != "Liquidation":
                                tradeHistoryItem.averagePrice = trade["info"]["lastPx"] * trade["amount"]
                            else:
                                tradeHistoryItem.averagePrice = trade["info"]["stopPx"] * trade["amount"]
                            tradeHistoryItem.feeCost = trade["fee"]["cost"]
                            tradeHistoryItem.feeRate = trade["fee"]["rate"]
                            tradeHistoryItem.side = trade["side"]
                            tradeHistoryItem.orderType = trade["type"]
                            
                            
                    for trade in self.tradeHistoryList:
                        if trade.execType == "Trade":
                            self.sendTradeMessage(amount=trade.amount, price=trade.averagePrice, feeCost=trade.feeCost, side=trade.side, orderType=trade.orderType, text=trade.text)
                        elif trade.execType == "Funding":
                            self.sendFundingMessage(amount=trade.amount, price=trade.averagePrice, feeCost=trade.feeCost, feeRate=trade.feeRate)

            self.tradeHistory = tradeHistory
            self.latestTradeHistory = self.tradeHistory[0]
            self.tradeHistoryList.clear()

    def sendTradeMessage(self, amount:int, price:float, feeCost:float, side:str, orderType:str, text:str):
        message = "取引情報を通知します\r\n"
        if side == "buy":
            message += "方向:ロング\r\n"
        elif side == "sell":
            message += "方向:ショート\r\n"
        if orderType == "limit":
            message += "タイプ:指値\r\n"
        elif orderType == "market":
            message += "タイプ:成行\r\n"
        elif orderType == "stop":
            message += "タイプ:ストップ成行\r\n"
        elif orderType == "stoplimit" and text == "Liquidation":
            message += "タイプ:精算\r\n"
        elif orderType == "marketiftouched":
            message += "タイプ:利食い成行\r\n"
        elif orderType == "limitiftouched":
            message += "タイプ:利食い指値\r\n"
        else:
            message += "タイプ:不明\r\n"
        message += "取引量:" + str(int(amount)) + "\r\n"
        message += "価格:" + "{:.1f}".format(price) + "\r\n"
        if (feeCost >= 0):
            message += "取引手数料:-" + "{:.4f}".format(abs(feeCost)) + " XBT"
        else:
            message += "取引手数料:+" + "{:.4f}".format(abs(feeCost)) + " XBT"

        LineNotify.PostMessage(message)

    def sendFundingMessage(self, amount:int, price:float, feeCost:float, feeRate:float):
        message = "Funding情報を通知します\r\n"
        message += "数量:" + str(amount) + "\r\n"
        message += "執行価格:" + "{:.1f}".format(price) + "\r\n"
        if (feeCost >= 0):
            message += "Funding手数料:-" + "{:.4f}".format(abs(feeCost)) + " XBT(" + "{:.4f}".format(feeRate * 100) + "%)"
        else:
            message += "Funding手数料:+" + "{:.4f}".format(abs(feeCost)) + " XBT(" + "{:.4f}".format(feeRate * 100) + "%)"

        LineNotify.PostMessage(message)


    class tradeHistoryItem:
        execType = None
        orderType = None
        orderId = None
        text = None
        id = None
        side = None
        takerOrMaker = None
        amount = 0.0
        averagePrice = 0.0
        feeCost = 0.0
        feeRate = 0.0

 

取引情報を取得するにはfetch_my_trade()を使用します。

引数のsinceを指定しない場合は最新/最古から、limitを指定しない場合はBitMexの場合はDefaultで100件取得します。

paramを指定しないとデフォルトでは古い方から取得されてindexの0から詰められるようなので、params={"reverse": True}を指定して最新情報から取得し、その後sortメソッドで"datetime"をキーにindexの0が最新になるようにソートしています。

 

  
    def fetchMyTrades(self, since, limit):
        trades = None
        symbol = "BTC/USD"
        print("fetchTarde()")
        try:
            #"reverse":Trueで新しい順に配列の最後から詰められる
            trades = self.__bitmex.fetch_my_trades(symbol, since=since, limit=limit, params={"reverse": True})
            #listの"datetime"をキーにreverseする事で配列の最初(index=0)からソートする
            trades.sort(key=lambda x: x["datetime"],reverse=True)
        except Exception as error:
            print(error)
            trades = None
        return trades

 

メインプログラムに取引履歴を監視するメソッドを追加しました。

最初はorderManager.py内で資産に変更があったタイミングで取引履歴を取得していましたが、資産監視と取引履歴は別機能だし一緒にするとコードがグチャグチャになると思い(なった)、tradeHistory.pyとして分離させました。

#!/usr/bin/python3
import time
from datetime import datetime
import calendar
import ccxt
import ccxtWrapper
import math
import io

import orderManager
import tradeHistory

import LineNotify


# ------------------------------------------------------------------------------------------------
# メインプログラム
# 初期化
# bitmex = ccxtWrapper.ccxtWrapper(ccxtWrapper.ccxtWrapper.EXCHANGE_TYPE_TESTNET) #テストネット
bitmex = ccxtWrapper.ccxtWrapper(ccxtWrapper.ccxtWrapper.EXCHANGE_TYPE_MAINNET)
mTradeHistory = tradeHistory.tradeHistory(bitmex)
mOrderManager = orderManager.orderManager(bitmex)

openOrders = bitmex.fetchOpenOrders()

while True:
    currentTime = datetime.utcnow()  # 現在時刻(UTC)

    mTradeHistory.checkTradeHistory()
    mOrderManager.switchState()
    time.sleep(6)

 

まとめ

既に前回の記事のコードを使用されている方も

  • tradeHistory.pyをプロジェクトに追加
  • ccxtWrapper.pyを上書き
  • LineNotifyBot.pyを上書き

すれば取引履歴の通知機能を追加できます。

逆に資産監視か取引履歴どちらかの通知だけでよい、という方はLineNotifyBot.pyのmTradeHistory.checkTradeHistory()mOrderManager.switchState()を任意でコメントアウトすればOKです。

 

 

スポンサーリンク
最新の記事はこちらから