UNPKG

ccxt-look

Version:

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

1,056 lines (1,036 loc) 119 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, AccountSuspended, InvalidNonce, NotSupported, BadRequest, AuthenticationError, BadSymbol, RateLimitExceeded, PermissionDenied, InvalidAddress } = require ('./base/errors'); const Precise = require ('./base/Precise'); // --------------------------------------------------------------------------- module.exports = class kucoin extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'kucoin', 'name': 'KuCoin', 'countries': [ 'SC' ], // note "only some endpoints are rate-limited" // so I set the 'ratelimit' on those which supposedly 'arent ratelimited' // to the limit of the cheapest endpoint // 60 requests in 3 seconds = 20 requests per second => ( 1000ms / 20 ) = 50 ms between requests on average 'rateLimit': 50, 'version': 'v2', 'certified': false, 'pro': true, 'comment': 'Platform 2.0', 'quoteJsonNumbers': false, 'has': { 'CORS': undefined, 'spot': true, 'margin': undefined, 'swap': false, 'future': false, 'option': undefined, 'cancelAllOrders': true, 'cancelOrder': true, 'createDepositAddress': true, 'createOrder': true, 'createStopLimitOrder': true, 'createStopMarketOrder': true, 'createStopOrder': true, 'fetchAccounts': true, 'fetchBalance': true, 'fetchBorrowRate': false, 'fetchBorrowRates': false, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingFee': true, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchL3OrderBook': true, 'fetchLedger': true, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrdersByStatus': true, 'fetchPremiumIndexOHLCV': false, 'fetchStatus': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': true, 'fetchTradingFees': false, 'fetchWithdrawals': true, 'transfer': true, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/51840849/87295558-132aaf80-c50e-11ea-9801-a2fb0c57c799.jpg', 'referral': 'https://www.kucoin.com/?rcode=E5wkqe', 'api': { 'public': 'https://api.kucoin.com', 'private': 'https://api.kucoin.com', 'futuresPrivate': 'https://api-futures.kucoin.com', 'futuresPublic': 'https://api-futures.kucoin.com', }, 'test': { 'public': 'https://openapi-sandbox.kucoin.com', 'private': 'https://openapi-sandbox.kucoin.com', 'futuresPrivate': 'https://api-sandbox-futures.kucoin.com', 'futuresPublic': 'https://api-sandbox-futures.kucoin.com', }, 'www': 'https://www.kucoin.com', 'doc': [ 'https://docs.kucoin.com', ], }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'password': true, }, 'api': { 'public': { 'get': { 'timestamp': 1, 'status': 1, 'symbols': 1, 'markets': 1, 'market/allTickers': 1, 'market/orderbook/level{level}_{limit}': 1, 'market/orderbook/level2_20': 1, 'market/orderbook/level2_100': 1, 'market/histories': 1, 'market/candles': 1, 'market/stats': 1, 'currencies': 1, 'currencies/{currency}': 1, 'prices': 1, 'mark-price/{symbol}/current': 1, 'margin/config': 1, }, 'post': { 'bullet-public': 1, }, }, 'private': { 'get': { 'market/orderbook/level{level}': 1, 'market/orderbook/level2': { 'v3': 2 }, // 30/3s = 10/s => cost = 20 / 10 = 2 'market/orderbook/level3': 1, 'accounts': 1, 'accounts/{accountId}': 1, // 'accounts/{accountId}/ledgers': 1, Deprecated endpoint 'accounts/ledgers': 3.333, // 18/3s = 6/s => cost = 20 / 6 = 3.333 'accounts/{accountId}/holds': 1, 'accounts/transferable': 1, 'base-fee': 1, 'sub/user': 1, 'sub-accounts': 1, 'sub-accounts/{subUserId}': 1, 'deposit-addresses': 1, 'deposits': 10, // 6/3s = 2/s => cost = 20 / 2 = 10 'hist-deposits': 10, // 6/3 = 2/s => cost = 20 / 2 = 10 'hist-orders': 1, 'hist-withdrawals': 10, // 6/3 = 2/s => cost = 20 / 2 = 10 'withdrawals': 10, // 6/3 = 2/s => cost = 20 / 2 = 10 'withdrawals/quotas': 1, 'orders': 2, // 30/3s = 10/s => cost = 20 / 10 = 2 'order/client-order/{clientOid}': 1, 'orders/{orderId}': 1, 'limit/orders': 1, 'fills': 6.66667, // 9/3s = 3/s => cost = 20 / 3 = 6.666667 'limit/fills': 1, 'margin/account': 1, 'margin/borrow': 1, 'margin/borrow/outstanding': 1, 'margin/borrow/borrow/repaid': 1, 'margin/lend/active': 1, 'margin/lend/done': 1, 'margin/lend/trade/unsettled': 1, 'margin/lend/trade/settled': 1, 'margin/lend/assets': 1, 'margin/market': 1, 'margin/trade/last': 1, 'stop-order/{orderId}': 1, 'stop-order': 1, 'stop-order/queryOrderByClientOid': 1, 'trade-fees': 1.3333, // 45/3s = 15/s => cost = 20 / 15 = 1.333 }, 'post': { 'accounts': 1, 'accounts/inner-transfer': { 'v2': 1 }, 'accounts/sub-transfer': { 'v2': 25 }, // bad docs 'deposit-addresses': 1, 'withdrawals': 1, 'orders': 4, // 45/3s = 15/s => cost = 20 / 15 = 1.333333 'orders/multi': 20, // 3/3s = 1/s => cost = 20 / 1 = 20 'margin/borrow': 1, 'margin/order': 1, 'margin/repay/all': 1, 'margin/repay/single': 1, 'margin/lend': 1, 'margin/toggle-auto-lend': 1, 'bullet-private': 1, 'stop-order': 1, }, 'delete': { 'withdrawals/{withdrawalId}': 1, 'orders': 20, // 3/3s = 1/s => cost = 20/1 'orders/client-order/{clientOid}': 1, 'orders/{orderId}': 1, // rateLimit: 60/3s = 20/s => cost = 1 'margin/lend/{orderId}': 1, 'stop-order/cancelOrderByClientOid': 1, 'stop-order/{orderId}': 1, 'stop-order/cancel': 1, }, }, 'futuresPublic': { // cheapest futures 'limited' endpoint is 40 requests per 3 seconds = 14.333 per second => cost = 20/14.333 = 1.3953 'get': { 'contracts/active': 1.3953, 'contracts/{symbol}': 1.3953, 'ticker': 1.3953, 'level2/snapshot': 2, // 30 requests per 3 seconds = 10 requests per second => cost = 20/10 = 2 'level2/depth20': 1.3953, 'level2/depth100': 1.3953, 'level2/message/query': 1.3953, 'level3/message/query': 1.3953, // deprecated,level3/snapshot is suggested 'level3/snapshot': 1.3953, // v2 'trade/history': 1.3953, 'interest/query': 1.3953, 'index/query': 1.3953, 'mark-price/{symbol}/current': 1.3953, 'premium/query': 1.3953, 'funding-rate/{symbol}/current': 1.3953, 'timestamp': 1.3953, 'status': 1.3953, 'kline/query': 1.3953, }, 'post': { 'bullet-public': 1.3953, }, }, 'futuresPrivate': { 'get': { 'account-overview': 2, // 30 requests per 3 seconds = 10 per second => cost = 20/10 = 2 'transaction-history': 6.666, // 9 requests per 3 seconds = 3 per second => cost = 20/3 = 6.666 'deposit-address': 1.3953, 'deposit-list': 1.3953, 'withdrawals/quotas': 1.3953, 'withdrawal-list': 1.3953, 'transfer-list': 1.3953, 'orders': 1.3953, 'stopOrders': 1.3953, 'recentDoneOrders': 1.3953, 'orders/{order-id}': 1.3953, // ?clientOid={client-order-id} // get order by orderId 'orders/byClientOid': 1.3953, // ?clientOid=eresc138b21023a909e5ad59 // get order by clientOid 'fills': 6.666, // 9 requests per 3 seconds = 3 per second => cost = 20/3 = 6.666 'recentFills': 6.666, // 9 requests per 3 seconds = 3 per second => cost = 20/3 = 6.666 'openOrderStatistics': 1.3953, 'position': 1.3953, 'positions': 6.666, // 9 requests per 3 seconds = 3 per second => cost = 20/3 = 6.666 'funding-history': 6.666, // 9 requests per 3 seconds = 3 per second => cost = 20/3 = 6.666 }, 'post': { 'withdrawals': 1.3953, 'transfer-out': 1.3953, // v2 'orders': 1.3953, 'position/margin/auto-deposit-status': 1.3953, 'position/margin/deposit-margin': 1.3953, 'bullet-private': 1.3953, }, 'delete': { 'withdrawals/{withdrawalId}': 1.3953, 'cancel/transfer-out': 1.3953, 'orders/{order-id}': 1.3953, // 40 requests per 3 seconds = 14.333 per second => cost = 20/14.333 = 1.395 'orders': 6.666, // 9 requests per 3 seconds = 3 per second => cost = 20/3 = 6.666 'stopOrders': 1.3953, }, }, }, 'timeframes': { '1m': '1min', '3m': '3min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1hour', '2h': '2hour', '4h': '4hour', '6h': '6hour', '8h': '8hour', '12h': '12hour', '1d': '1day', '1w': '1week', }, 'exceptions': { 'exact': { 'order not exist': OrderNotFound, 'order not exist.': OrderNotFound, // duplicated error temporarily 'order_not_exist': OrderNotFound, // {"code":"order_not_exist","msg":"order_not_exist"} ¯\_(ツ)_/¯ 'order_not_exist_or_not_allow_to_cancel': InvalidOrder, // {"code":"400100","msg":"order_not_exist_or_not_allow_to_cancel"} 'Order size below the minimum requirement.': InvalidOrder, // {"code":"400100","msg":"Order size below the minimum requirement."} 'The withdrawal amount is below the minimum requirement.': ExchangeError, // {"code":"400100","msg":"The withdrawal amount is below the minimum requirement."} 'Unsuccessful! Exceeded the max. funds out-transfer limit': InsufficientFunds, // {"code":"200000","msg":"Unsuccessful! Exceeded the max. funds out-transfer limit"} '400': BadRequest, '401': AuthenticationError, '403': NotSupported, '404': NotSupported, '405': NotSupported, '429': RateLimitExceeded, '500': ExchangeNotAvailable, // Internal Server Error -- We had a problem with our server. Try again later. '503': ExchangeNotAvailable, '101030': PermissionDenied, // {"code":"101030","msg":"You haven't yet enabled the margin trading"} '200004': InsufficientFunds, '230003': InsufficientFunds, // {"code":"230003","msg":"Balance insufficient!"} '260100': InsufficientFunds, // {"code":"260100","msg":"account.noBalance"} '300000': InvalidOrder, '400000': BadSymbol, '400001': AuthenticationError, '400002': InvalidNonce, '400003': AuthenticationError, '400004': AuthenticationError, '400005': AuthenticationError, '400006': AuthenticationError, '400007': AuthenticationError, '400008': NotSupported, '400100': BadRequest, '400200': InvalidOrder, // {"code":"400200","msg":"Forbidden to place an order"} '400350': InvalidOrder, // {"code":"400350","msg":"Upper limit for holding: 10,000USDT, you can still buy 10,000USDT worth of coin."} '400370': InvalidOrder, // {"code":"400370","msg":"Max. price: 0.02500000000000000000"} '400500': InvalidOrder, // {"code":"400500","msg":"Your located country/region is currently not supported for the trading of this token"} '400600': BadSymbol, // {"code":"400600","msg":"validation.createOrder.symbolNotAvailable"} '401000': BadRequest, // {"code":"401000","msg":"The interface has been deprecated"} '411100': AccountSuspended, '415000': BadRequest, // {"code":"415000","msg":"Unsupported Media Type"} '500000': ExchangeNotAvailable, // {"code":"500000","msg":"Internal Server Error"} '260220': InvalidAddress, // { "code": "260220", "msg": "deposit.address.not.exists" } }, 'broad': { 'Exceeded the access frequency': RateLimitExceeded, 'require more permission': PermissionDenied, }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': this.parseNumber ('0.001'), 'maker': this.parseNumber ('0.001'), 'tiers': { 'taker': [ [ this.parseNumber ('0'), this.parseNumber ('0.001') ], [ this.parseNumber ('50'), this.parseNumber ('0.001') ], [ this.parseNumber ('200'), this.parseNumber ('0.0009') ], [ this.parseNumber ('500'), this.parseNumber ('0.0008') ], [ this.parseNumber ('1000'), this.parseNumber ('0.0007') ], [ this.parseNumber ('2000'), this.parseNumber ('0.0007') ], [ this.parseNumber ('4000'), this.parseNumber ('0.0006') ], [ this.parseNumber ('8000'), this.parseNumber ('0.0005') ], [ this.parseNumber ('15000'), this.parseNumber ('0.00045') ], [ this.parseNumber ('25000'), this.parseNumber ('0.0004') ], [ this.parseNumber ('40000'), this.parseNumber ('0.00035') ], [ this.parseNumber ('60000'), this.parseNumber ('0.0003') ], [ this.parseNumber ('80000'), this.parseNumber ('0.00025') ], ], 'maker': [ [ this.parseNumber ('0'), this.parseNumber ('0.001') ], [ this.parseNumber ('50'), this.parseNumber ('0.0009') ], [ this.parseNumber ('200'), this.parseNumber ('0.0007') ], [ this.parseNumber ('500'), this.parseNumber ('0.0005') ], [ this.parseNumber ('1000'), this.parseNumber ('0.0003') ], [ this.parseNumber ('2000'), this.parseNumber ('0') ], [ this.parseNumber ('4000'), this.parseNumber ('0') ], [ this.parseNumber ('8000'), this.parseNumber ('0') ], [ this.parseNumber ('15000'), this.parseNumber ('-0.00005') ], [ this.parseNumber ('25000'), this.parseNumber ('-0.00005') ], [ this.parseNumber ('40000'), this.parseNumber ('-0.00005') ], [ this.parseNumber ('60000'), this.parseNumber ('-0.00005') ], [ this.parseNumber ('80000'), this.parseNumber ('-0.00005') ], ], }, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': {}, 'deposit': {}, }, }, 'commonCurrencies': { 'HOT': 'HOTNOW', 'EDGE': 'DADI', // https://github.com/ccxt/ccxt/issues/5756 'WAX': 'WAXP', 'TRY': 'Trias', 'VAI': 'VAIOT', }, 'options': { 'version': 'v1', 'symbolSeparator': '-', 'fetchMyTradesMethod': 'private_get_fills', 'fetchBalance': 'trade', 'fetchMarkets': { 'fetchTickersFees': true, }, // endpoint versions 'versions': { 'public': { 'GET': { 'status': 'v1', 'market/orderbook/level2_20': 'v1', 'market/orderbook/level2_100': 'v1', 'market/orderbook/level{level}_{limit}': 'v1', }, }, 'private': { 'GET': { 'market/orderbook/level2': 'v3', 'market/orderbook/level3': 'v3', 'market/orderbook/level{level}': 'v3', }, 'POST': { 'accounts/inner-transfer': 'v2', 'accounts/sub-transfer': 'v2', }, }, 'futuresPrivate': { 'GET': { 'account-overview': 'v1', 'positions': 'v1', }, 'POST': { 'transfer-out': 'v2', }, }, 'futuresPublic': { 'GET': { 'level3/snapshot': 'v2', }, }, }, 'accountsByType': { 'spot': 'trade', 'margin': 'margin', 'main': 'main', 'funding': 'main', 'future': 'contract', 'mining': 'pool', }, 'networks': { 'ETH': 'eth', 'ERC20': 'eth', 'TRX': 'trx', 'TRC20': 'trx', 'KCC': 'kcc', 'TERRA': 'luna', }, }, }); } nonce () { return this.milliseconds (); } async fetchTime (params = {}) { const response = await this.publicGetTimestamp (params); // // { // "code":"200000", // "msg":"success", // "data":1546837113087 // } // return this.safeInteger (response, 'data'); } async fetchStatus (params = {}) { const response = await this.publicGetStatus (params); // // { // "code":"200000", // "data":{ // "status":"open", //open, close, cancelonly // "msg":"upgrade match engine" //remark for operation // } // } // const data = this.safeValue (response, 'data', {}); const status = this.safeString (data, 'status'); return { 'status': (status === 'open') ? 'ok' : 'maintenance', 'updated': this.milliseconds (), 'eta': undefined, 'url': undefined, 'info': response, }; } async fetchMarkets (params = {}) { const response = await this.publicGetSymbols (params); // // { // "code": "200000", // "data": [ // { // "symbol": "XLM-USDT", // "name": "XLM-USDT", // "baseCurrency": "XLM", // "quoteCurrency": "USDT", // "feeCurrency": "USDT", // "market": "USDS", // "baseMinSize": "0.1", // "quoteMinSize": "0.01", // "baseMaxSize": "10000000000", // "quoteMaxSize": "99999999", // "baseIncrement": "0.0001", // "quoteIncrement": "0.000001", // "priceIncrement": "0.000001", // "priceLimitRate": "0.1", // "isMarginEnabled": true, // "enableTrading": true // }, // ] // } // const data = this.safeValue (response, 'data'); const options = this.safeValue (this.options, 'fetchMarkets', {}); const fetchTickersFees = this.safeValue (options, 'fetchTickersFees', true); let tickersResponse = {}; if (fetchTickersFees) { tickersResponse = await this.publicGetMarketAllTickers (params); } // // { // "code": "200000", // "data": { // "time":1602832092060, // "ticker":[ // { // "symbol": "BTC-USDT", // symbol // "symbolName":"BTC-USDT", // Name of trading pairs, it would change after renaming // "buy": "11328.9", // bestAsk // "sell": "11329", // bestBid // "changeRate": "-0.0055", // 24h change rate // "changePrice": "-63.6", // 24h change price // "high": "11610", // 24h highest price // "low": "11200", // 24h lowest price // "vol": "2282.70993217", // 24h volume,the aggregated trading volume in BTC // "volValue": "25984946.157790431", // 24h total, the trading volume in quote currency of last 24 hours // "last": "11328.9", // last price // "averagePrice": "11360.66065903", // 24h average transaction price yesterday // "takerFeeRate": "0.001", // Basic Taker Fee // "makerFeeRate": "0.001", // Basic Maker Fee // "takerCoefficient": "1", // Taker Fee Coefficient // "makerCoefficient": "1" // Maker Fee Coefficient // } // ] // } // } // const tickersData = this.safeValue (tickersResponse, 'data', {}); const tickers = this.safeValue (tickersData, 'ticker', []); const tickersByMarketId = this.indexBy (tickers, 'symbol'); const result = []; for (let i = 0; i < data.length; i++) { const market = data[i]; const id = this.safeString (market, 'symbol'); const [ baseId, quoteId ] = id.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const baseMaxSize = this.safeNumber (market, 'baseMaxSize'); const baseMinSizeString = this.safeString (market, 'baseMinSize'); const quoteMaxSizeString = this.safeString (market, 'quoteMaxSize'); const baseMinSize = this.parseNumber (baseMinSizeString); const quoteMaxSize = this.parseNumber (quoteMaxSizeString); const quoteMinSize = this.safeNumber (market, 'quoteMinSize'); // const quoteIncrement = this.safeNumber (market, 'quoteIncrement'); const ticker = this.safeValue (tickersByMarketId, id, {}); const makerFeeRate = this.safeString (ticker, 'makerFeeRate'); const takerFeeRate = this.safeString (ticker, 'makerFeeRate'); const makerCoefficient = this.safeString (ticker, 'makerCoefficient'); const takerCoefficient = this.safeString (ticker, 'takerCoefficient'); result.push ({ 'id': id, 'symbol': base + '/' + quote, 'base': base, 'quote': quote, 'settle': undefined, 'baseId': baseId, 'quoteId': quoteId, 'settleId': undefined, 'type': 'spot', 'spot': true, 'margin': this.safeValue (market, 'isMarginEnabled'), 'swap': false, 'future': false, 'option': false, 'active': this.safeValue (market, 'enableTrading'), 'contract': false, 'linear': undefined, 'inverse': undefined, 'taker': this.parseNumber (Precise.stringMul (takerFeeRate, takerCoefficient)), 'maker': this.parseNumber (Precise.stringMul (makerFeeRate, makerCoefficient)), 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.precisionFromString (this.safeString (market, 'baseIncrement')), 'price': this.precisionFromString (this.safeString (market, 'priceIncrement')), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': baseMinSize, 'max': baseMaxSize, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': quoteMinSize, 'max': quoteMaxSize, }, }, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrencies (params); // // { // "currency": "OMG", // "name": "OMG", // "fullName": "OmiseGO", // "precision": 8, // "confirms": 12, // "withdrawalMinSize": "4", // "withdrawalMinFee": "1.25", // "isWithdrawEnabled": false, // "isDepositEnabled": false, // "isMarginEnabled": false, // "isDebitEnabled": false // } // const data = this.safeValue (response, 'data', []); const result = {}; for (let i = 0; i < data.length; i++) { const entry = data[i]; const id = this.safeString (entry, 'currency'); const name = this.safeString (entry, 'fullName'); const code = this.safeCurrencyCode (id); const precision = this.safeInteger (entry, 'precision'); const isWithdrawEnabled = this.safeValue (entry, 'isWithdrawEnabled', false); const isDepositEnabled = this.safeValue (entry, 'isDepositEnabled', false); const fee = this.safeNumber (entry, 'withdrawalMinFee'); const active = (isWithdrawEnabled && isDepositEnabled); result[code] = { 'id': id, 'name': name, 'code': code, 'precision': precision, 'info': entry, 'active': active, 'deposit': isDepositEnabled, 'withdraw': isWithdrawEnabled, 'fee': fee, 'limits': this.limits, }; } return result; } async fetchAccounts (params = {}) { const response = await this.privateGetAccounts (params); // // { // code: "200000", // data: [ // { // balance: "0.00009788", // available: "0.00009788", // holds: "0", // currency: "BTC", // id: "5c6a4fd399a1d81c4f9cc4d0", // type: "trade" // }, // { // balance: "0.00000001", // available: "0.00000001", // holds: "0", // currency: "ETH", // id: "5c6a49ec99a1d819392e8e9f", // type: "trade" // } // ] // } // const data = this.safeValue (response, 'data'); const result = []; for (let i = 0; i < data.length; i++) { const account = data[i]; const accountId = this.safeString (account, 'id'); const currencyId = this.safeString (account, 'currency'); const code = this.safeCurrencyCode (currencyId); const type = this.safeString (account, 'type'); // main or trade result.push ({ 'id': accountId, 'type': type, 'currency': code, 'info': account, }); } return result; } async fetchFundingFee (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privateGetWithdrawalsQuotas (this.extend (request, params)); const data = response['data']; const withdrawFees = {}; withdrawFees[code] = this.safeNumber (data, 'withdrawMinFee'); return { 'info': response, 'withdraw': withdrawFees, 'deposit': {}, }; } isFuturesMethod (methodName, params) { // // Helper // @methodName (string): The name of the method // @params (dict): The parameters passed into {methodName} // @return: true if the method used is meant for futures trading, false otherwise // const defaultType = this.safeString2 (this.options, methodName, 'defaultType', 'trade'); const requestedType = this.safeString (params, 'type', defaultType); const accountsByType = this.safeValue (this.options, 'accountsByType'); const type = this.safeString (accountsByType, requestedType); if (type === undefined) { const keys = Object.keys (accountsByType); throw new ExchangeError (this.id + ' isFuturesMethod() type must be one of ' + keys.join (', ')); } params = this.omit (params, 'type'); return (type === 'contract') || (type === 'future') || (type === 'futures'); // * (type === 'futures') deprecated, use (type === 'future') } parseTicker (ticker, market = undefined) { // // { // "symbol": "BTC-USDT", // symbol // "symbolName":"BTC-USDT", // Name of trading pairs, it would change after renaming // "buy": "11328.9", // bestAsk // "sell": "11329", // bestBid // "changeRate": "-0.0055", // 24h change rate // "changePrice": "-63.6", // 24h change price // "high": "11610", // 24h highest price // "low": "11200", // 24h lowest price // "vol": "2282.70993217", // 24h volume,the aggregated trading volume in BTC // "volValue": "25984946.157790431", // 24h total, the trading volume in quote currency of last 24 hours // "last": "11328.9", // last price // "averagePrice": "11360.66065903", // 24h average transaction price yesterday // "takerFeeRate": "0.001", // Basic Taker Fee // "makerFeeRate": "0.001", // Basic Maker Fee // "takerCoefficient": "1", // Taker Fee Coefficient // "makerCoefficient": "1" // Maker Fee Coefficient // } // // { // "trading": true, // "symbol": "KCS-BTC", // "buy": 0.00011, // "sell": 0.00012, // "sort": 100, // "volValue": 3.13851792584, //total // "baseCurrency": "KCS", // "market": "BTC", // "quoteCurrency": "BTC", // "symbolCode": "KCS-BTC", // "datetime": 1548388122031, // "high": 0.00013, // "vol": 27514.34842, // "low": 0.0001, // "changePrice": -1.0e-5, // "changeRate": -0.0769, // "lastTradedPrice": 0.00012, // "board": 0, // "mark": 0 // } // // market/ticker ws subscription // // { // bestAsk: '62258.9', // bestAskSize: '0.38579986', // bestBid: '62258.8', // bestBidSize: '0.0078381', // price: '62260.7', // sequence: '1621383297064', // size: '0.00002841', // time: 1634641777363 // } // let percentage = this.safeString (ticker, 'changeRate'); if (percentage !== undefined) { percentage = Precise.stringMul (percentage, '100'); } let last = this.safeString2 (ticker, 'last', 'lastTradedPrice'); last = this.safeString (ticker, 'price', last); const marketId = this.safeString (ticker, 'symbol'); market = this.safeMarket (marketId, market, '-'); const symbol = market['symbol']; const baseVolume = this.safeString (ticker, 'vol'); const quoteVolume = this.safeString (ticker, 'volValue'); const timestamp = this.safeInteger2 (ticker, 'time', 'datetime'); return this.safeTicker ({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeString (ticker, 'high'), 'low': this.safeString (ticker, 'low'), 'bid': this.safeString2 (ticker, 'buy', 'bestBid'), 'bidVolume': this.safeString (ticker, 'bestBidSize'), 'ask': this.safeString2 (ticker, 'sell', 'bestAsk'), 'askVolume': this.safeString (ticker, 'bestAskSize'), 'vwap': undefined, 'open': this.safeString (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': this.safeString (ticker, 'changePrice'), 'percentage': percentage, 'average': this.safeString (ticker, 'averagePrice'), 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }, market, false); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetMarketAllTickers (params); // // { // "code": "200000", // "data": { // "time":1602832092060, // "ticker":[ // { // "symbol": "BTC-USDT", // symbol // "symbolName":"BTC-USDT", // Name of trading pairs, it would change after renaming // "buy": "11328.9", // bestAsk // "sell": "11329", // bestBid // "changeRate": "-0.0055", // 24h change rate // "changePrice": "-63.6", // 24h change price // "high": "11610", // 24h highest price // "low": "11200", // 24h lowest price // "vol": "2282.70993217", // 24h volume,the aggregated trading volume in BTC // "volValue": "25984946.157790431", // 24h total, the trading volume in quote currency of last 24 hours // "last": "11328.9", // last price // "averagePrice": "11360.66065903", // 24h average transaction price yesterday // "takerFeeRate": "0.001", // Basic Taker Fee // "makerFeeRate": "0.001", // Basic Maker Fee // "takerCoefficient": "1", // Taker Fee Coefficient // "makerCoefficient": "1" // Maker Fee Coefficient // } // ] // } // } // const data = this.safeValue (response, 'data', {}); const tickers = this.safeValue (data, 'ticker', []); const time = this.safeInteger (data, 'time'); const result = {}; for (let i = 0; i < tickers.length; i++) { tickers[i]['time'] = time; const ticker = this.parseTicker (tickers[i]); const symbol = this.safeString (ticker, 'symbol'); if (symbol !== undefined) { result[symbol] = ticker; } } return this.filterByArray (result, 'symbol', symbols); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetMarketStats (this.extend (request, params)); // // { // "code": "200000", // "data": { // "time": 1602832092060, // time // "symbol": "BTC-USDT", // symbol // "buy": "11328.9", // bestAsk // "sell": "11329", // bestBid // "changeRate": "-0.0055", // 24h change rate // "changePrice": "-63.6", // 24h change price // "high": "11610", // 24h highest price // "low": "11200", // 24h lowest price // "vol": "2282.70993217", // 24h volume,the aggregated trading volume in BTC // "volValue": "25984946.157790431", // 24h total, the trading volume in quote currency of last 24 hours // "last": "11328.9", // last price // "averagePrice": "11360.66065903", // 24h average transaction price yesterday // "takerFeeRate": "0.001", // Basic Taker Fee // "makerFeeRate": "0.001", // Basic Maker Fee // "takerCoefficient": "1", // Taker Fee Coefficient // "makerCoefficient": "1" // Maker Fee Coefficient // } // } // return this.parseTicker (response['data'], market); } parseOHLCV (ohlcv, market = undefined) { // // [ // "1545904980", // Start time of the candle cycle // "0.058", // opening price // "0.049", // closing price // "0.058", // highest price // "0.049", // lowest price // "0.018", // base volume // "0.000945", // quote volume // ] // return [ this.safeTimestamp (ohlcv, 0), this.safeNumber (ohlcv, 1), this.safeNumber (ohlcv, 3), this.safeNumber (ohlcv, 4), this.safeNumber (ohlcv, 2), this.safeNumber (ohlcv, 5), ]; } async fetchOHLCV (symbol, timeframe = '15m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const marketId = market['id']; const request = { 'symbol': marketId, 'type': this.timeframes[timeframe], }; const duration = this.parseTimeframe (timeframe) * 1000; let endAt = this.milliseconds (); // required param if (since !== undefined) { request['startAt'] = parseInt (Math.floor (since / 1000)); if (limit === undefined) { // https://docs.kucoin.com/#get-klines // https://docs.kucoin.com/#details // For each query, the system would return at most 1500 pieces of data. // To obtain more data, please page the data by time. limit = this.safeInteger (this.options, 'fetchOHLCVLimit', 1500); } endAt = this.sum (since, limit * duration); } else if (limit !== undefined) { since = endAt - limit * duration; request['startAt'] = parseInt (Math.floor (since / 1000)); } request['endAt'] = parseInt (Math.floor (endAt / 1000)); const response = await this.publicGetMarketCandles (this.extend (request, params)); // // { // "code":"200000", // "data":[ // ["1591517700","0.025078","0.025069","0.025084","0.025064","18.9883256","0.4761861079404"], // ["1591516800","0.025089","0.025079","0.025089","0.02506","99.4716622","2.494143499081"], // ["1591515900","0.025079","0.02509","0.025091","0.025068","59.83701271","1.50060885172798"], // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOHLCVs (data, market, timeframe, since, limit); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'] }; const response = await this.privatePostDepositAddresses (this.extend (request, params)); // BCH {"code":"200000","data":{"address":"bitcoincash:qza3m4nj9rx7l9r0cdadfqxts6f92shvhvr5ls4q7z","memo":""}} // BTC {"code":"200000","data":{"address":"36SjucKqQpQSvsak9A7h6qzFjrVXpRNZhE","memo":""}} const data = this.safeValue (response, 'data', {}); let address = this.safeString (data, 'address'); // BCH/BSV is returned with a "bitcoincash:" prefix, which we cut off here and only keep the address if (address !== undefined) { address = address.replace ('bitcoincash:', ''); } const tag = this.safeString (data, 'memo'); if (code !== 'NIM') { // contains spaces this.checkAddress (address); } return { 'info': response, 'currency': code, 'address': address, 'tag': tag, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], // for USDT - OMNI, ERC20, TRC20, default is ERC20 // for BTC - Native, Segwit, TRC20, the parameters are bech32, btc, trx, default is Native // 'chain': 'ERC20', // optional }; // same as for withdraw const networks = this.safeValue (this.options, 'networks', {}); let network = this.safeStringUpper (params, 'network'); // this line allows the user to specify either ERC20 or ETH network = this.safeStringLower (networks, network, network); // handle ERC20>ETH alias if (network !== undefined) { request['chain'] = network; params = this.omit (params, 'network'); } const response = await this.privateGetDepositAddresses (this.extend (request, params)); // BCH {"code":"200000","data":{"address":"bitcoincash:qza3m4nj9rx7l9r0cdadfqxts6f92shvhvr5ls4q7z","memo":""}} // BTC {"code":"200000","data":{"address":"36SjucKqQpQSvsak9A7h6qzFjrVXpRNZhE","memo":""}} const data = this.safeValue (response, 'data', {}); const address = this.safeString (data, 'address'); const tag = this.safeString (data, 'memo'); if (code !== 'NIM') { // contains spaces this.checkAddress (address); } return { 'info': response, 'currency': code, 'address': address, 'tag': tag, 'network': undefined, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const marketId = this.marketId (symbol); const level = this.safeInteger (params, 'level', 2); const request = { 'symbol': marketId }; let method = 'publicGetMarketOrderbookLevelLevelLimit'; const isAuthenticated = this.checkRequiredCredentials (false); let response = undefined; if (!isAuthenticated) { if (level === 2) { request['level'] = level; if (limit !== undefined) { if ((limit === 20) || (limit === 100)) { request['limit'] = limit; } else { throw new ExchangeError (this.id + ' fetchOrderBook() limit argument must be 20 or 100'); } } request['limit'] = limit ? limit : 100; method = 'publicGetMarketOrderbo