UNPKG

ccxt-bybit

Version:

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

1,220 lines (1,188 loc) 69.6 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, AccountSuspended, InvalidNonce, NotSupported, BadRequest, AuthenticationError, BadSymbol, RateLimitExceeded } = 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, '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/1294454/57369448-3cc3aa80-7196-11e9-883e-5ebeb35e4f57.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', 'symbols', '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}', ], 'post': [ 'bullet-public', ], }, 'private': { 'get': [ 'accounts', 'accounts/{accountId}', 'accounts/{accountId}/ledgers', 'accounts/{accountId}/holds', 'deposit-addresses', 'deposits', 'hist-deposits', 'hist-orders', 'hist-withdrawals', 'withdrawals', 'withdrawals/quotas', 'orders', 'orders/{orderId}', 'fills', 'limit/fills', ], 'post': [ 'accounts', 'accounts/inner-transfer', 'accounts/sub-transfer', 'deposit-addresses', 'withdrawals', 'orders', 'bullet-private', ], 'delete': [ 'withdrawals/{withdrawalId}', 'orders/{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': ExchangeError, '503': ExchangeNotAvailable, '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, }, }, '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', }, 'options': { 'version': 'v1', 'symbolSeparator': '-', 'fetchMyTradesMethod': 'private_get_fills', 'fetchBalance': { 'type': 'trade', // or 'main' }, // endpoint versions 'versions': { 'public': { 'GET': { '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 () { const response = await this.publicGetTimestamp (); 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 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); // // { // precision: 10, // name: 'KCS', // fullName: 'KCS shares', // currency: 'KCS' // } // 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'); result[code] = { 'id': id, 'name': name, 'code': code, 'precision': precision, 'info': entry, }; } 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'); let symbol = undefined; const marketId = this.safeString (ticker, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } 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': undefined, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': this.safeFloat (ticker, 'changePrice'), 'percentage': percentage, 'average': this.safeFloat (ticker, 'averagePrice'), 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': this.safeFloat (ticker, 'volValue'), '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 result; } 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, timeframe = '1m', since = undefined, limit = 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 [ parseInt (ohlcv[0]) * 1000, parseFloat (ohlcv[1]), parseFloat (ohlcv[3]), parseFloat (ohlcv[4]), parseFloat (ohlcv[2]), parseFloat (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)); const responseData = this.safeValue (response, 'data', []); return this.parseOHLCVs (responseData, 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 fetchOrderBook (symbol, limit = undefined, params = {}) { let level = '2'; if (limit !== undefined) { if ((limit !== 20) && (limit !== 100)) { throw new ExchangeError (this.id + ' fetchOrderBook limit argument must be undefined, 20 or 100'); } level += '_' + limit.toString (); } await this.loadMarkets (); const marketId = this.marketId (symbol); const request = this.extend ({ 'symbol': marketId, 'level': level }, params); const response = await this.publicGetMarketOrderbookLevelLevel (request); // // { sequence: '1547731421688', // asks: [ [ '5c419328ef83c75456bd615c', '0.9', '0.09' ], ... ], // bids: [ [ '5c419328ef83c75456bd615c', '0.9', '0.09' ], ... ], } // const data = response['data']; const timestamp = this.safeInteger (data, 'time'); // level can be a string such as 2_20 or 2_100 const levelString = this.safeString (request, 'level'); const levelParts = levelString.split ('_'); const offset = parseInt (levelParts[0]); const orderbook = this.parseOrderBook (data, timestamp, 'bids', 'asks', offset - 2, offset - 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 clientOid = this.uuid (); const request = { 'clientOid': clientOid, '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 order = { 'id': this.safeString (data, 'orderId'), 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': undefined, 'filled': undefined, 'remaining': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': undefined, 'status': 'open', 'clientOid': clientOid, 'info': data, }; 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 (); 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 = 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 // } // let symbol = undefined; const marketId = this.safeString (order, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } market = this.safeValue (this.markets_by_id, marketId); } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } 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 let status = order['isActive'] ? 'open' : 'closed'; status = order['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; } } } } return { 'id': orderId, 'symbol': symbol, 'type': type, 'side': side, 'amount': amount, 'price': price, 'cost': cost, 'filled': filled, 'remaining': remaining, 'timestamp': timestamp, 'datetime': datetime, 'fee': fee, 'status': status, 'info': order, }; } 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", // "forceTaker":true, // "price":"0.083", // "size":"0.8424304", // "funds":"0.0699217232", // "fee":"0", // "feeRate":"0", // "feeCurrency":"USDT", // "stop":"", // "type":"limit", // "createdAt":1547026472000 // } // // fetchMyTrades v2 alternative format since 2019-05-21 https://github.com/ccxt/ccxt/pull/5162 // // { // symbol: "OPEN-BTC", // forceTaker: false, // orderId: "5ce36420054b4663b1fff2c9", // fee: "0", // feeCurrency: "", // type: "", // feeRate: "0", // createdAt: 1558417615000, // size: "12.8206", // stop: "", // price: "0", // funds: "0", // tradeId: "5ce390cf6e0db23b861c6e80" // } // // fetchMyTrades (private) v1 (historical) // // { // "symbol": "SNOV-ETH", // "dealPrice": "0.0000246", // "dealValue": "0.018942", // "amount": "770", // "fee": "0.00001137", // "side": "sell", // "createdAt": 1540080199 // "id":"5c4d389e4c8c60413f78e2e5", // } // let symbol = undefined; const marketId = this.safeString (trade, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } const id = this.safeString2 (trade, 'tradeId', 'id'); const orderId = this.safeString (trade, 'orderId'); const takerOrMaker = this.safeString (trade, 'liquidity'); const amount = this.safeFloat2 (trade, 'size', 'amount'); let timestamp = this.safeInteger (trade, 'time'); if (timestamp !== undefined) { timestamp = parseInt (timestamp / 1000000); } else { timestamp = this.safeInteger (trade, 'createdAt'); // if it's a historical v1 trade, the exchange returns timestamp in seconds if (('dealValue' in trade) && (timestamp !== undefined)) { timestamp = timestamp * 1000; } } const price = this.safeFloat2 (trade, 'price', 'dealPrice'); const side = this.safeString (trade, 'side'); let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'feeCurrency'); let feeCurrency = this.safeCurrencyCode (feeCurrencyId); if (feeCurrency === undefined) { if (market !== undefined) { feeCurrency = (side === 'sell') ? market['quote'] : market['base']; } } fee = { 'cost': feeCost, 'currency': feeCurrency, 'rate': this.safeFloat (trade, 'feeRate'), }; } const type = this.safeString (trade, 'type'); let cost = this.safeFloat2 (trade, 'funds', 'dealValue'); if (cost === undefined) { if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } } return { 'info': trade, 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { await this.loadMarkets (); this.checkAddress (address); const currency = this.currencyId (code); const request = { 'currency': currency, 'address': address, 'amount': amount, }; if (tag !== undefined) { request['memo'] = tag; } const response = await this.privatePostWithdrawals (this.extend (request, params)); // // https://github.com/ccxt/ccxt/issues/5558 // // { // "code": 200000, // "data": { // "withdrawalId": "abcdefghijklmnopqrstuvwxyz" // } // } // const data = this.safeValue (response, 'data', {}); return { 'id': this.safeString (data, 'withdrawalId'), 'info': response, }; } parseTransactionStatus (status) { const statuses = { 'SUCCESS': 'ok',