# -*- 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 ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp import hashlib from ccxt.base.types import Any, Balances, Int, Market, Order, OrderBook, Position, Str, Strings, Ticker, Tickers from ccxt.async_support.base.ws.client import Client from typing import List from ccxt.base.errors import ExchangeError from ccxt.base.errors import ArgumentsRequired from ccxt.base.precise import Precise class bydfi(ccxt.async_support.bydfi): def describe(self) -> Any: return self.deep_extend(super(bydfi, self).describe(), { 'has': { 'ws': True, 'watchBalance': True, 'watchBidsAsks': False, 'watchMyTrades': False, 'watchOHLCV': True, 'watchOHLCVForSymbols': True, 'watchOrderBook': True, 'watchOrderBookForSymbols': True, 'watchOrders': True, 'watchOrdersForSymbols': True, 'watchPositions': True, 'watchTicker': True, 'watchTickers': True, 'watchTrades': False, 'watchTradesForSymbols': False, 'unwatchBidsAsks': False, 'unwatchOHLCV': True, 'unwatchOHLCVForSymbols': True, 'unwatchOrderBook': True, 'unwatchOrderBookForSymbols': True, 'unwatchTicker': True, 'unwatchTickers': True, 'unWatchTrades': False, 'unWatchTradesForSymbols': False, 'unWatchOrders': False, 'unWatchOrdersForSymbols': False, 'unWatchPositions': False, }, 'urls': { 'api': { 'ws': 'wss://stream.bydfi.com/v1/public/fapi', }, }, 'options': { 'watchOrderBookForSymbols': { 'depth': '100', # 10, 50, 100 'frequency': '1000ms', # 100ms, 1000ms }, 'watchBalance': { 'fetchBalanceSnapshot': False, # or True 'awaitBalanceSnapshot': True, # whether to wait for the balance snapshot before providing updates }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '6h': '6h', '8h': '8h', '12h': '12h', '1d': '1d', '1w': '1w', '1M': '1M', }, }, 'streaming': { 'ping': self.ping, 'keepAlive': 119000, # 2 minutes }, }) def ping(self, client: Client): return { 'id': self.request_id(), 'method': 'ping', } def request_id(self): self.lock_id() reqid = self.sum(self.safe_integer(self.options, 'reqid', 0), 1) self.options['reqid'] = reqid self.unlock_id() return reqid async def watch_public(self, messageHashes, channels, params={}, subscription={}): url = self.urls['api']['ws'] id = self.request_id() subscriptionParams: dict = { 'id': id, } unsubscribe = self.safe_bool(params, 'unsubscribe', False) method = 'SUBSCRIBE' if unsubscribe: method = 'UNSUBSCRIBE' params = self.omit(params, 'unsubscribe') subscriptionParams['unsubscribe'] = True subscriptionParams['messageHashes'] = messageHashes message: dict = { 'id': id, 'method': method, 'params': channels, } return await self.watch_multiple(url, messageHashes, self.deep_extend(message, params), messageHashes, self.extend(subscriptionParams, subscription)) async def watch_private(self, messageHashes, params={}): self.check_required_credentials() url = self.urls['api']['ws'] subHash = 'private' client = self.client(url) privateSubscription = self.safe_value(client.subscriptions, subHash) subscription: dict = {} if privateSubscription is None: id = self.request_id() timestamp = str(self.milliseconds()) payload = self.apiKey + timestamp signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'hex') request: dict = { 'id': id, 'method': 'LOGIN', 'params': { 'apiKey': self.apiKey, 'timestamp': timestamp, 'sign': signature, }, } params = self.deep_extend(request, params) subscription['id'] = id return await self.watch_multiple(url, messageHashes, params, ['private'], subscription) 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://developers.bydfi.com/en/futures/websocket-market#ticker-by-symbol :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) marketId = market['id'] messageHash = 'ticker::' + symbol channel = marketId + '@ticker' return await self.watch_public([messageHash], [channel], params) async def un_watch_ticker(self, symbol: str, params={}) -> Any: """ unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market https://developers.bydfi.com/en/futures/websocket-market#ticker-by-symbol :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 ` """ return await self.un_watch_tickers([symbol], 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://developers.bydfi.com/en/futures/websocket-market#ticker-by-symbol https://developers.bydfi.com/en/futures/websocket-market#market-wide-ticker :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) messageHashes = [] messageHash = 'ticker::' channels = [] channel = '@ticker' if symbols is None: messageHashes.append(messageHash + 'all') channels.append('not ticker@arr') else: for i in range(0, len(symbols)): symbol = symbols[i] marketId = self.market_id(symbol) messageHashes.append(messageHash + symbol) channels.append(marketId + channel) await self.watch_public(messageHashes, channels, params) return self.filter_by_array(self.tickers, 'symbol', symbols) async def un_watch_tickers(self, symbols: Strings = None, params={}) -> Any: """ unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list https://developers.bydfi.com/en/futures/websocket-market#ticker-by-symbol https://developers.bydfi.com/en/futures/websocket-market#market-wide-ticker :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 ` """ symbols = self.market_symbols(symbols, None, True) messageHashes = [] messageHash = 'unsubscribe::ticker::' channels = [] channel = '@ticker' subscription: dict = { 'topic': 'ticker', } if symbols is None: # all tickers and tickers for specific symbols are different channels # we need to unsubscribe from all ticker channels subHashes = self.get_message_hashes_for_tickers_unsubscription() subscription['subHashIsPrefix'] = True for i in range(0, len(subHashes)): subHash = self.safe_string(subHashes, i) if subHash is not None: parts = subHash.split('::') symbol = self.safe_string(parts, 1) if symbol == 'all': continue marketId = self.market_id(symbol) channels.append(marketId + channel) messageHashes.append(messageHash) channels.append('not ticker@arr') else: for i in range(0, len(symbols)): symbol = symbols[i] marketId = self.market_id(symbol) messageHashes.append(messageHash + symbol) channels.append(marketId + channel) subscription['symbols'] = symbols params = self.extend(params, {'unsubscribe': True}) return await self.watch_public(messageHashes, channels, params, subscription) def get_message_hashes_for_tickers_unsubscription(self): url = self.urls['api']['ws']['public'] client = self.client(url) subscriptions = client.subscriptions messageHashes = [] keys = list(subscriptions.keys()) for i in range(0, len(keys)): key = keys[i] if key.find('ticker::') == 0: messageHashes.append(key) return messageHashes def handle_ticker(self, client: Client, message): # # { # "s": "KAS-USDT", # "c": 0.04543, # "e": "24hrTicker", # "E": 1766528295905, # "v": 98278925, # "h": 0.04685, # "l": 0.04404, # "o": 0.04657 # } # ticker = self.parse_ticker(message) symbol = ticker['symbol'] messageHash = 'ticker::' + symbol self.tickers[symbol] = ticker client.resolve(self.tickers[symbol], messageHash) client.resolve(self.tickers, 'ticker::all') async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]: """ watches historical candlestick data containing the open, high, low, close price, and the volume of a market https://developers.bydfi.com/en/futures/websocket-market#candlestick-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 """ result = await self.watch_ohlcv_for_symbols([[symbol, timeframe]], since, limit, params) return result[symbol][timeframe] async def un_watch_ohlcv(self, symbol: str, timeframe: str = '1m', params={}) -> Any: """ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market https://developers.bydfi.com/en/futures/websocket-market#candlestick-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 dict [params]: extra parameters specific to the exchange API endpoint :returns int[][]: A list of candles ordered, open, high, low, close, volume """ return await self.un_watch_ohlcv_for_symbols([[symbol, timeframe]], params) async def watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], since: Int = None, limit: Int = None, params={}): """ watches historical candlestick data containing the open, high, low, close price, and the volume of a market https://developers.bydfi.com/en/futures/websocket-market#candlestick-data :param str[][] symbolsAndTimeframes: array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']] :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 """ symbolsLength = len(symbolsAndTimeframes) if symbolsLength == 0 or not isinstance(symbolsAndTimeframes[0], list): raise ArgumentsRequired(self.id + " watchOHLCVForSymbols() requires a an array of symbols and timeframes, like ['ETH/USDC', '1m']") await self.load_markets() channels = [] messageHashes = [] for i in range(0, len(symbolsAndTimeframes)): symbolAndTimeframe = symbolsAndTimeframes[i] marketId = self.safe_string(symbolAndTimeframe, 0) market = self.market(marketId) tf = self.safe_string(symbolAndTimeframe, 1) timeframes = self.safe_dict(self.options, 'timeframes', {}) interval = self.safe_string(timeframes, tf, tf) channels.append(market['id'] + '@kline_' + interval) messageHashes.append('ohlcv::' + market['symbol'] + '::' + interval) symbol, timeframe, candles = await self.watch_public(messageHashes, channels, params) if self.newUpdates: limit = candles.getLimit(symbol, limit) filtered = self.filter_by_since_limit(candles, since, limit, 0, True) return self.create_ohlcv_object(symbol, timeframe, filtered) async def un_watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], params={}) -> Any: """ unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market https://developers.bydfi.com/en/futures/websocket-market#candlestick-data :param str[][] symbolsAndTimeframes: array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']] :param dict [params]: extra parameters specific to the exchange API endpoint :returns int[][]: A list of candles ordered, open, high, low, close, volume """ symbolsLength = len(symbolsAndTimeframes) if symbolsLength == 0 or not isinstance(symbolsAndTimeframes[0], list): raise ArgumentsRequired(self.id + " unWatchOHLCVForSymbols() requires a an array of symbols and timeframes, like ['ETH/USDC', '1m']") await self.load_markets() channels = [] messageHashes = [] for i in range(0, len(symbolsAndTimeframes)): symbolAndTimeframe = symbolsAndTimeframes[i] marketId = self.safe_string(symbolAndTimeframe, 0) market = self.market(marketId) tf = self.safe_string(symbolAndTimeframe, 1) interval = self.safe_string(self.timeframes, tf, tf) channels.append(market['id'] + '@kline_' + interval) messageHashes.append('unsubscribe::ohlcv::' + market['symbol'] + '::' + interval) params = self.extend(params, {'unsubscribe': True}) subscription: dict = { 'topic': 'ohlcv', 'symbolsAndTimeframes': symbolsAndTimeframes, } return await self.watch_public(messageHashes, channels, params, subscription) def handle_ohlcv(self, client: Client, message): # # { # "s": "ETH-USDC", # "c": 2956.13, # "t": 1766506860000, # "T": 1766506920000, # "e": "kline", # "v": 3955, # "h": 2956.41, # "i": "1m", # "l": 2956.05, # "o": 2956.05 # } # marketId = self.safe_string(message, 's') market = self.safe_market(marketId) symbol = market['symbol'] interval = self.safe_string(message, 'i') timeframes = self.safe_dict(self.options, 'timeframes', {}) timeframe = self.find_timeframe(interval, timeframes) if not (symbol in self.ohlcvs): self.ohlcvs[symbol] = {} if not (timeframe in self.ohlcvs[symbol]): limit = self.safe_integer(self.options, 'OHLCVLimit', 1000) stored = ArrayCacheByTimestamp(limit) self.ohlcvs[symbol][timeframe] = stored ohlcv = self.ohlcvs[symbol][timeframe] parsed = self.parse_ws_ohlcv(message) ohlcv.append(parsed) messageHash = 'ohlcv::' + symbol + '::' + timeframe client.resolve([symbol, timeframe, ohlcv], 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://developers.bydfi.com/en/futures/websocket-market#limited-depth-information :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(default and maxi is 100) :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 un_watch_order_book(self, symbol: str, params={}) -> Any: """ unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data https://developers.bydfi.com/en/futures/websocket-market#limited-depth-information :param str symbol: unified array of symbols :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.un_watch_order_book_for_symbols([symbol], 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://developers.bydfi.com/en/futures/websocket-market#limited-depth-information :param str[] symbols: unified array of symbols :param int [limit]: the maximum amount of order book entries to return(default and max is 100) :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() symbols = self.market_symbols(symbols, None, False) depth = '100' depth, params = self.handle_option_and_params(params, 'watchOrderBookForSymbols', 'depth', depth) frequency = '100ms' frequency, params = self.handle_option_and_params(params, 'watchOrderBookForSymbols', 'frequency', frequency) channelSuffix = '' if frequency == '100ms': channelSuffix = '@100ms' channels = [] messageHashes = [] for i in range(0, len(symbols)): symbol = symbols[i] market = self.market(symbol) channels.append(market['id'] + '@depth' + depth + channelSuffix) messageHashes.append('orderbook::' + symbol) orderbook = await self.watch_public(messageHashes, channels, params) return orderbook.limit() async def un_watch_order_book_for_symbols(self, symbols: List[str], params={}) -> Any: """ unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data https://developers.bydfi.com/en/futures/websocket-market#limited-depth-information :param str[] symbols: unified array of symbols :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.method]: either '/market/level2' or '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50' default is '/market/level2' :returns dict: A dictionary of `order book structures ` indexed by market symbols """ await self.load_markets() symbols = self.market_symbols(symbols, None, False) depth = '100' depth, params = self.handle_option_and_params(params, 'watchOrderBookForSymbols', 'depth', depth) frequency = '100ms' frequency, params = self.handle_option_and_params(params, 'watchOrderBookForSymbols', 'frequency', frequency) channelSuffix = '' if frequency == '100ms': channelSuffix = '@100ms' channels = [] messageHashes = [] for i in range(0, len(symbols)): symbol = symbols[i] market = self.market(symbol) channels.append(market['id'] + '@depth' + depth + channelSuffix) messageHashes.append('unsubscribe::orderbook::' + symbol) subscription: dict = { 'topic': 'orderbook', 'symbols': symbols, } params = self.extend(params, {'unsubscribe': True}) return await self.watch_public(messageHashes, channels, params, subscription) def handle_order_book(self, client: Client, message): # # { # "a": [[150000, 15], ...], # "b": [[90450.7, 3615], ...], # "s": "BTC-USDT", # "e": "depthUpdate", # "E": 1766577624512 # } # marketId = self.safe_string(message, 's') symbol = self.safe_symbol(marketId) timestamp = self.safe_integer(message, 'E') if not (symbol in self.orderbooks): self.orderbooks[symbol] = self.order_book() orderbook = self.orderbooks[symbol] parsed = self.parse_order_book(message, symbol, timestamp, 'b', 'a') orderbook.reset(parsed) messageHash = 'orderbook::' + symbol self.orderbooks[symbol] = orderbook client.resolve(orderbook, messageHash) 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://developers.bydfi.com/en/futures/websocket-account#order-trade-update-push :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 ` """ symbols = None if symbol is not None: symbols = [symbol] return await self.watch_orders_for_symbols(symbols, since, limit, params) async def watch_orders_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Order]: """ watches information on multiple orders made by the user https://developers.bydfi.com/en/futures/websocket-account#order-trade-update-push :param str[] symbols: unified symbol of the market to fetch orders for :param int [since]: the earliest time in ms to fetch orders 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 `order structures ` """ await self.load_markets() symbols = self.market_symbols(symbols, None, True) messageHashes = [] if symbols is None: messageHashes.append('orders') else: for i in range(0, len(symbols)): symbol = symbols[i] messageHashes.append('orders::' + symbol) orders = await self.watch_private(messageHashes, params) if self.newUpdates: first = self.safe_value(orders, 0) tradeSymbol = self.safe_string(first, 'symbol') limit = orders.getLimit(tradeSymbol, limit) return self.filter_by_since_limit(orders, since, limit, 'timestamp', True) def handle_order(self, client: Client, message): # # { # "T": 1766588450558, # "E": 1766588450685, # "e": "ORDER_TRADE_UPDATE", # "o": { # "S": "BUY", # "ap": "0", # "cpt": False, # "ct": "future", # "ev": "0", # "fee": "0", # "lv": 2, # "mt": "isolated", # "o": "7409609004526010368", # "p": "1000", # "ps": "BOTH", # "pt": "ONE_WAY", # "ro": False, # "s": "ETH-USDC", # "st": "NEW", # "t": "LIMIT", # "tp": "0", # "u": "0.001", # "v": "2" # } # } # rawOrder = self.safe_dict(message, 'o', {}) marketId = self.safe_string(rawOrder, 's') market = self.safe_market(marketId) symbol = market['symbol'] messageHash = 'orders' symbolMessageHash = messageHash + '::' + symbol if self.orders is None: limit = self.safe_integer(self.options, 'ordersLimit', 1000) self.orders = ArrayCacheBySymbolById(limit) orders = self.orders order = self.parse_ws_order(rawOrder, market) lastUpdateTimestamp = self.safe_integer(message, 'T') order['lastUpdateTimestamp'] = lastUpdateTimestamp orders.append(order) client.resolve(orders, messageHash) client.resolve(orders, symbolMessageHash) def parse_ws_order(self, order: dict, market: Market = None) -> Order: # # { # "S": "BUY", # "ap": "0", # "cpt": False, # "ct": "future", # "ev": "0", # "fee": "0", # "lv": 2, # "mt": "isolated", # "o": "7409609004526010368", # "p": "1000", # "ps": "BOTH", # "pt": "ONE_WAY", # "ro": False, # "s": "ETH-USDC", # "st": "NEW", # "t": "LIMIT", # "tp": "0", # "u": "0.001", # "v": "2" # } # marketId = self.safe_string(order, 's') market = self.safe_market(marketId, market) rawStatus = self.safe_string(order, 'st') rawType = self.safe_string(order, 't') fee = None feeCost = self.safe_string(order, 'fee') if feeCost is not None: fee = { 'cost': Precise.string_abs(feeCost), 'currency': market['quote'], } return self.safe_order({ 'info': order, 'id': self.safe_string(order, 'o'), 'clientOrderId': self.safe_string(order, 'cid'), 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'lastUpdateTimestamp': None, 'status': self.parse_order_status(rawStatus), 'symbol': market['symbol'], 'type': self.parseOrderType(rawType), 'timeInForce': None, 'postOnly': None, 'reduceOnly': self.safe_bool(order, 'ro'), 'side': self.safe_string_lower(order, 'S'), 'price': self.safe_string(order, 'p'), 'triggerPrice': None, 'stopLossPrice': None, 'takeProfitPrice': None, 'amount': self.safe_string(order, 'v'), 'filled': self.safe_string(order, 'ev'), 'remaining': self.safe_string(order, 'qty'), 'cost': None, 'trades': None, 'fee': fee, 'average': self.omit_zero(self.safe_string(order, 'ap')), }, market) async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]: """ watch all open positions https://developers.bydfi.com/en/futures/websocket-account#balance-and-position-update-push :param str[] [symbols]: list of unified market symbols :param int [since]: the earliest time in ms to fetch 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() symbols = self.market_symbols(symbols, None, True) messageHashes = [] messageHash = 'positions' if symbols is None: messageHashes.append(messageHash) else: for i in range(0, len(symbols)): symbol = symbols[i] messageHashes.append(messageHash + '::' + symbol) positions = await self.watch_private(messageHashes, params) if self.newUpdates: return positions return self.filter_by_symbols_since_limit(self.positions, symbols, since, limit, True) def handle_positions(self, client, message): # # { # "a": { # "B": [ # { # "a": "USDC", # "ba": "0", # "im": "1.46282986", # "om": "0", # "tfm": "1.46282986", # "wb": "109.86879703" # } # ], # "m": "ORDER", # "p": [ # { # "S": "1", # "ap": "2925.81666667", # "c": "USDC", # "ct": "FUTURE", # "l": 2, # "lq": "1471.1840621072728637", # "lv": "0", # "ma": "0", # "mt": "ISOLATED", # "pm": "1.4628298566666665", # "pt": "ONEWAY", # "rp": "-0.00036721", # "s": "ETH-USDC", # "t": "0", # "uq": "0.001", # "v": "1" # } # ] # }, # "T": 1766592694451, # "E": 1766592694554, # "e": "ACCOUNT_UPDATE" # } # data = self.safe_dict(message, 'a', {}) positionsData = self.safe_list(data, 'p', []) rawPosition = self.safe_dict(positionsData, 0, {}) marketId = self.safe_string(rawPosition, 's') market = self.safe_market(marketId) symbol = market['symbol'] messageHash = 'positions' symbolMessageHash = messageHash + '::' + symbol if self.positions is None: self.positions = ArrayCacheBySymbolBySide() cache = self.positions parsedPosition = self.parse_ws_position(rawPosition, market) timestamp = self.safe_integer(message, 'T') parsedPosition['timestamp'] = timestamp parsedPosition['datetime'] = self.iso8601(timestamp) cache.append(parsedPosition) client.resolve([parsedPosition], messageHash) client.resolve([parsedPosition], symbolMessageHash) def parse_ws_position(self, position, market=None): # # { # "S": "1", # "ap": "2925.81666667", # "c": "USDC", # "ct": "FUTURE", # "l": 2, # "lq": "1471.1840621072728637", # "lv": "0", # "ma": "0", # "mt": "ISOLATED", # "pm": "1.4628298566666665", # "pt": "ONEWAY", # "rp": "-0.00036721", # "s": "ETH-USDC", # "t": "0", # "uq": "0.001", # "v": "1" # } # marketId = self.safe_string(position, 's') market = self.safe_market(marketId, market) rawPositionSide = self.safe_string(position, 'S') positionMode = self.safe_string(position, 'pt') return self.safe_position({ 'info': position, 'id': self.safe_string(position, 'id'), 'symbol': market['symbol'], 'entryPrice': self.parse_number(self.safe_string(position, 'ap')), 'markPrice': None, 'lastPrice': None, 'notional': None, 'collateral': None, 'unrealizedPnl': None, 'realizedPnl': self.parse_number(self.safe_string(position, 'rp')), 'side': self.parse_ws_position_side(rawPositionSide), 'contracts': self.parse_number(self.safe_string(position, 'v')), 'contractSize': self.parse_number(self.safe_string(position, 'uq')), 'timestamp': None, 'datetime': None, 'lastUpdateTimestamp': None, 'hedged': (positionMode != 'ONEWAY'), 'maintenanceMargin': None, 'maintenanceMarginPercentage': None, 'initialMargin': self.parse_number(self.safe_string(position, 'pm')), 'initialMarginPercentage': None, 'leverage': self.safe_integer(position, 'l'), 'liquidationPrice': self.parse_number(self.safe_string(position, 'lq')), 'marginRatio': None, 'marginMode': self.safe_string_lower(position, 'mt'), 'percentage': None, }) def parse_ws_position_side(self, rawPositionSide: Str) -> Str: sides = { '1': 'long', '2': 'short', } return self.safe_string(sides, rawPositionSide, rawPositionSide) async def watch_balance(self, params={}) -> Balances: """ watch balance and get the amount of funds available for trading or funds locked in orders https://developers.bydfi.com/en/futures/websocket-account#balance-and-position-update-push :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `balance structure ` """ await self.load_markets() url = self.urls['api']['ws'] client = self.client(url) self.fetch_balance_snapshot(client) options = self.safe_dict(self.options, 'watchBalance') fetchBalanceSnapshot = self.safe_bool(options, 'fetchBalanceSnapshot', False) awaitBalanceSnapshot = self.safe_bool(options, 'awaitBalanceSnapshot', True) if fetchBalanceSnapshot and awaitBalanceSnapshot: await client.future('fetchBalanceSnapshot') messageHash = 'balance' return await self.watch_private([messageHash], params) def fetch_balance_snapshot(self, client: Client): options = self.safe_value(self.options, 'watchBalance') fetchBalanceSnapshot = self.safe_bool(options, 'fetchBalanceSnapshot', False) if fetchBalanceSnapshot: messageHash = 'fetchBalanceSnapshot' if not (messageHash in client.futures): client.future(messageHash) self.spawn(self.load_balance_snapshot, client, messageHash) async def load_balance_snapshot(self, client, messageHash): params: dict = { 'type': 'swap', } response = await self.fetch_balance(params) self.balance = self.extend(response, self.balance) # don't remove the future from the .futures cache future = client.futures[messageHash] future.resolve() client.resolve(self.balance, 'balance') def handle_balance(self, client: Client, message): # # { # "a": { # "B": [ # { # "a": "USDC", # "ba": "0", # "im": "1.46282986", # "om": "0", # "tfm": "1.46282986", # "wb": "109.86879703" # } # ], # "m": "ORDER", # "p": [ # { # "S": "1", # "ap": "2925.81666667", # "c": "USDC", # "ct": "FUTURE", # "l": 2, # "lq": "1471.1840621072728637", # "lv": "0", # "ma": "0", # "mt": "ISOLATED", # "pm": "1.4628298566666665", # "pt": "ONEWAY", # "rp": "-0.00036721", # "s": "ETH-USDC", # "t": "0", # "uq": "0.001", # "v": "1" # } # ] # }, # "T": 1766592694451, # "E": 1766592694554, # "e": "ACCOUNT_UPDATE" # } # messageHash = 'balance' if messageHash in client.futures: data = self.safe_dict(message, 'a', {}) balances = self.safe_list(data, 'B', []) timestamp = self.safe_integer(message, 'T') result: dict = { 'info': message, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), } for i in range(0, len(balances)): balance = balances[i] currencyId = self.safe_string(balance, 'a') code = self.safe_currency_code(currencyId) account = self.account() account['total'] = self.safe_string(balance, 'wb') account['used'] = self.safe_string(balance, 'tfm') result[code] = account parsedBalance = self.safe_balance(result) self.balance = self.extend(self.balance, parsedBalance) client.resolve(self.balance, messageHash) def handle_subscription_status(self, client: Client, message): # # { # "result": True, # "id": 1 # } # id = self.safe_string(message, 'id') subscriptionsById = self.index_by(client.subscriptions, 'id') subscription = self.safe_dict(subscriptionsById, id, {}) isUnSubMessage = self.safe_bool(subscription, 'unsubscribe', False) if isUnSubMessage: self.handle_un_subscription(client, subscription) return message def handle_un_subscription(self, client: Client, subscription: dict): messageHashes = self.safe_list(subscription, 'messageHashes', []) subHashIsPrefix = self.safe_bool(subscription, 'subHashIsPrefix', False) for i in range(0, len(messageHashes)): unsubHash = messageHashes[i] subHash = unsubHash.replace('unsubscribe::', '') self.clean_unsubscription(client, subHash, unsubHash, subHashIsPrefix) self.clean_cache(subscription) def handle_pong(self, client: Client, message): # # { # "id": 1, # "result": "pong" # } # client.lastPong = self.milliseconds() return message def handle_error_message(self, client: Client, message): # # { # "msg": "Service error", # "code": "-1" # } # code = self.safe_string(message, 'code') msg = self.safe_string(message, 'msg') feedback = self.id + ' ' + self.json(message) self.throw_exactly_matched_exception(self.exceptions['exact'], msg, feedback) self.throw_broadly_matched_exception(self.exceptions['broad'], msg, feedback) self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback) raise ExchangeError(feedback) def handle_message(self, client: Client, message): code = self.safe_string(message, 'code') if code is not None and (code != '0'): self.handle_error_message(client, message) result = self.safe_string(message, 'result') if result == 'pong': self.handle_pong(client, message) elif result is not None: self.handle_subscription_status(client, message) else: event = self.safe_string(message, 'e') if event == '24hrTicker': self.handle_ticker(client, message) elif event == 'kline': self.handle_ohlcv(client, message) elif event == 'depthUpdate': self.handle_order_book(client, message) elif event == 'ORDER_TRADE_UPDATE': self.handle_order(client, message) elif event == 'ACCOUNT_UPDATE': account = self.safe_dict(message, 'a', {}) balances = self.safe_list(account, 'B', []) balancesLength = len(balances) if balancesLength > 0: self.handle_balance(client, message) positions = self.safe_list(account, 'p', []) positionsLength = len(positions) if positionsLength > 0: self.handle_positions(client, message)