# -*- 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 import hashlib from ccxt.base.types import Any, Balances, Bool, Int, Order, OrderBook, Str, 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 NetworkError class ascendex(ccxt.async_support.ascendex): def describe(self) -> Any: return self.deep_extend(super(ascendex, self).describe(), { 'has': { 'ws': True, 'watchBalance': True, 'watchOHLCV': True, 'watchOrderBook': True, 'watchOrders': True, 'watchTicker': False, 'watchTrades': True, 'watchTradesForSymbols': True, }, 'urls': { 'api': { 'ws': { 'public': 'wss://ascendex.com:443/api/pro/v2/stream', 'private': 'wss://ascendex.com:443/{accountGroup}/api/pro/v2/stream', }, }, 'test': { 'ws': { 'public': 'wss://api-test.ascendex-sandbox.com:443/api/pro/v2/stream', 'private': 'wss://api-test.ascendex-sandbox.com:443/{accountGroup}/api/pro/v2/stream', }, }, }, 'options': { 'tradesLimit': 1000, 'ordersLimit': 1000, 'OHLCVLimit': 1000, 'categoriesAccount': { 'cash': 'spot', 'futures': 'swap', 'margin': 'margin', }, }, }) async def watch_public(self, messageHash, params={}): url = self.urls['api']['ws']['public'] id = self.nonce() request: dict = { 'id': str(id), 'op': 'sub', } message = self.extend(request, params) return await self.watch(url, messageHash, message, messageHash) async def watch_public_multiple(self, messageHashes, params={}): url = self.urls['api']['ws']['public'] id = self.nonce() request: dict = { 'id': str(id), 'op': 'sub', } message = self.extend(request, params) return await self.watch_multiple(url, messageHashes, message, messageHashes) async def watch_private(self, channel, messageHash, params={}): await self.load_accounts() accountGroup = self.safe_string(self.options, 'account-group') url = self.urls['api']['ws']['private'] url = self.implode_params(url, {'accountGroup': accountGroup}) id = self.nonce() request: dict = { 'id': str(id), 'op': 'sub', 'ch': channel, } message = self.extend(request, params) await self.authenticate(url, params) return await self.watch(url, messageHash, message, channel) 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://ascendex.github.io/ascendex-pro-api/#channel-bar-data :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'] if (limit is None) or (limit > 1440): limit = 100 interval = self.safe_string(self.timeframes, timeframe, timeframe) channel = 'bar' + ':' + interval + ':' + market['id'] params = { 'ch': channel, } ohlcv = await self.watch_public(channel, 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): # # { # "m": "bar", # "s": "ASD/USDT", # "data": { # "i": "1", # "ts": 1575398940000, # "o": "0.04993", # "c": "0.04970", # "h": "0.04993", # "l": "0.04970", # "v": "8052" # } # } # marketId = self.safe_string(message, 's') symbol = self.safe_symbol(marketId) channel = self.safe_string(message, 'm') data = self.safe_value(message, 'data', {}) interval = self.safe_string(data, 'i') messageHash = channel + ':' + interval + ':' + marketId timeframe = self.find_timeframe(interval) market = self.market(symbol) parsed = self.parse_ohlcv(message, market) 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(parsed) client.resolve(stored, messageHash) 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://ascendex.github.io/ascendex-pro-api/#channel-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 ` """ return await self.watch_trades_for_symbols([symbol], since, limit, params) 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://ascendex.github.io/ascendex-pro-api/#channel-market-trades :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 :param str [params.name]: the name of the method to call, 'trade' or 'aggTrade', default is 'trade' :returns dict[]: a list of `trade structures ` """ await self.load_markets() symbols = self.market_symbols(symbols, None, False, True, True) marketIds = [] messageHashes = [] if symbols is not None: for i in range(0, len(symbols)): market = self.market(symbols[i]) marketIds.append(market['id']) messageHashes.append('trades:' + market['id']) channel = 'trades:' + ','.join(marketIds) params = self.extend(params, { 'ch': channel, }) trades = await self.watch_public_multiple(messageHashes, params) 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) def handle_trades(self, client: Client, message): # # { # "m": "trades", # "symbol": "BTC/USDT", # "data": [ # { # "p": "40744.28", # "q": "0.00150", # "ts": 1647514330758, # "bm": True, # "seqnum": 72057633465800320 # } # ] # } # marketId = self.safe_string(message, 'symbol') symbol = self.safe_symbol(marketId) channel = self.safe_string(message, 'm') messageHash = channel + ':' + marketId market = self.market(symbol) rawData = self.safe_value(message, 'data') if rawData is None: rawData = [] trades = self.parse_trades(rawData, market) tradesArray = self.safe_value(self.trades, symbol) if tradesArray is None: limit = self.safe_integer(self.options, 'tradesLimit', 1000) tradesArray = ArrayCache(limit) for i in range(0, len(trades)): tradesArray.append(trades[i]) self.trades[symbol] = tradesArray client.resolve(tradesArray, messageHash) 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://ascendex.github.io/ascendex-pro-api/#channel-level-2-order-book-updates :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) channel = 'depth' + ':' + market['id'] params = self.extend(params, { 'ch': channel, }) orderbook = await self.watch_public(channel, params) return orderbook.limit() async def watch_order_book_snapshot(self, symbol: str, limit: Int = None, params={}): await self.load_markets() market = self.market(symbol) action = 'depth-snapshot' channel = action + ':' + market['id'] params = self.extend(params, { 'action': action, 'args': { 'symbol': market['id'], }, 'op': 'req', }) orderbook = await self.watch_public(channel, params) return orderbook.limit() async def fetch_order_book_snapshot_custom(self, symbol: str, limit: Int = None, params={}): restOrderBook = await self.fetch_rest_order_book_safe(symbol, limit, params) if not (symbol in self.orderbooks): self.orderbooks[symbol] = self.order_book() orderbook = self.orderbooks[symbol] orderbook.reset(restOrderBook) return orderbook def handle_order_book_snapshot(self, client: Client, message): # # { # "m": "depth", # "symbol": "BTC/USDT", # "data": { # "ts": 1647520500149, # "seqnum": 28590487626, # "asks": [ # [Array], [Array], [Array], # [Array], [Array], [Array], # ], # "bids": [ # [Array], [Array], [Array], # [Array], [Array], [Array], # ] # } # } # marketId = self.safe_string(message, 'symbol') symbol = self.safe_symbol(marketId) channel = self.safe_string(message, 'm') messageHash = channel + ':' + symbol orderbook = self.orderbooks[symbol] data = self.safe_value(message, 'data') snapshot = self.parse_order_book(data, symbol) snapshot['nonce'] = self.safe_integer(data, 'seqnum') orderbook.reset(snapshot) # unroll the accumulated deltas messages = orderbook.cache for i in range(0, len(messages)): messageItem = messages[i] self.handle_order_book_message(client, messageItem, orderbook) self.orderbooks[symbol] = orderbook client.resolve(orderbook, messageHash) def handle_order_book(self, client: Client, message): # # { # "m": "depth", # "symbol": "BTC/USDT", # "data": { # "ts": 1647515136144, # "seqnum": 28590470736, # "asks": [[Array], [Array]], # "bids": [[Array], [Array], [Array], [Array], [Array], [Array]] # } # } # channel = self.safe_string(message, 'm') marketId = self.safe_string(message, 'symbol') symbol = self.safe_symbol(marketId) messageHash = channel + ':' + marketId if not (symbol in self.orderbooks): self.orderbooks[symbol] = self.order_book({}) orderbook = self.orderbooks[symbol] if orderbook['nonce'] is None: orderbook.cache.append(message) else: self.handle_order_book_message(client, message, orderbook) client.resolve(orderbook, messageHash) def handle_delta(self, bookside, delta): # # ["40990.47","0.01619"], # 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]) def handle_order_book_message(self, client: Client, message, orderbook): # # { # "m":"depth", # "symbol":"BTC/USDT", # "data":{ # "ts":1647527417715, # "seqnum":28590257013, # "asks":[ # ["40990.47","0.01619"], # ["41021.21","0"], # ["41031.59","0.06096"] # ], # "bids":[ # ["40990.46","0.76114"], # ["40985.18","0"] # ] # } # } # data = self.safe_value(message, 'data', {}) seqNum = self.safe_integer(data, 'seqnum') if seqNum > orderbook['nonce']: asks = self.safe_value(data, 'asks', []) bids = self.safe_value(data, 'bids', []) self.handle_deltas(orderbook['asks'], asks) self.handle_deltas(orderbook['bids'], bids) orderbook['nonce'] = seqNum timestamp = self.safe_integer(data, 'ts') orderbook['timestamp'] = timestamp orderbook['datetime'] = self.iso8601(timestamp) return orderbook async def watch_balance(self, params={}) -> Balances: """ watch balance and get the amount of funds available for trading or funds locked in orders https://ascendex.github.io/ascendex-pro-api/#channel-order-and-balance :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `balance structure ` """ await self.load_markets() type, query = self.handle_market_type_and_params('watchBalance', None, params) channel = None messageHash = None if (type == 'spot') or (type == 'margin'): accountCategories = self.safe_value(self.options, 'accountCategories', {}) accountCategory = self.safe_string(accountCategories, type, 'cash') # cash, margin, accountCategory = accountCategory.upper() channel = 'order:' + accountCategory # order and balance share the same channel messageHash = 'balance:' + type else: channel = 'futures-account-update' messageHash = 'balance:swap' return await self.watch_private(channel, messageHash, query) def handle_balance(self, client: Client, message): # # cash account # # { # "m": "balance", # "accountId": "cshQtyfq8XLAA9kcf19h8bXHbAwwoqEo", # "ac": "CASH", # "data": { # "a" : "USDT", # "sn": 8159798, # "tb": "600", # "ab": "600" # } # } # # margin account # # { # "m": "balance", # "accountId": "marOxpKJV83dxTRx0Eyxpa0gxc4Txt0P", # "ac": "MARGIN", # "data": { # "a" : "USDT", # "sn" : 8159802, # "tb" : "400", # total Balance # "ab" : "400", # available balance # "brw": "0", # borrowws # "int": "0" # interest # } # } # # futures # { # "m" : "futures-account-update", # message # "e" : "ExecutionReport", # event type # "t" : 1612508562129, # time # "acc" : "futures-account-id", # account ID # "at" : "FUTURES", # account type # "sn" : 23128, # sequence number, # "id" : "r177710001cbU3813942147C5kbFGOan", # "col": [ # { # "a": "USDT", # asset code # "b": "1000000", # balance # "f": "1" # discount factor # } # ], # (...) # channel = self.safe_string(message, 'm') result = None type = None if (channel == 'order') or (channel == 'futures-order'): data = self.safe_value(message, 'data') marketId = self.safe_string(data, 's') market = self.safe_market(marketId) baseAccount = self.account() baseAccount['free'] = self.safe_string(data, 'bab') baseAccount['total'] = self.safe_string(data, 'btb') quoteAccount = self.account() quoteAccount['free'] = self.safe_string(data, 'qab') quoteAccount['total'] = self.safe_string(data, 'qtb') if market['contract']: type = 'swap' result = self.safe_value(self.balance, type, {}) else: type = market['type'] result = self.safe_value(self.balance, type, {}) result[market['base']] = baseAccount result[market['quote']] = quoteAccount else: accountType = self.safe_string_lower_2(message, 'ac', 'at') categoriesAccounts = self.safe_value(self.options, 'categoriesAccount') type = self.safe_string(categoriesAccounts, accountType, 'spot') result = self.safe_value(self.balance, type, {}) data = self.safe_value(message, 'data') balances = None if data is None: balances = self.safe_value(message, 'col') else: balances = [data] for i in range(0, len(balances)): balance = balances[i] code = self.safe_currency_code(self.safe_string(balance, 'a')) account = self.account() account['free'] = self.safe_string(balance, 'ab') account['total'] = self.safe_string_2(balance, 'tb', 'b') result[code] = account messageHash = 'balance' + ':' + type client.resolve(self.safe_balance(result), messageHash) async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: """ https://ascendex.github.io/ascendex-pro-api/#channel-order-and-balance watches information on multiple orders made by the user :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() market = None if symbol is not None: market = self.market(symbol) symbol = market['symbol'] type, query = self.handle_market_type_and_params('watchOrders', market, params) messageHash = None channel = None if type != 'spot' and type != 'margin': channel = 'futures-order' messageHash = 'order:FUTURES' else: accountCategories = self.safe_value(self.options, 'accountCategories', {}) accountCategory = self.safe_string(accountCategories, type, 'cash') # cash, margin accountCategory = accountCategory.upper() messageHash = 'order' + ':' + accountCategory channel = messageHash if symbol is not None: messageHash = messageHash + ':' + symbol orders = await self.watch_private(channel, messageHash, query) if self.newUpdates: limit = orders.getLimit(symbol, limit) return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True) def handle_order(self, client: Client, message): # # spot order # { # "m": "order", # "accountId": "cshF5SlR9ukAXoDOuXbND4dVpBMw9gzH", # "ac": "CASH", # "data": { # "sn": 19399016185, # "orderId": "r17f9d7983faU7223046196CMlrj3bfC", # "s": "LTC/USDT", # "ot": "Limit", # "t": 1647614461160, # "p": "50", # "q": "0.1", # "sd": "Buy", # "st": "New", # "ap": "0", # "cfq": "0", # "sp": '', # "err": '', # "btb": "0", # "bab": "0", # "qtb": "8", # "qab": "2.995", # "cf": "0", # "fa": "USDT", # "ei": "NULL_VAL" # } # } # # futures order # { # "m": "futures-order", # "sn": 19399927636, # "e": "ExecutionReport", # "a": "futF5SlR9ukAXoDOuXbND4dVpBMw9gzH", # account id # "ac": "FUTURES", # "t": 1647622515434, # last execution time # (...) # } # accountType = self.safe_string(message, 'ac') messageHash = 'order:' + accountType data = self.safe_value(message, 'data', message) order = self.parse_ws_order(data) if self.orders is None: limit = self.safe_integer(self.options, 'ordersLimit', 1000) self.orders = ArrayCacheBySymbolById(limit) orders = self.orders orders.append(order) symbolMessageHash = messageHash + ':' + order['symbol'] client.resolve(orders, symbolMessageHash) client.resolve(orders, messageHash) def parse_ws_order(self, order, market=None): # # spot order # { # "sn": 19399016185, #sequence number # "orderId": "r17f9d7983faU7223046196CMlrj3bfC", # "s": "LTC/USDT", # "ot": "Limit", # order type # "t": 1647614461160, # last execution timestamp # "p": "50", # price # "q": "0.1", # quantity # "sd": "Buy", # side # "st": "New", # status # "ap": "0", # average fill price # "cfq": "0", # cumulated fill quantity # "sp": '', # stop price # "err": '', # "btb": "0", # base asset total balance # "bab": "0", # base asset available balance # "qtb": "8", # quote asset total balance # "qab": "2.995", # quote asset available balance # "cf": "0", # cumulated commission # "fa": "USDT", # fee asset # "ei": "NULL_VAL" # } # # futures order # { # "m": "futures-order", # "sn": 19399927636, # "e": "ExecutionReport", # "a": "futF5SlR9ukAXoDOuXbND4dVpBMw9gzH", # account id # "ac": "FUTURES", # "t": 1647622515434, # last execution time # "ct": 1647622515413, # order creation time # "orderId": "r17f9df469b1U7223046196Okf5Kbmd", # "sd": "Buy", # side # "ot": "Limit", # order type # "ei": "NULL_VAL", # "q": "1", # quantity # "p": "50", #price # "sp": "0", # stopPrice # "spb": '', # stopTrigger # "s": "LTC-PERP", # symbol # "st": "New", # state # "err": '', # "lp": "0", # last filled price # "lq": "0", # last filled quantity(base asset) # "ap": "0", # average filled price # "cfq": "0", # cummulative filled quantity(base asset) # "f": "0", # commission fee of the current execution # "cf": "0", # cumulative commission fee # "fa": "USDT", # fee asset # "psl": "0", # "pslt": "market", # "ptp": "0", # "ptpt": "market" # } # status = self.parse_order_status(self.safe_string(order, 'st')) marketId = self.safe_string(order, 's') timestamp = self.safe_integer(order, 't') symbol = self.safe_symbol(marketId, market, '/') lastTradeTimestamp = self.safe_integer(order, 't') price = self.safe_string(order, 'p') amount = self.safe_string(order, 'q') average = self.safe_string(order, 'ap') filled = self.safe_string(order, 'cfq') id = self.safe_string(order, 'orderId') type = self.safe_string_lower(order, 'ot') side = self.safe_string_lower(order, 'sd') feeCost = self.safe_number(order, 'cf') fee = None if feeCost is not None: feeCurrencyId = self.safe_string(order, 'fa') feeCurrencyCode = self.safe_currency_code(feeCurrencyId) fee = { 'cost': feeCost, 'currency': feeCurrencyCode, } stopPrice = self.parse_number(self.omit_zero(self.safe_string(order, 'sp'))) return self.safe_order({ 'info': order, 'id': id, 'clientOrderId': None, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'timeInForce': None, 'postOnly': None, 'side': side, 'price': price, 'stopPrice': stopPrice, 'triggerPrice': stopPrice, 'amount': amount, 'cost': None, 'average': average, 'filled': filled, 'remaining': None, 'status': status, 'fee': fee, 'trades': None, }, market) def handle_error_message(self, client: Client, message) -> Bool: # # { # "m": "disconnected", # "code": 100005, # "reason": "INVALID_WS_REQUEST_DATA", # "info": "Session is disconnected due to missing pong message from the client" # } # errorCode = self.safe_integer(message, 'code') try: if errorCode is not None: feedback = self.id + ' ' + self.json(message) self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback) messageString = self.safe_value(message, 'message') if messageString is not None: self.throw_broadly_matched_exception(self.exceptions['broad'], messageString, feedback) return False except Exception as e: if isinstance(e, AuthenticationError): messageHash = 'authenticated' client.reject(e, messageHash) if messageHash in client.subscriptions: del client.subscriptions[messageHash] else: client.reject(e) return True def handle_authenticate(self, client: Client, message): # # {m: "auth", id: "1647605234", code: 0} # messageHash = 'authenticated' client.resolve(message, messageHash) def handle_message(self, client: Client, message): if self.handle_error_message(client, message): return # # {m: "ping", hp: 3} # # {m: "sub", ch: "bar:BTC/USDT", code: 0} # # {m: 'sub', id: "1647515701", ch: "depth:BTC/USDT", code: 0} # # {m: "connected", type: "unauth"} # # {m: "auth", id: "1647605234", code: 0} # # order or balance sub # { # "m": "sub", # "id": "1647605952", # "ch": "order:cshF5SlR9ukAXoDOuXbND4dVpBMw9gzH", or futures-order # "code": 0 # } # # ohlcv # { # "m": "bar", # "s": "BTC/USDT", # "data": { # "i": "1", # "ts": 1647510060000, # "o": "40813.93", # "c": "40804.57", # "h": "40814.21", # "l": "40804.56", # "v": "0.01537" # } # } # # trades # # { # "m": "trades", # "symbol": "BTC/USDT", # "data": [ # { # "p": "40762.26", # "q": "0.01500", # "ts": 1647514306759, # "bm": True, # "seqnum": 72057633465795180 # } # ] # } # # orderbook deltas # # { # "m":"depth", # "symbol":"BTC/USDT", # "data":{ # "ts":1647527417715, # "seqnum":28590257013, # "asks":[ # ["40990.47","0.01619"], # ["41021.21","0"], # ["41031.59","0.06096"] # ], # "bids":[ # ["40990.46","0.76114"], # ["40985.18","0"] # ] # } # } # # orderbook snapshot # { # "m": "depth-snapshot", # "symbol": "BTC/USDT", # "data": { # "ts": 1647525938513, # "seqnum": 28590504772, # "asks": [ # [Array], [Array], [Array], [Array], [Array], [Array], [Array], # [Array], [Array], [Array], [Array], [Array], [Array], [Array], # [Array], [Array], [Array], [Array], [Array], [Array], [Array], # (...) # ] # } # # spot order update # { # "m": "order", # "accountId": "cshQtyfq8XLAA9kcf19h8bXHbAwwoqDo", # "ac": "CASH", # "data": { # "s": "BTC/USDT", # "sn": 8159711, # "sd": "Buy", # "ap": "0", # "bab": "2006.5974027", # "btb": "2006.5974027", # "cf": "0", # "cfq": "0", # (...) # } # } # future order update # { # "m": "futures-order", # "sn": 19404258063, # "e": "ExecutionReport", # "a": "futF5SlR9ukAXoDOuXbND4dVpBMw9gzH", # "ac": "FUTURES", # "t": 1647681792543, # "ct": 1647622515413, # "orderId": "r17f9df469b1U7223046196Okf5KbmdL", # (...) # "ptpt": "None" # } # # balance update cash # { # "m": "balance", # "accountId": "cshQtyfq8XLAA9kcf19h8bXHbAwwoqDo", # "ac": "CASH", # "data": { # "a" : "USDT", # "sn": 8159798, # "tb": "600", # "ab": "600" # } # } # # balance update margin # { # "m": "balance", # "accountId": "marOxpKJV83dxTRx0Eyxpa0gxc4Txt0P", # "ac": "MARGIN", # "data": { # "a" : "USDT", # "sn" : 8159802, # "tb" : "400", # "ab" : "400", # "brw": "0", # "int": "0" # } # } # subject = self.safe_string(message, 'm') methods: dict = { 'ping': self.handle_ping, 'auth': self.handle_authenticate, 'sub': self.handle_subscription_status, 'depth': self.handle_order_book, 'depth-snapshot': self.handle_order_book_snapshot, 'trades': self.handle_trades, 'bar': self.handle_ohlcv, 'balance': self.handle_balance, 'futures-account-update': self.handle_balance, } method = self.safe_value(methods, subject) if method is not None: method(client, message) if (subject == 'order') or (subject == 'futures-order'): # self.handle_order(client, message) # balance updates may be in the order structure # they may also be standalone balance updates related to account transfers self.handle_order(client, message) if subject == 'order': self.handle_balance(client, message) def handle_subscription_status(self, client: Client, message): # # {m: "sub", ch: "bar:BTC/USDT", code: 0} # # {m: 'sub', id: "1647515701", ch: "depth:BTC/USDT", code: 0} # channel = self.safe_string(message, 'ch', '') if channel.find('depth') > -1 and not (channel.find('depth-snapshot') > -1): self.handle_order_book_subscription(client, message) return message def handle_order_book_subscription(self, client: Client, message): channel = self.safe_string(message, 'ch') parts = channel.split(':') marketId = parts[1] market = self.safe_market(marketId) symbol = market['symbol'] if symbol in self.orderbooks: del self.orderbooks[symbol] self.orderbooks[symbol] = self.order_book({}) if self.options['defaultType'] == 'swap' or market['contract']: self.spawn(self.fetch_order_book_snapshot_custom, symbol) else: self.spawn(self.watch_order_book_snapshot, symbol) async def pong(self, client, message): # # {m: "ping", hp: 3} # try: await client.send({'op': 'pong', 'hp': self.safe_integer(message, 'hp')}) except Exception as e: error = NetworkError(self.id + ' handlePing failed with error ' + self.json(e)) client.reset(error) def handle_ping(self, client: Client, message): self.spawn(self.pong, client, message) async def authenticate(self, url, params={}): self.check_required_credentials() messageHash = 'authenticated' client = self.client(url) future = self.safe_value(client.subscriptions, messageHash) if future is None: timestamp = str(self.milliseconds()) urlParts = url.split('/') partsLength = len(urlParts) path = self.safe_string(urlParts, partsLength - 1) version = self.safe_string(urlParts, partsLength - 2) auth = timestamp + '+' + version + '/' + path secret = self.base64_to_binary(self.secret) signature = self.hmac(self.encode(auth), secret, hashlib.sha256, 'base64') request: dict = { 'op': 'auth', 'id': str(self.nonce()), 't': timestamp, 'key': self.apiKey, 'sig': signature, } future = await self.watch(url, messageHash, self.extend(request, params), messageHash) client.subscriptions[messageHash] = future return future