UNPKG

sfccxt

Version:

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

953 lines (943 loc) 195 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { BadRequest, BadSymbol, ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, NotSupported, OrderNotFound, ExchangeNotAvailable, RateLimitExceeded, PermissionDenied, InvalidOrder, InvalidAddress, OnMaintenance, RequestTimeout, AccountSuspended, NetworkError, DDoSProtection, DuplicateOrderId, BadResponse } = require ('./base/errors'); const { TICK_SIZE } = require ('./base/functions/number'); const Precise = require ('./base/Precise'); // --------------------------------------------------------------------------- module.exports = class zb extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'zb', 'name': 'ZB', 'countries': [ 'CN' ], // previously rateLimit = 100 // Trading and Margin 10 000 per minute (IP) => 10000 / 60 = 166.66666... per second => rateLimit = 1000/166.66666 = 6 // Trade and Margin 60 per second (apiKey) => weight = 166.666 / 60 = 2.778 (2.7777777...) // Kline 1 per second => weight = 166.667 // v2 Futures API 100 per 2 seconds => 50 per second => weight = 3.334 (3.3333333...) // for endpoints not mentioned in docs // previous rateLimit was 100 translating to 10 requests per second => weight = 166.666 / 10 = 16.667 (16.666666...) 'rateLimit': 6, 'version': 'v1', 'pro': true, 'has': { 'CORS': undefined, 'spot': true, 'margin': true, 'swap': true, 'future': undefined, 'option': undefined, 'addMargin': true, 'borrowMargin': true, 'cancelAllOrders': true, 'cancelOrder': true, 'createMarketOrder': undefined, 'createOrder': true, 'createReduceOnlyOrder': false, 'createStopLimitOrder': true, 'createStopMarketOrder': true, 'createStopOrder': true, 'fetchBalance': true, 'fetchBorrowRate': true, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchBorrowRates': true, 'fetchCanceledOrders': true, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDepositAddresses': true, 'fetchDeposits': true, 'fetchFundingHistory': false, 'fetchFundingRate': true, 'fetchFundingRateHistory': true, 'fetchFundingRates': true, 'fetchIndexOHLCV': true, 'fetchLedger': true, 'fetchLeverage': false, 'fetchLeverageTiers': false, 'fetchMarketLeverageTiers': false, 'fetchMarkets': true, 'fetchMarkOHLCV': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchPosition': true, 'fetchPositions': true, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchWithdrawals': true, 'reduceMargin': true, 'setLeverage': true, 'setMarginMode': false, 'setPositionMode': false, 'transfer': true, 'withdraw': true, }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '6h': '6h', '12h': '12h', '1d': '1d', '3d': '3d', '5d': '5d', '1w': '1w', }, 'hostname': 'zb.com', // zb.cafe for users in China 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/32859187-cd5214f0-ca5e-11e7-967d-96568e2e2bd1.jpg', 'api': { 'spot': { 'v1': { 'public': 'https://api.{hostname}/data', 'private': 'https://trade.{hostname}/api', }, }, 'contract': { 'v1': { 'public': 'https://fapi.{hostname}/api/public', }, 'v2': { 'public': 'https://fapi.{hostname}/Server/api', 'private': 'https://fapi.{hostname}/Server/api', }, }, }, 'www': 'https://www.zb.com', 'doc': 'https://www.zb.com/i/developer', 'fees': 'https://www.zb.com/i/rate', 'referral': { 'url': 'https://www.zb.com/en/register?ref=4301lera', 'discount': 0.16, }, }, 'api': { 'spot': { 'v1': { 'public': { 'get': { 'markets': 16.667, 'ticker': 16.667, 'allTicker': 16.667, 'depth': 16.667, 'trades': 16.667, 'kline': 166.667, // Kline 1 per second 'getGroupMarkets': 16.667, 'getFeeInfo': 16.667, }, }, 'private': { 'get': { // spot API 'order': 1, // Trade API 'orderMoreV2': 1, // Trade API 'cancelOrder': 1, // Trade API 'cancelAllOrdersAfter': 1, // Trade API TODO add cancelAllOrders 'getOrder': 1, // Trade API 'getOrders': 1, // Trade API 'getOrdersNew': 16.667, 'getOrdersIgnoreTradeType': 1, // Trade API 'getUnfinishedOrdersIgnoreTradeType': 1, // Trade API 'getFinishedAndPartialOrders': 1, // Trade API 'getAccountInfo': 16.667, 'getUserAddress': 16.667, 'getPayinAddress': 16.667, 'getWithdrawAddress': 16.667, 'getWithdrawRecord': 16.667, 'getChargeRecord': 16.667, 'getCnyWithdrawRecord': 16.667, 'getCnyChargeRecord': 16.667, 'withdraw': 16.667, // sub accounts 'addSubUser': 16.667, 'getSubUserList': 16.667, 'doTransferFunds': 16.667, 'createSubUserKey': 16.667, // removed on 2021-03-16 according to the update log in the API doc // leverage API 'getLeverAssetsInfo': 16.667, 'getLeverBills': 16.667, 'transferInLever': 16.667, 'transferOutLever': 16.667, 'loan': 16.667, 'cancelLoan': 16.667, 'getLoans': 16.667, 'getLoanRecords': 16.667, 'borrow': 16.667, 'autoBorrow': 16.667, 'repay': 16.667, 'doAllRepay': 16.667, 'getRepayments': 16.667, 'getFinanceRecords': 16.667, 'changeInvestMark': 16.667, 'changeLoop': 16.667, // cross API 'getCrossAssets': 16.667, 'getCrossBills': 16.667, 'transferInCross': 16.667, 'transferOutCross': 16.667, 'doCrossLoan': 16.667, 'doCrossRepay': 16.667, 'getCrossRepayRecords': 16.667, }, }, }, }, 'contract': { 'v1': { 'public': { 'get': { 'depth': 16.667, 'fundingRate': 16.667, 'indexKline': 16.667, 'indexPrice': 16.667, 'kline': 16.667, 'markKline': 16.667, 'markPrice': 16.667, 'ticker': 16.667, 'trade': 16.667, }, }, }, 'v2': { 'public': { 'get': { 'allForceOrders': 3.334, 'config/marketList': 3.334, 'topLongShortAccountRatio': 3.334, 'topLongShortPositionRatio': 3.334, 'fundingRate': 3.334, 'premiumIndex': 3.334, }, }, 'private': { 'get': { 'Fund/balance': 3.334, 'Fund/getAccount': 3.334, 'Fund/getBill': 3.334, 'Fund/getBillTypeList': 3.334, 'Fund/marginHistory': 3.334, 'Positions/getPositions': 3.334, 'Positions/getNominalValue': 3.334, 'Positions/marginInfo': 3.334, 'setting/get': 3.334, 'trade/getAllOrders': 3.334, 'trade/getOrder': 3.334, 'trade/getOrderAlgos': 3.334, 'trade/getTradeList': 3.334, 'trade/getUndoneOrders': 3.334, 'trade/tradeHistory': 3.334, }, 'post': { 'activity/buyTicket': 3.334, 'Fund/transferFund': 3.334, 'Positions/setMarginCoins': 3.334, 'Positions/updateAppendUSDValue': 3.334, 'Positions/updateMargin': 3.334, 'setting/setLeverage': 3.334, 'setting/setPositionsMode': 3.334, 'trade/batchOrder': 3.334, 'trade/batchCancelOrder': 3.334, 'trade/cancelAlgos': 3.334, 'trade/cancelAllOrders': 3.334, 'trade/cancelOrder': 3.334, 'trade/order': 3.334, 'trade/orderAlgo': 3.334, 'trade/updateOrderAlgo': 3.334, }, }, }, }, }, 'fees': { 'funding': { 'withdraw': {}, }, 'trading': { 'maker': this.parseNumber ('0.002'), 'taker': this.parseNumber ('0.002'), }, }, 'commonCurrencies': { 'ANG': 'Anagram', 'ENT': 'ENTCash', 'BCHABC': 'BCHABC', // conflict with BCH / BCHA 'BCHSV': 'BCHSV', // conflict with BCH / BSV }, 'options': { 'timeframes': { 'spot': { '1m': '1min', '3m': '3min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1hour', '2h': '2hour', '4h': '4hour', '6h': '6hour', '12h': '12hour', '1d': '1day', '3d': '3day', '1w': '1week', }, 'swap': { '1m': '1M', '5m': '5M', '15m': '15M', '30m': '30M', '1h': '1H', '6h': '6H', '1d': '1D', '5d': '5D', }, }, }, 'precisionMode': TICK_SIZE, 'exceptions': { 'ws': { // '1000': ExchangeError, // The call is successful. '1001': ExchangeError, // General error prompt '1002': ExchangeError, // Internal Error '1003': AuthenticationError, // Fail to verify '1004': AuthenticationError, // The transaction password is locked '1005': AuthenticationError, // Wrong transaction password, please check it and re-enter。 '1006': PermissionDenied, // Real-name authentication is pending approval or unapproved '1007': ExchangeError, // Channel does not exist '1009': OnMaintenance, // This interface is under maintenance '1010': ExchangeNotAvailable, // Not available now '1012': PermissionDenied, // Insufficient permissions '1013': ExchangeError, // Cannot trade, please contact email: support@zb.cn for support. '1014': ExchangeError, // Cannot sell during the pre-sale period '2001': InsufficientFunds, // Insufficient CNY account balance '2002': InsufficientFunds, // Insufficient BTC account balance '2003': InsufficientFunds, // Insufficient LTC account balance '2005': InsufficientFunds, // Insufficient ETH account balance '2006': InsufficientFunds, // ETCInsufficient account balance '2007': InsufficientFunds, // BTSInsufficient account balance '2008': InsufficientFunds, // EOSInsufficient account balance '2009': InsufficientFunds, // BCCInsufficient account balance '3001': OrderNotFound, // Order not found or is completed '3002': InvalidOrder, // Invalid amount '3003': InvalidOrder, // Invalid quantity '3004': AuthenticationError, // User does not exist '3005': BadRequest, // Invalid parameter '3006': PermissionDenied, // Invalid IP or not consistent with the bound IP '3007': RequestTimeout, // The request time has expired '3008': ExchangeError, // Transaction not found '3009': InvalidOrder, // The price exceeds the limit '3010': PermissionDenied, // It fails to place an order, due to you have set up to prohibit trading of this market. '3011': InvalidOrder, // The entrusted price is abnormal, please modify it and place order again '3012': InvalidOrder, // Duplicate custom customerOrderId '4001': AccountSuspended, // APIThe interface is locked for one hour '4002': RateLimitExceeded, // Request too frequently }, 'exact': { // '1000': 'Successful operation', '10001': ExchangeError, // Operation failed '10002': PermissionDenied, // Operation is forbidden '10003': BadResponse, // Data existed '10004': BadResponse, // Date not exist '10005': PermissionDenied, // Forbidden to access the interface '10006': BadRequest, // Currency invalid or expired '10007': ExchangeError, // {0} '10008': ExchangeError, // Operation failed: {0} '10009': ExchangeError, // URL error '1001': ExchangeError, // 'General error message', '10010': AuthenticationError, // API KEY not exist '10011': AuthenticationError, // API KEY CLOSED '10012': AccountSuspended, // User API has been frozen, please contact customer service for processing '10013': AuthenticationError, // API verification failed '10014': AuthenticationError, // Invalid signature(1001) '10015': AuthenticationError, // Invalid signature(1002) '10016': AuthenticationError, // Invalid ip '10017': PermissionDenied, // Permission denied '10018': AccountSuspended, // User has been frozen, please contact customer service '10019': RequestTimeout, // Request time has expired '1002': ExchangeError, // 'Internal error', '10020': BadRequest, // {0}Parameter cannot be empty '10021': BadRequest, // {0}Invalid parameter '10022': BadRequest, // Request method error '10023': RateLimitExceeded, // Request frequency is too fast, exceeding the limit allowed by the interface '10024': AuthenticationError, // Login failed '10025': ExchangeError, // Non-personal operation '10026': NetworkError, // Failed to request interface, please try again '10027': RequestTimeout, // Timed out, please try again later '10028': ExchangeNotAvailable, // System busy, please try again later '10029': DDoSProtection, // Frequent operation, please try again later '1003': AuthenticationError, // 'Verification does not pass', '10030': BadRequest, // Currency already exist '10031': BadRequest, // Currency does not exist '10032': BadRequest, // Market existed '10033': BadRequest, // Market not exist '10034': BadRequest, // Currency error '10035': BadRequest, // Market not open '10036': BadRequest, // Ineffective market type '10037': ArgumentsRequired, // User id cannot be empty '10038': BadRequest, // Market id cannot be empty '10039': BadResponse, // Failed to get mark price '1004': AuthenticationError, // 'Funding security password lock', '10040': BadResponse, // Failed to obtain the opening margin configuration '10041': BadResponse, // Failed to obtain maintenance margin allocation '10042': ExchangeError, // Avg. price error '10043': ExchangeError, // Abnormal acquisition of liquidation price '10044': ExchangeError, // Unrealized profit and loss acquisition exception '10045': ExchangeError, // jdbcData source acquisition failed '10046': ExchangeError, // Invalid position opening direction '10047': ExchangeError, // The maximum position allowed by the current leverage multiple has been exceeded '10048': ExchangeError, // The maximum allowable order quantity has been exceeded '10049': NetworkError, // Failed to get the latest price '1005': AuthenticationError, // 'Funds security password is incorrect, please confirm and re-enter.', '1006': AuthenticationError, // 'Real-name certification pending approval or audit does not pass', '1009': ExchangeNotAvailable, // 'This interface is under maintenance', '1010': ExchangeNotAvailable, // Not available now '10100': OnMaintenance, // Sorry! System maintenance, stop operation '1012': PermissionDenied, // Insufficient permissions '1013': ExchangeError, // Cannot trade, please contact email: support@zb.cn for support. '1014': ExchangeError, // Cannot sell during the pre-sale period '11000': ExchangeError, // Funding change failed '11001': ExchangeError, // Position change failed '110011': ExchangeError, // Exceeds the maximum leverage allowed by the position '11002': ExchangeError, // Funding not exist '11003': ExchangeError, // Freeze records not exist '11004': InsufficientFunds, // Insufficient frozen funds '11005': InvalidOrder, // Insufficient positions '11006': InsufficientFunds, // Insufficient frozen positions '11007': OrderNotFound, // Position not exist '11008': ExchangeError, // The contract have positions, cannot be modified '11009': ExchangeError, // Failed to query data '110110': ExchangeError, // Exceed the market's maximum leverage '11012': InsufficientFunds, // Insufficient margin '11013': ExchangeError, // Exceeding accuracy limit '11014': ExchangeError, // Invalid bill type '11015': AuthenticationError, // Failed to add default account '11016': AuthenticationError, // Account not exist '11017': ExchangeError, // Funds are not frozen or unfrozen '11018': InsufficientFunds, // Insufficient funds '11019': ExchangeError, // Bill does not exist '11021': InsufficientFunds, // Inconsistent currency for funds transfer '11023': ExchangeError, // Same transaction currency '11030': PermissionDenied, // Position is locked, the operation is prohibited '11031': ExchangeError, // The number of bill changes is zero '11032': ExchangeError, // The same request is being processed, please do not submit it repeatedly '11033': ArgumentsRequired, // Position configuration data is empty '11034': ExchangeError, // Funding fee is being settled, please do not operate '12000': InvalidOrder, // Invalid order price '12001': InvalidOrder, // Invalid order amount '12002': InvalidOrder, // Invalid order type '12003': InvalidOrder, // Invalid price accuracy '12004': InvalidOrder, // Invalid quantity precision '12005': InvalidOrder, // order value less than the minimum or greater than the maximum '12006': InvalidOrder, // Customize's order number format is wrong '12007': InvalidOrder, // Direction error '12008': InvalidOrder, // Order type error '12009': InvalidOrder, // Commission type error '12010': InvalidOrder, // Failed to place the order, the loss of the order placed at this price will exceed margin '12011': InvalidOrder, // it's not a buz order '12012': OrderNotFound, // order not exist '12013': InvalidOrder, // Order user does not match '12014': InvalidOrder, // Order is still in transaction '12015': InvalidOrder, // Order preprocessing failed '12016': InvalidOrder, // Order cannot be canceled '12017': InvalidOrder, // Transaction Record not exist '12018': InvalidOrder, // Order failed '12019': ArgumentsRequired, // extend parameter cannot be empty '12020': ExchangeError, // extend Parameter error '12021': InvalidOrder, // The order price is not within the price limit rules! '12022': InvalidOrder, // Stop placing an order while the system is calculating the fund fee '12023': OrderNotFound, // There are no positions to close '12024': InvalidOrder, // Orders are prohibited, stay tuned! '12025': InvalidOrder, // Order cancellation is prohibited, so stay tuned! '12026': DuplicateOrderId, // Order failed, customize order number exists '12027': ExchangeNotAvailable, // System busy, please try again later '12028': InvalidOrder, // The market has banned trading '12029': InvalidOrder, // Forbidden place order, stay tuned '12201': InvalidOrder, // Delegation strategy does not exist or the status has changed '12202': InvalidOrder, // Delegation strategy has been changed, cannot be canceled '12203': InvalidOrder, // Wrong order type '12204': InvalidOrder, // Invalid trigger price '12205': InvalidOrder, // The trigger price must be greater than the market’s selling price or lower than the buying price. '12206': InvalidOrder, // Direction and order type do not match '12207': RateLimitExceeded, // Submission failed, exceeding the allowed limit '13001': AuthenticationError, // User not exist '13002': PermissionDenied, // User did not activate futures // '13003': AuthenticationError, // User is locked '13003': InvalidOrder, // Margin gear is not continuous '13004': InvalidOrder, // The margin quick calculation amount is less than 0 '13005': RateLimitExceeded, // You have exceeded the number of exports that day '13006': ExchangeError, // No markets are bookmarked '13007': ExchangeError, // Market not favorited '13008': ExchangeError, // Not in any market user whitelist '13009': ExchangeError, // Not in the whitelist of users in this market '14000': ExchangeError, // {0}not support '14001': AuthenticationError, // Already logged in, no need to log in multiple times '14002': AuthenticationError, // Not logged in yet, please log in before subscribing '14003': ExchangeError, // This is a channel for one-time queries, no need to unsubscribe '14100': ExchangeError, // Accuracy does not support '14101': RateLimitExceeded, // Request exceeded frequency limit '14200': ArgumentsRequired, // id empty '14300': ExchangeError, // activity not exist '14301': ExchangeError, // The event has been opened and cannot be admitted '14302': ExchangeError, // The purchase time has passed and cannot be admitted '14303': ExchangeError, // Not yet open for the purchase '14305': ExchangeError, // Cannot enter, the maximum number of returns has been exceeded '14306': ExchangeError, // Cannot repeat admission '14307': InvalidOrder, // Unable to cancel, status has been changed '14308': InvalidOrder, // Unable to cancel, the amount does not match '14309': ExchangeError, // Activity has not started '14310': NotSupported, // Activity is over '14311': NotSupported, // The activity does not support orders placed in this market '14312': ExchangeError, // You have not participated in this activity '14313': PermissionDenied, // Sorry! The purchase failed, the maximum number of participants has been reached '14314': ExchangeError, // Active period id error '2001': InsufficientFunds, // 'Insufficient CNY Balance', '2002': InsufficientFunds, // 'Insufficient BTC Balance', '2003': InsufficientFunds, // 'Insufficient LTC Balance', '2005': InsufficientFunds, // 'Insufficient ETH Balance', '2006': InsufficientFunds, // 'Insufficient ETC Balance', '2007': InsufficientFunds, // 'Insufficient BTS Balance', '2008': InsufficientFunds, // EOSInsufficient account balance '2009': InsufficientFunds, // 'Account balance is not enough', '3001': OrderNotFound, // 'Pending orders not found', '3002': InvalidOrder, // 'Invalid price', '3003': InvalidOrder, // 'Invalid amount', '3004': AuthenticationError, // 'User does not exist', '3005': BadRequest, // 'Invalid parameter', '3006': AuthenticationError, // 'Invalid IP or inconsistent with the bound IP', '3007': AuthenticationError, // 'The request time has expired', '3008': OrderNotFound, // 'Transaction records not found', '3009': InvalidOrder, // 'The price exceeds the limit', '3010': PermissionDenied, // It fails to place an order, due to you have set up to prohibit trading of this market. '3011': InvalidOrder, // 'The entrusted price is abnormal, please modify it and place order again', '3012': InvalidOrder, // Duplicate custom customerOrderId '4001': ExchangeNotAvailable, // 'API interface is locked or not enabled', '4002': RateLimitExceeded, // 'Request too often', '9999': ExchangeError, // Unknown error }, 'broad': { '提币地址有误, 请先添加提币地址。': InvalidAddress, // {"code":1001,"message":"提币地址有误,请先添加提币地址。"} '资金不足,无法划账': InsufficientFunds, // {"code":1001,"message":"资金不足,无法划账"} '响应超时': RequestTimeout, // {"code":1001,"message":"响应超时"} }, }, }); } async fetchMarkets (params = {}) { /** * @method * @name zb#fetchMarkets * @description retrieves data on all markets for zb * @param {object} params extra parameters specific to the exchange api endpoint * @returns {[object]} an array of objects representing market data */ // // { // "zb_qc":{ // "amountScale":2, // "minAmount":0.01, // "minSize":5, // "priceScale":4, // }, // } // let promises = [ this.spotV1PublicGetMarkets (params), this.contractV2PublicGetConfigMarketList (params) ]; promises = await Promise.all (promises); const markets = promises[0]; const contracts = promises[1]; // // { // BTC_USDT: { // symbol: 'BTC_USDT', // buyerCurrencyId: '6', // contractType: '1', // defaultMarginMode: '1', // marketType: '2', // historyDBName: 'trade_history_readonly.dbc', // defaultLeverage: '20', // id: '100', // canCancelOrder: true, // area: '1', // mixMarginCoinName: 'usdt', // fundingRateRatio: '0.25', // marginCurrencyName: 'usdt', // minTradeMoney: '0.0001', // enableTime: '1638954000000', // maxTradeMoney: '10000000', // canTrade: true, // maxLeverage: '125', // defaultPositionsMode: '2', // onlyWhitelistVisible: false, // riskWarnRatio: '0.8', // marginDecimal: '8', // spot: false, // status: '1', // amountDecimal: '3', // leverage: false, // minAmount: '0.001', // canOrder: true, // duration: '1', // feeDecimal: '8', // sellerCurrencyId: '1', // maxAmount: '1000', // canOpenPosition: true, // isSupportMixMargin: false, // markPriceLimitRate: '0.05', // marginCurrencyId: '6', // stopFundingFee: false, // priceDecimal: '2', // lightenUpFeeRate: '0', // futures: true, // sellerCurrencyName: 'btc', // marketPriceLimitRate: '0.05', // canRebate: true, // marketName: 'BTC_USDT', // depth: [ 0.01, 0.1, 1 ], // createTime: '1607590430094', // mixMarginCoinIds: [ 6 ], // buyerCurrencyName: 'usdt', // stopService: false // }, // } // const contractsData = this.safeValue (contracts, 'data', []); const contractsById = this.indexBy (contractsData, 'marketName'); const dataById = this.deepExtend (contractsById, markets); const keys = Object.keys (dataById); const result = []; for (let i = 0; i < keys.length; i++) { const id = keys[i]; const market = dataById[id]; const [ baseId, quoteId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const settleId = this.safeValue (market, 'marginCurrencyName'); const settle = this.safeCurrencyCode (settleId); const spot = settle === undefined; const swap = this.safeValue (market, 'futures', false); const linear = swap ? true : undefined; let active = true; let symbol = base + '/' + quote; if (swap) { const status = this.safeString (market, 'status'); active = (status === '1'); symbol = base + '/' + quote + ':' + settle; } result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': swap ? 'swap' : 'spot', 'spot': spot, 'margin': false, 'swap': swap, 'future': false, 'option': false, 'active': active, 'contract': swap, 'linear': linear, 'inverse': swap ? !linear : undefined, 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.parseNumber (this.parsePrecision (this.safeString2 (market, 'amountScale', 'amountDecimal'))), 'price': this.parseNumber (this.parsePrecision (this.safeString2 (market, 'priceScale', 'priceDecimal'))), }, 'limits': { 'leverage': { 'min': undefined, 'max': this.safeNumber (market, 'maxLeverage'), }, 'amount': { 'min': this.safeNumber (market, 'minAmount'), 'max': this.safeNumber (market, 'maxAmount'), }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeNumber2 (market, 'minSize', 'minTradeMoney'), 'max': this.safeNumber (market, 'maxTradeMoney'), }, }, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { /** * @method * @name zb#fetchCurrencies * @description fetches all available currencies on an exchange * @param {object} params extra parameters specific to the zb api endpoint * @returns {object} an associative dictionary of currencies */ const response = await this.spotV1PublicGetGetFeeInfo (params); // // { // "code":1000, // "message":"success", // "result":{ // "USDT":[ // { // "chainName":"TRC20", // "canWithdraw":true, // "fee":1.0, // "mainChainName":"TRX", // "canDeposit":true // }, // { // "chainName":"OMNI", // "canWithdraw":true, // "fee":5.0, // "mainChainName":"BTC", // "canDeposit":true // }, // { // "chainName":"ERC20", // "canWithdraw":true, // "fee":15.0, // "mainChainName":"ETH", // "canDeposit":true // } // ], // } // } // const currencies = this.safeValue (response, 'result', {}); const ids = Object.keys (currencies); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const currency = currencies[id]; const code = this.safeCurrencyCode (id); let isWithdrawEnabled = true; let isDepositEnabled = true; const fees = {}; for (let j = 0; j < currency.length; j++) { const networkItem = currency[j]; const network = this.safeString (networkItem, 'chainName'); // const name = this.safeString (networkItem, 'name'); const withdrawFee = this.safeNumber (networkItem, 'fee'); const depositEnable = this.safeValue (networkItem, 'canDeposit'); const withdrawEnable = this.safeValue (networkItem, 'canWithdraw'); isDepositEnabled = isDepositEnabled || depositEnable; isWithdrawEnabled = isWithdrawEnabled || withdrawEnable; fees[network] = withdrawFee; } const active = (isWithdrawEnabled && isDepositEnabled); result[code] = { 'id': id, 'name': undefined, 'code': code, 'precision': undefined, 'info': currency, 'active': active, 'deposit': isDepositEnabled, 'withdraw': isWithdrawEnabled, 'fee': undefined, 'fees': fees, 'limits': this.limits, }; } return result; } parseBalance (response) { const balances = this.safeValue (response['result'], 'coins'); const result = { 'info': response, }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; // { enName: "BTC", // freez: "0.00000000", // unitDecimal: 8, // always 8 // cnName: "BTC", // isCanRecharge: true, // TODO: should use this // unitTag: "฿", // isCanWithdraw: true, // TODO: should use this // available: "0.00000000", // key: "btc" } const account = this.account (); const currencyId = this.safeString (balance, 'key'); const code = this.safeCurrencyCode (currencyId); account['free'] = this.safeString (balance, 'available'); account['used'] = this.safeString (balance, 'freez'); result[code] = account; } return this.safeBalance (result); } parseSwapBalance (response) { const result = { 'info': response, }; const data = this.safeValue (response, 'data', {}); for (let i = 0; i < data.length; i++) { const balance = data[i]; // // { // "userId": "6896693805014120448", // "currencyId": "6", // "currencyName": "usdt", // "amount": "30.56585118", // "freezeAmount": "0", // "contractType": 1, // "id": "6899113714763638819", // "createTime": "1644876888934", // "modifyTime": "1645787446037", // "accountBalance": "30.56585118", // "allMargin": "0", // "allowTransferOutAmount": "30.56585118" // }, // const code = this.safeCurrencyCode (this.safeString (balance, 'currencyName')); const account = this.account (); account['total'] = this.safeString (balance, 'accountBalance'); account['free'] = this.safeString (balance, 'allowTransferOutAmount'); account['used'] = this.safeString (balance, 'freezeAmount'); result[code] = account; } return this.safeBalance (result); } parseMarginBalance (response, marginMode) { const result = { 'info': response, }; let levers = undefined; if (marginMode === 'isolated') { const message = this.safeValue (response, 'message', {}); const data = this.safeValue (message, 'datas', {}); levers = this.safeValue (data, 'levers', []); } else { const crossResponse = this.safeValue (response, 'result', {}); levers = this.safeValue (crossResponse, 'list', []); } for (let i = 0; i < levers.length; i++) { const balance = levers[i]; // // Isolated Margin // // { // "cNetUSD": "0.00", // "repayLeverShow": "-", // "cCanLoanIn": "0.002115400000000", // "fNetCNY": "147.76081161", // "fLoanIn": "0.00", // "repayLevel": 0, // "level": 1, // "netConvertCNY": "147.760811613032", // "cFreeze": "0.00", // "cUnitTag": "BTC", // "version": 1646783178609, // "cAvailableUSD": "0.00", // "cNetCNY": "0.00", // "riskRate": "-", // "fAvailableUSD": "20.49273433", // "fNetUSD": "20.49273432", // "cShowName": "BTC", // "leverMultiple": "5.00", // "couldTransferOutFiat": "20.49273433", // "noticeLine": "1.13", // "fFreeze": "0.00", // "cUnitDecimal": 8, // "fCanLoanIn": "81.970937320000000", // "cAvailable": "0.00", // "repayLock": false, // "status": 1, // "forbidType": 0, // "totalConvertCNY": "147.760811613032", // "cAvailableCNY": "0.00", // "unwindPrice": "0.00", // "fOverdraft": "0.00", // "fShowName": "USDT", // "statusShow": "%E6%AD%A3%E5%B8%B8", // "cOverdraft": "0.00", // "netConvertUSD": "20.49273433", // "cNetBtc": "0.00", // "loanInConvertCNY": "0.00", // "fAvailableCNY": "147.760811613032", // "key": "btcusdt", // "fNetBtc": "0.0005291", // "fUnitDecimal": 8, // "loanInConvertUSD": "0.00", // "showName": "BTC/USDT", // "startLine": "1.25", // "totalConvertUSD": "20.49273433", // "couldTransferOutCoin": "0.00", // "cEnName": "BTC", // "leverMultipleInterest": "3.00", // "fAvailable": "20.49273433", // "fEnName": "USDT", // "forceRepayLine": "1.08", // "cLoanIn": "0.00" // } // // Cross Margin // // [ // { // "fundType": 2, // "loanIn": 0, // "amount": 0, // "freeze": 0, // "overdraft": 0, // "key": "BTC", // "canTransferOut": 0 // }, // ], // const account = this.account (); if (marginMode === 'isolated') { const code = this.safeCurrencyCode (this.safeString (balance, 'fShowName')); account['total'] = this.safeString (balance, 'fAvailableUSD'); // total amount in USD account['free'] = this.safeString (balance, 'couldTransferOutFiat'); account['used'] = this.safeString (balance, 'fFreeze'); result[code] = account; } else { const code = this.safeCurrencyCode (this.safeString (balance, 'key')); account['total'] = this.safeString (balance, 'amount'); account['free'] = this.safeString (balance, 'canTransferOut'); account['used'] = this.safeString (balance, 'freeze'); result[code] = account; } } return this.safeBalance (result); } async fetchBalance (params = {}) { /** * @method * @name zb#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 zb api endpoint * @param {string} params.marginMode 'cross' or 'isolated' * @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure} */ await this.loadMarkets (); const [ marketType, marketTypeQuery ] = this.handleMarketTypeAndParams ('fetchBalance', undefined, params); const [ marginMode, query ] = this.handleMarginModeAndParams ('fetchBalance', marketTypeQuery); const swap = (marketType === 'swap'); const marginMethod = (marginMode === 'cross') ? 'spotV1PrivateGetGetCrossAssets' : 'spotV1PrivateGetGetLeverAssetsInfo'; let method = this.getSupportedMapping (marketTyp