sfccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
953 lines (943 loc) • 195 kB
JavaScript
'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