UNPKG

@madnai/ccxt

Version:

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

1,191 lines (1,160 loc) 75.2 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'); // --------------------------------------------------------------------------- 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, 'fetchStatus': true, 'fetchTime': true, 'fetchMarkets': true, 'fetchCurrencies': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchOrderBook': true, 'fetchOrder': true, 'fetchClosedOrders': true, 'fetchOpenOrders': true, 'fetchDepositAddress': true, 'createDepositAddress': true, 'withdraw': true, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchBalance': true, 'fetchTrades': true, 'fetchMyTrades': true, 'createOrder': true, 'cancelOrder': true, 'fetchAccounts': true, 'fetchFundingFee': true, 'fetchOHLCV': true, 'fetchLedger': 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', }, '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}', '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', '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', ], 'post': [ 'accounts', 'accounts/inner-transfer', 'accounts/sub-transfer', 'deposit-addresses', 'withdrawals', 'orders', 'orders/multi', 'margin/borrow', 'margin/repay/all', 'margin/repay/single', 'margin/lend', 'margin/toggle-auto-lend', 'bullet-private', ], 'delete': [ 'withdrawals/{withdrawalId}', 'orders', 'orders/{orderId}', 'margin/lend/{orderId}', ], }, }, '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."} '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', }, 'options': { 'version': 'v1', 'symbolSeparator': '-', 'fetchMyTradesMethod': 'private_get_fills', 'fetchBalance': { 'type': 'trade', // or 'main' }, // endpoint versions 'versions': { 'public': { 'GET': { 'status': 'v1', 'market/orderbook/level{level}': 'v1', 'market/orderbook/level2': 'v2', 'market/orderbook/level2_20': 'v1', 'market/orderbook/level2_100': 'v1', }, }, 'private': { 'POST': { 'accounts/inner-transfer': 'v2', 'accounts/sub-transfer': 'v2', }, }, }, }, }); } 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.safeFloat (market, 'baseMaxSize'); const baseMinSize = this.safeFloat (market, 'baseMinSize'); const quoteMaxSize = this.safeFloat (market, 'quoteMaxSize'); const quoteMinSize = this.safeFloat (market, 'quoteMinSize'); // const quoteIncrement = this.safeFloat (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.safeFloat (market, 'priceIncrement'), 'max': quoteMaxSize / baseMinSize, }, '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 responseData = response['data']; const result = {}; for (let i = 0; i < responseData.length; i++) { const entry = responseData[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.safeFloat (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.safeFloat (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.safeFloat (ticker, 'changeRate'); if (percentage !== undefined) { percentage = percentage * 100; } const last = this.safeFloat2 (ticker, 'last', 'lastTradedPrice'); const marketId = this.safeString (ticker, 'symbol'); const symbol = this.safeSymbol (marketId, market, '-'); const baseVolume = this.safeFloat (ticker, 'vol'); const quoteVolume = this.safeFloat (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.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': vwap, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': this.safeFloat (ticker, 'changePrice'), 'percentage': percentage, 'average': this.safeFloat (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.safeFloat (ohlcv, 1), this.safeFloat (ohlcv, 3), this.safeFloat (ohlcv, 4), this.safeFloat (ohlcv, 2), this.safeFloat (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'); 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', {}); 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'); 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 = {}) { const level = this.safeInteger (params, 'level', 2); let levelLimit = level.toString (); if (levelLimit === '2') { if (limit !== undefined) { if ((limit !== 20) && (limit !== 100)) { throw new ExchangeError (this.id + ' fetchOrderBook limit argument must be undefined, 20 or 100'); } levelLimit += '_' + limit.toString (); } } await this.loadMarkets (); const marketId = this.marketId (symbol); const request = { 'symbol': marketId, 'level': levelLimit }; const response = await this.publicGetMarketOrderbookLevelLevel (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, 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, }; if (type !== 'market') { request['price'] = this.priceToPrecision (symbol, price); request['size'] = this.amountToPrecision (symbol, amount); } else { if (this.safeValue (params, 'quoteAmount')) { // used to create market order by quote amount - https://github.com/ccxt/ccxt/issues/4876 request['funds'] = this.amountToPrecision (symbol, amount); } else { 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 (!this.safeValue (params, 'quoteAmount')) { order['amount'] = amount; } return order; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'orderId': id }; const response = await this.privateDeleteOrdersOrderId (this.extend (request, params)); return response; } 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 (); // 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'); } const request = { 'orderId': id, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const response = await this.privateGetOrdersOrderId (this.extend (request, params)); 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.safeFloat (order, 'price'); const side = this.safeString (order, 'side'); const feeCurrencyId = this.safeString (order, 'feeCurrency'); const feeCurrency = this.safeCurrencyCode (feeCurrencyId); const feeCost = this.safeFloat (order, 'fee'); const amount = this.safeFloat (order, 'size'); const filled = this.safeFloat (order, 'dealSize'); const cost = this.safeFloat (order, 'dealFunds'); const remaining = amount - filled; // 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, }; if (type === 'market') { if (price === 0.0) { if ((cost !== undefined) && (filled !== undefined)) { if ((cost > 0) && (filled > 0)) { price = cost / filled; } } } } const clientOrderId = this.safeString (order, 'clientOid'); const timeInForce = this.safeString (order, 'timeInForce'); return { 'id': orderId, 'clientOrderId': clientOrderId, 'symbol': symbol, 'type': type, 'timeInForce': timeInForce, 'side': side, 'amount': amount, 'price': price, 'cost': cost, 'filled': filled, 'remaining': remaining, '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 parseResponseData = false; if (method === 'private_get_fills') { // does not return trades earlier than 2019-02-18T00:00:00Z if (since !== undefined) { // only returns trades up to one week after the since param request['startAt'] = since; } } else if (method === 'private_get_limit_fills') { // does not return trades earlier than 2019-02-18T00:00:00Z // takes no params // only returns first 1000 trades (not only "in the last 24 hours" as stated in the docs) parseResponseData = true; } else if (method === 'private_get_hist_orders') { // despite that this endpoint is called `HistOrders` // it returns historical trades instead of orders // returns trades earlier than 2019-02-18T00:00:00Z only if (since !== undefined) { request['startAt'] = parseInt (since / 1000); } } else { throw new ExchangeError (this.id + ' invalid fetchClosedOrder method'); } const response = await this[method] (this.extend (request, params)); // // { // "currentPage": 1, // "pageSize": 50, // "totalNum": 1, // "totalPage": 1, // "items": [ // { // "symbol":"BTC-USDT", // symbol // "tradeId":"5c35c02709e4f67d5266954e", // trade id // "orderId":"5c35c02703aa673ceec2a168", // order id // "counterOrderId":"5c1ab46003aa676e487fa8e3", // counter order id // "side":"buy", // transaction direction,include buy and sell // "liquidity":"taker", // include taker and maker // "forceTaker":true, // forced to become taker // "price":"0.083", // order price // "size":"0.8424304", // order quantity // "funds":"0.0699217232", // order funds // "fee":"0", // fee // "feeRate":"0", // fee rate // "feeCurrency":"USDT", // charge fee currency // "stop":"", // stop type // "type":"limit", // order type, e.g. limit, market, stop_limit. // "createdAt":1547026472000 // time // }, // //------------------------------------------------------ // // v1 (historical) trade response structure // { // "symbol": "SNOV-ETH", // "dealPrice": "0.0000246", // "dealValue": "0.018942", // "amount": "770", // "fee": "0.00001137", // "side": "sell", // "createdAt": 1540080199 // "id":"5c4d389e4c8c60413f78e2e5", // } // ] // } // const data = this.safeValue (response, 'data', {}); let trades = undefined; if (parseResponseData) { trades = data; } else { trades = this.safeValue (data, 'items', []); } return this.parseTrades (trades, market, since, limit); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (since !== undefined) { request['startAt'] = Math.floor (since / 1000); } if (limit !== undefined) { request['pageSize'] = limit; } const response = await this.publicGetMarketHistories (this.extend (request, params)); // // { // "code": "200000", // "data": [ // { // "sequence": "1548764654235", // "side": "sell", // "size":"0.6841354", // "price":"0.03202", // "time":1548848575203567174 // } // ] // } // const trades = this.safeValue (response, 'data', []); return this.parseTrades (trades, market, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "sequence": "1548764654235", // "side": "sell", // "size":"0.6841354", // "price":"0.03202", // "time":1548848575203567174 // } // // { // sequence: '1568787654360', // symbol: 'BTC-USDT', // side: 'buy', // size: '0.00536577', // price: '9345', // takerOrderId: '5e356c4a9f1a790008f8d921', // time: '1580559434436443257', // type: 'match', // makerOrderId: '5e356bffedf0010008fa5d7f', // tradeId: '5e356c4aeefabd62c62a1ece' // } // // fetchMyTrades (private) v2 // // { // "symbol":"BTC-USDT", // "tradeId":"5c35c02709e4f67d5266954e", // "orderId":"5c35c02703aa673ceec2a168", // "counterOrderId":"5c1ab46003aa676e487fa8e3", // "side":"buy", // "liquidity":"taker", // "forceT