Files
Superalgos/TaskServer/node_modules/ccxt/js/lbank.js
T
2020-05-10 14:17:33 +02:00

672 lines
25 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, DDoSProtection, AuthenticationError, InvalidOrder, ArgumentsRequired } = require ('./base/errors');
// ---------------------------------------------------------------------------
module.exports = class lbank extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'lbank',
'name': 'LBank',
'countries': [ 'CN' ],
'version': 'v1',
'has': {
'fetchTickers': true,
'fetchOHLCV': true,
'fetchOrder': true,
'fetchOrders': true,
'fetchOpenOrders': false, // status 0 API doesn't work
'fetchClosedOrders': true,
},
'timeframes': {
'1m': 'minute1',
'5m': 'minute5',
'15m': 'minute15',
'30m': 'minute30',
'1h': 'hour1',
'2h': 'hour2',
'4h': 'hour4',
'6h': 'hour6',
'8h': 'hour8',
'12h': 'hour12',
'1d': 'day1',
'1w': 'week1',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/38063602-9605e28a-3302-11e8-81be-64b1e53c4cfb.jpg',
'api': 'https://api.lbank.info',
'www': 'https://www.lbank.info',
'doc': 'https://github.com/LBank-exchange/lbank-official-api-docs',
'fees': 'https://lbankinfo.zendesk.com/hc/en-gb/articles/360012072873-Trading-Fees',
'referral': 'https://www.lbex.io/invite?icode=7QCY',
},
'api': {
'public': {
'get': [
'currencyPairs',
'ticker',
'depth',
'trades',
'kline',
'accuracy',
],
},
'private': {
'post': [
'user_info',
'create_order',
'cancel_order',
'orders_info',
'orders_info_history',
'withdraw',
'withdrawCancel',
'withdraws',
'withdrawConfigs',
],
},
},
'fees': {
'trading': {
'maker': 0.1 / 100,
'taker': 0.1 / 100,
},
'funding': {
'withdraw': {
'BTC': undefined,
'ZEC': 0.01,
'ETH': 0.01,
'ETC': 0.01,
// 'QTUM': amount => Math.max (0.01, amount * (0.1 / 100)),
'VEN': 10.0,
'BCH': 0.0002,
'SC': 50.0,
'BTM': 20.0,
'NAS': 1.0,
'EOS': 1.0,
'XWC': 5.0,
'BTS': 1.0,
'INK': 10.0,
'BOT': 3.0,
'YOYOW': 15.0,
'TGC': 10.0,
'NEO': 0.0,
'CMT': 20.0,
'SEER': 2000.0,
'FIL': undefined,
'BTG': undefined,
},
},
},
'commonCurrencies': {
'VET_ERC20': 'VEN',
},
'options': {
'cacheSecretAsPem': true,
},
});
}
async fetchMarkets (params = {}) {
const response = await this.publicGetAccuracy (params);
const result = [];
for (let i = 0; i < response.length; i++) {
const market = response[i];
const id = market['symbol'];
const parts = id.split ('_');
let baseId = undefined;
let quoteId = undefined;
const numParts = parts.length;
// lbank will return symbols like "vet_erc20_usdt"
if (numParts > 2) {
baseId = parts[0] + '_' + parts[1];
quoteId = parts[2];
} else {
baseId = parts[0];
quoteId = parts[1];
}
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const precision = {
'amount': this.safeInteger (market, 'quantityAccuracy'),
'price': this.safeInteger (market, 'priceAccuracy'),
};
result.push ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': true,
'precision': precision,
'limits': {
'amount': {
'min': Math.pow (10, -precision['amount']),
'max': undefined,
},
'price': {
'min': Math.pow (10, -precision['price']),
'max': Math.pow (10, precision['price']),
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'info': id,
});
}
return result;
}
parseTicker (ticker, market = undefined) {
let symbol = undefined;
if (market === undefined) {
const marketId = this.safeString (ticker, 'symbol');
if (marketId in this.markets_by_id) {
const market = this.marketsById[marketId];
symbol = market['symbol'];
} else {
const parts = marketId.split ('_');
let baseId = undefined;
let quoteId = undefined;
const numParts = parts.length;
// lbank will return symbols like "vet_erc20_usdt"
if (numParts > 2) {
baseId = parts[0] + '_' + parts[1];
quoteId = parts[2];
} else {
baseId = parts[0];
quoteId = parts[1];
}
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
symbol = base + '/' + quote;
}
}
const timestamp = this.safeInteger (ticker, 'timestamp');
const info = ticker;
ticker = info['ticker'];
const last = this.safeFloat (ticker, 'latest');
const percentage = this.safeFloat (ticker, 'change');
let open = undefined;
if (percentage !== undefined) {
const relativeChange = this.sum (1, percentage / 100);
if (relativeChange > 0) {
open = last / this.sum (1, relativeChange);
}
}
let change = undefined;
let average = undefined;
if (last !== undefined && open !== undefined) {
change = last - open;
average = this.sum (last, open) / 2;
}
if (market !== undefined) {
symbol = market['symbol'];
}
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeFloat (ticker, 'high'),
'low': this.safeFloat (ticker, 'low'),
'bid': undefined,
'bidVolume': undefined,
'ask': undefined,
'askVolume': undefined,
'vwap': undefined,
'open': undefined,
'close': last,
'last': last,
'previousClose': undefined,
'change': change,
'percentage': percentage,
'average': average,
'baseVolume': this.safeFloat (ticker, 'vol'),
'quoteVolume': this.safeFloat (ticker, 'turnover'),
'info': info,
};
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
const response = await this.publicGetTicker (this.extend (request, params));
return this.parseTicker (response, market);
}
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'symbol': 'all',
};
const response = await this.publicGetTicker (this.extend (request, params));
const result = {};
for (let i = 0; i < response.length; i++) {
const ticker = this.parseTicker (response[i]);
const symbol = ticker['symbol'];
result[symbol] = ticker;
}
return result;
}
async fetchOrderBook (symbol, limit = 60, params = {}) {
await this.loadMarkets ();
let size = 60;
if (limit !== undefined) {
size = Math.min (limit, size);
}
const request = {
'symbol': this.marketId (symbol),
'size': size,
};
const response = await this.publicGetDepth (this.extend (request, params));
return this.parseOrderBook (response);
}
parseTrade (trade, market = undefined) {
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
const timestamp = this.safeInteger (trade, 'date_ms');
const price = this.safeFloat (trade, 'price');
const amount = this.safeFloat (trade, 'amount');
let cost = undefined;
if (price !== undefined) {
if (amount !== undefined) {
cost = parseFloat (this.costToPrecision (symbol, price * amount));
}
}
const id = this.safeString (trade, 'tid');
const type = undefined;
const side = this.safeString (trade, 'type');
return {
'id': id,
'info': this.safeValue (trade, 'info', trade),
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'order': undefined,
'type': type,
'side': side,
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': cost,
'fee': undefined,
};
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
'size': 100,
};
if (since !== undefined) {
request['time'] = parseInt (since);
}
if (limit !== undefined) {
request['size'] = limit;
}
const response = await this.publicGetTrades (this.extend (request, params));
return this.parseTrades (response, market, since, limit);
}
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
return [
ohlcv[0] * 1000,
ohlcv[1],
ohlcv[2],
ohlcv[3],
ohlcv[4],
ohlcv[5],
];
}
async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = 1000, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
if (since === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a `since` argument');
}
if (limit === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a `limit` argument');
}
const request = {
'symbol': market['id'],
'type': this.timeframes[timeframe],
'size': limit,
'time': parseInt (since / 1000),
};
const response = await this.publicGetKline (this.extend (request, params));
return this.parseOHLCVs (response, market, timeframe, since, limit);
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privatePostUserInfo (params);
//
// {
// "result":"true",
// "info":{
// "freeze":{
// "iog":"0.00000000",
// "ssc":"0.00000000",
// "eon":"0.00000000",
// },
// "asset":{
// "iog":"0.00000000",
// "ssc":"0.00000000",
// "eon":"0.00000000",
// },
// "free":{
// "iog":"0.00000000",
// "ssc":"0.00000000",
// "eon":"0.00000000",
// },
// }
// }
//
const result = { 'info': response };
const info = this.safeValue (response, 'info', {});
const free = this.safeValue (info, 'free', {});
const freeze = this.safeValue (info, 'freeze', {});
const asset = this.safeValue (info, 'asset', {});
const currencyIds = Object.keys (free);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeFloat (free, currencyId);
account['used'] = this.safeFloat (freeze, currencyId);
account['total'] = this.safeFloat (asset, currencyId);
result[code] = account;
}
return this.parseBalance (result);
}
parseOrderStatus (status) {
const statuses = {
'-1': 'cancelled', // cancelled
'0': 'open', // not traded
'1': 'open', // partial deal
'2': 'closed', // complete deal
'4': 'closed', // disposal processing
};
return this.safeString (statuses, status);
}
parseOrder (order, market = undefined) {
//
// {
// "symbol":"eth_btc",
// "amount":10.000000,
// "create_time":1484289832081,
// "price":5000.000000,
// "avg_price":5277.301200,
// "type":"sell",
// "order_id":"ab704110-af0d-48fd-a083-c218f19a4a55",
// "deal_amount":10.000000,
// "status":2
// }
//
let symbol = undefined;
const responseMarket = this.safeValue (this.marketsById, order['symbol']);
if (responseMarket !== undefined) {
symbol = responseMarket['symbol'];
} else if (market !== undefined) {
symbol = market['symbol'];
}
const timestamp = this.safeInteger (order, 'create_time');
// Limit Order Request Returns: Order Price
// Market Order Returns: cny amount of market order
const price = this.safeFloat (order, 'price');
const amount = this.safeFloat (order, 'amount', 0.0);
const filled = this.safeFloat (order, 'deal_amount', 0.0);
const av_price = this.safeFloat (order, 'avg_price');
let cost = undefined;
if (av_price !== undefined) {
cost = filled * av_price;
}
const status = this.parseOrderStatus (this.safeString (order, 'status'));
const id = this.safeString (order, 'order_id');
const type = this.safeString (order, 'order_type');
const side = this.safeString (order, 'type');
let remaining = undefined;
if (amount !== undefined) {
if (filled !== undefined) {
remaining = amount - filled;
}
}
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
'status': status,
'symbol': symbol,
'type': type,
'side': side,
'price': price,
'cost': cost,
'amount': amount,
'filled': filled,
'remaining': remaining,
'trades': undefined,
'fee': undefined,
'info': this.safeValue (order, 'info', order),
'average': undefined,
};
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
let order = {
'symbol': market['id'],
'type': side,
'amount': amount,
};
if (type === 'market') {
order['type'] += '_market';
} else {
order['price'] = price;
}
const response = await this.privatePostCreateOrder (this.extend (order, params));
order = this.omit (order, 'type');
order['order_id'] = response['order_id'];
order['type'] = side;
order['order_type'] = type;
order['create_time'] = this.milliseconds ();
order['info'] = response;
order = this.parseOrder (order, market);
const id = order['id'];
this.orders[id] = order;
return order;
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
'order_id': id,
};
const response = await this.privatePostCancelOrder (this.extend (request, params));
return response;
}
async fetchOrder (id, symbol = undefined, params = {}) {
// Id can be a list of ids delimited by a comma
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
'order_id': id,
};
const response = await this.privatePostOrdersInfo (this.extend (request, params));
const orders = this.parseOrders (response['orders'], market);
const numOrders = orders.length;
if (numOrders === 1) {
return orders[0];
} else {
return orders;
}
}
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
if (limit === undefined) {
limit = 100;
}
const market = this.market (symbol);
const request = {
'symbol': market['id'],
'current_page': 1,
'page_length': limit,
};
const response = await this.privatePostOrdersInfoHistory (this.extend (request, params));
return this.parseOrders (response['orders'], undefined, since, limit);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
const orders = await this.fetchOrders (symbol, since, limit, params);
const closed = this.filterBy (orders, 'status', 'closed');
const canceled = this.filterBy (orders, 'status', 'cancelled'); // cancelled orders may be partially filled
const allOrders = this.arrayConcat (closed, canceled);
return this.filterBySymbolSinceLimit (allOrders, symbol, since, limit);
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
// mark and fee are optional params, mark is a note and must be less than 255 characters
this.checkAddress (address);
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'assetCode': currency['id'],
'amount': amount,
'account': address,
};
if (tag !== undefined) {
request['memo'] = tag;
}
const response = this.privatePostWithdraw (this.extend (request, params));
return {
'id': response['id'],
'info': response,
};
}
convertSecretToPem (secret) {
const lineLength = 64;
const secretLength = secret.length - 0;
let numLines = parseInt (secretLength / lineLength);
numLines = this.sum (numLines, 1);
let pem = "-----BEGIN PRIVATE KEY-----\n"; // eslint-disable-line
for (let i = 0; i < numLines; i++) {
const start = i * lineLength;
const end = this.sum (start, lineLength);
pem += this.secret.slice (start, end) + "\n"; // eslint-disable-line
}
return pem + '-----END PRIVATE KEY-----';
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
const query = this.omit (params, this.extractParams (path));
let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
// Every endpoint ends with ".do"
url += '.do';
if (api === 'public') {
if (Object.keys (query).length) {
url += '?' + this.urlencode (query);
}
} else {
this.checkRequiredCredentials ();
const query = this.keysort (this.extend ({
'api_key': this.apiKey,
}, params));
const queryString = this.rawencode (query);
const message = this.hash (this.encode (queryString)).toUpperCase ();
const cacheSecretAsPem = this.safeValue (this.options, 'cacheSecretAsPem', true);
let pem = undefined;
if (cacheSecretAsPem) {
pem = this.safeValue (this.options, 'pem');
if (pem === undefined) {
pem = this.convertSecretToPem (this.secret);
this.options['pem'] = pem;
}
} else {
pem = this.convertSecretToPem (this.secret);
}
const sign = this.binaryToBase64 (this.rsa (message, this.encode (pem), 'RS256'));
query['sign'] = sign;
body = this.urlencode (query);
headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
const response = await this.fetch2 (path, api, method, params, headers, body);
const success = this.safeString (response, 'result');
if (success === 'false') {
const errorCode = this.safeString (response, 'error_code');
const message = this.safeString ({
'10000': 'Internal error',
'10001': 'The required parameters can not be empty',
'10002': 'verification failed',
'10003': 'Illegal parameters',
'10004': 'User requests are too frequent',
'10005': 'Key does not exist',
'10006': 'user does not exist',
'10007': 'Invalid signature',
'10008': 'This currency pair is not supported',
'10009': 'Limit orders can not be missing orders and the number of orders',
'10010': 'Order price or order quantity must be greater than 0',
'10011': 'Market orders can not be missing the amount of the order',
'10012': 'market sell orders can not be missing orders',
'10013': 'is less than the minimum trading position 0.001',
'10014': 'Account number is not enough',
'10015': 'The order type is wrong',
'10016': 'Account balance is not enough',
'10017': 'Abnormal server',
'10018': 'order inquiry can not be more than 50 less than one',
'10019': 'withdrawal orders can not be more than 3 less than one',
'10020': 'less than the minimum amount of the transaction limit of 0.001',
'10022': 'Insufficient key authority',
}, errorCode, this.json (response));
const ErrorClass = this.safeValue ({
'10002': AuthenticationError,
'10004': DDoSProtection,
'10005': AuthenticationError,
'10006': AuthenticationError,
'10007': AuthenticationError,
'10009': InvalidOrder,
'10010': InvalidOrder,
'10011': InvalidOrder,
'10012': InvalidOrder,
'10013': InvalidOrder,
'10014': InvalidOrder,
'10015': InvalidOrder,
'10016': InvalidOrder,
'10022': AuthenticationError,
}, errorCode, ExchangeError);
throw new ErrorClass (message);
}
return response;
}
};