# -*- 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, ArrayCacheByTimestamp from ccxt.base.types import Any, Balances, Bool, Int, Order, OrderBook, Str, Strings, Ticker, Tickers, Trade from ccxt.async_support.base.ws.client import Client from typing import List from ccxt.base.errors import AuthenticationError from ccxt.base.errors import ArgumentsRequired from ccxt.base.errors import BadRequest from ccxt.base.precise import Precise class whitebit(ccxt.async_support.whitebit): def describe(self) -> Any: return self.deep_extend(super(whitebit, self).describe(), { 'has': { 'ws': True, 'watchBalance': True, 'watchMyTrades': True, 'watchOHLCV': True, 'watchOrderBook': True, 'watchOrders': True, 'watchTicker': True, 'watchTickers': True, 'watchTrades': True, 'watchTradesForSymbols': False, }, 'urls': { 'api': { 'ws': 'wss://api.whitebit.com/ws', }, }, 'options': { 'timeframes': { '1m': '60', '5m': '300', '15m': '900', '30m': '1800', '1h': '3600', '4h': '14400', '8h': '28800', '1d': '86400', '1w': '604800', }, 'watchOrderBook': { 'priceInterval': 0, # "0" - no interval, available values - "0.00000001", "0.0000001", "0.000001", "0.00001", "0.0001", "0.001", "0.01", "0.1" }, }, 'streaming': { 'ping': self.ping, }, 'exceptions': { 'ws': { 'exact': { '1': BadRequest, # {error: {code: 1, message: 'invalid argument'}, result: null, id: 1656404342} '2': BadRequest, # {error: {code: 2, message: 'internal error'}, result: null, id: 1656404075} '4': BadRequest, # {error: {code: 4, message: 'method not found'}, result: null, id: 1656404250} '6': AuthenticationError, # {error: {code: 6, message: 'require authentication'}, result: null, id: 1656404076} }, }, }, }) 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://docs.whitebit.com/public/websocket/#kline :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'] timeframes = self.safe_value(self.options, 'timeframes', {}) interval = self.safe_integer(timeframes, timeframe) marketId = market['id'] # currently there is no way of knowing # the interval upon getting an update # so that can't be part of the message hash, and the user can only subscribe # to one timeframe per symbol messageHash = 'candles:' + symbol reqParams = [marketId, interval] method = 'candles_subscribe' ohlcv = await self.watch_public(messageHash, method, reqParams, params) 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): # # { # "method": "candles_update", # "params": [ # [ # 1655204760, # "22374.15", # "22351.34", # "22374.27", # "22342.52", # "30.213426", # "675499.29718947", # "BTC_USDT" # ] # ], # "id": null # } # params = self.safe_value(message, 'params', []) for i in range(0, len(params)): data = params[i] marketId = self.safe_string(data, 7) market = self.safe_market(marketId) symbol = market['symbol'] messageHash = 'candles' + ':' + symbol parsed = self.parse_ohlcv(data, market) # self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol) if not (symbol in self.ohlcvs): self.ohlcvs[symbol] = {} # stored = self.ohlcvs[symbol]['unknown'] # we don't know the timeframe but we need to respect the type if not ('unknown' in self.ohlcvs[symbol]): limit = self.safe_integer(self.options, 'OHLCVLimit', 1000) stored = ArrayCacheByTimestamp(limit) self.ohlcvs[symbol]['unknown'] = stored ohlcv = self.ohlcvs[symbol]['unknown'] ohlcv.append(parsed) client.resolve(ohlcv, messageHash) return message 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://docs.whitebit.com/public/websocket/#market-depth :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 """ await self.load_markets() market = self.market(symbol) if limit is None: limit = 10 # max 100 messageHash = 'orderbook' + ':' + market['symbol'] method = 'depth_subscribe' options = self.safe_value(self.options, 'watchOrderBook', {}) defaultPriceInterval = self.safe_string(options, 'priceInterval', '0') priceInterval = self.safe_string(params, 'priceInterval', defaultPriceInterval) params = self.omit(params, 'priceInterval') reqParams = [ market['id'], limit, priceInterval, True, # True for allowing multiple subscriptions ] orderbook = await self.watch_public(messageHash, method, reqParams, params) return orderbook.limit() def handle_order_book(self, client: Client, message): # # { # "method":"depth_update", # "params":[ # True, # { # "timestamp": 1708679568.940867, # "asks":[ # ["21252.45","0.01957"], # ["21252.55","0.126205"], # ["21252.66","0.222689"], # ["21252.76","0.185358"], # ["21252.87","0.210077"], # ["21252.98","0.303991"], # ["21253.08","0.327909"], # ["21253.19","0.399007"], # ["21253.3","0.427695"], # ["21253.4","0.492901"] # ], # "bids":[ # ["21248.82","0.22"], # ["21248.73","0.000467"], # ["21248.62","0.100864"], # ["21248.51","0.061436"], # ["21248.42","0.091"], # ["21248.41","0.126839"], # ["21248.3","0.063511"], # ["21248.2","0.110547"], # ["21248","0.25257"], # ["21247.7","1.71813"] # ] # }, # "BTC_USDT" # ], # "id":null # } # params = self.safe_value(message, 'params', []) isSnapshot = self.safe_value(params, 0) marketId = self.safe_string(params, 2) market = self.safe_market(marketId) symbol = market['symbol'] data = self.safe_value(params, 1) timestamp = self.safe_timestamp(data, 'timestamp') if not (symbol in self.orderbooks): ob = self.order_book() self.orderbooks[symbol] = ob orderbook = self.orderbooks[symbol] orderbook['timestamp'] = timestamp orderbook['datetime'] = self.iso8601(timestamp) if isSnapshot: snapshot = self.parse_order_book(data, symbol) orderbook.reset(snapshot) else: asks = self.safe_value(data, 'asks', []) bids = self.safe_value(data, 'bids', []) self.handle_deltas(orderbook['asks'], asks) self.handle_deltas(orderbook['bids'], bids) messageHash = 'orderbook' + ':' + symbol client.resolve(orderbook, messageHash) def handle_delta(self, bookside, delta): price = self.safe_float(delta, 0) amount = self.safe_float(delta, 1) bookside.store(price, amount) def handle_deltas(self, bookside, deltas): for i in range(0, len(deltas)): self.handle_delta(bookside, deltas[i]) 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://docs.whitebit.com/public/websocket/#market-statistics :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() market = self.market(symbol) symbol = market['symbol'] method = 'market_subscribe' messageHash = 'ticker:' + symbol # every time we want to subscribe to another market we have to "re-subscribe" sending it all again return await self.watch_multiple_subscription(messageHash, method, symbol, False, params) 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://docs.whitebit.com/public/websocket/#market-statistics :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, False) method = 'market_subscribe' url = self.urls['api']['ws'] id = self.nonce() messageHashes = [] args = [] for i in range(0, len(symbols)): market = self.market(symbols[i]) messageHashes.append('ticker:' + market['symbol']) args.append(market['id']) request: dict = { 'id': id, 'method': method, 'params': args, } await self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes) return self.filter_by_array(self.tickers, 'symbol', symbols) def handle_ticker(self, client: Client, message): # # { # "method": "market_update", # "params": [ # "BTC_USDT", # { # "close": "22293.86", # "deal": "1986990019.96552952", # "high": "24360.7", # "last": "22293.86", # "low": "20851.44", # "open": "24076.12", # "period": 86400, # "volume": "87016.995668" # } # ], # "id": null # } # tickers = self.safe_value(message, 'params', []) marketId = self.safe_string(tickers, 0) market = self.safe_market(marketId, None) symbol = market['symbol'] rawTicker = self.safe_value(tickers, 1, {}) messageHash = 'ticker' + ':' + symbol ticker = self.parse_ticker(rawTicker, market) self.tickers[symbol] = ticker # watchTicker client.resolve(ticker, messageHash) # watchTickers messageHashes = list(client.futures.keys()) for i in range(0, len(messageHashes)): currentMessageHash = messageHashes[i] if currentMessageHash.find('tickers') >= 0 and currentMessageHash.find(symbol) >= 0: # Example: user calls watchTickers with ['LTC/USDT', 'ETH/USDT'] # the associated messagehash will be: 'tickers:LTC/USDT:ETH/USDT' # since we only have access to a single symbol at a time # we have to do a reverse lookup into the tickers hashes # and check if the current symbol is a part of one or more # tickers hashes and resolve them # user might have multiple watchTickers promises # watchTickers( ['LTC/USDT', 'ETH/USDT'] ), watchTickers( ['ETC/USDT', 'DOGE/USDT'] ) # and we want to make sure we resolve only the correct ones client.resolve(ticker, currentMessageHash) return message 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://docs.whitebit.com/public/websocket/#market-trades :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 ` """ await self.load_markets() market = self.market(symbol) symbol = market['symbol'] messageHash = 'trades' + ':' + symbol method = 'trades_subscribe' # every time we want to subscribe to another market we have to 're-subscribe' sending it all again trades = await self.watch_multiple_subscription(messageHash, method, symbol, False, params) if self.newUpdates: limit = trades.getLimit(symbol, limit) return self.filter_by_since_limit(trades, since, limit, 'timestamp', True) def handle_trades(self, client: Client, message): # # { # "method":"trades_update", # "params":[ # "BTC_USDT", # [ # { # "id":1900632398, # "time":1656320231.404343, # "price":"21443.04", # "amount":"0.072844", # "type":"buy" # }, # { # "id":1900632397, # "time":1656320231.400001, # "price":"21443.15", # "amount":"0.060757", # "type":"buy" # } # ] # ] # } # params = self.safe_value(message, 'params', []) marketId = self.safe_string(params, 0) market = self.safe_market(marketId) symbol = market['symbol'] 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 data = self.safe_value(params, 1, []) parsedTrades = self.parse_trades(data, market) for j in range(0, len(parsedTrades)): stored.append(parsedTrades[j]) messageHash = 'trades:' + market['symbol'] client.resolve(stored, messageHash) async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]: """ watches trades made by the user https://docs.whitebit.com/private/websocket/#deals :param str symbol: unified market symbol :param int [since]: the earliest time in ms to fetch trades for :param int [limit]: the maximum number of trades structures to retrieve :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `trade structures ` """ if symbol is None: raise ArgumentsRequired(self.id + ' watchMyTrades() requires a symbol argument') await self.load_markets() await self.authenticate() market = self.market(symbol) symbol = market['symbol'] messageHash = 'myTrades:' + symbol method = 'deals_subscribe' trades = await self.watch_multiple_subscription(messageHash, method, symbol, True, params) 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, subscription=None): # # { # "method": "deals_update", # "params": [ # 1894994106, # 1656151427.729706, # "LTC_USDT", # 96624037337, # "56.78", # "0.16717", # "0.0094919126", # '' # ], # "id": null # } # trade = self.safe_value(message, 'params') if self.myTrades is None: limit = self.safe_integer(self.options, 'tradesLimit', 1000) self.myTrades = ArrayCache(limit) stored = self.myTrades parsed = self.parse_ws_trade(trade) stored.append(parsed) symbol = parsed['symbol'] messageHash = 'myTrades:' + symbol client.resolve(stored, messageHash) def parse_ws_trade(self, trade, market=None): # # [ # 1894994106, # id # 1656151427.729706, # deal time # "LTC_USDT", # symbol # 96624037337, # order id # "56.78", # price # "0.16717", # amount # "0.0094919126", # fee # '' # client order id # ] # orderId = self.safe_string(trade, 3) timestamp = self.safe_timestamp(trade, 1) id = self.safe_string(trade, 0) price = self.safe_string(trade, 4) amount = self.safe_string(trade, 5) marketId = self.safe_string(trade, 2) market = self.safe_market(marketId, market) fee = None feeCost = self.safe_string(trade, 6) if feeCost is not None: fee = { 'cost': feeCost, 'currency': market['quote'], } return self.safe_trade({ 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'symbol': market['symbol'], 'order': orderId, 'type': None, 'side': None, 'takerOrMaker': None, 'price': price, 'amount': amount, 'cost': None, 'fee': fee, }, market) 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://docs.whitebit.com/private/websocket/#orders-pending :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 ` """ if symbol is None: raise ArgumentsRequired(self.id + ' watchOrders() requires a symbol argument') await self.load_markets() await self.authenticate() market = self.market(symbol) symbol = market['symbol'] messageHash = 'orders:' + symbol method = 'ordersPending_subscribe' trades = await self.watch_multiple_subscription(messageHash, method, symbol, False, params) if self.newUpdates: limit = trades.getLimit(symbol, limit) return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True) def handle_order(self, client: Client, message, subscription=None): # # { # "method": "ordersPending_update", # "params": [ # 1, # 1 = new, 2 = update 3 = cancel or execute # { # "id": 96433622651, # "market": "LTC_USDT", # "type": 1, # "side": 2, # "ctime": 1656092215.39375, # "mtime": 1656092215.39375, # "price": "25", # "amount": "0.202", # "taker_fee": "0.001", # "maker_fee": "0.001", # "left": "0.202", # "deal_stock": "0", # "deal_money": "0", # "deal_fee": "0", # "client_order_id": '' # } # ] # "id": null # } # params = self.safe_value(message, 'params', []) data = self.safe_value(params, 1) if self.orders is None: limit = self.safe_integer(self.options, 'ordersLimit', 1000) self.orders = ArrayCacheBySymbolById(limit) stored = self.orders status = self.safe_integer(params, 0) parsed = self.parse_ws_order(self.extend(data, {'status': status})) stored.append(parsed) symbol = parsed['symbol'] messageHash = 'orders:' + symbol client.resolve(self.orders, messageHash) def parse_ws_order(self, order, market=None): # # { # "id": 96433622651, # "market": "LTC_USDT", # "type": 1, # "side": 2, #1- sell 2-buy # "ctime": 1656092215.39375, # "mtime": 1656092215.39375, # "price": "25", # "amount": "0.202", # "taker_fee": "0.001", # "maker_fee": "0.001", # "left": "0.202", # "deal_stock": "0", # "deal_money": "0", # "deal_fee": "0", # "activation_price": "40", # "activation_condition": "lte", # "client_order_id": '' # "status": 1, # 1 = new, 2 = update 3 = cancel or execute # } # status = self.safe_integer(order, 'status') marketId = self.safe_string(order, 'market') market = self.safe_market(marketId, market) id = self.safe_string(order, 'id') clientOrderId = self.omit_zero(self.safe_string(order, 'client_order_id')) price = self.safe_string(order, 'price') filled = self.safe_string(order, 'deal_stock') cost = self.safe_string(order, 'deal_money') stopPrice = self.safe_string(order, 'activation_price') rawType = self.safe_string(order, 'type') type = self.parse_ws_order_type(rawType) amount = None remaining = None if type == 'market': amount = self.safe_string(order, 'deal_stock') remaining = '0' else: remaining = self.safe_string(order, 'left') amount = self.safe_string(order, 'amount') timestamp = self.safe_timestamp(order, 'ctime') lastTradeTimestamp = self.safe_timestamp(order, 'mtime') symbol = market['symbol'] rawSide = self.safe_integer(order, 'side') side = 'sell' if (rawSide == 1) else 'buy' dealFee = self.safe_string(order, 'deal_fee') fee = None if dealFee is not None: fee = { 'cost': self.parse_number(dealFee), 'currency': market['quote'], } unifiedStatus = None if (status == 1) or (status == 2): unifiedStatus = 'open' else: if Precise.string_equals(remaining, '0'): unifiedStatus = 'closed' else: unifiedStatus = 'canceled' return self.safe_order({ 'info': order, 'symbol': symbol, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'type': type, 'timeInForce': None, 'postOnly': None, 'side': side, 'price': price, 'stopPrice': stopPrice, 'triggerPrice': stopPrice, 'amount': amount, 'cost': cost, 'average': None, 'filled': filled, 'remaining': remaining, 'status': unifiedStatus, 'fee': fee, 'trades': None, }, market) def parse_ws_order_type(self, status): statuses: dict = { '1': 'limit', '2': 'market', '202': 'market', '3': 'limit', '4': 'market', '5': 'limit', '6': 'market', '8': 'limit', '10': 'market', } return self.safe_string(statuses, status, status) async def watch_balance(self, params={}) -> Balances: """ watch balance and get the amount of funds available for trading or funds locked in orders https://docs.whitebit.com/private/websocket/#balance-spot https://docs.whitebit.com/private/websocket/#balance-margin :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.type]: spot or contract if not provided self.options['defaultType'] is used :returns dict: a `balance structure ` """ await self.load_markets() type = None type, params = self.handle_market_type_and_params('watchBalance', None, params) messageHash = 'wallet:' method = None if type == 'spot': method = 'balanceSpot_subscribe' messageHash += 'spot' else: method = 'balanceMargin_subscribe' messageHash += 'margin' currencies = list(self.currencies.keys()) return await self.watch_private(messageHash, method, currencies, params) def handle_balance(self, client: Client, message): # # { # "method":"balanceSpot_update", # "params":[ # { # "LTC":{ # "available":"0.16587", # "freeze":"0" # } # } # ], # "id":null # } # method = self.safe_string(message, 'method') data = self.safe_value(message, 'params') balanceDict = self.safe_value(data, 0) self.balance['info'] = balanceDict keys = list(balanceDict.keys()) currencyId = self.safe_value(keys, 0) rawBalance = self.safe_value(balanceDict, currencyId) code = self.safe_currency_code(currencyId) account = self.account() account['free'] = self.safe_string(rawBalance, 'available') account['used'] = self.safe_string(rawBalance, 'freeze') self.balance[code] = account self.balance = self.safe_balance(self.balance) messageHash = 'wallet:' if method.find('Spot') >= 0: messageHash += 'spot' else: messageHash += 'margin' client.resolve(self.balance, messageHash) async def watch_public(self, messageHash, method, reqParams=[], params={}): url = self.urls['api']['ws'] id = self.nonce() request: dict = { 'id': id, 'method': method, 'params': reqParams, } message = self.extend(request, params) return await self.watch(url, messageHash, message, messageHash) async def watch_multiple_subscription(self, messageHash, method, symbol, isNested=False, params={}): await self.load_markets() url = self.urls['api']['ws'] id = self.nonce() client = self.safe_value(self.clients, url) request = None marketIds = [] if client is None: subscription: dict = {} market = self.market(symbol) marketId = market['id'] subscription[marketId] = True marketIds = [marketId] if isNested: marketIds = [marketIds] request = { 'id': id, 'method': method, 'params': marketIds, } message = self.extend(request, params) return await self.watch(url, messageHash, message, method, subscription) else: subscription = self.safe_value(client.subscriptions, method, {}) hasSymbolSubscription = True market = self.market(symbol) marketId = market['id'] isSubscribed = self.safe_bool(subscription, marketId, False) if not isSubscribed: subscription[marketId] = True hasSymbolSubscription = False if hasSymbolSubscription: # already subscribed to self market(s) return await self.watch(url, messageHash, request, method, subscription) else: # resubscribe marketIdsNew = [] marketIdsNew = list(subscription.keys()) if isNested: marketIdsNew = [marketIdsNew] resubRequest: dict = { 'id': id, 'method': method, 'params': marketIdsNew, } if method in client.subscriptions: del client.subscriptions[method] return await self.watch(url, messageHash, resubRequest, method, subscription) async def watch_private(self, messageHash, method, reqParams=[], params={}): self.check_required_credentials() await self.authenticate() url = self.urls['api']['ws'] id = self.nonce() request: dict = { 'id': id, 'method': method, 'params': reqParams, } message = self.extend(request, params) return await self.watch(url, messageHash, message, messageHash) async def authenticate(self, params={}): self.check_required_credentials() url = self.urls['api']['ws'] messageHash = 'authenticated' client = self.client(url) future = client.reusableFuture('authenticated') authenticated = self.safe_value(client.subscriptions, messageHash) if authenticated is None: authToken = await self.v4PrivatePostProfileWebsocketToken() # # { # "websocket_token": "$2y$10$lxCvTXig/XrcTBFY1bdFseCKQmFTDtCpEzHNVnXowGplExFxPJp9y" # } # token = self.safe_string(authToken, 'websocket_token') id = self.nonce() request: dict = { 'id': id, 'method': 'authorize', 'params': [ token, 'public', ], } subscription: dict = { 'id': id, 'method': self.handle_authenticate, } try: await self.watch(url, messageHash, request, messageHash, subscription) except Exception as e: del client.subscriptions[messageHash] future.reject(e) return await future def handle_authenticate(self, client: Client, message): # # {error: null, result: {status: "success"}, id: 1656084550} # future = client.futures['authenticated'] future.resolve(1) return message def handle_error_message(self, client: Client, message) -> Bool: # # { # "error": {code: 1, message: "invalid argument"}, # "result": null, # "id": 1656090882 # } # error = self.safe_value(message, 'error') try: if error is not None: code = self.safe_string(message, 'code') feedback = self.id + ' ' + self.json(message) self.throw_exactly_matched_exception(self.exceptions['ws']['exact'], code, feedback) except Exception as e: if isinstance(e, AuthenticationError): client.reject(e, 'authenticated') if 'authenticated' in client.subscriptions: del client.subscriptions['authenticated'] return False return message def handle_message(self, client: Client, message): # # auth # {error: null, result: {status: "success"}, id: 1656084550} # # pong # {error: null, result: "pong", id: 0} # if not self.handle_error_message(client, message): return result = self.safe_string(message, 'result') if result == 'pong': self.handle_pong(client, message) return id = self.safe_integer(message, 'id') if id is not None: self.handle_subscription_status(client, message, id) return methods: dict = { 'market_update': self.handle_ticker, 'trades_update': self.handle_trades, 'depth_update': self.handle_order_book, 'candles_update': self.handle_ohlcv, 'ordersPending_update': self.handle_order, 'ordersExecuted_update': self.handle_order, 'balanceSpot_update': self.handle_balance, 'balanceMargin_update': self.handle_balance, 'deals_update': self.handle_my_trades, } topic = self.safe_value(message, 'method') method = self.safe_value(methods, topic) if method is not None: method(client, message) def handle_subscription_status(self, client: Client, message, id): # not every method stores its subscription # object so we can't do indeById here subs = client.subscriptions values = list(subs.values()) for i in range(0, len(values)): subscription = values[i] if subscription is not True: subId = self.safe_integer(subscription, 'id') if (subId is not None) and (subId == id): method = self.safe_value(subscription, 'method') if method is not None: method(client, message) return def handle_pong(self, client: Client, message): client.lastPong = self.milliseconds() return message def ping(self, client: Client): return { 'id': 0, 'method': 'ping', 'params': [], }