UNPKG

consequunturatque

Version:

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

1,158 lines (1,128 loc) 92.1 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, AccountSuspended, InvalidNonce, NotSupported, BadRequest, AuthenticationError, BadSymbol, RateLimitExceeded, PermissionDenied } = 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' ], 'rateLimit': 334, 'version': 'v2', 'certified': false, 'pro': true, 'comment': 'Platform 2.0', 'has': { 'CORS': false, 'cancelAllOrders': true, 'cancelOrder': true, 'createDepositAddress': true, 'createOrder': true, 'fetchAccounts': true, 'fetchBalance': true, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingFee': true, 'fetchLedger': true, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchStatus': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchWithdrawals': true, 'withdraw': true, 'transfer': 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://openapi-v2.kucoin.com', 'private': 'https://openapi-v2.kucoin.com', 'futuresPrivate': 'https://api-futures.kucoin.com', }, 'test': { 'public': 'https://openapi-sandbox.kucoin.com', 'private': 'https://openapi-sandbox.kucoin.com', }, 'www': 'https://www.kucoin.com', 'doc': [ 'https://docs.kucoin.com', ], }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'password': true, }, 'api': { 'public': { 'get': [ 'timestamp', 'status', 'symbols', 'markets', 'market/allTickers', 'market/orderbook/level{level}_{limit}', 'market/orderbook/level{level}', 'market/orderbook/level2', 'market/orderbook/level2_20', 'market/orderbook/level2_100', 'market/orderbook/level3', 'market/histories', 'market/candles', 'market/stats', 'currencies', 'currencies/{currency}', 'prices', 'mark-price/{symbol}/current', 'margin/config', ], 'post': [ 'bullet-public', ], }, 'private': { 'get': [ 'accounts', 'accounts/{accountId}', 'accounts/{accountId}/ledgers', 'accounts/{accountId}/holds', 'accounts/transferable', 'sub/user', 'sub-accounts', 'sub-accounts/{subUserId}', 'deposit-addresses', 'deposits', 'hist-deposits', 'hist-orders', 'hist-withdrawals', 'withdrawals', 'withdrawals/quotas', 'orders', 'order/client-order/{clientOid}', 'orders/{orderId}', 'limit/orders', 'fills', 'limit/fills', 'margin/account', 'margin/borrow', 'margin/borrow/outstanding', 'margin/borrow/borrow/repaid', 'margin/lend/active', 'margin/lend/done', 'margin/lend/trade/unsettled', 'margin/lend/trade/settled', 'margin/lend/assets', 'margin/market', 'margin/trade/last', 'stop-order/{orderId}', 'stop-order', 'stop-order/queryOrderByClientOid', ], 'post': [ 'accounts', 'accounts/inner-transfer', 'accounts/sub-transfer', 'deposit-addresses', 'withdrawals', 'orders', 'orders/multi', 'margin/borrow', 'margin/order', 'margin/repay/all', 'margin/repay/single', 'margin/lend', 'margin/toggle-auto-lend', 'bullet-private', 'stop-order', ], 'delete': [ 'withdrawals/{withdrawalId}', 'orders', 'orders/client-order/{clientOid}', 'orders/{orderId}', 'margin/lend/{orderId}', 'stop-order/cancelOrderByClientOid', 'stop-order/{orderId}', 'stop-order/cancel', ], }, 'futuresPrivate': { 'get': [ 'account-overview', 'positions', ], 'post': [ 'transfer-out', ], }, }, '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, '411100': AccountSuspended, '415000': BadRequest, // {"code":"415000","msg":"Unsupported Media Type"} '500000': ExchangeError, }, 'broad': { 'Exceeded the access frequency': RateLimitExceeded, 'require more permission': PermissionDenied, }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.001, 'maker': 0.001, }, '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', // endpoint versions 'versions': { 'public': { 'GET': { 'status': 'v1', 'market/orderbook/level2': 'v2', 'market/orderbook/level3': 'v2', 'market/orderbook/level2_20': 'v1', 'market/orderbook/level2_100': 'v1', 'market/orderbook/level{level}': 'v2', 'market/orderbook/level{level}_{limit}': 'v1', }, }, 'private': { 'POST': { 'accounts/inner-transfer': 'v2', 'accounts/sub-transfer': 'v2', }, }, 'futuresPrivate': { 'GET': { 'account-overview': 'v1', 'positions': 'v1', }, 'POST': { 'transfer-out': 'v2', }, }, }, 'accountsByType': { 'trade': 'trade', 'trading': 'trade', 'margin': 'margin', 'main': 'main', 'futures': 'contract', 'contract': 'contract', 'pool': 'pool', 'pool-x': 'pool', }, }, }); } nonce () { return this.milliseconds (); } async loadTimeDifference (params = {}) { const response = await this.publicGetTimestamp (params); const after = this.milliseconds (); const kucoinTime = this.safeInteger (response, 'data'); this.options['timeDifference'] = parseInt (after - kucoinTime); return this.options['timeDifference']; } 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":{ // "msg":"", // "status":"open" // } // } // const data = this.safeValue (response, 'data', {}); let status = this.safeValue (data, 'status'); if (status !== undefined) { status = (status === 'open') ? 'ok' : 'maintenance'; this.status = this.extend (this.status, { 'status': status, 'updated': this.milliseconds (), }); } return this.status; } async fetchMarkets (params = {}) { const response = await this.publicGetSymbols (params); // // { // quoteCurrency: 'BTC', // symbol: 'KCS-BTC', // quoteMaxSize: '9999999', // quoteIncrement: '0.000001', // baseMinSize: '0.01', // quoteMinSize: '0.00001', // enableTrading: true, // priceIncrement: '0.00000001', // name: 'KCS-BTC', // baseIncrement: '0.01', // baseMaxSize: '9999999', // baseCurrency: 'KCS' // } // const data = response['data']; 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 symbol = base + '/' + quote; const active = this.safeValue (market, 'enableTrading'); 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 precision = { 'amount': this.precisionFromString (this.safeString (market, 'baseIncrement')), 'price': this.precisionFromString (this.safeString (market, 'priceIncrement')), }; const limits = { 'amount': { 'min': baseMinSize, 'max': baseMaxSize, }, 'price': { 'min': this.safeNumber (market, 'priceIncrement'), 'max': this.parseNumber (Precise.stringDiv (quoteMaxSizeString, baseMinSizeString)), }, 'cost': { 'min': quoteMinSize, 'max': quoteMaxSize, }, }; result.push ({ 'id': id, 'symbol': symbol, 'baseId': baseId, 'quoteId': quoteId, 'base': base, 'quote': quote, 'active': active, 'precision': precision, 'limits': limits, '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, '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 = {}) { const currencyId = this.currencyId (code); const request = { 'currency': currencyId, }; 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': {}, }; } parseTicker (ticker, market = undefined) { // // { // symbol: "ETH-BTC", // high: "0.019518", // vol: "7997.82836194", // last: "0.019329", // low: "0.019", // buy: "0.019329", // sell: "0.01933", // changePrice: "-0.000139", // time: 1580553706304, // averagePrice: "0.01926386", // changeRate: "-0.0071", // volValue: "154.40791568183474" // } // // { // "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 // } // let percentage = this.safeNumber (ticker, 'changeRate'); if (percentage !== undefined) { percentage = percentage * 100; } const last = this.safeNumber2 (ticker, 'last', 'lastTradedPrice'); const marketId = this.safeString (ticker, 'symbol'); const symbol = this.safeSymbol (marketId, market, '-'); const baseVolume = this.safeNumber (ticker, 'vol'); const quoteVolume = this.safeNumber (ticker, 'volValue'); const vwap = this.vwap (baseVolume, quoteVolume); const timestamp = this.safeInteger2 (ticker, 'time', 'datetime'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeNumber (ticker, 'high'), 'low': this.safeNumber (ticker, 'low'), 'bid': this.safeNumber (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeNumber (ticker, 'sell'), 'askVolume': undefined, 'vwap': vwap, 'open': this.safeNumber (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': this.safeNumber (ticker, 'changePrice'), 'percentage': percentage, 'average': this.safeNumber (ticker, 'averagePrice'), 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetMarketAllTickers (params); // // { // "code": "200000", // "data": { // "date": 1550661940645, // "ticker": [ // 'buy': '0.00001168', // 'changePrice': '-0.00000018', // 'changeRate': '-0.0151', // 'datetime': 1550661146316, // 'high': '0.0000123', // 'last': '0.00001169', // 'low': '0.00001159', // 'sell': '0.00001182', // 'symbol': 'LOOM-BTC', // 'vol': '44399.5669' // }, // ] // } // const data = this.safeValue (response, 'data', {}); const tickers = this.safeValue (data, 'ticker', []); const result = {}; for (let i = 0; i < tickers.length; i++) { 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": { // 'buy': '0.00001168', // 'changePrice': '-0.00000018', // 'changeRate': '-0.0151', // 'datetime': 1550661146316, // 'high': '0.0000123', // 'last': '0.00001169', // 'low': '0.00001159', // 'sell': '0.00001182', // 'symbol': 'LOOM-BTC', // 'vol': '44399.5669' // }, // } // 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 currencyId = this.currencyId (code); const request = { 'currency': currencyId }; 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 currencyId = this.currencyId (code); const request = { 'currency': currencyId }; 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, }; } async fetchL3OrderBook (symbol, limit = undefined, params = {}) { return await this.fetchOrderBook (symbol, limit, { 'level': 3 }); } 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, 'level': level }; let method = 'publicGetMarketOrderbookLevelLevel'; if (level === 2) { if (limit !== undefined) { if ((limit === 20) || (limit === 100)) { request['limit'] = limit; method = 'publicGetMarketOrderbookLevelLevelLimit'; } else { throw new ExchangeError (this.id + ' fetchOrderBook limit argument must be undefined, 20 or 100'); } } } const response = await this[method] (this.extend (request, params)); // // 'market/orderbook/level2' // 'market/orderbook/level2_20' // 'market/orderbook/level2_100' // // { // "code":"200000", // "data":{ // "sequence":"1583235112106", // "asks":[ // // ... // ["0.023197","12.5067468"], // ["0.023194","1.8"], // ["0.023191","8.1069672"] // ], // "bids":[ // ["0.02319","1.6000002"], // ["0.023189","2.2842325"], // ], // "time":1586584067274 // } // } // // 'market/orderbook/level3' // // { // "code":"200000", // "data":{ // "sequence":"1583731857120", // "asks":[ // // id, price, size, timestamp in nanoseconds // ["5e915f8acd26670009675300","6925.7","0.2","1586585482194286069"], // ["5e915f8ace35a200090bba48","6925.7","0.001","1586585482229569826"], // ["5e915f8a8857740009ca7d33","6926","0.00001819","1586585482149148621"], // ], // "bids":[ // ["5e915f8acca406000ac88194","6925.6","0.05","1586585482384384842"], // ["5e915f93cd26670009676075","6925.6","0.08","1586585491334914600"], // ["5e915f906aa6e200099b49f6","6925.4","0.2","1586585488941126340"], // ], // "time":1586585492487 // } // } // const data = this.safeValue (response, 'data', {}); const timestamp = this.safeInteger (data, 'time'); const orderbook = this.parseOrderBook (data, symbol, timestamp, 'bids', 'asks', level - 2, level - 1); orderbook['nonce'] = this.safeInteger (data, 'sequence'); return orderbook; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const marketId = this.marketId (symbol); // required param, cannot be used twice const clientOrderId = this.safeString2 (params, 'clientOid', 'clientOrderId', this.uuid ()); params = this.omit (params, [ 'clientOid', 'clientOrderId' ]); const request = { 'clientOid': clientOrderId, 'side': side, 'symbol': marketId, 'type': type, // limit or market // 'remark': '', // optional remark for the order, length cannot exceed 100 utf8 characters // 'stp': '', // self trade prevention, CN, CO, CB or DC // To improve the system performance and to accelerate order placing and processing, KuCoin has added a new interface for margin orders // The current one will no longer accept margin orders by May 1st, 2021 (UTC) // At the time, KuCoin will notify users via the announcement, please pay attention to it // 'tradeType': 'TRADE', // TRADE, MARGIN_TRADE // not used with margin orders // limit orders --------------------------------------------------- // 'timeInForce': 'GTC', // GTC, GTT, IOC, or FOK (default is GTC), limit orders only // 'cancelAfter': long, // cancel after n seconds, requires timeInForce to be GTT // 'postOnly': false, // Post only flag, invalid when timeInForce is IOC or FOK // 'hidden': false, // Order will not be displayed in the order book // 'iceberg': false, // Only a portion of the order is displayed in the order book // 'visibleSize': this.amountToPrecision (symbol, visibleSize), // The maximum visible size of an iceberg order // market orders -------------------------------------------------- // 'size': this.amountToPrecision (symbol, amount), // Amount in base currency // 'funds': this.costToPrecision (symbol, cost), // Amount of quote currency to use // stop orders ---------------------------------------------------- // 'stop': 'loss', // loss or entry, the default is loss, requires stopPrice // 'stopPrice': this.priceToPrecision (symbol, amount), // need to be defined if stop is specified // margin orders -------------------------------------------------- // 'marginMode': 'cross', // cross (cross mode) and isolated (isolated mode), set to cross by default, the isolated mode will be released soon, stay tuned // 'autoBorrow': false, // The system will first borrow you funds at the optimal interest rate and then place an order for you }; const quoteAmount = this.safeNumber2 (params, 'cost', 'funds'); if (type === 'market') { if (quoteAmount !== undefined) { params = this.omit (params, [ 'cost', 'funds' ]); // kucoin uses base precision even for quote values request['funds'] = this.amountToPrecision (symbol, quoteAmount); } else { request['size'] = this.amountToPrecision (symbol, amount); } } else { request['price'] = this.priceToPrecision (symbol, price); request['size'] = this.amountToPrecision (symbol, amount); } const response = await this.privatePostOrders (this.extend (request, params)); // // { // code: '200000', // data: { // "orderId": "5bd6e9286d99522a52e458de" // } // } // const data = this.safeValue (response, 'data', {}); const timestamp = this.milliseconds (); const id = this.safeString (data, 'orderId'); const order = { 'id': id, 'clientOrderId': clientOrderId, 'info': data, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': undefined, 'cost': undefined, 'average': undefined, 'filled': undefined, 'remaining': undefined, 'status': undefined, 'fee': undefined, 'trades': undefined, }; if (quoteAmount === undefined) { order['amount'] = amount; } else { order['cost'] = quoteAmount; } return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = {}; const clientOrderId = this.safeString2 (params, 'clientOid', 'clientOrderId'); let method = 'privateDeleteOrdersOrderId'; if (clientOrderId !== undefined) { request['clientOid'] = clientOrderId; method = 'privateDeleteOrdersClientOrderClientOid'; } else { request['orderId'] = id; } params = this.omit (params, [ 'clientOid', 'clientOrderId' ]); return await this[method] (this.extend (request, params)); } async cancelAllOrders (symbol = undefined, params = {}) { await this.loadMarkets (); const request = { // 'symbol': market['id'], // 'tradeType': 'TRADE', // default is to cancel the spot trading order }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } return await this.privateDeleteOrders (this.extend (request, params)); } async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'status': status, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['startAt'] = since; } if (limit !== undefined) { request['pageSize'] = limit; } const response = await this.privateGetOrders (this.extend (request, params)); // // { // code: '200000', // data: { // "currentPage": 1, // "pageSize": 1, // "totalNum": 153408, // "totalPage": 153408, // "items": [ // { // "id": "5c35c02703aa673ceec2a168", //orderid // "symbol": "BTC-USDT", //symbol // "opType": "DEAL", // operation type,deal is pending order,cancel is cancel order // "type": "limit", // order type,e.g. limit,markrt,stop_limit. // "side": "buy", // transaction direction,include buy and sell // "price": "10", // order price // "size": "2", // order quantity // "funds": "0", // order funds // "dealFunds": "0.166", // deal funds // "dealSize": "2", // deal quantity // "fee": "0", // fee // "feeCurrency": "USDT", // charge fee currency // "stp": "", // self trade prevention,include CN,CO,DC,CB // "stop": "", // stop type // "stopTriggered": false, // stop order is triggered // "stopPrice": "0", // stop price // "timeInForce": "GTC", // time InForce,include GTC,GTT,IOC,FOK // "postOnly": false, // postOnly // "hidden": false, // hidden order // "iceberg": false, // iceberg order // "visibleSize": "0", // display quantity for iceberg order // "cancelAfter": 0, // cancel orders time,requires timeInForce to be GTT // "channel": "IOS", // order source // "clientOid": "", // user-entered order unique mark // "remark": "", // remark // "tags": "", // tag order source // "isActive": false, // status before unfilled or uncancelled // "cancelExist": false, // order cancellation transaction record // "createdAt": 1547026471000 // time // }, // ] // } // } const responseData = this.safeValue (response, 'data', {}); const orders = this.safeValue (responseData, 'items', []); return this.parseOrders (orders, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByStatus ('done', symbol, since, limit, params); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByStatus ('active', symbol, since, limit, params); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = {}; const clientOrderId = this.safeString2 (params, 'clientOid', 'clientOrderId'); let method = 'privateGetOrdersOrderId'; if (clientOrderId !== undefined) { request['clientOid'] = clientOrderId; method = 'privateGetOrdersClientOrderClientOid'; } else { // a special case for undefined ids // otherwise a wrong endpoint for all orders will be triggered // https://github.com/ccxt/ccxt/issues/7234 if (id === undefined) { throw new InvalidOrder (this.id + ' fetchOrder() requires an order id'); } request['orderId'] = id; } params = this.omit (params, [ 'clientOid', 'clientOrderId' ]); const response = await this[method] (this.extend (request, params)); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const responseData = this.safeValue (response, 'data'); return this.parseOrder (responseData, market); } parseOrder (order, market = undefined) { // // fetchOpenOrders, fetchClosedOrders // // { // "id": "5c35c02703aa673ceec2a168", //orderid // "symbol": "BTC-USDT", //symbol // "opType": "DEAL", // operation type,deal is pending order,cancel is cancel order // "type": "limit", // order type,e.g. limit,markrt,stop_limit. // "side": "buy", // transaction direction,include buy and sell // "price": "10", // order price // "size": "2", // order quantity // "funds": "0", // order funds // "dealFunds": "0.166", // deal funds // "dealSize": "2", // deal quantity // "fee": "0", // fee // "feeCurrency": "USDT", // charge fee currency // "stp": "", // self trade prevention,include CN,CO,DC,CB // "stop": "", // stop type // "stopTriggered": false, // stop order is triggered // "stopPrice": "0", // stop price // "timeInForce": "GTC", // time InForce,include GTC,GTT,IOC,FOK // "postOnly": false, // postOnly // "hidden": false, // hidden order // "iceberg": false, // iceberg order // "visibleSize": "0", // display quantity for iceberg order // "cancelAfter": 0, // cancel orders time,requires timeInForce to be GTT // "channel": "IOS", // order source // "clientOid": "", // user-entered order unique mark // "remark": "", // remark // "tags": "", // tag order source // "isActive": false, // status before unfilled or uncancelled // "cancelExist": false, // order cancellation transaction record // "createdAt": 1547026471000 // time // } // const marketId = this.safeString (order, 'symbol'); const symbol = this.safeSymbol (marketId, market, '-'); const orderId = this.safeString (order, 'id'); const type = this.safeString (order, 'type'); const timestamp = this.safeInteger (order, 'createdAt'); const datetime = this.iso8601 (timestamp); let price = this.safeNumber (order, 'price'); if (price === 0.0) { // market orders price = undefined; } const side = this.safeString (order, 'side'); const feeCurrencyId = this.safeString (order, 'feeCurrency'); const feeCurrency = this.safeCurrencyCode (feeCurrencyId); const feeCost = this.safeNumber (order, 'fee'); const amount = this.safeNumber (order, 'size'); const filled = this.safeNumber (order, 'dealSize'); const cost = this.safeNumber (order, 'dealFunds'); // bool const isActive = this.safeValue (order, 'isActive', false); const cancelExist = this.safeValue (order, 'cancelExist', false); let status = isActive ? 'open' : 'closed'; status = cancelExist ? 'canceled' : status; const fee = { 'currency': feeCurrency, 'cost': feeCost, }; const clientOrderId = this.safeString (order, 'clientOid'); const timeInForce = this.safeString (order, 'timeInForce'); const stopPrice = this.safeNumber (order, 'stopPrice'); const postOnly = this.safeValue (order, 'postOnly'); return this.safeOrder ({ 'id': orderId, 'clientOrderId': clientOrderId, 'symbol': symbol, 'type': type, 'timeInForce': timeInForce, 'postOnly': postOnly, 'side': side, 'amount': amount, 'price': price, 'stopPrice': stopPrice, 'cost': cost, 'filled': filled, 'remaining': undefined, 'timestamp': timestamp, 'datetime': datetime, 'fee': fee, 'status': status, 'info': order, 'lastTradeTimestamp': undefined, 'average': undefined, 'trades': undefined, }); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['pageSize'] = limit; } const method = this.options['fetchMyTradesMethod']; let parseResponseDat