consequunturatque
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,158 lines (1,128 loc) • 92.1 kB
JavaScript
'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