# -*- coding: utf-8 -*- # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code import ccxt.async_support from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp import hashlib from ccxt.base.types import Any, Balances, Bool, Int, Liquidation, Order, OrderBook, Position, Str, Strings, Ticker, Tickers, Trade from ccxt.async_support.base.ws.client import Client from typing import List from ccxt.base.errors import ExchangeError from ccxt.base.errors import AuthenticationError from ccxt.base.errors import RateLimitExceeded class bitmex(ccxt.async_support.bitmex): def describe(self) -> Any: return self.deep_extend(super(bitmex, self).describe(), { 'has': { 'ws': True, 'watchBalance': True, 'watchLiquidations': True, 'watchLiquidationsForSymbols': True, 'watchMyLiquidations': None, 'watchMyLiquidationsForSymbols': None, 'watchMyTrades': True, 'watchOHLCV': True, 'watchOrderBook': True, 'watchOrderBookForSymbols': True, 'watchOrders': True, 'watchPostions': True, 'watchTicker': True, 'watchTickers': True, 'watchTrades': True, 'watchTradesForSymbols': True, }, 'urls': { 'test': { 'ws': 'wss://ws.testnet.bitmex.com/realtime', }, 'api': { 'ws': 'wss://ws.bitmex.com/realtime', }, }, # 'versions': { # 'ws': '0.2.0', # }, 'options': { 'watchOrderBookLevel': 'orderBookL2', # 'orderBookL2' = L2 full order book, 'orderBookL2_25' = L2 top 25, 'orderBook10' L3 top 10 'tradesLimit': 1000, 'OHLCVLimit': 1000, }, 'exceptions': { 'ws': { 'exact': { }, 'broad': { 'Rate limit exceeded': RateLimitExceeded, }, }, }, }) async def watch_ticker(self, symbol: str, params={}) -> Ticker: """ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market https://www.bitmex.com/app/wsAPI#Subscriptions :param str symbol: unified symbol of the market to fetch the ticker for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `ticker structure ` """ await self.load_markets() symbol = self.symbol(symbol) tickers = await self.watch_tickers([symbol], params) return tickers[symbol] async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers: """ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list https://www.bitmex.com/app/wsAPI#Subscriptions :param str[] symbols: unified symbol of the market to fetch the ticker for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `ticker structure ` """ await self.load_markets() symbols = self.market_symbols(symbols, None, True) name = 'instrument' url = self.urls['api']['ws'] messageHashes = [] rawSubscriptions = [] if symbols is not None: for i in range(0, len(symbols)): symbol = symbols[i] market = self.market(symbol) subscription = name + ':' + market['id'] rawSubscriptions.append(subscription) messageHash = 'ticker:' + symbol messageHashes.append(messageHash) else: rawSubscriptions.append(name) messageHashes.append('alltickers') request: dict = { 'op': 'subscribe', 'args': rawSubscriptions, } ticker = await self.watch_multiple(url, messageHashes, self.extend(request, params), rawSubscriptions) if self.newUpdates: result: dict = {} result[ticker['symbol']] = ticker return result return self.filter_by_array(self.tickers, 'symbol', symbols) def handle_ticker(self, client: Client, message): # # { # "table": "instrument", # "action": "partial", # "keys": ["symbol"], # "types": { # "symbol": "symbol", # "rootSymbol": "symbol", # "state": "symbol", # "typ": "symbol", # "listing": "timestamp", # "front": "timestamp", # "expiry": "timestamp", # "settle": "timestamp", # "relistInterval": "timespan", # "inverseLeg": "symbol", # "sellLeg": "symbol", # "buyLeg": "symbol", # "optionStrikePcnt": "float", # "optionStrikeRound": "float", # "optionStrikePrice": "float", # "optionMultiplier": "float", # "positionCurrency": "symbol", # "underlying": "symbol", # "quoteCurrency": "symbol", # "underlyingSymbol": "symbol", # "reference": "symbol", # "referenceSymbol": "symbol", # "calcInterval": "timespan", # "publishInterval": "timespan", # "publishTime": "timespan", # "maxOrderQty": "long", # "maxPrice": "float", # "lotSize": "long", # "tickSize": "float", # "multiplier": "long", # "settlCurrency": "symbol", # "underlyingToPositionMultiplier": "long", # "underlyingToSettleMultiplier": "long", # "quoteToSettleMultiplier": "long", # "isQuanto": "boolean", # "isInverse": "boolean", # "initMargin": "float", # "maintMargin": "float", # "riskLimit": "long", # "riskStep": "long", # "limit": "float", # "capped": "boolean", # "taxed": "boolean", # "deleverage": "boolean", # "makerFee": "float", # "takerFee": "float", # "settlementFee": "float", # "insuranceFee": "float", # "fundingBaseSymbol": "symbol", # "fundingQuoteSymbol": "symbol", # "fundingPremiumSymbol": "symbol", # "fundingTimestamp": "timestamp", # "fundingInterval": "timespan", # "fundingRate": "float", # "indicativeFundingRate": "float", # "rebalanceTimestamp": "timestamp", # "rebalanceInterval": "timespan", # "openingTimestamp": "timestamp", # "closingTimestamp": "timestamp", # "sessionInterval": "timespan", # "prevClosePrice": "float", # "limitDownPrice": "float", # "limitUpPrice": "float", # "bankruptLimitDownPrice": "float", # "bankruptLimitUpPrice": "float", # "prevTotalVolume": "long", # "totalVolume": "long", # "volume": "long", # "volume24h": "long", # "prevTotalTurnover": "long", # "totalTurnover": "long", # "turnover": "long", # "turnover24h": "long", # "homeNotional24h": "float", # "foreignNotional24h": "float", # "prevPrice24h": "float", # "vwap": "float", # "highPrice": "float", # "lowPrice": "float", # "lastPrice": "float", # "lastPriceProtected": "float", # "lastTickDirection": "symbol", # "lastChangePcnt": "float", # "bidPrice": "float", # "midPrice": "float", # "askPrice": "float", # "impactBidPrice": "float", # "impactMidPrice": "float", # "impactAskPrice": "float", # "hasLiquidity": "boolean", # "openInterest": "long", # "openValue": "long", # "fairMethod": "symbol", # "fairBasisRate": "float", # "fairBasis": "float", # "fairPrice": "float", # "markMethod": "symbol", # "markPrice": "float", # "indicativeTaxRate": "float", # "indicativeSettlePrice": "float", # "optionUnderlyingPrice": "float", # "settledPrice": "float", # "timestamp": "timestamp" # }, # "foreignKeys": { # "inverseLeg": "instrument", # "sellLeg": "instrument", # "buyLeg": "instrument" # }, # "attributes": {symbol: "unique"}, # "filter": {symbol: "XBTUSD"}, # "data": [ # { # "symbol": "XBTUSD", # "rootSymbol": "XBT", # "state": "Open", # "typ": "FFWCSX", # "listing": "2016-05-13T12:00:00.000Z", # "front": "2016-05-13T12:00:00.000Z", # "expiry": null, # "settle": null, # "relistInterval": null, # "inverseLeg": '', # "sellLeg": '', # "buyLeg": '', # "optionStrikePcnt": null, # "optionStrikeRound": null, # "optionStrikePrice": null, # "optionMultiplier": null, # "positionCurrency": "USD", # "underlying": "XBT", # "quoteCurrency": "USD", # "underlyingSymbol": "XBT=", # "reference": "BMEX", # "referenceSymbol": ".BXBT", # "calcInterval": null, # "publishInterval": null, # "publishTime": null, # "maxOrderQty": 10000000, # "maxPrice": 1000000, # "lotSize": 1, # "tickSize": 0.5, # "multiplier": -100000000, # "settlCurrency": "XBt", # "underlyingToPositionMultiplier": null, # "underlyingToSettleMultiplier": -100000000, # "quoteToSettleMultiplier": null, # "isQuanto": False, # "isInverse": True, # "initMargin": 0.01, # "maintMargin": 0.005, # "riskLimit": 20000000000, # "riskStep": 10000000000, # "limit": null, # "capped": False, # "taxed": True, # "deleverage": True, # "makerFee": -0.00025, # "takerFee": 0.00075, # "settlementFee": 0, # "insuranceFee": 0, # "fundingBaseSymbol": ".XBTBON8H", # "fundingQuoteSymbol": ".USDBON8H", # "fundingPremiumSymbol": ".XBTUSDPI8H", # "fundingTimestamp": "2020-01-29T12:00:00.000Z", # "fundingInterval": "2000-01-01T08:00:00.000Z", # "fundingRate": 0.000597, # "indicativeFundingRate": 0.000652, # "rebalanceTimestamp": null, # "rebalanceInterval": null, # "openingTimestamp": "2020-01-29T11:00:00.000Z", # "closingTimestamp": "2020-01-29T12:00:00.000Z", # "sessionInterval": "2000-01-01T01:00:00.000Z", # "prevClosePrice": 9063.96, # "limitDownPrice": null, # "limitUpPrice": null, # "bankruptLimitDownPrice": null, # "bankruptLimitUpPrice": null, # "prevTotalVolume": 1989881049026, # "totalVolume": 1990196740950, # "volume": 315691924, # "volume24h": 4491824765, # "prevTotalTurnover": 27865497128425564, # "totalTurnover": 27868891594857150, # "turnover": 3394466431587, # "turnover24h": 48863390064843, # "homeNotional24h": 488633.9006484273, # "foreignNotional24h": 4491824765, # "prevPrice24h": 9091, # "vwap": 9192.8663, # "highPrice": 9440, # "lowPrice": 8886, # "lastPrice": 9287, # "lastPriceProtected": 9287, # "lastTickDirection": "PlusTick", # "lastChangePcnt": 0.0216, # "bidPrice": 9286, # "midPrice": 9286.25, # "askPrice": 9286.5, # "impactBidPrice": 9285.9133, # "impactMidPrice": 9286.75, # "impactAskPrice": 9287.6382, # "hasLiquidity": True, # "openInterest": 967826984, # "openValue": 10432207060536, # "fairMethod": "FundingRate", # "fairBasisRate": 0.6537149999999999, # "fairBasis": 0.33, # "fairPrice": 9277.2, # "markMethod": "FairPrice", # "markPrice": 9277.2, # "indicativeTaxRate": 0, # "indicativeSettlePrice": 9276.87, # "optionUnderlyingPrice": null, # "settledPrice": null, # "timestamp": "2020-01-29T11:31:37.114Z" # } # ] # } # data = self.safe_list(message, 'data', []) tickers: dict = {} for i in range(0, len(data)): update = data[i] marketId = self.safe_string(update, 'symbol') symbol = self.safe_symbol(marketId) if not (symbol in self.tickers): self.tickers[symbol] = self.parse_ticker({}) updatedTicker = self.parse_ticker(update) fullParsedTicker = self.deep_extend(self.tickers[symbol], updatedTicker) tickers[symbol] = fullParsedTicker self.tickers[symbol] = fullParsedTicker messageHash = 'ticker:' + symbol client.resolve(fullParsedTicker, messageHash) client.resolve(fullParsedTicker, 'alltickers') return message async def watch_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]: """ watch the public liquidations of a trading pair https://www.bitmex.com/app/wsAPI#Liquidation :param str symbol: unified CCXT market symbol :param int [since]: the earliest time in ms to fetch liquidations for :param int [limit]: the maximum number of liquidation structures to retrieve :param dict [params]: exchange specific parameters for the bitmex api endpoint :returns dict: an array of `liquidation structures ` """ return self.watch_liquidations_for_symbols([symbol], since, limit, params) async def watch_liquidations_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Liquidation]: """ watch the public liquidations of a trading pair https://www.bitmex.com/app/wsAPI#Liquidation :param str[] symbols: :param int [since]: the earliest time in ms to fetch liquidations for :param int [limit]: the maximum number of liquidation structures to retrieve :param dict [params]: exchange specific parameters for the bitmex api endpoint :returns dict: an array of `liquidation structures ` """ await self.load_markets() symbols = self.market_symbols(symbols, None, True, True) messageHashes = [] subscriptionHashes = [] if self.is_empty(symbols): subscriptionHashes.append('liquidation') messageHashes.append('liquidations') else: for i in range(0, len(symbols)): symbol = symbols[i] market = self.market(symbol) subscriptionHashes.append('liquidation:' + market['id']) messageHashes.append('liquidations::' + symbol) url = self.urls['api']['ws'] request = { 'op': 'subscribe', 'args': subscriptionHashes, } newLiquidations = await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), subscriptionHashes) if self.newUpdates: return newLiquidations return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True) def handle_liquidation(self, client: Client, message): # # { # "table":"liquidation", # "action":"partial", # "keys":[ # "orderID" # ], # "types":{ # "orderID":"guid", # "symbol":"symbol", # "side":"symbol", # "price":"float", # "leavesQty":"long" # }, # "filter":{}, # "data":[ # { # "orderID":"e0a568ee-7830-4428-92c3-73e82b9576ce", # "symbol":"XPLAUSDT", # "side":"Sell", # "price":0.206, # "leavesQty":340 # } # ] # } # rawLiquidations = self.safe_value(message, 'data', []) newLiquidations = [] for i in range(0, len(rawLiquidations)): rawLiquidation = rawLiquidations[i] liquidation = self.parse_liquidation(rawLiquidation) symbol = liquidation['symbol'] liquidations = self.safe_value(self.liquidations, symbol) if liquidations is None: limit = self.safe_integer(self.options, 'liquidationsLimit', 1000) liquidations = ArrayCache(limit) liquidations.append(liquidation) self.liquidations[symbol] = liquidations newLiquidations.append(liquidation) client.resolve(newLiquidations, 'liquidations') liquidationsBySymbol = self.index_by(newLiquidations, 'symbol') symbols = list(liquidationsBySymbol.keys()) for i in range(0, len(symbols)): symbol = symbols[i] client.resolve(liquidationsBySymbol[symbol], 'liquidations::' + symbol) async def watch_balance(self, params={}) -> Balances: """ watch balance and get the amount of funds available for trading or funds locked in orders https://www.bitmex.com/app/wsAPI#Subscriptions :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `balance structure ` """ await self.load_markets() await self.authenticate() messageHash = 'margin' url = self.urls['api']['ws'] request: dict = { 'op': 'subscribe', 'args': [ messageHash, ], } return await self.watch(url, messageHash, self.extend(request, params), messageHash) def handle_balance(self, client: Client, message): # # { # "table": "margin", # "action": "partial", # "keys": ["account"], # "types": { # "account": "long", # "currency": "symbol", # "riskLimit": "long", # "prevState": "symbol", # "state": "symbol", # "action": "symbol", # "amount": "long", # "pendingCredit": "long", # "pendingDebit": "long", # "confirmedDebit": "long", # "prevRealisedPnl": "long", # "prevUnrealisedPnl": "long", # "grossComm": "long", # "grossOpenCost": "long", # "grossOpenPremium": "long", # "grossExecCost": "long", # "grossMarkValue": "long", # "riskValue": "long", # "taxableMargin": "long", # "initMargin": "long", # "maintMargin": "long", # "sessionMargin": "long", # "targetExcessMargin": "long", # "varMargin": "long", # "realisedPnl": "long", # "unrealisedPnl": "long", # "indicativeTax": "long", # "unrealisedProfit": "long", # "syntheticMargin": "long", # "walletBalance": "long", # "marginBalance": "long", # "marginBalancePcnt": "float", # "marginLeverage": "float", # "marginUsedPcnt": "float", # "excessMargin": "long", # "excessMarginPcnt": "float", # "availableMargin": "long", # "withdrawableMargin": "long", # "timestamp": "timestamp", # "grossLastValue": "long", # "commission": "float" # }, # "foreignKeys": {}, # "attributes": {account: "sorted"}, # "filter": {account: 1455728}, # "data": [ # { # "account": 1455728, # "currency": "XBt", # "riskLimit": 1000000000000, # "prevState": '', # "state": '', # "action": '', # "amount": 263542, # "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": null, # "walletBalance": 263542, # "marginBalance": 263542, # "marginBalancePcnt": 1, # "marginLeverage": 0, # "marginUsedPcnt": 0, # "excessMargin": 263542, # "excessMarginPcnt": 1, # "availableMargin": 263542, # "withdrawableMargin": 263542, # "timestamp": "2020-08-03T12:01:01.246Z", # "grossLastValue": 0, # "commission": null # } # ] # } # data = self.safe_value(message, 'data') balance = self.parse_balance(data) self.balance = self.extend(self.balance, balance) messageHash = self.safe_string(message, 'table') client.resolve(self.balance, messageHash) def handle_trades(self, client: Client, message): # # initial snapshot # # { # "table": "trade", # "action": "partial", # "keys": [], # "types": { # "timestamp": "timestamp", # "symbol": "symbol", # "side": "symbol", # "size": "long", # "price": "float", # "tickDirection": "symbol", # "trdMatchID": "guid", # "grossValue": "long", # "homeNotional": "float", # "foreignNotional": "float" # }, # "foreignKeys": {symbol: "instrument", side: "side"}, # "attributes": {timestamp: "sorted", symbol: "grouped"}, # "filter": {symbol: "XBTUSD"}, # "data": [ # { # "timestamp": "2020-01-30T17:03:07.854Z", # "symbol": "XBTUSD", # "side": "Buy", # "size": 15000, # "price": 9378, # "tickDirection": "ZeroPlusTick", # "trdMatchID": "5b426e7f-83d1-2c80-295d-ee995b8ceb4a", # "grossValue": 159945000, # "homeNotional": 1.59945, # "foreignNotional": 15000 # } # ] # } # # updates # # { # "table": "trade", # "action": "insert", # "data": [ # { # "timestamp": "2020-01-30T17:31:40.160Z", # "symbol": "XBTUSD", # "side": "Sell", # "size": 37412, # "price": 9521.5, # "tickDirection": "ZeroMinusTick", # "trdMatchID": "a4bfc6bc-6cf1-1a11-622e-270eef8ca5c7", # "grossValue": 392938236, # "homeNotional": 3.92938236, # "foreignNotional": 37412 # } # ] # } # table = 'trade' data = self.safe_value(message, 'data', []) dataByMarketIds = self.group_by(data, 'symbol') marketIds = list(dataByMarketIds.keys()) for i in range(0, len(marketIds)): marketId = marketIds[i] market = self.safe_market(marketId) symbol = market['symbol'] messageHash = table + ':' + symbol trades = self.parse_trades(dataByMarketIds[marketId], market) stored = self.safe_value(self.trades, symbol) if stored is None: limit = self.safe_integer(self.options, 'tradesLimit', 1000) stored = ArrayCache(limit) self.trades[symbol] = stored for j in range(0, len(trades)): stored.append(trades[j]) client.resolve(stored, messageHash) async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]: """ get the list of most recent trades for a particular symbol https://www.bitmex.com/app/wsAPI#Subscriptions :param str symbol: unified symbol of the market to fetch trades for :param int [since]: timestamp in ms of the earliest trade to fetch :param int [limit]: the maximum amount of trades to fetch :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `trade structures ` """ return await self.watch_trades_for_symbols([symbol], since, limit, params) async def authenticate(self, params={}): url = self.urls['api']['ws'] client = self.client(url) messageHash = 'authenticated' future = client.reusableFuture(messageHash) authenticated = self.safe_value(client.subscriptions, messageHash) if authenticated is None: self.check_required_credentials() timestamp = self.milliseconds() payload = 'GET' + '/realtime' + str(timestamp) signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256) request: dict = { 'op': 'authKeyExpires', 'args': [ self.apiKey, timestamp, signature, ], } message = self.extend(request, params) self.watch(url, messageHash, message, messageHash) return await future def handle_authentication_message(self, client: Client, message): authenticated = self.safe_bool(message, 'success', False) messageHash = 'authenticated' if authenticated: # we resolve the future here permanently so authentication only happens once future = self.safe_value(client.futures, messageHash) future.resolve(True) else: error = AuthenticationError(self.json(message)) client.reject(error, messageHash) if messageHash in client.subscriptions: del client.subscriptions[messageHash] async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]: """ watch all open positions https://www.bitmex.com/app/wsAPI#Subscriptions :param str[]|None symbols: list of unified market symbols :param int [since]: the earliest time in ms to watch positions for :param int [limit]: the maximum number of positions to retrieve :param dict params: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `position structure ` """ await self.load_markets() await self.authenticate() subscriptionHash = 'position' messageHash = 'positions' if not self.is_empty(symbols): messageHash = '::' + ','.join(symbols) url = self.urls['api']['ws'] request: dict = { 'op': 'subscribe', 'args': [ subscriptionHash, ], } newPositions = await self.watch(url, messageHash, request, subscriptionHash) if self.newUpdates: return newPositions return self.filter_by_symbols_since_limit(self.positions, symbols, since, limit, True) def handle_positions(self, client, message): # # partial # { # table: 'position', # action: 'partial', # keys: ['account', 'symbol'], # types: { # account: 'long', # symbol: 'symbol', # currency: 'symbol', # underlying: 'symbol', # quoteCurrency: 'symbol', # commission: 'float', # initMarginReq: 'float', # maintMarginReq: 'float', # riskLimit: 'long', # leverage: 'float', # crossMargin: 'boolean', # deleveragePercentile: 'float', # rebalancedPnl: 'long', # prevRealisedPnl: 'long', # prevUnrealisedPnl: 'long', # openingQty: 'long', # openOrderBuyQty: 'long', # openOrderBuyCost: 'long', # openOrderBuyPremium: 'long', # openOrderSellQty: 'long', # openOrderSellCost: 'long', # openOrderSellPremium: 'long', # currentQty: 'long', # currentCost: 'long', # currentComm: 'long', # realisedCost: 'long', # unrealisedCost: 'long', # grossOpenPremium: 'long', # isOpen: 'boolean', # markPrice: 'float', # markValue: 'long', # riskValue: 'long', # homeNotional: 'float', # foreignNotional: 'float', # posState: 'symbol', # posCost: 'long', # posCross: 'long', # posComm: 'long', # posLoss: 'long', # posMargin: 'long', # posMaint: 'long', # initMargin: 'long', # maintMargin: 'long', # realisedPnl: 'long', # unrealisedPnl: 'long', # unrealisedPnlPcnt: 'float', # unrealisedRoePcnt: 'float', # avgCostPrice: 'float', # avgEntryPrice: 'float', # breakEvenPrice: 'float', # marginCallPrice: 'float', # liquidationPrice: 'float', # bankruptPrice: 'float', # timestamp: 'timestamp' # }, # filter: {account: 412475}, # data: [ # { # account: 412475, # symbol: 'XBTUSD', # currency: 'XBt', # underlying: 'XBT', # quoteCurrency: 'USD', # commission: 0.00075, # initMarginReq: 0.01, # maintMarginReq: 0.0035, # riskLimit: 20000000000, # leverage: 100, # crossMargin: True, # deleveragePercentile: 1, # rebalancedPnl: 0, # prevRealisedPnl: 0, # prevUnrealisedPnl: 0, # openingQty: 400, # openOrderBuyQty: 0, # openOrderBuyCost: 0, # openOrderBuyPremium: 0, # openOrderSellQty: 0, # openOrderSellCost: 0, # openOrderSellPremium: 0, # currentQty: 400, # currentCost: -912269, # currentComm: 684, # realisedCost: 0, # unrealisedCost: -912269, # grossOpenPremium: 0, # isOpen: True, # markPrice: 43772, # markValue: -913828, # riskValue: 913828, # homeNotional: 0.00913828, # foreignNotional: -400, # posCost: -912269, # posCross: 1559, # posComm: 694, # posLoss: 0, # posMargin: 11376, # posMaint: 3887, # initMargin: 0, # maintMargin: 9817, # realisedPnl: -684, # unrealisedPnl: -1559, # unrealisedPnlPcnt: -0.0017, # unrealisedRoePcnt: -0.1709, # avgCostPrice: 43846.7643, # avgEntryPrice: 43846.7643, # breakEvenPrice: 43880, # marginCallPrice: 20976, # liquidationPrice: 20976, # bankruptPrice: 20941, # timestamp: '2023-12-07T00:09:00.709Z' # } # ] # } # update # { # table: 'position', # action: 'update', # data: [ # { # account: 412475, # symbol: 'XBTUSD', # currency: 'XBt', # currentQty: 400, # markPrice: 43772.75, # markValue: -913812, # riskValue: 913812, # homeNotional: 0.00913812, # posCross: 1543, # posComm: 693, # posMargin: 11359, # posMaint: 3886, # maintMargin: 9816, # unrealisedPnl: -1543, # unrealisedRoePcnt: -0.1691, # liquidationPrice: 20976, # timestamp: '2023-12-07T00:09:10.760Z' # } # ] # } # if self.positions is None: self.positions = ArrayCacheBySymbolBySide() cache = self.positions rawPositions = self.safe_value(message, 'data', []) newPositions = [] for i in range(0, len(rawPositions)): rawPosition = rawPositions[i] position = self.parse_position(rawPosition) newPositions.append(position) cache.append(position) messageHashes = self.find_message_hashes(client, 'positions::') for i in range(0, len(messageHashes)): messageHash = messageHashes[i] parts = messageHash.split('::') symbolsString = parts[1] symbols = symbolsString.split(',') positions = self.filter_by_array(newPositions, 'symbol', symbols, False) if not self.is_empty(positions): client.resolve(positions, messageHash) client.resolve(newPositions, 'positions') async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: """ watches information on multiple orders made by the user https://www.bitmex.com/app/wsAPI#Subscriptions :param str symbol: unified market symbol of the market orders were made in :param int [since]: the earliest time in ms to fetch orders for :param int [limit]: the maximum number of order structures to retrieve :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `order structures ` """ await self.load_markets() await self.authenticate() name = 'order' subscriptionHash = name messageHash = name if symbol is not None: symbol = self.symbol(symbol) messageHash += ':' + symbol url = self.urls['api']['ws'] request: dict = { 'op': 'subscribe', 'args': [ subscriptionHash, ], } orders = await self.watch(url, messageHash, request, subscriptionHash) if self.newUpdates: limit = orders.getLimit(symbol, limit) return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True) def handle_orders(self, client: Client, message): # # { # "table": "order", # "action": "partial", # "keys": ["orderID"], # "types": { # "orderID": "guid", # "clOrdID": "string", # "clOrdLinkID": "symbol", # "account": "long", # "symbol": "symbol", # "side": "symbol", # "simpleOrderQty": "float", # "orderQty": "long", # "price": "float", # "displayQty": "long", # "stopPx": "float", # "pegOffsetValue": "float", # "pegPriceType": "symbol", # "currency": "symbol", # "settlCurrency": "symbol", # "ordType": "symbol", # "timeInForce": "symbol", # "execInst": "symbol", # "contingencyType": "symbol", # "exDestination": "symbol", # "ordStatus": "symbol", # "triggered": "symbol", # "workingIndicator": "boolean", # "ordRejReason": "symbol", # "simpleLeavesQty": "float", # "leavesQty": "long", # "simpleCumQty": "float", # "cumQty": "long", # "avgPx": "float", # "multiLegReportingType": "symbol", # "text": "string", # "transactTime": "timestamp", # "timestamp": "timestamp" # }, # "foreignKeys": {symbol: 'instrument', side: "side", ordStatus: "ordStatus"}, # "attributes": { # "orderID": "grouped", # "account": "grouped", # "ordStatus": "grouped", # "workingIndicator": "grouped" # }, # "filter": {account: 1455728}, # "data": [ # { # "orderID": "56222c7a-9956-413a-82cf-99f4812c214b", # "clOrdID": '', # "clOrdLinkID": '', # "account": 1455728, # "symbol": "XBTUSD", # "side": "Sell", # "simpleOrderQty": null, # "orderQty": 1, # "price": 40000, # "displayQty": null, # "stopPx": null, # "pegOffsetValue": null, # "pegPriceType": '', # "currency": "USD", # "settlCurrency": "XBt", # "ordType": "Limit", # "timeInForce": "GoodTillCancel", # "execInst": '', # "contingencyType": '', # "exDestination": "XBME", # "ordStatus": "New", # "triggered": '', # "workingIndicator": True, # "ordRejReason": '', # "simpleLeavesQty": null, # "leavesQty": 1, # "simpleCumQty": null, # "cumQty": 0, # "avgPx": null, # "multiLegReportingType": "SingleSecurity", # "text": "Submitted via API.", # "transactTime": "2021-01-02T21:38:49.246Z", # "timestamp": "2021-01-02T21:38:49.246Z" # } # ] # } # # { # "table": "order", # "action": "insert", # "data": [ # { # "orderID": "fa993d8e-f7e4-46ed-8097-04f8e9393585", # "clOrdID": '', # "clOrdLinkID": '', # "account": 1455728, # "symbol": "XBTUSD", # "side": "Sell", # "simpleOrderQty": null, # "orderQty": 1, # "price": 40000, # "displayQty": null, # "stopPx": null, # "pegOffsetValue": null, # "pegPriceType": '', # "currency": "USD", # "settlCurrency": "XBt", # "ordType": "Limit", # "timeInForce": "GoodTillCancel", # "execInst": '', # "contingencyType": '', # "exDestination": "XBME", # "ordStatus": "New", # "triggered": '', # "workingIndicator": True, # "ordRejReason": '', # "simpleLeavesQty": null, # "leavesQty": 1, # "simpleCumQty": null, # "cumQty": 0, # "avgPx": null, # "multiLegReportingType": "SingleSecurity", # "text": "Submitted via API.", # "transactTime": "2021-01-02T23:49:02.286Z", # "timestamp": "2021-01-02T23:49:02.286Z" # } # ] # } # # # # { # "table": "order", # "action": "update", # "data": [ # { # "orderID": "fa993d8e-f7e4-46ed-8097-04f8e9393585", # "ordStatus": "Canceled", # "workingIndicator": False, # "leavesQty": 0, # "text": "Canceled: Canceled via API.\nSubmitted via API.", # "timestamp": "2021-01-02T23:50:51.272Z", # "clOrdID": '', # "account": 1455728, # "symbol": "XBTUSD" # } # ] # } # data = self.safe_value(message, 'data', []) messageHash = 'order' # initial subscription response with multiple orders dataLength = len(data) if dataLength > 0: if self.orders is None: limit = self.safe_integer(self.options, 'ordersLimit', 1000) self.orders = ArrayCacheBySymbolById(limit) stored = self.orders symbols: dict = {} for i in range(0, dataLength): currentOrder = data[i] orderId = self.safe_string(currentOrder, 'orderID') previousOrder = self.safe_value(stored.hashmap, orderId) rawOrder = currentOrder if previousOrder is not None: rawOrder = self.extend(previousOrder['info'], currentOrder) order = self.parse_order(rawOrder) stored.append(order) symbol = order['symbol'] symbols[symbol] = True client.resolve(self.orders, messageHash) keys = list(symbols.keys()) for i in range(0, len(keys)): symbol = keys[i] client.resolve(self.orders, messageHash + ':' + symbol) async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]: """ watches information on multiple trades made by the user https://www.bitmex.com/app/wsAPI#Subscriptions :param str symbol: unified market symbol of the market trades were made in :param int [since]: the earliest time in ms to fetch trades for :param int [limit]: the maximum number of trade structures to retrieve :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `trade structures ` """ await self.load_markets() await self.authenticate() name = 'execution' subscriptionHash = name messageHash = name if symbol is not None: symbol = self.symbol(symbol) messageHash += ':' + symbol url = self.urls['api']['ws'] request: dict = { 'op': 'subscribe', 'args': [ subscriptionHash, ], } trades = await self.watch(url, messageHash, request, subscriptionHash) if self.newUpdates: limit = trades.getLimit(symbol, limit) return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True) def handle_my_trades(self, client: Client, message): # # { # "table":"execution", # "action":"insert", # "data":[ # { # "execID":"0193e879-cb6f-2891-d099-2c4eb40fee21", # "orderID":"00000000-0000-0000-0000-000000000000", # "clOrdID":"", # "clOrdLinkID":"", # "account":2, # "symbol":"XBTUSD", # "side":"Sell", # "lastQty":1, # "lastPx":1134.37, # "underlyingLastPx":null, # "lastMkt":"XBME", # "lastLiquidityInd":"RemovedLiquidity", # "simpleOrderQty":null, # "orderQty":1, # "price":1134.37, # "displayQty":null, # "stopPx":null, # "pegOffsetValue":null, # "pegPriceType":"", # "currency":"USD", # "settlCurrency":"XBt", # "execType":"Trade", # "ordType":"Limit", # "timeInForce":"ImmediateOrCancel", # "execInst":"", # "contingencyType":"", # "exDestination":"XBME", # "ordStatus":"Filled", # "triggered":"", # "workingIndicator":false, # "ordRejReason":"", # "simpleLeavesQty":0, # "leavesQty":0, # "simpleCumQty":0.001, # "cumQty":1, # "avgPx":1134.37, # "commission":0.00075, # "tradePublishIndicator":"DoNotPublishTrade", # "multiLegReportingType":"SingleSecurity", # "text":"Liquidation", # "trdMatchID":"7f4ab7f6-0006-3234-76f4-ae1385aad00f", # "execCost":88155, # "execComm":66, # "homeNotional":-0.00088155, # "foreignNotional":1, # "transactTime":"2017-04-04T22:07:46.035Z", # "timestamp":"2017-04-04T22:07:46.035Z" # } # ] # } # messageHash = self.safe_string(message, 'table') data = self.safe_value(message, 'data', []) dataByExecType = self.group_by(data, 'execType') rawTrades = self.safe_value(dataByExecType, 'Trade', []) trades = self.parse_trades(rawTrades) if self.myTrades is None: limit = self.safe_integer(self.options, 'tradesLimit', 1000) self.myTrades = ArrayCacheBySymbolById(limit) stored = self.myTrades symbols: dict = {} for j in range(0, len(trades)): trade = trades[j] symbol = trade['symbol'] stored.append(trade) symbols[symbol] = trade numTrades = len(trades) if numTrades > 0: client.resolve(stored, messageHash) keys = list(symbols.keys()) for i in range(0, len(keys)): client.resolve(stored, messageHash + ':' + keys[i]) async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook: """ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data https://www.bitmex.com/app/wsAPI#OrderBookL2 :param str symbol: unified symbol of the market to fetch the order book for :param int [limit]: the maximum amount of order book entries to return :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: A dictionary of `order book structures ` indexed by market symbols """ return await self.watch_order_book_for_symbols([symbol], limit, params) async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook: """ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data https://www.bitmex.com/app/wsAPI#OrderBookL2 :param str[] symbols: unified array of symbols :param int [limit]: the maximum amount of order book entries to return :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: A dictionary of `order book structures ` indexed by market symbols """ table = None if limit is None: table = self.safe_string(self.options, 'watchOrderBookLevel', 'orderBookL2') elif limit == 25: table = 'orderBookL2_25' elif limit == 10: table = 'orderBookL10' else: raise ExchangeError(self.id + ' watchOrderBookForSymbols limit argument must be None(L2), 25(L2) or 10(L3)') await self.load_markets() symbols = self.market_symbols(symbols) topics = [] messageHashes = [] for i in range(0, len(symbols)): symbol = symbols[i] market = self.market(symbol) topic = table + ':' + market['id'] topics.append(topic) messageHash = table + ':' + symbol messageHashes.append(messageHash) url = self.urls['api']['ws'] request: dict = { 'op': 'subscribe', 'args': topics, } orderbook = await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), topics) return orderbook.limit() async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]: """ get the list of most recent trades for a list of symbols https://www.bitmex.com/app/wsAPI#Subscriptions :param str[] symbols: unified symbol of the market to fetch trades for :param int [since]: timestamp in ms of the earliest trade to fetch :param int [limit]: the maximum amount of trades to fetch :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `trade structures ` """ await self.load_markets() symbols = self.market_symbols(symbols, None, False) table = 'trade' topics = [] messageHashes = [] for i in range(0, len(symbols)): symbol = symbols[i] market = self.market(symbol) topic = table + ':' + market['id'] topics.append(topic) messageHash = table + ':' + symbol messageHashes.append(messageHash) url = self.urls['api']['ws'] request: dict = { 'op': 'subscribe', 'args': topics, } trades = await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), topics) if self.newUpdates: first = self.safe_value(trades, 0) tradeSymbol = self.safe_string(first, 'symbol') limit = trades.getLimit(tradeSymbol, limit) return self.filter_by_since_limit(trades, since, limit, 'timestamp', True) async def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]: """ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market https://www.bitmex.com/app/wsAPI#Subscriptions :param str symbol: unified symbol of the market to fetch OHLCV data for :param str timeframe: the length of time each candle represents :param int [since]: timestamp in ms of the earliest candle to fetch :param int [limit]: the maximum amount of candles to fetch :param dict [params]: extra parameters specific to the exchange API endpoint :returns int[][]: A list of candles ordered, open, high, low, close, volume """ await self.load_markets() market = self.market(symbol) symbol = market['symbol'] table = 'tradeBin' + self.safe_string(self.timeframes, timeframe, timeframe) messageHash = table + ':' + market['id'] url = self.urls['api']['ws'] request: dict = { 'op': 'subscribe', 'args': [ messageHash, ], } ohlcv = await self.watch(url, messageHash, self.extend(request, params), messageHash) if self.newUpdates: limit = ohlcv.getLimit(symbol, limit) return self.filter_by_since_limit(ohlcv, since, limit, 0, True) def handle_ohlcv(self, client: Client, message): # # { # "table": "tradeBin1m", # "action": "partial", # "keys": [], # "types": { # "timestamp": "timestamp", # "symbol": "symbol", # "open": "float", # "high": "float", # "low": "float", # "close": "float", # "trades": "long", # "volume": "long", # "vwap": "float", # "lastSize": "long", # "turnover": "long", # "homeNotional": "float", # "foreignNotional": "float" # }, # "foreignKeys": {symbol: "instrument"}, # "attributes": {timestamp: "sorted", symbol: "grouped"}, # "filter": {symbol: "XBTUSD"}, # "data": [ # { # "timestamp": "2020-02-03T01:13:00.000Z", # "symbol": "XBTUSD", # "open": 9395, # "high": 9395.5, # "low": 9394.5, # "close": 9395, # "trades": 221, # "volume": 839204, # "vwap": 9394.9643, # "lastSize": 1874, # "turnover": 8932641535, # "homeNotional": 89.32641534999999, # "foreignNotional": 839204 # } # ] # } # # # { # "table": "tradeBin1m", # "action": "insert", # "data": [ # { # "timestamp": "2020-02-03T18:28:00.000Z", # "symbol": "XBTUSD", # "open": 9256, # "high": 9256.5, # "low": 9256, # "close": 9256, # "trades": 29, # "volume": 79057, # "vwap": 9256.688, # "lastSize": 100, # "turnover": 854077082, # "homeNotional": 8.540770820000002, # "foreignNotional": 79057 # } # ] # } # table = self.safe_string(message, 'table') interval = table.replace('tradeBin', '') timeframe = self.find_timeframe(interval) duration = self.parse_timeframe(timeframe) candles = self.safe_value(message, 'data', []) results: dict = {} for i in range(0, len(candles)): candle = candles[i] marketId = self.safe_string(candle, 'symbol') market = self.safe_market(marketId) symbol = market['symbol'] messageHash = table + ':' + market['id'] result = [ self.parse8601(self.safe_string(candle, 'timestamp')) - duration * 1000, None, # set open price to None, see: https://github.com/ccxt/ccxt/pull/21356#issuecomment-1969565862 self.safe_float(candle, 'high'), self.safe_float(candle, 'low'), self.safe_float(candle, 'close'), self.safe_float(candle, 'volume'), ] self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {}) stored = self.safe_value(self.ohlcvs[symbol], timeframe) if stored is None: limit = self.safe_integer(self.options, 'OHLCVLimit', 1000) stored = ArrayCacheByTimestamp(limit) self.ohlcvs[symbol][timeframe] = stored stored.append(result) results[messageHash] = stored messageHashes = list(results.keys()) for i in range(0, len(messageHashes)): messageHash = messageHashes[i] client.resolve(results[messageHash], messageHash) async def watch_heartbeat(self, params={}): await self.load_markets() event = 'heartbeat' url = self.urls['api']['ws'] return await self.watch(url, event) def handle_order_book(self, client: Client, message): # # first snapshot # # { # "table": "orderBookL2", # "action": "partial", # "keys": ['symbol', "id", "side"], # "types": { # "symbol": "symbol", # "id": "long", # "side": "symbol", # "size": "long", # "price": "float" # }, # "foreignKeys": {symbol: "instrument", side: "side"}, # "attributes": {symbol: "parted", id: "sorted"}, # "filter": {symbol: "XBTUSD"}, # "data": [ # {symbol: "XBTUSD", id: 8700000100, side: "Sell", size: 1, price: 999999}, # {symbol: "XBTUSD", id: 8700000200, side: "Sell", size: 3, price: 999998}, # {symbol: "XBTUSD", id: 8716991250, side: "Sell", size: 26, price: 830087.5}, # {symbol: "XBTUSD", id: 8728701950, side: "Sell", size: 1720, price: 712980.5}, # ] # } # # subsequent updates # # { # "table": "orderBookL2", # "action": "update", # "data": [ # { # "table": "orderBookL2", # "action": "insert", # "data": [ # { # "symbol": "ETH_USDT", # "id": 85499965912, # "side": "Buy", # "size": 83000000, # "price": 1704.4, # "timestamp": "2023-03-26T22:29:00.299Z" # } # ] # } # ... # ] # } # action = self.safe_string(message, 'action') table = self.safe_string(message, 'table') if table is None: return # protecting from weird updates data = self.safe_value(message, 'data', []) # if it's an initial snapshot if action == 'partial': filter = self.safe_dict(message, 'filter', {}) marketId = self.safe_value(filter, 'symbol') if marketId is None: return # protecting from weird update market = self.safe_market(marketId) symbol = market['symbol'] if table == 'orderBookL2': self.orderbooks[symbol] = self.indexed_order_book() elif table == 'orderBookL2_25': self.orderbooks[symbol] = self.indexed_order_book({}, 25) elif table == 'orderBook10': self.orderbooks[symbol] = self.indexed_order_book({}, 10) orderbook = self.orderbooks[symbol] orderbook['symbol'] = symbol for i in range(0, len(data)): price = self.safe_float(data[i], 'price') size = self.convertFromRawQuantity(symbol, self.safe_string(data[i], 'size')) id = self.safe_string(data[i], 'id') side = self.safe_string(data[i], 'side') side = 'bids' if (side == 'Buy') else 'asks' bookside = orderbook[side] bookside.storeArray([price, size, id]) datetime = self.safe_string(data[i], 'timestamp') orderbook['timestamp'] = self.parse8601(datetime) orderbook['datetime'] = datetime messageHash = table + ':' + symbol client.resolve(orderbook, messageHash) else: numUpdatesByMarketId: dict = {} for i in range(0, len(data)): marketId = self.safe_value(data[i], 'symbol') if marketId is None: return # protecting from weird update if not (marketId in numUpdatesByMarketId): numUpdatesByMarketId[marketId] = 0 numUpdatesByMarketId[marketId] = self.sum(numUpdatesByMarketId, 1) market = self.safe_market(marketId) symbol = market['symbol'] orderbook = self.orderbooks[symbol] price = self.safe_number(data[i], 'price') size = 0 if (action == 'delete') else self.convertFromRawQuantity(symbol, self.safe_string(data[i], 'size', '0')) id = self.safe_string(data[i], 'id') side = self.safe_string(data[i], 'side') side = 'bids' if (side == 'Buy') else 'asks' bookside = orderbook[side] bookside.storeArray([price, size, id]) datetime = self.safe_string(data[i], 'timestamp') orderbook['timestamp'] = self.parse8601(datetime) orderbook['datetime'] = datetime marketIds = list(numUpdatesByMarketId.keys()) for i in range(0, len(marketIds)): marketId = marketIds[i] market = self.safe_market(marketId) symbol = market['symbol'] messageHash = table + ':' + symbol orderbook = self.orderbooks[symbol] client.resolve(orderbook, messageHash) def handle_system_status(self, client: Client, message): # # todo answer the question whether handleSystemStatus should be renamed # and unified for any usage pattern that # involves system status and maintenance updates # # { # "info": "Welcome to the BitMEX Realtime API.", # "version": "2019-11-22T00:24:37.000Z", # "timestamp": "2019-11-23T09:02:27.771Z", # "docs": "https://www.bitmex.com/app/wsAPI", # "limit": {remaining: 39} # } # return message def handle_subscription_status(self, client: Client, message): # # { # "success": True, # "subscribe": "orderBookL2:XBTUSD", # "request": {op: "subscribe", args: ["orderBookL2:XBTUSD"]} # } # return message def handle_error_message(self, client: Client, message) -> Bool: # # generic error format # # {"error": errorMessage} # # examples # # { # "status": 429, # "error": "Rate limit exceeded, retry in 1 seconds.", # "meta": {"retryAfter": 1}, # "request": {"op": "subscribe", "args": "orderBook"}, # } # # {"error": "Rate limit exceeded, retry in 29 seconds."} # error = self.safe_string(message, 'error') if error is not None: request = self.safe_value(message, 'request', {}) args = self.safe_value(request, 'args', []) numArgs = len(args) if numArgs > 0: messageHash = args[0] broad = self.exceptions['ws']['broad'] broadKey = self.find_broadly_matched_key(broad, error) exception = None if broadKey is None: exception = ExchangeError(error) # c# requirement for now else: exception = broad[broadKey](error) client.reject(exception, messageHash) return False return True def handle_message(self, client: Client, message): # # { # "info": "Welcome to the BitMEX Realtime API.", # "version": "2019-11-22T00:24:37.000Z", # "timestamp": "2019-11-23T09:04:42.569Z", # "docs": "https://www.bitmex.com/app/wsAPI", # "limit": {remaining: 38} # } # # { # "success": True, # "subscribe": "orderBookL2:XBTUSD", # "request": {op: "subscribe", args: ["orderBookL2:XBTUSD"]} # } # # { # "table": "orderBookL2", # "action": "update", # "data": [ # {symbol: "XBTUSD", id: 8799284800, side: "Sell", size: 721000}, # {symbol: "XBTUSD", id: 8799285100, side: "Sell", size: 70590}, # {symbol: "XBTUSD", id: 8799285550, side: "Sell", size: 217652}, # {symbol: "XBTUSD", id: 8799285850, side: "Sell", size: 105578}, # {symbol: "XBTUSD", id: 8799286350, side: "Sell", size: 172093}, # {symbol: "XBTUSD", id: 8799286650, side: "Sell", size: 201125}, # {symbol: "XBTUSD", id: 8799288950, side: "Buy", size: 47552}, # {symbol: "XBTUSD", id: 8799289250, side: "Buy", size: 78217}, # {symbol: "XBTUSD", id: 8799289700, side: "Buy", size: 193677}, # {symbol: "XBTUSD", id: 8799290000, side: "Buy", size: 818161}, # {symbol: "XBTUSD", id: 8799290500, side: "Buy", size: 218806}, # {symbol: "XBTUSD", id: 8799290800, side: "Buy", size: 102946} # ] # } # if self.handle_error_message(client, message): table = self.safe_string(message, 'table') methods: dict = { 'orderBookL2': self.handle_order_book, 'orderBookL2_25': self.handle_order_book, 'orderBook10': self.handle_order_book, 'instrument': self.handle_ticker, 'trade': self.handle_trades, 'tradeBin1m': self.handle_ohlcv, 'tradeBin5m': self.handle_ohlcv, 'tradeBin1h': self.handle_ohlcv, 'tradeBin1d': self.handle_ohlcv, 'order': self.handle_orders, 'execution': self.handle_my_trades, 'margin': self.handle_balance, 'liquidation': self.handle_liquidation, 'position': self.handle_positions, } method = self.safe_value(methods, table) if method is None: request = self.safe_value(message, 'request', {}) op = self.safe_value(request, 'op') if op == 'authKeyExpires': self.handle_authentication_message(client, message) else: method(client, message)