UNPKG

sfccxt

Version:

A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges

1,229 lines (1,206 loc) 80.5 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, BadRequest, ArgumentsRequired, InsufficientFunds, InvalidOrder } = require ('./base/errors'); const { TICK_SIZE } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class bkex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bkex', 'name': 'BKEX', 'countries': [ 'BVI' ], // British Virgin Islands 'rateLimit': 100, 'version': 'v2', 'certified': false, 'has': { 'CORS': undefined, 'spot': undefined, 'margin': undefined, 'swap': undefined, 'future': undefined, 'option': undefined, 'addMargin': undefined, 'cancelAllOrders': undefined, 'cancelOrder': true, 'cancelOrders': true, 'createDepositAddress': undefined, 'createLimitOrder': undefined, 'createMarketOrder': undefined, 'createOrder': true, 'editOrder': undefined, 'fetchAccounts': undefined, 'fetchBalance': true, 'fetchBidsAsks': undefined, 'fetchBorrowRate': undefined, 'fetchBorrowRateHistory': undefined, 'fetchBorrowRates': undefined, 'fetchBorrowRatesPerSymbol': undefined, 'fetchCanceledOrders': undefined, 'fetchClosedOrder': undefined, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDeposit': false, 'fetchDepositAddress': true, 'fetchDepositAddresses': undefined, 'fetchDepositAddressesByNetwork': undefined, 'fetchDeposits': true, 'fetchDepositWithdrawFee': 'emulated', 'fetchDepositWithdrawFees': true, 'fetchFundingHistory': undefined, 'fetchFundingRate': undefined, 'fetchFundingRateHistory': true, 'fetchFundingRates': undefined, 'fetchIndexOHLCV': undefined, 'fetchL2OrderBook': undefined, 'fetchLedger': undefined, 'fetchLedgerEntry': undefined, 'fetchLeverageTiers': undefined, 'fetchMarginMode': false, 'fetchMarketLeverageTiers': true, 'fetchMarkets': true, 'fetchMarkOHLCV': undefined, 'fetchMyTrades': undefined, 'fetchOHLCV': true, 'fetchOpenOrder': true, 'fetchOpenOrders': true, 'fetchOrder': false, 'fetchOrderBook': true, 'fetchOrderBooks': undefined, 'fetchOrders': undefined, 'fetchOrderTrades': undefined, 'fetchPosition': undefined, 'fetchPositionMode': false, 'fetchPositions': undefined, 'fetchPositionsRisk': undefined, 'fetchPremiumIndexOHLCV': undefined, 'fetchStatus': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTradingLimits': undefined, 'fetchTransactionFee': 'emulated', 'fetchTransactionFees': true, 'fetchTransactions': undefined, 'fetchTransfer': false, 'fetchTransfers': false, 'fetchWithdrawal': false, 'fetchWithdrawals': true, 'privateAPI': true, 'publicAPI': true, 'reduceMargin': undefined, 'setLeverage': undefined, 'setMarginMode': undefined, 'setPositionMode': undefined, 'signIn': undefined, 'transfer': false, 'withdraw': false, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '4h': '4h', '6h': '6h', '12h': '12h', '1d': '1d', '1w': '1w', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/158043180-bb079a65-69e8-45a2-b393-f094d334e610.jpg', 'api': { 'spot': 'https://api.bkex.com', 'swap': 'https://fapi.bkex.com', }, 'www': 'https://www.bkex.com/', 'doc': [ 'https://bkexapi.github.io/docs/api_en.htm', ], 'fees': [ 'https://www.bkex.com/help/instruction/33', ], }, 'api': { 'public': { 'spot': { 'get': { '/common/symbols': 1, '/common/currencys': 1, '/common/timestamp': 1, '/q/kline': 1, '/q/tickers': 1, '/q/ticker/price': 1, '/q/depth': 1, '/q/deals': 1, }, }, 'swap': { 'get': { '/market/candle': 1, '/market/deals': 1, '/market/depth': 1, '/market/fundingRate': 1, '/market/index': 1, '/market/riskLimit': 1, '/market/symbols': 1, '/market/ticker/price': 1, '/market/tickers': 1, '/server/ping': 1, }, }, }, 'private': { 'spot': { 'get': { '/u/api/info': 1, '/u/account/balance': 1, '/u/wallet/address': 1, '/u/wallet/depositRecord': 1, '/u/wallet/withdrawRecord': 1, '/u/order/openOrders': 1, '/u/order/openOrder/detail': 1, '/u/order/historyOrders': 1, }, 'post': { '/u/account/transfer': 1, '/u/wallet/withdraw': 1, '/u/order/create': 1, '/u/order/cancel': 1, '/u/order/batchCreate': 1, '/u/order/batchCancel': 1, }, }, 'swap': { 'get': { '/account/balance': 1, '/account/balanceRecord': 1, '/account/order': 1, '/account/orderForced': 1, '/account/position': 1, '/entrust/finished': 1, '/entrust/unFinish': 1, '/order/finished': 1, '/order/finishedInfo': 1, '/order/unFinish': 1, '/position/info': 1, }, 'post': { '/account/setLeverage': 1, '/entrust/add': 1, '/entrust/cancel': 1, '/order/batchCancel': 1, '/order/batchOpen': 1, '/order/cancel': 1, '/order/close': 1, '/order/closeAll': 1, '/order/open': 1, '/position/setSpSl': 1, '/position/update': 1, }, }, }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': this.parseNumber ('0.0015'), 'taker': this.parseNumber ('0.002'), }, }, 'options': { 'timeframes': { 'spot': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '4h': '4h', '6h': '6h', '12h': '12h', '1d': '1d', '1w': '1w', }, 'swap': { '1m': 'M1', '5m': 'M5', '15m': 'M15', '30m': 'M30', '1h': 'H1', '4h': 'H4', '6h': 'H6', '1d': 'D1', }, }, 'defaultType': 'spot', // spot, swap 'networks': { 'TRX': 'TRC-20', 'TRC20': 'TRC-20', 'ETH': 'ERC-20', 'ERC20': 'ERC-20', 'BEP20': 'BEP-20(BSC)', }, }, 'commonCurrencies': { }, 'precisionMode': TICK_SIZE, 'exceptions': { 'exact': { '1005': InsufficientFunds, }, 'broad': { 'Not Enough balance': InsufficientFunds, 'Order does not exist': InvalidOrder, 'System busy, please try again later': BadRequest, // in my tests, this was thrown mostly when request was bad, not the problem of exchange. It is easily reproduced in 'cancelOrders' }, }, }); } async fetchMarkets (params = {}) { /** * @method * @name bkex#fetchMarkets * @description retrieves data on all markets for bkex * @see https://bkexapi.github.io/docs/api_en.htm?shell#basicInformation-1 * @see https://bkexapi.github.io/docs/api_en.htm?shell#contract-market-symbols * @param {object} params extra parameters specific to the exchange api endpoint * @returns {[object]} an array of objects representing market data */ let promises = [ this.publicSpotGetCommonSymbols (params), this.publicSwapGetMarketSymbols (params), ]; promises = await Promise.all (promises); const spotMarkets = promises[0]; // // { // "code": "0", // "data": [ // { // "minimumOrderSize": "0", // "minimumTradeVolume": "0E-18", // "pricePrecision": "11", // "supportTrade": true, // "symbol": "COMT_USDT", // "volumePrecision": 0 // }, // ], // "msg": "success", // "status": 0 // } // const swapMarkets = promises[1]; // // { // "code": 0, // "msg": "success", // "data": [ // { // "symbol": "luna_usdt", // "supportTrade": false, // "volumePrecision": 0, // "pricePrecision": 3, // "marketMiniAmount": "1", // "limitMiniAmount": "1" // }, // ] // } // const spotData = this.safeValue (spotMarkets, 'data', []); const swapData = this.safeValue (swapMarkets, 'data', []); const data = this.arrayConcat (spotData, swapData); const result = []; for (let i = 0; i < data.length; i++) { const market = data[i]; const marketId = this.safeString (market, 'symbol'); const id = this.safeStringUpper (market, 'symbol'); const [ baseId, quoteId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const minimumOrderSize = this.safeString (market, 'minimumOrderSize'); const type = (minimumOrderSize !== undefined) ? 'spot' : 'swap'; const swap = (type === 'swap'); let symbol = base + '/' + quote; let settleId = undefined; let settle = undefined; if (swap) { settleId = quoteId; settle = quote; symbol = base + '/' + quote + ':' + settle; } const linear = swap ? true : undefined; result.push ({ 'id': marketId, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': type, 'spot': (type === 'spot'), 'margin': false, 'future': false, 'swap': swap, 'option': false, 'active': this.safeValue (market, 'supportTrade'), 'contract': swap, 'linear': linear, 'inverse': undefined, 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.parseNumber (this.parsePrecision (this.safeString (market, 'volumePrecision'))), 'price': this.parseNumber (this.parsePrecision (this.safeString (market, 'pricePrecision'))), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': this.safeNumberN (market, [ 'minimumOrderSize', 'marketMiniAmount', 'limitMiniAmount' ]), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeNumber (market, 'minimumTradeVolume'), 'max': undefined, }, }, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { /** * @method * @name bkex#fetchCurrencies * @description fetches all available currencies on an exchange * @param {object} params extra parameters specific to the bkex api endpoint * @returns {object} an associative dictionary of currencies */ const response = await this.publicSpotGetCommonCurrencys (params); // // { // "code": "0", // "data": [ // { // "currency": "ETH", // "maxWithdrawOneDay": "100.000000000000000000", // "maxWithdrawSingle": "50.000000000000000000", // "minWithdrawSingle": "0.005000000000000000", // "supportDeposit": true, // "supportTrade": true, // "supportWithdraw": true, // "withdrawFee": 0.01 // }, // ], // "msg": "success", // "status": 0 // } // const data = this.safeValue (response, 'data', []); const result = {}; for (let i = 0; i < data.length; i++) { const currency = data[i]; const id = this.safeString (currency, 'currency'); const code = this.safeCurrencyCode (id); const name = this.safeString (currency, 'name'); const withdrawEnabled = this.safeValue (currency, 'supportWithdraw'); const depositEnabled = this.safeValue (currency, 'supportDeposit'); const tradeEnabled = this.safeValue (currency, 'supportTrade'); const active = withdrawEnabled && depositEnabled && tradeEnabled; result[code] = { 'id': id, 'code': code, 'name': name, 'deposit': depositEnabled, 'withdraw': withdrawEnabled, 'active': active, 'fee': this.safeNumber (currency, 'withdrawFee'), 'precision': undefined, 'limits': { 'amount': { 'min': undefined, 'max': undefined }, 'price': { 'min': undefined, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, 'withdraw': { 'min': this.safeNumber (currency, 'minWithdrawSingle'), 'max': this.safeNumber (currency, 'maxWithdrawSingle') }, }, 'info': currency, }; } return result; } async fetchTime (params = {}) { /** * @method * @name bkex#fetchTime * @description fetches the current integer timestamp in milliseconds from the exchange server * @param {object} params extra parameters specific to the bkex api endpoint * @returns {int} the current integer timestamp in milliseconds from the exchange server */ const response = await this.publicSpotGetCommonTimestamp (params); // // { // "code": '0', // "data": 1573542445411, // "msg": "success", // "status": 0 // } // return this.safeInteger (response, 'data'); } async fetchStatus (params = {}) { /** * @method * @name bkex#fetchStatus * @description the latest known information on the availability of the exchange API * @param {object} params extra parameters specific to the bkex api endpoint * @returns {object} a [status structure]{@link https://docs.ccxt.com/en/latest/manual.html#exchange-status-structure} */ const response = await this.publicSpotGetCommonTimestamp (params); // // { // "code": '0', // "data": 1573542445411, // "msg": "success", // "status": 0 // } // const statusRaw = this.safeInteger (response, 'status'); const codeRaw = this.safeInteger (response, 'code'); const updated = this.safeInteger (response, 'data'); return { 'status': (statusRaw === 0 && codeRaw === 0) ? 'ok' : statusRaw, 'updated': updated, 'eta': undefined, 'url': undefined, 'info': response, }; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { /** * @method * @name bkex#fetchOHLCV * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @see https://bkexapi.github.io/docs/api_en.htm?shell#quotationData-1 * @see https://bkexapi.github.io/docs/api_en.htm?shell#contract-kline * @param {string} symbol unified symbol of the market to fetch OHLCV data for * @param {string} timeframe the length of time each candle represents * @param {int|undefined} since timestamp in ms of the earliest candle to fetch * @param {int|undefined} limit the maximum amount of candles to fetch * @param {object} params extra parameters specific to the bkex api endpoint * @returns {[[int]]} A list of candles ordered as timestamp, open, high, low, close, volume */ await this.loadMarkets (); const market = this.market (symbol); const swap = market['swap']; const request = { 'symbol': market['id'], }; let method = 'publicSpotGetQKline'; const timeframes = this.safeValue (this.options, 'timeframes'); if (swap) { const swapTimeframes = this.safeValue (timeframes, 'swap'); method = 'publicSwapGetMarketCandle'; request['period'] = swapTimeframes[timeframe]; if (limit !== undefined) { request['count'] = limit; } } else { const spotTimeframes = this.safeValue (timeframes, 'spot'); request['symbol'] = market['id']; request['period'] = spotTimeframes[timeframe]; } if (limit !== undefined) { const limitRequest = swap ? 'count' : 'size'; request[limitRequest] = limit; } // their docs says that 'from/to' arguments are mandatory, however that's not true in reality if (since !== undefined) { const sinceRequest = swap ? 'start' : 'from'; request[sinceRequest] = since; // when 'since' [from] argument is set, then exchange also requires 'to' value to be set. So we have to set 'to' argument depending 'limit' amount (if limit was not provided, then exchange-default 500). if (limit === undefined) { limit = 500; } const duration = this.parseTimeframe (timeframe); const timerange = limit * duration * 1000; const toRequest = swap ? 'end' : 'to'; request[toRequest] = this.sum (request[sinceRequest], timerange); } const response = await this[method] (request); // // spot // // { // "code": "0", // "data": [ // { // "close": "43414.68", // "high": "43446.47", // "low": "43403.05", // "open": "43406.05", // "quoteVolume": "61500.40099", // "symbol": "BTC_USDT", // "ts": "1646152440000", // "volume": 1.41627 // }, // ], // "msg": "success", // "status": 0 // } // // swap // // { // "code": 0, // "msg": "success", // "data": [ // { // "symbol": "btc_usdt", // "amount": "10.26", // "volume": "172540.9433", // "open": "16817.29", // "close": "1670476440000", // "high": "16816.45", // "low": "16817.29", // "ts": 1670476440000 // }, // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOHLCVs (data, market, timeframe, since, limit); } parseOHLCV (ohlcv, market = undefined) { const baseCurrencyVolume = market['swap'] ? 'amount' : 'volume'; return [ this.safeInteger (ohlcv, 'ts'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, baseCurrencyVolume), ]; } async fetchTicker (symbol, params = {}) { /** * @method * @name bkex#fetchTicker * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://bkexapi.github.io/docs/api_en.htm?shell#quotationData-2 * @see https://bkexapi.github.io/docs/api_en.htm?shell#contract-ticker-data * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} params extra parameters specific to the bkex api endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure} */ await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const [ marketType, query ] = this.handleMarketTypeAndParams ('fetchTicker', market, params); const method = (marketType === 'swap') ? 'publicSwapGetMarketTickers' : 'publicSpotGetQTickers'; const response = await this[method] (this.extend (request, query)); // // spot // // { // "code": "0", // "data": [ // { // "change": "6.52", // "close": "43573.470000", // "high": "44940.540000", // "low": "40799.840000", // "open": "40905.780000", // "quoteVolume": "225621691.5991", // "symbol": "BTC_USDT", // "ts": "1646156490781", // "volume": 5210.349 // } // ], // "msg": "success", // "status": 0 // } // // swap // // { // "code": 0, // "msg": "success", // "data": [ // { // "symbol": "btc_usdt", // "amount": "171035.45", // "volume": "2934757466.3859", // "open": "17111.43", // "close": "17135.74", // "high": "17225.99", // "low": "17105.77", // "lastPrice": "17135.74", // "lastAmount": "1.05", // "lastTime": 1670709364912, // "change": "0.14" // } // ] // } // const tickers = this.safeValue (response, 'data', []); const ticker = this.safeValue (tickers, 0); return this.parseTicker (ticker, market); } async fetchTickers (symbols = undefined, params = {}) { /** * @method * @name bkex#fetchTickers * @description fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market * @see https://bkexapi.github.io/docs/api_en.htm?shell#quotationData-2 * @see https://bkexapi.github.io/docs/api_en.htm?shell#contract-ticker-data * @param {[string]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned * @param {object} params extra parameters specific to the bkex api endpoint * @returns {object} an array of [ticker structures]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure} */ await this.loadMarkets (); const request = {}; if (symbols !== undefined) { if (!Array.isArray (symbols)) { throw new BadRequest (this.id + ' fetchTickers() symbols argument should be an array'); } } let market = undefined; if (symbols !== undefined) { const marketIds = this.marketIds (symbols); const symbol = this.safeString (symbols, 0); market = this.market (symbol); if (market['swap']) { if (Array.isArray (symbols)) { const symbolsLength = symbols.length; if (symbolsLength > 1) { throw new BadRequest (this.id + ' fetchTickers() symbols argument cannot contain more than 1 symbol for swap markets'); } } request['symbol'] = market['id']; } else { request['symbol'] = marketIds.join (','); } } const [ marketType, query ] = this.handleMarketTypeAndParams ('fetchTickers', market, params); const method = (marketType === 'swap') ? 'publicSwapGetMarketTickers' : 'publicSpotGetQTickers'; const response = await this[method] (this.extend (request, query)); // // spot // // { // "code": "0", // "data": [ // { // "change": "6.52", // "close": "43573.470000", // "high": "44940.540000", // "low": "40799.840000", // "open": "40905.780000", // "quoteVolume": "225621691.5991", // "symbol": "BTC_USDT", // "ts": "1646156490781", // "volume": 5210.349 // } // ], // "msg": "success", // "status": 0 // } // // swap // // { // "code": 0, // "msg": "success", // "data": [ // { // "symbol": "btc_usdt", // "amount": "171035.45", // "volume": "2934757466.3859", // "open": "17111.43", // "close": "17135.74", // "high": "17225.99", // "low": "17105.77", // "lastPrice": "17135.74", // "lastAmount": "1.05", // "lastTime": 1670709364912, // "change": "0.14" // } // ] // } // const tickers = this.safeValue (response, 'data', []); return this.parseTickers (tickers, symbols, query); } parseTicker (ticker, market = undefined) { // // spot // // { // "change":-0.46, // "close":29664.46, // "high":30784.99, // "low":29455.36, // "open":29803.38, // "quoteVolume":714653752.6991, // "symbol":"BTC_USDT", // "ts":1652812048118, // "volume":23684.9416 // } // // swap // // { // "symbol": "btc_usdt", // "amount": "171035.45", // "volume": "2934757466.3859", // "open": "17111.43", // "close": "17135.74", // "high": "17225.99", // "low": "17105.77", // "lastPrice": "17135.74", // "lastAmount": "1.05", // "lastTime": 1670709364912, // "change": "0.14" // } // const marketId = this.safeString (ticker, 'symbol'); const symbol = this.safeSymbol (marketId, market); market = this.market (symbol); const timestamp = this.safeInteger2 (ticker, 'ts', 'lastTime'); const baseCurrencyVolume = market['swap'] ? 'amount' : 'volume'; const quoteCurrencyVolume = market['swap'] ? 'volume' : 'quoteVolume'; const lastPrice = market['swap'] ? 'lastPrice' : 'close'; return this.safeTicker ({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeString (ticker, 'high'), 'low': this.safeString (ticker, 'low'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': this.safeString (ticker, 'open'), 'close': this.safeString (ticker, 'close'), 'last': this.safeString (ticker, lastPrice), 'previousClose': undefined, 'change': undefined, 'percentage': this.safeString (ticker, 'change'), // 24h percentage change (close - open) / open * 100 'average': undefined, 'baseVolume': this.safeString (ticker, baseCurrencyVolume), 'quoteVolume': this.safeString (ticker, quoteCurrencyVolume), 'info': ticker, }, market); } async fetchOrderBook (symbol, limit = undefined, params = {}) { /** * @method * @name bkex#fetchOrderBook * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://bkexapi.github.io/docs/api_en.htm?shell#quotationData-4 * @see https://bkexapi.github.io/docs/api_en.htm?shell#contract-deep-data * @param {string} symbol unified symbol of the market to fetch the order book for * @param {int|undefined} limit the maximum amount of order book entries to return * @param {object} params extra parameters specific to the bkex api endpoint * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-book-structure} indexed by market symbols */ await this.loadMarkets (); const market = this.market (symbol); const swap = market['swap']; const request = { 'symbol': market['id'], }; let method = 'publicSpotGetQDepth'; if (swap) { method = 'publicSwapGetMarketDepth'; } else { if (limit !== undefined) { request['depth'] = Math.min (limit, 50); } } const response = await this[method] (this.extend (request, params)); // // spot // // { // "code": "0", // "data": { // "ask": [ // ["43820.07","0.86947"], // ["43820.25","0.07503"], // ], // "bid": [ // ["43815.94","0.43743"], // ["43815.72","0.08901"], // ], // "symbol": "BTC_USDT", // "timestamp": 1646161595841 // }, // "msg": "success", // "status": 0 // } // // swap // // { // "code": 0, // "msg": "success", // "data": { // "bid": [ // ["16803.170000","4.96"], // ["16803.140000","11.07"], // ], // "ask": [ // ["16803.690000","9.2"], // ["16804.180000","9.43"], // ] // } // } // const data = this.safeValue (response, 'data'); const timestamp = this.safeInteger (data, 'timestamp'); return this.parseOrderBook (data, market['symbol'], timestamp, 'bid', 'ask'); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { /** * @method * @name bkex#fetchTrades * @description get the list of most recent trades for a particular symbol * @see https://bkexapi.github.io/docs/api_en.htm?shell#quotationData-5 * @see https://bkexapi.github.io/docs/api_en.htm?shell#contract-trades-history * @param {string} symbol unified symbol of the market to fetch trades for * @param {int|undefined} since timestamp in ms of the earliest trade to fetch * @param {int|undefined} limit the maximum amount of trades to fetch * @param {object} params extra parameters specific to the bkex api endpoint * @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades} */ await this.loadMarkets (); const market = this.market (symbol); const swap = market['swap']; const request = { 'symbol': market['id'], }; let method = 'publicSpotGetQDeals'; if (swap) { method = 'publicSwapGetMarketDeals'; } else { if (limit !== undefined) { request['size'] = Math.min (limit, 50); } } const response = await this[method] (this.extend (request, params)); // // spot // // { // "code": "0", // "data": [ // { // "direction": "S", // "price": "43930.63", // "symbol": "BTC_USDT", // "ts": "1646224171992", // "volume": 0.030653 // }, // first item is most recent // ], // "msg": "success", // "status": 0 // } // // swap // // { // "code": 0, // "msg": "success", // "data": [ // { // "symbol": "btc_usdt", // "amount": "0.06", // "price": "17134.66", // "side": "sell", // "time": 1670651851646 // }, // ] // } // const trades = this.safeValue (response, 'data'); return this.parseTrades (trades, market, since, limit); } parseTrade (trade, market = undefined) { const timestamp = this.safeInteger2 (trade, 'ts', 'time'); const marketId = this.safeString (trade, 'symbol'); market = this.safeMarket (marketId, market); const side = this.parseTradeSide (this.safeString2 (trade, 'direction', 'side')); const amount = this.safeNumber2 (trade, 'volume', 'amount'); const price = this.safeNumber (trade, 'price'); const type = undefined; let id = this.safeString (trade, 'tid'); if (id === undefined) { id = this.syntheticTradeId (market, timestamp, side, amount, price, type); } return this.safeTrade ({ 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': market['symbol'], 'order': undefined, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': undefined, 'fee': undefined, 'info': trade, }, market); } parseTradeSide (side) { const sides = { 'B': 'buy', 'S': 'sell', 'buy': 'buy', 'sell': 'sell', }; return this.safeString (sides, side, side); } syntheticTradeId (market = undefined, timestamp = undefined, side = undefined, amount = undefined, price = undefined, orderType = undefined, takerOrMaker = undefined) { // TODO: can be unified method? this approach is being used by multiple exchanges (mexc, woo-coinsbit, dydx, ...) let id = ''; if (timestamp !== undefined) { id = this.numberToString (timestamp) + '-' + this.safeString (market, 'id', '_'); if (side !== undefined) { id += '-' + side; } if (orderType !== undefined) { id += '-' + orderType; } if (takerOrMaker !== undefined) { id += '-' + takerOrMaker; } if (amount !== undefined) { id += '-' + this.numberToString (amount); } if (price !== undefined) { id += '-' + this.numberToString (price); } } return id; } async fetchBalance (params = {}) { /** * @method * @name bkex#fetchBalance * @description query for balance and get the amount of funds available for trading or funds locked in orders * @param {object} params extra parameters specific to the bkex api endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure} */ await this.loadMarkets (); const query = this.omit (params, 'type'); const response = await this.privateSpotGetUAccountBalance (query); // // { // "code": "0", // "data": { // "WALLET": [ // { // "available": "0.221212121000000000", // "currency": "PHX", // "frozen": "0E-18", // "total": 0.221212121 // }, // { // "available": "44.959577229600000000", // "currency": "USDT", // "frozen": "0E-18", // "total": 44.9595772296 // } // ] // }, // "msg": "success", // "status": 0 // } // const balances = this.safeValue (response, 'data'); const wallets = this.safeValue (balances, 'WALLET', []); const result = { 'info': wallets }; for (let i = 0; i < wallets.length; i++) { const wallet = wallets[i]; const currencyId = wallet['currency']; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeNumber (wallet, 'available'); account['used'] = this.safeNumber (wallet, 'frozen'); account['total'] = this.safeNumber (wallet, 'total'); result[code] = account; } return this.safeBalance (result); } async fetchDepositAddress (code, params = {}) { /** * @method * @name bkex#fetchDepositAddress * @description fetch the deposit address for a currency associated with this account * @param {string} code unified currency code * @param {object} params extra parameters specific to the bkex api endpoint * @returns {object} an [address structure]{@link https://docs.ccxt.com/en/latest/manual.html#address-structure} */ await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privateSpotGetUWalletAddress (this.extend (request, params)); // NOTE: You can only retrieve addresses of already generated wallets - so should already have generated that COIN deposit address in UI. Otherwise, it seems from API you can't create/obtain addresses for those coins. // // { // "code": "0", // "data": [ // { // "currency": "BTC", // "address": "1m4k2yUKTSrX6SM9FGgvwMyxQbYtRVi2N", // "memo": "" // } // ], // "msg": "success", // "status": 0 // } // const data = this.safeValue (response, 'data', {}); return this.parseDepositAddress (data, currency); } parseDepositAddress (data, currency = undefined) { const depositObject = this.safeValue (data, 0); const address = this.safeString (depositObject, 'address'); const tag = this.safeString (depositObject, 'memo'); const currencyId = this.safeString (depositObject, 'currency'); currency = this.safeCurrency (currencyId, currency); return { 'currency': currency['code'], 'address': address, 'tag': tag, 'network': undefined, 'info': data, }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name bkex#fetchDeposits * @description fetch all deposits made to an account * @param {string} code unified currency code * @param {int|undefined} since the earliest time in ms to fetch deposits for * @param {int|undefined} limit the maximum number of deposits structures to retrieve * @param {object} params extra parameters specific to the bkex api endpoint * @returns {[object]} a list of [transaction structures]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure} */ if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchDeposits() requires code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; if (since !== undefined) { request['startTime'] = since; const endTime = this.milliseconds (); request['endTime'] = endTime; } if (limit !== undefined) { request['Size'] = limit; // Todo: id api-docs, 'size' is incorrectly required to be in Uppercase } const response = await this.privateSpotGetUWalletDepositRecord (this.extend (request, params)); // // { // "code": "0", // "data": { // "data": [ // { // "createTime": "1622274255000", // "currency": "BNB", // "fromAddress": "bnb10af52w77pkehgxhnwgeca50q2t2354q4xexa5y", // "hash": "97B982F497782C2777C0F6AD16CEAAC65A93A364B684A23A71CFBB8C010DEEA6", // "id": "2021052923441510234383337", // "status": "0", // "toAddress": "bnb13w64gkc42c0l45m2p5me4qn35z0a3ej9ldks3j_82784659", // "volume": 0.073 // } // ], // "total": 1 // }, // "msg": "success", // "status": 0 // } // const data = this.safeValue (response, 'data', {}); const dataInner = this.safeValue (data, 'data', []); for (let i = 0; i < dataInner.length; i++) { dataInner[i]['transactType'] = 'deposit'; } return this.parseTransactions (dataInner, currency, since, limit, params); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name bkex#fetchWithdrawals * @description fetch all withdrawals made from an account * @param {string} code unified currency code * @param {int|undefined} since the earliest time in ms to fetch withdrawals for * @param {int|undefined} limit the maximum number of withdrawals structures to retrieve * @param {object} params extra parameters specific to the bkex api endpoint * @returns {[object]} a list of [transaction structures]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure} */ if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; if (since !== undefined) { request['startTime'] = since; const endTime = this.milliseconds (); request['endTime'] = endTime; } if (limit !== undefined) { request['Size'] = limit; // Todo: id api-docs, 'size' is incorrectly required to be in Uppercase } const response = await this.privateSpotGetUWalletWithdrawRecord (this.extend (request, params)); // // { // "code": "0", // "data": { // "data": [ // { // ... // } // ], // "total": 1 // }, // "msg": "success", // "status": 0 // } // const data = this.safeValue (response, 'data', {}); const dataInner = this.safeVal