kamiswiss-ccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,235 lines (1,202 loc) • 55.7 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, AccountSuspended, InvalidNonce, DDoSProtection, NotSupported, BadRequest, AuthenticationError } = 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': true,
'comment': 'Platform 2.0',
'has': {
'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,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/57369448-3cc3aa80-7196-11e9-883e-5ebeb35e4f57.jpg',
'referral': 'https://www.kucoin.com/ucenter/signup?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/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',
'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': {
'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': DDoSProtection,
'500': ExchangeError,
'503': ExchangeNotAvailable,
'200004': InsufficientFunds,
'260100': InsufficientFunds, // {"code":"260100","msg":"account.noBalance"}
'300000': InvalidOrder,
'400001': AuthenticationError,
'400002': InvalidNonce,
'400003': AuthenticationError,
'400004': AuthenticationError,
'400005': AuthenticationError,
'400006': AuthenticationError,
'400007': AuthenticationError,
'400008': NotSupported,
'400100': ArgumentsRequired,
'411100': AccountSuspended,
'500000': ExchangeError,
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'taker': 0.001,
'maker': 0.001,
},
'funding': {
'tierBased': false,
'percentage': false,
'withdraw': {},
'deposit': {},
},
},
'commonCurrencies': {
'HOT': 'HOTNOW',
},
'options': {
'version': 'v1',
'symbolSeparator': '-',
},
});
}
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 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 = market['name'];
const baseId = market['baseCurrency'];
const quoteId = market['quoteCurrency'];
const base = this.commonCurrencyCode (baseId);
const quote = this.commonCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const active = 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, 'name');
const name = entry['fullName'];
const code = this.commonCurrencyCode (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.commonCurrencyCode (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'];
let withdrawFees = {};
withdrawFees[code] = this.safeFloat (data, 'withdrawMinFee');
return {
'info': response,
'withdraw': withdrawFees,
'deposit': {},
};
}
parseTicker (ticker, market = undefined) {
//
// {
// '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'
// }
//
let percentage = this.safeFloat (ticker, 'changeRate');
if (percentage !== undefined) {
percentage = percentage * 100;
}
const last = this.safeFloat (ticker, 'last');
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.commonCurrencyCode (baseId);
const quote = this.commonCurrencyCode (quoteId);
symbol = base + '/' + quote;
}
}
if (symbol === undefined) {
if (market !== undefined) {
symbol = market['symbol'];
}
}
return {
'symbol': symbol,
'timestamp': undefined,
'datetime': undefined,
'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': undefined,
'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,
'endAt': this.seconds (), // required param
'type': this.timeframes[timeframe],
};
if (since !== undefined) {
request['startAt'] = Math.floor (since / 1000);
}
const response = await this.publicGetMarketCandles (this.extend (request, params));
const responseData = 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 = {}) {
await this.loadMarkets ();
const marketId = this.marketId (symbol);
const request = this.extend ({ 'symbol': marketId, 'level': 2 }, 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, 'sequence');
// level can be a string such as 2_20 or 2_100
const levelString = this.safeString (request, 'level');
const levelParts = levelString.split ('_');
const level = parseInt (levelParts[0]);
return this.parseOrderBook (data, timestamp, 'bids', 'asks', level - 2, level - 1);
}
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,
'size': this.amountToPrecision (symbol, amount),
'symbol': marketId,
'type': type,
};
if (type !== 'market') {
request['price'] = this.priceToPrecision (symbol, price);
}
const response = await this.privatePostOrders (this.extend (request, params));
const responseData = response['data'];
return {
'id': responseData['orderId'],
'symbol': symbol,
'type': type,
'side': side,
'status': 'open',
'clientOid': clientOid,
'info': responseData,
};
}
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.commonCurrencyCode (baseId);
const quote = this.commonCurrencyCode (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.commonCurrencyCode (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 status = order['isActive'] ? 'open' : 'closed';
let 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;
}
let method = 'privateGetFills';
if (since !== undefined) {
// if since is earlier than 2019-02-18T00:00:00Z
if (since < 1550448000000) {
request['startAt'] = parseInt (since / 1000);
// despite that this endpoint is called `HistOrders`
// it returns historical trades instead of orders
method = 'privateGetHistOrders';
} else {
request['startAt'] = since;
}
}
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', {});
const 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
// }
//
// 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.commonCurrencyCode (baseId);
const quote = this.commonCurrencyCode (quoteId);
symbol = base + '/' + quote;
}
}
if (symbol === undefined) {
if (market !== undefined) {
symbol = market['symbol'];
}
}
let id = this.safeString2 (trade, 'tradeId', 'id');
if (id !== undefined) {
id = id.toString ();
}
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.commonCurrencyCode (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));
//
// { "withdrawalId": "5bffb63303aa675e8bbe18f9" }
//
return {
'id': this.safeString (response, 'withdrawalId'),
'info': response,
};
}
parseTransactionStatus (status) {
const statuses = {
'SUCCESS': 'ok',
'PROCESSING': 'ok',
'FAILURE': 'failed',
};
return this.safeString (statuses, status);
}
parseTransaction (transaction, currency = undefined) {
//
// fetchDeposits
//
// {
// "address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989",
// "memo": "5c247c8a03aa677cea2a251d",
// "amount": 1,
// "fee": 0.0001,
// "currency": "KCS",
// "isInner": false,
// "walletTxId": "5bbb57386d99522d9f954c5a@test004",
// "status": "SUCCESS",
// "createdAt": 1544178843000,
// "updatedAt": 1544178891000
// }
//
// fetchWithdrawals
//
// {
// "id": "5c2dc64e03aa675aa263f1ac",
// "address": "0x5bedb060b8eb8d823e2414d82acce78d38be7fe9",
// "memo": "",
// "currency": "ETH",
// "amount": 1.0000000,
// "fee": 0.0100000,
// "walletTxId": "3e2414d82acce78d38be7fe9",
// "isInner": false,
// "status": "FAILURE",
// "createdAt": 1546503758000,
// "updatedAt": 1546504603000
// }
//
let code = undefined;
let currencyId = this.safeString (transaction, 'currency');
currency = this.safeValue (this.currencies_by_id, currencyId);
if (currency !== undefined) {
code = currency['code'];
} else {
code = this.commonCurrencyCode (currencyId);
}
let address = this.safeString (transaction, 'address');
const amount = this.safeFloat (transaction, 'amount');
let txid = this.safeString (transaction, 'walletTxId');
if (txid !== undefined) {
const txidParts = txid.split ('@');
const numTxidParts = txidParts.length;
if (numTxidParts > 1) {
if (address === undefined) {
if (txidParts[1].length > 1) {
address = txidParts[1];
}
}
}
txid = txidParts[0];
}
let type = txid === undefined ? 'withdrawal' : 'deposit';
const rawStatus = this.safeString (transaction, 'status');
const status = this.parseTransactionStatus (rawStatus);
let fee = undefined;
const feeCost = this.safeFloat (transaction, 'fee');
if (feeCost !== undefined) {
let rate = undefined;
if (amount !== undefined) {
rate = feeCost / amount;
}
fee = {
'cost': feeCost,
'rate': rate,
'currency': code,
};
}
const tag = this.safeString (transaction, 'memo');
let timestamp = this.safeInteger2 (transaction, 'createdAt', 'createAt');
const id = this.safeString (transaction, 'id');
let updated = this.safeInteger (transaction, 'updatedAt');
const isV1 = !('createdAt' in transaction);
// if it's a v1 structure
if (isV1) {
type = ('address' in transaction) ? 'withdrawal' : 'deposit';
if (timestamp !== undefined) {
timestamp = timestamp * 1000;
}
if (updated !== undefined) {
updated = updated * 1000;
}
}
return {
'id': id,
'address': address,
'tag': tag,
'currency': code,
'amount': amount,
'txid': txid,
'type': type,
'status': status,
'fee': fee,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'updated': updated,
'info': transaction,
};
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {};
let currency = undefined;
if (code !== undefined) {
currency = this.currency (code);
request['currency'] = currency['id'];
}
if (limit !== undefined) {
request['pageSize'] = limit;
}
let method = 'privateGetDeposits';
if (since !== undefined) {
// if since is earlier than 2019-02-18T00:00:00Z
if (since < 1550448000000) {
request['startAt'] = parseInt (since / 1000);
method = 'privateGetHistDeposits';
} else {
request['startAt'] = since;
}
}
const response = await this[method] (this.extend (request, params));
//
// {
// code: '200000',
// data: {
// "currentPage": 1,
// "pageSize": 5,
// "totalNum": 2,
// "totalPage": 1,
// "items": [
// //--------------------------------------------------
// // version 2 deposit response structure
// {
// "address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989",
// "memo": "5c247c8a03aa677cea2a251d",
// "amount": 1,
// "fee": 0.0001,
// "currency": "KCS",
// "isInner": false,
// "walletTxId": "5bbb57386d99522d9f954c5a@test004",
// "status": "SUCCESS",
// "createdAt": 1544178843000,
// "updatedAt": 1544178891000
// },
// //--------------------------------------------------
// // version 1 (historical) deposit response structure
// {
// "currency": "BTC",
// "createAt": 1528536998,
// "amount": "0.03266638",
// "walletTxId": "55c643bc2c68d6f17266383ac1be9e454038864b929ae7cee0bc408cc5c869e8@12ffGWmMMD1zA1WbFm7Ho3JZ1w6NYXjpFk@234",
// "isInner": false,
// "status": "SUCCESS",
// }
// ]
// }
// }
//
const responseData = response['data']['items'];
return this.parseTransactions (responseData, currency, since, limit, { 'type': 'deposit' });
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {};
let currency = undefined;
if (code !== undefined) {
currency = this.currency (code);
request['