UNPKG

@madnai/ccxt

Version:

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

1,045 lines (1,033 loc) 117 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, InvalidAddress, RateLimitExceeded, PermissionDenied, NotSupported, BadRequest, BadSymbol, AccountSuspended, OrderImmediatelyFillable } = require ('./base/errors'); const { ROUND, TRUNCATE } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class binance extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'binance', 'name': 'Binance', 'countries': [ 'JP', 'MT' ], // Japan, Malta 'rateLimit': 500, 'certified': true, 'pro': true, // new metainfo interface 'has': { 'cancelAllOrders': true, 'cancelOrder': true, 'CORS': false, 'createOrder': true, 'fetchBalance': true, 'fetchBidsAsks': true, 'fetchClosedOrders': 'emulated', 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingFees': true, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOrderBook': true, 'fetchStatus': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': true, 'fetchTradingFees': true, 'fetchTransactions': false, 'fetchWithdrawals': true, 'withdraw': true, }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '6h': '6h', '8h': '8h', '12h': '12h', '1d': '1d', '3d': '3d', '1w': '1w', '1M': '1M', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg', 'test': { 'dapiPublic': 'https://testnet.binancefuture.com/dapi/v1', 'dapiPrivate': 'https://testnet.binancefuture.com/dapi/v1', 'fapiPublic': 'https://testnet.binancefuture.com/fapi/v1', 'fapiPrivate': 'https://testnet.binancefuture.com/fapi/v1', 'fapiPrivateV2': 'https://testnet.binancefuture.com/fapi/v2', 'public': 'https://testnet.binance.vision/api/v3', 'private': 'https://testnet.binance.vision/api/v3', 'v3': 'https://testnet.binance.vision/api/v3', 'v1': 'https://testnet.binance.vision/api/v1', }, 'api': { 'wapi': 'https://api.binance.com/wapi/v3', 'sapi': 'https://api.binance.com/sapi/v1', 'dapiPublic': 'https://dapi.binance.com/dapi/v1', 'dapiPrivate': 'https://dapi.binance.com/dapi/v1', 'dapiData': 'https://dapi.binance.com/futures/data', 'fapiPublic': 'https://fapi.binance.com/fapi/v1', 'fapiPrivate': 'https://fapi.binance.com/fapi/v1', 'fapiData': 'https://fapi.binance.com/futures/data', 'fapiPrivateV2': 'https://fapi.binance.com/fapi/v2', 'public': 'https://api.binance.com/api/v3', 'private': 'https://api.binance.com/api/v3', 'v3': 'https://api.binance.com/api/v3', 'v1': 'https://api.binance.com/api/v1', }, 'www': 'https://www.binance.com', 'referral': 'https://www.binance.com/?ref=10205187', 'doc': [ 'https://binance-docs.github.io/apidocs/spot/en', ], 'api_management': 'https://www.binance.com/en/usercenter/settings/api-management', 'fees': 'https://www.binance.com/en/fee/schedule', }, 'api': { // the API structure below will need 3-layer apidefs 'sapi': { 'get': [ 'accountSnapshot', // these endpoints require this.apiKey 'margin/asset', 'margin/pair', 'margin/allAssets', 'margin/allPairs', 'margin/priceIndex', // these endpoints require this.apiKey + this.secret 'asset/assetDividend', 'margin/loan', 'margin/repay', 'margin/account', 'margin/transfer', 'margin/interestHistory', 'margin/forceLiquidationRec', 'margin/order', 'margin/openOrders', 'margin/allOrders', 'margin/myTrades', 'margin/maxBorrowable', 'margin/maxTransferable', 'margin/isolated/transfer', 'margin/isolated/account', 'margin/isolated/pair', 'margin/isolated/allPairs', 'futures/transfer', 'futures/loan/borrow/history', 'futures/loan/repay/history', 'futures/loan/wallet', 'futures/loan/configs', 'futures/loan/calcAdjustLevel', 'futures/loan/calcMaxAdjustAmount', 'futures/loan/adjustCollateral/history', 'futures/loan/liquidationHistory', // https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi 'capital/config/getall', // get networks for withdrawing USDT ERC20 vs USDT Omni 'capital/deposit/address', 'capital/deposit/hisrec', 'capital/deposit/subAddress', 'capital/deposit/subHisrec', 'capital/withdraw/history', 'sub-account/futures/account', 'sub-account/futures/accountSummary', 'sub-account/futures/positionRisk', 'sub-account/futures/internalTransfer', 'sub-account/margin/account', 'sub-account/margin/accountSummary', 'sub-account/spotSummary', 'sub-account/status', 'sub-account/transfer/subUserHistory', // lending endpoints 'lending/daily/product/list', 'lending/daily/userLeftQuota', 'lending/daily/userRedemptionQuota', 'lending/daily/token/position', 'lending/union/account', 'lending/union/purchaseRecord', 'lending/union/redemptionRecord', 'lending/union/interestHistory', 'lending/project/list', 'lending/project/position/list', // mining endpoints 'mining/pub/algoList', 'mining/pub/coinList', 'mining/worker/detail', 'mining/worker/list', 'mining/payment/list', 'mining/statistics/user/status', 'mining/statistics/user/list', // liquid swap endpoints 'bswap/pools', 'bswap/liquidity', 'bswap/liquidityOps', 'bswap/quote', 'bswap/swap', ], 'post': [ 'asset/dust', 'account/disableFastWithdrawSwitch', 'account/enableFastWithdrawSwitch', 'capital/withdraw/apply', 'margin/transfer', 'margin/loan', 'margin/repay', 'margin/order', 'margin/isolated/create', 'margin/isolated/transfer', 'sub-account/margin/transfer', 'sub-account/margin/enable', 'sub-account/margin/enable', 'sub-account/futures/enable', 'sub-account/futures/transfer', 'sub-account/futures/internalTransfer', 'sub-account/transfer/subToSub', 'sub-account/transfer/subToMaster', 'userDataStream', 'userDataStream/isolated', 'futures/transfer', 'futures/loan/borrow', 'futures/loan/repay', 'futures/loan/adjustCollateral', // lending 'lending/customizedFixed/purchase', 'lending/daily/purchase', 'lending/daily/redeem', // liquid swap endpoints 'bswap/liquidityAdd', 'bswap/liquidityRemove', 'bswap/swap', ], 'put': [ 'userDataStream', 'userDataStream/isolated', ], 'delete': [ 'margin/order', 'userDataStream', 'userDataStream/isolated', ], }, 'wapi': { 'post': [ 'withdraw', 'sub-account/transfer', ], 'get': [ 'depositHistory', 'withdrawHistory', 'depositAddress', 'accountStatus', 'systemStatus', 'apiTradingStatus', 'userAssetDribbletLog', 'tradeFee', 'assetDetail', 'sub-account/list', 'sub-account/transfer/history', 'sub-account/assets', ], }, 'dapiPublic': { 'get': [ 'ping', 'time', 'exchangeInfo', 'depth', 'trades', 'historicalTrades', 'aggTrades', 'premiumIndex', 'fundingRate', 'klines', 'continuousKlines', 'indexPriceKlines', 'markPriceKlines', 'ticker/24hr', 'ticker/price', 'ticker/bookTicker', 'allForceOrders', 'openInterest', ], }, 'dapiData': { 'get': [ 'openInterestHist', 'topLongShortAccountRatio', 'topLongShortPositionRatio', 'globalLongShortAccountRatio', 'takerBuySellVol', 'basis', ], }, 'dapiPrivate': { 'get': [ 'positionSide/dual', 'order', 'openOrder', 'openOrders', 'allOrders', 'balance', 'account', 'positionMargin/history', 'positionRisk', 'userTrades', 'income', 'leverageBracket', 'forceOrders', 'adlQuantile', ], 'post': [ 'positionSide/dual', 'order', 'batchOrders', 'countdownCancelAll', 'leverage', 'marginType', 'positionMargin', 'listenKey', ], 'put': [ 'listenKey', ], 'delete': [ 'order', 'allOpenOrders', 'batchOrders', 'listenKey', ], }, 'fapiPublic': { 'get': [ 'ping', 'time', 'exchangeInfo', 'depth', 'trades', 'historicalTrades', 'aggTrades', 'klines', 'fundingRate', 'premiumIndex', 'ticker/24hr', 'ticker/price', 'ticker/bookTicker', 'allForceOrders', 'openInterest', ], }, 'fapiData': { 'get': [ 'openInterestHist', 'topLongShortAccountRatio', 'topLongShortPositionRatio', 'globalLongShortAccountRatio', 'takerlongshortRatio', ], }, 'fapiPrivate': { 'get': [ 'allForceOrders', 'allOrders', 'openOrder', 'openOrders', 'order', 'account', 'balance', 'leverageBracket', 'positionMargin/history', 'positionRisk', 'positionSide/dual', 'userTrades', 'income', ], 'post': [ 'batchOrders', 'positionSide/dual', 'positionMargin', 'marginType', 'order', 'leverage', 'listenKey', 'countdownCancelAll', ], 'put': [ 'listenKey', ], 'delete': [ 'batchOrders', 'order', 'allOpenOrders', 'listenKey', ], }, 'fapiPrivateV2': { 'get': [ 'account', 'balance', 'positionRisk', ], }, 'v3': { 'get': [ 'ticker/price', 'ticker/bookTicker', ], }, 'public': { 'get': [ 'ping', 'time', 'depth', 'trades', 'aggTrades', 'historicalTrades', 'klines', 'ticker/24hr', 'ticker/price', 'ticker/bookTicker', 'exchangeInfo', ], 'put': [ 'userDataStream' ], 'post': [ 'userDataStream' ], 'delete': [ 'userDataStream' ], }, 'private': { 'get': [ 'allOrderList', // oco 'openOrderList', // oco 'orderList', // oco 'order', 'openOrders', 'allOrders', 'account', 'myTrades', ], 'post': [ 'order/oco', 'order', 'order/test', ], 'delete': [ 'openOrders', // added on 2020-04-25 for canceling all open orders per symbol 'orderList', // oco 'order', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.001, 'maker': 0.001, }, }, 'commonCurrencies': { 'BCC': 'BCC', // kept for backward-compatibility https://github.com/ccxt/ccxt/issues/4848 'YOYO': 'YOYOW', }, // exchange-specific options 'options': { // 'fetchTradesMethod': 'publicGetAggTrades', // publicGetTrades, publicGetHistoricalTrades 'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel 'defaultType': 'spot', // 'spot', 'future', 'margin', 'delivery' 'hasAlreadyAuthenticatedSuccessfully': false, 'warnOnFetchOpenOrdersWithoutSymbol': true, 'recvWindow': 5 * 1000, // 5 sec, binance default 'timeDifference': 0, // the difference between system clock and Binance clock 'adjustForTimeDifference': false, // controls the adjustment logic upon instantiation 'parseOrderToPrecision': false, // force amounts and costs in parseOrder to precision 'newOrderRespType': { 'market': 'FULL', // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills 'limit': 'RESULT', // we change it from 'ACK' by default to 'RESULT' }, 'quoteOrderQty': true, // whether market orders support amounts in quote currency }, // https://binance-docs.github.io/apidocs/spot/en/#error-codes-2 'exceptions': { 'API key does not exist': AuthenticationError, 'Order would trigger immediately.': OrderImmediatelyFillable, 'Order would immediately match and take.': OrderImmediatelyFillable, // {"code":-2010,"msg":"Order would immediately match and take."} 'Account has insufficient balance for requested action.': InsufficientFunds, 'Rest API trading is not enabled.': ExchangeNotAvailable, "You don't have permission.": PermissionDenied, // {"msg":"You don't have permission.","success":false} 'Market is closed.': ExchangeNotAvailable, // {"code":-1013,"msg":"Market is closed."} 'Too many requests.': DDoSProtection, // {"msg":"Too many requests. Please try again later.","success":false} '-1000': ExchangeNotAvailable, // {"code":-1000,"msg":"An unknown error occured while processing the request."} '-1001': ExchangeNotAvailable, // 'Internal error; unable to process your request. Please try again.' '-1002': AuthenticationError, // 'You are not authorized to execute this request.' '-1003': RateLimitExceeded, // {"code":-1003,"msg":"Too much request weight used, current limit is 1200 request weight per 1 MINUTE. Please use the websocket for live updates to avoid polling the API."} '-1013': InvalidOrder, // createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL '-1015': RateLimitExceeded, // 'Too many new orders; current limit is %s orders per %s.' '-1016': ExchangeNotAvailable, // 'This service is no longer available.', '-1020': BadRequest, // 'This operation is not supported.' '-1021': InvalidNonce, // 'your time is ahead of server' '-1022': AuthenticationError, // {"code":-1022,"msg":"Signature for this request is not valid."} '-1100': BadRequest, // createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price' '-1101': BadRequest, // Too many parameters; expected %s and received %s. '-1102': BadRequest, // Param %s or %s must be sent, but both were empty '-1103': BadRequest, // An unknown parameter was sent. '-1104': BadRequest, // Not all sent parameters were read, read 8 parameters but was sent 9 '-1105': BadRequest, // Parameter %s was empty. '-1106': BadRequest, // Parameter %s sent when not required. '-1111': BadRequest, // Precision is over the maximum defined for this asset. '-1112': InvalidOrder, // No orders on book for symbol. '-1114': BadRequest, // TimeInForce parameter sent when not required. '-1115': BadRequest, // Invalid timeInForce. '-1116': BadRequest, // Invalid orderType. '-1117': BadRequest, // Invalid side. '-1118': BadRequest, // New client order ID was empty. '-1119': BadRequest, // Original client order ID was empty. '-1120': BadRequest, // Invalid interval. '-1121': BadSymbol, // Invalid symbol. '-1125': AuthenticationError, // This listenKey does not exist. '-1127': BadRequest, // More than %s hours between startTime and endTime. '-1128': BadRequest, // {"code":-1128,"msg":"Combination of optional parameters invalid."} '-1130': BadRequest, // Data sent for paramter %s is not valid. '-1131': BadRequest, // recvWindow must be less than 60000 '-2010': ExchangeError, // generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc... '-2011': OrderNotFound, // cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER' '-2013': OrderNotFound, // fetchOrder (1, 'BTC/USDT') -> 'Order does not exist' '-2014': AuthenticationError, // { "code":-2014, "msg": "API-key format invalid." } '-2015': AuthenticationError, // "Invalid API-key, IP, or permissions for action." '-3005': InsufficientFunds, // {"code":-3005,"msg":"Transferring out not allowed. Transfer out amount exceeds max amount."} '-3008': InsufficientFunds, // {"code":-3008,"msg":"Borrow not allowed. Your borrow amount has exceed maximum borrow amount."} '-3010': ExchangeError, // {"code":-3010,"msg":"Repay not allowed. Repay amount exceeds borrow amount."} '-3022': AccountSuspended, // You account's trading is banned. }, }); } nonce () { return this.milliseconds () - this.options['timeDifference']; } async fetchTime (params = {}) { const type = this.safeString2 (this.options, 'fetchTime', 'defaultType', 'spot'); let method = 'publicGetTime'; if (type === 'future') { method = 'fapiPublicGetTime'; } else if (type === 'delivery') { method = 'dapiPublicGetTime'; } const response = await this[method] (params); return this.safeInteger (response, 'serverTime'); } async loadTimeDifference (params = {}) { const serverTime = await this.fetchTime (params); const after = this.milliseconds (); this.options['timeDifference'] = after - serverTime; return this.options['timeDifference']; } async fetchMarkets (params = {}) { const defaultType = this.safeString2 (this.options, 'fetchMarkets', 'defaultType', 'spot'); const type = this.safeString (params, 'type', defaultType); const query = this.omit (params, 'type'); if ((type !== 'spot') && (type !== 'future') && (type !== 'margin') && (type !== 'delivery')) { throw new ExchangeError (this.id + " does not support '" + type + "' type, set exchange.options['defaultType'] to 'spot', 'margin', 'delivery' or 'future'"); // eslint-disable-line quotes } let method = 'publicGetExchangeInfo'; if (type === 'future') { method = 'fapiPublicGetExchangeInfo'; } else if (type === 'delivery') { method = 'dapiPublicGetExchangeInfo'; } const response = await this[method] (query); // // spot / margin // // { // "timezone":"UTC", // "serverTime":1575416692969, // "rateLimits":[ // {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200}, // {"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":100}, // {"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":200000} // ], // "exchangeFilters":[], // "symbols":[ // { // "symbol":"ETHBTC", // "status":"TRADING", // "baseAsset":"ETH", // "baseAssetPrecision":8, // "quoteAsset":"BTC", // "quotePrecision":8, // "baseCommissionPrecision":8, // "quoteCommissionPrecision":8, // "orderTypes":["LIMIT","LIMIT_MAKER","MARKET","STOP_LOSS_LIMIT","TAKE_PROFIT_LIMIT"], // "icebergAllowed":true, // "ocoAllowed":true, // "quoteOrderQtyMarketAllowed":true, // "isSpotTradingAllowed":true, // "isMarginTradingAllowed":true, // "filters":[ // {"filterType":"PRICE_FILTER","minPrice":"0.00000100","maxPrice":"100000.00000000","tickSize":"0.00000100"}, // {"filterType":"PERCENT_PRICE","multiplierUp":"5","multiplierDown":"0.2","avgPriceMins":5}, // {"filterType":"LOT_SIZE","minQty":"0.00100000","maxQty":"100000.00000000","stepSize":"0.00100000"}, // {"filterType":"MIN_NOTIONAL","minNotional":"0.00010000","applyToMarket":true,"avgPriceMins":5}, // {"filterType":"ICEBERG_PARTS","limit":10}, // {"filterType":"MARKET_LOT_SIZE","minQty":"0.00000000","maxQty":"63100.00000000","stepSize":"0.00000000"}, // {"filterType":"MAX_NUM_ALGO_ORDERS","maxNumAlgoOrders":5} // ] // }, // ], // } // // futures/usdt-margined (fapi) // // { // "timezone":"UTC", // "serverTime":1575417244353, // "rateLimits":[ // {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200}, // {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":1200} // ], // "exchangeFilters":[], // "symbols":[ // { // "symbol":"BTCUSDT", // "status":"TRADING", // "maintMarginPercent":"2.5000", // "requiredMarginPercent":"5.0000", // "baseAsset":"BTC", // "quoteAsset":"USDT", // "pricePrecision":2, // "quantityPrecision":3, // "baseAssetPrecision":8, // "quotePrecision":8, // "filters":[ // {"minPrice":"0.01","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.01"}, // {"stepSize":"0.001","filterType":"LOT_SIZE","maxQty":"1000","minQty":"0.001"}, // {"stepSize":"0.001","filterType":"MARKET_LOT_SIZE","maxQty":"1000","minQty":"0.001"}, // {"limit":200,"filterType":"MAX_NUM_ORDERS"}, // {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"} // ], // "orderTypes":["LIMIT","MARKET","STOP"], // "timeInForce":["GTC","IOC","FOK","GTX"] // } // ] // } // // delivery/coin-margined (dapi) // // { // "timezone": "UTC", // "serverTime": 1597667052958, // "rateLimits": [ // {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000}, // {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":6000} // ], // "exchangeFilters": [], // "symbols": [ // { // "symbol": "BTCUSD_200925", // "pair": "BTCUSD", // "contractType": "CURRENT_QUARTER", // "deliveryDate": 1601020800000, // "onboardDate": 1590739200000, // "contractStatus": "TRADING", // "contractSize": 100, // "marginAsset": "BTC", // "maintMarginPercent": "2.5000", // "requiredMarginPercent": "5.0000", // "baseAsset": "BTC", // "quoteAsset": "USD", // "pricePrecision": 1, // "quantityPrecision": 0, // "baseAssetPrecision": 8, // "quotePrecision": 8, // "equalQtyPrecision": 4, // "filters": [ // {"minPrice":"0.1","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.1"}, // {"stepSize":"1","filterType":"LOT_SIZE","maxQty":"100000","minQty":"1"}, // {"stepSize":"0","filterType":"MARKET_LOT_SIZE","maxQty":"100000","minQty":"1"}, // {"limit":200,"filterType":"MAX_NUM_ORDERS"}, // {"multiplierDown":"0.9500","multiplierUp":"1.0500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"} // ], // "orderTypes": ["LIMIT","MARKET","STOP","STOP_MARKET","TAKE_PROFIT","TAKE_PROFIT_MARKET","TRAILING_STOP_MARKET"], // "timeInForce": ["GTC","IOC","FOK","GTX"] // }, // { // "symbol": "BTCUSD_PERP", // "pair": "BTCUSD", // "contractType": "PERPETUAL", // "deliveryDate": 4133404800000, // "onboardDate": 1596006000000, // "contractStatus": "TRADING", // "contractSize": 100, // "marginAsset": "BTC", // "maintMarginPercent": "2.5000", // "requiredMarginPercent": "5.0000", // "baseAsset": "BTC", // "quoteAsset": "USD", // "pricePrecision": 1, // "quantityPrecision": 0, // "baseAssetPrecision": 8, // "quotePrecision": 8, // "equalQtyPrecision": 4, // "filters": [ // {"minPrice":"0.1","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.1"}, // {"stepSize":"1","filterType":"LOT_SIZE","maxQty":"100000","minQty":"1"}, // {"stepSize":"1","filterType":"MARKET_LOT_SIZE","maxQty":"100000","minQty":"1"}, // {"limit":200,"filterType":"MAX_NUM_ORDERS"}, // {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"} // ], // "orderTypes": ["LIMIT","MARKET","STOP","STOP_MARKET","TAKE_PROFIT","TAKE_PROFIT_MARKET","TRAILING_STOP_MARKET"], // "timeInForce": ["GTC","IOC","FOK","GTX"] // } // ] // } // if (this.options['adjustForTimeDifference']) { await this.loadTimeDifference (); } const markets = this.safeValue (response, 'symbols'); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; let marketType = 'spot'; let future = false; let delivery = false; if ('maintMarginPercent' in market) { delivery = ('deliveryDate' in market); future = !delivery; marketType = delivery ? 'delivery' : 'future'; } const spot = !(future || delivery); const id = this.safeString (market, 'symbol'); const lowercaseId = this.safeStringLower (market, 'symbol'); const baseId = this.safeString (market, 'baseAsset'); const quoteId = this.safeString (market, 'quoteAsset'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = delivery ? id : (base + '/' + quote); const filters = this.safeValue (market, 'filters', []); const filtersByType = this.indexBy (filters, 'filterType'); const precision = { 'base': this.safeInteger (market, 'baseAssetPrecision'), 'quote': this.safeInteger (market, 'quotePrecision'), 'amount': this.safeInteger (market, 'baseAssetPrecision'), 'price': this.safeInteger (market, 'quotePrecision'), }; const status = this.safeString2 (market, 'status', 'contractStatus'); const active = (status === 'TRADING'); const margin = this.safeValue (market, 'isMarginTradingAllowed', future || delivery); const entry = { 'id': id, 'lowercaseId': lowercaseId, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'type': marketType, 'spot': spot, 'margin': margin, 'future': future, 'delivery': delivery, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, }; if ('PRICE_FILTER' in filtersByType) { const filter = this.safeValue (filtersByType, 'PRICE_FILTER', {}); // PRICE_FILTER reports zero values for maxPrice // since they updated filter types in November 2018 // https://github.com/ccxt/ccxt/issues/4286 // therefore limits['price']['max'] doesn't have any meaningful value except undefined entry['limits']['price'] = { 'min': this.safeFloat (filter, 'minPrice'), 'max': undefined, }; const maxPrice = this.safeFloat (filter, 'maxPrice'); if ((maxPrice !== undefined) && (maxPrice > 0)) { entry['limits']['price']['max'] = maxPrice; } entry['precision']['price'] = this.precisionFromString (filter['tickSize']); } if ('LOT_SIZE' in filtersByType) { const filter = this.safeValue (filtersByType, 'LOT_SIZE', {}); const stepSize = this.safeString (filter, 'stepSize'); entry['precision']['amount'] = this.precisionFromString (stepSize); entry['limits']['amount'] = { 'min': this.safeFloat (filter, 'minQty'), 'max': this.safeFloat (filter, 'maxQty'), }; } if ('MARKET_LOT_SIZE' in filtersByType) { const filter = this.safeValue (filtersByType, 'MARKET_LOT_SIZE', {}); entry['limits']['market'] = { 'min': this.safeFloat (filter, 'minQty'), 'max': this.safeFloat (filter, 'maxQty'), }; } if ('MIN_NOTIONAL' in filtersByType) { const filter = this.safeValue (filtersByType, 'MIN_NOTIONAL', {}); entry['limits']['cost']['min'] = this.safeFloat (filter, 'minNotional'); } result.push (entry); } return result; } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; let key = 'quote'; const rate = market[takerOrMaker]; let cost = amount * rate; let precision = market['precision']['price']; if (side === 'sell') { cost *= price; } else { key = 'base'; precision = market['precision']['amount']; } cost = this.decimalToPrecision (cost, ROUND, precision, this.precisionMode); return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': parseFloat (cost), }; } async fetchBalance (params = {}) { await this.loadMarkets (); const defaultType = this.safeString2 (this.options, 'fetchBalance', 'defaultType', 'spot'); const type = this.safeString (params, 'type', defaultType); let method = 'privateGetAccount'; if (type === 'future') { const options = this.safeValue (this.options, 'future', {}); const fetchBalanceOptions = this.safeValue (options, 'fetchBalance', {}); method = this.safeString (fetchBalanceOptions, 'method', 'fapiPrivateV2GetAccount'); } else if (type === 'delivery') { const options = this.safeValue (this.options, 'delivery', {}); const fetchBalanceOptions = this.safeValue (options, 'fetchBalance', {}); method = this.safeString (fetchBalanceOptions, 'method', 'dapiPrivateGetAccount'); } else if (type === 'margin') { method = 'sapiGetMarginAccount'; } const query = this.omit (params, 'type'); const response = await this[method] (query); // // spot // // { // makerCommission: 10, // takerCommission: 10, // buyerCommission: 0, // sellerCommission: 0, // canTrade: true, // canWithdraw: true, // canDeposit: true, // updateTime: 1575357359602, // accountType: "MARGIN", // balances: [ // { asset: "BTC", free: "0.00219821", locked: "0.00000000" }, // ] // } // // margin // // { // "borrowEnabled":true, // "marginLevel":"999.00000000", // "totalAssetOfBtc":"0.00000000", // "totalLiabilityOfBtc":"0.00000000", // "totalNetAssetOfBtc":"0.00000000", // "tradeEnabled":true, // "transferEnabled":true, // "userAssets":[ // {"asset":"MATIC","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"}, // {"asset":"VET","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"}, // {"asset":"USDT","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"} // ], // } // // futures (fapi) // // fapiPrivateGetAccount // // { // "feeTier":0, // "canTrade":true, // "canDeposit":true, // "canWithdraw":true, // "updateTime":0, // "totalInitialMargin":"0.00000000", // "totalMaintMargin":"0.00000000", // "totalWalletBalance":"4.54000000", // "totalUnrealizedProfit":"0.00000000", // "totalMarginBalance":"4.54000000", // "totalPositionInitialMargin":"0.00000000", // "totalOpenOrderInitialMargin":"0.00000000", // "maxWithdrawAmount":"4.54000000", // "assets":[ // { // "asset":"USDT", // "walletBalance":"4.54000000", // "unrealizedProfit":"0.00000000", // "marginBalance":"4.54000000", // "maintMargin":"0.00000000", // "initialMargin":"0.00000000", // "positionInitialMargin":"0.00000000", // "openOrderInitialMargin":"0.00000000", // "maxWithdrawAmount":"4.54000000" // } // ], // "positions":[ // { // "symbol":"BTCUSDT", // "initialMargin":"0.00000", // "maintMargin":"0.00000", // "unrealizedProfit":"0.00000000", // "positionInitialMargin":"0.00000", // "openOrderInitialMargin":"0.00000" // } // ] // } // // fapiPrivateV2GetAccount // // { // "feeTier":0, // "canTrade":true, // "canDeposit":true, // "canWithdraw":true, // "updateTime":0, // "totalInitialMargin":"0.00000000", // "totalMaintMargin":"0.00000000", // "totalWalletBalance":"0.00000000", // "totalUnrealizedProfit":"0.00000000", // "totalMarginBalance":"0.00000000", // "totalPositionInitialMargin":"0.00000000", // "totalOpenOrderInitialMargin":"0.00000000", // "totalCrossWalletBalance":"0.00000000", // "totalCrossUnPnl":"0.00000000", // "availableBalance":"0.00000000", // "maxWithdrawAmount":"0.00000000", // "assets":[ // { // "asset":"BNB", // "walletBalance":"0.01000000", // "unrealizedProfit":"0.00000000", // "marginBalance":"0.01000000", // "maintMargin":"0.00000000", // "initialMargin":"0.00000000", // "positionInitialMargin":"0.00000000", // "openOrderInitialMargin":"0.00000000", // "maxWithdrawAmount":"0.01000000", // "crossWalletBalance":"0.01000000", // "crossUnPnl":"0.00000000", // "availableBalance":"0.01000000" // } // ], // "positions":[ // { // "symbol":"BTCUSDT", // "initialMargin":"0", // "maintMargin":"0", // "unrealizedProfit":"0.00000000", // "positionInitialMargin":"0", // "openOrderInitialMargin":"0", // "leverage":"20", // "isolated":false, // "entryPrice":"0.00000", // "maxNotional":"5000000", // "positionSide":"BOTH" // }, // ] // } // // fapiPrivateV2GetBalance // // [ // { // "accountAlias":"FzFzXquXXqoC", // "asset":"BNB", // "balance":"0.01000000", // "crossWalletBalance":"0.01000000", // "crossUnPnl":"0.00000000", // "availableBalance":"0.01000000", // "maxWithdrawAmount":"0.01000000" // } // ] // const result = { 'info': response }; if ((type === 'spot') || (type === 'margin')) { const balances = this.safeValue2 (response, 'balances', 'userAssets', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'asset'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'free'); account['used'] = this.safeFloat (balance, 'locked'); result[code] = account; } } else { let balances = response; if (!Array.isArray (response)) { balances = this.safeValue (response, 'assets', []); } for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'asset'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'availableBalance'); account['used'] = this.safeFloat (balance, 'initialMargin'); account['total'] = this.safeFloat2 (balance, 'marginBalance', 'balance'); result[code] = account; } } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default 100, max 5000, see https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#order-book } let method = 'publicGetDepth'; if (market['future']) { method = 'fapiPublicGetDepth'; } else if (market['delivery']) { method = 'dapiPublicGetDepth'; } const response = await this[method] (this.extend (request, params)); const orderbook = this.parseOrderBook (response); orderbook['nonce'] = this.safeInteger (response, 'lastUpdateId'); return orderbook; } parseTicker (ticker, market = undefined) { // // { //