preidman-ccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 100+ exchanges
935 lines (903 loc) • 38.8 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const huobipro = require ('./huobipro.js');
const { ExchangeError, ArgumentsRequired, BadRequest, ExchangeNotAvailable, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, DDoSProtection } = require ('./base/errors');
// ---------------------------------------------------------------------------
module.exports = class cointiger extends huobipro {
describe () {
return this.deepExtend (super.describe (), {
'id': 'cointiger',
'name': 'CoinTiger',
'countries': [ 'CN' ],
'hostname': 'cointiger.pro',
'has': {
'fetchCurrencies': false,
'fetchTickers': true,
'fetchTradingLimits': false,
'fetchOrder': true,
'fetchOrders': true,
'fetchOpenOrders': true,
'fetchClosedOrders': true,
'fetchOrderTrades': false, // not tested yet
'cancelOrders': true,
},
'headers': {
'Language': 'en_US',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/39797261-d58df196-5363-11e8-9880-2ec78ec5bd25.jpg',
'api': {
'public': 'https://api.{hostname}/exchange/trading/api/market',
'private': 'https://api.{hostname}/exchange/trading/api',
'exchange': 'https://www.{hostname}/exchange',
'v2public': 'https://api.{hostname}/exchange/trading/api/v2',
'v2': 'https://api.{hostname}/exchange/trading/api/v2',
},
'www': 'https://www.cointiger.pro',
'referral': 'https://www.cointiger.pro/exchange/register.html?refCode=FfvDtt',
'doc': 'https://github.com/cointiger/api-docs-en/wiki',
},
'api': {
'v2public': {
'get': [
'timestamp',
'currencys',
],
},
'v2': {
'get': [
'order/orders',
'order/match_results',
'order/make_detail',
'order/details',
],
'post': [
'order',
'order/batch_cancel',
],
},
'public': {
'get': [
'history/kline', // 获取K线数据
'detail/merged', // 获取聚合行情(Ticker)
'depth', // 获取 Market Depth 数据
'trade', // 获取 Trade Detail 数据
'history/trade', // 批量获取最近的交易记录
'detail', // 获取 Market Detail 24小时成交量数据
],
},
'exchange': {
'get': [
'footer/tradingrule.html',
'api/public/market/detail',
],
},
'private': {
'get': [
'user/balance',
'order/new',
'order/history',
'order/trade',
],
'post': [
'order',
],
'delete': [
'order',
],
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': 0.0008,
'taker': 0.0015,
},
},
'exceptions': {
// {"code":"1","msg":"系统错误","data":null}
// {"code":"1","msg":"Balance insufficient,余额不足","data":null}
'1': ExchangeError,
'2': BadRequest, // {"code":"2","msg":"Parameter error","data":null}
'5': InvalidOrder,
'6': InvalidOrder,
'8': OrderNotFound,
'16': AuthenticationError, // funding password not set
'100001': ExchangeError,
'100002': ExchangeNotAvailable,
'100003': ExchangeError,
'100005': AuthenticationError,
'110030': DDoSProtection,
},
'commonCurrencies': {
'FGC': 'FoundGameCoin',
'TCT': 'The Tycoon Chain Token',
},
});
}
async fetchMarkets (params = {}) {
const response = await this.v2publicGetCurrencys ();
//
// {
// code: '0',
// msg: 'suc',
// data: {
// 'bitcny-partition': [
// {
// baseCurrency: 'btc',
// quoteCurrency: 'bitcny',
// pricePrecision: 2,
// amountPrecision: 4,
// withdrawFeeMin: 0.0005,
// withdrawFeeMax: 0.005,
// withdrawOneMin: 0.01,
// withdrawOneMax: 10,
// depthSelect: { step0: '0.01', step1: '0.1', step2: '1' }
// },
// ...
// ],
// ...
// },
// }
//
const keys = Object.keys (response['data']);
const result = [];
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let partition = response['data'][key];
for (let j = 0; j < partition.length; j++) {
let market = partition[j];
let baseId = this.safeString (market, 'baseCurrency');
let quoteId = this.safeString (market, 'quoteCurrency');
let base = baseId.toUpperCase ();
let quote = quoteId.toUpperCase ();
base = this.commonCurrencyCode (base);
quote = this.commonCurrencyCode (quote);
let id = baseId + quoteId;
let uppercaseId = id.toUpperCase ();
let symbol = base + '/' + quote;
let precision = {
'amount': market['amountPrecision'],
'price': market['pricePrecision'],
};
let active = true;
let entry = {
'id': id,
'uppercaseId': uppercaseId,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'info': market,
'active': active,
'precision': precision,
'limits': {
'amount': {
'min': Math.pow (10, -precision['amount']),
'max': undefined,
},
'price': {
'min': Math.pow (10, -precision['price']),
'max': undefined,
},
'cost': {
'min': 0,
'max': undefined,
},
},
};
result.push (entry);
}
}
this.options['marketsByUppercaseId'] = this.indexBy (result, 'uppercaseId');
return result;
}
parseTicker (ticker, market = undefined) {
let symbol = undefined;
if (market)
symbol = market['symbol'];
let timestamp = this.safeInteger (ticker, 'id');
let close = this.safeFloat (ticker, 'last');
let percentage = this.safeFloat (ticker, 'percentChange');
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeFloat (ticker, 'high24hr'),
'low': this.safeFloat (ticker, 'low24hr'),
'bid': this.safeFloat (ticker, 'highestBid'),
'bidVolume': undefined,
'ask': this.safeFloat (ticker, 'lowestAsk'),
'askVolume': undefined,
'vwap': undefined,
'open': undefined,
'close': close,
'last': close,
'previousClose': undefined,
'change': undefined,
'percentage': percentage,
'average': undefined,
'baseVolume': this.safeFloat (ticker, 'baseVolume'),
'quoteVolume': this.safeFloat (ticker, 'quoteVolume'),
'info': ticker,
};
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
let market = this.market (symbol);
let response = await this.publicGetDepth (this.extend ({
'symbol': market['id'], // this endpoint requires a lowercase market id
'type': 'step0',
}, params));
let data = response['data']['depth_data'];
if ('tick' in data) {
if (!data['tick']) {
throw new ExchangeError (this.id + ' fetchOrderBook() returned empty response: ' + this.json (response));
}
let orderbook = data['tick'];
let timestamp = data['ts'];
return this.parseOrderBook (orderbook, timestamp, 'buys');
}
throw new ExchangeError (this.id + ' fetchOrderBook() returned unrecognized response: ' + this.json (response));
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
let market = this.market (symbol);
let marketId = market['uppercaseId'];
let response = await this.exchangeGetApiPublicMarketDetail (params);
if (!(marketId in response))
throw new ExchangeError (this.id + ' fetchTicker symbol ' + symbol + ' (' + marketId + ') not found');
return this.parseTicker (response[marketId], market);
}
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
let response = await this.exchangeGetApiPublicMarketDetail (params);
let result = {};
let ids = Object.keys (response);
for (let i = 0; i < ids.length; i++) {
let id = ids[i];
let market = undefined;
let symbol = id;
if (id in this.options['marketsByUppercaseId']) {
// this endpoint returns uppercase ids
symbol = this.options['marketsByUppercaseId'][id]['symbol'];
market = this.options['marketsByUppercaseId'][id];
}
result[symbol] = this.parseTicker (response[id], market);
}
return result;
}
parseTrade (trade, market = undefined) {
//
// { volume: "0.014",
// symbol: "ethbtc",
// buy_fee: "0.00001400",
// orderId: 32235710,
// price: "0.06923825",
// created: 1531605169000,
// id: 3785005,
// source: 1,
// type: "buy-limit",
// bid_user_id: 326317 } ] }
//
// --------------------------------------------------------------------
//
// {
// "volume": {
// "amount": "1.000",
// "icon": "",
// "title": "成交量"
// },
// "price": {
// "amount": "0.04978883",
// "icon": "",
// "title": "委托价格"
// },
// "created_at": 1513245134000,
// "deal_price": {
// "amount": 0.04978883000000000000000000000000,
// "icon": "",
// "title": "成交价格"
// },
// "id": 138
// }
//
let id = this.safeString (trade, 'id');
let orderId = this.safeString (trade, 'orderId');
let orderType = this.safeString (trade, 'type');
let type = undefined;
let side = undefined;
if (orderType !== undefined) {
let parts = orderType.split ('-');
side = parts[0];
type = parts[1];
}
side = this.safeString (trade, 'side', side);
let amount = undefined;
let price = undefined;
let cost = undefined;
if (side === undefined) {
price = this.safeFloat (trade['price'], 'amount');
amount = this.safeFloat (trade['volume'], 'amount');
cost = this.safeFloat (trade['deal_price'], 'amount');
} else {
side = side.toLowerCase ();
price = this.safeFloat (trade, 'price');
amount = this.safeFloat2 (trade, 'amount', 'volume');
}
let fee = undefined;
let feeCost = this.safeFloat (trade, 'fee');
if (feeCost !== undefined) {
let feeCurrency = undefined;
if (market !== undefined) {
if (side === 'buy') {
feeCurrency = market['base'];
} else if (side === 'sell') {
feeCurrency = market['quote'];
}
}
fee = {
'cost': feeCost,
'currency': feeCurrency,
};
}
if (amount !== undefined)
if (price !== undefined)
if (cost === undefined)
cost = amount * price;
let timestamp = this.safeInteger2 (trade, 'created_at', 'ts');
timestamp = this.safeInteger2 (trade, 'created', 'mtime', timestamp);
let symbol = undefined;
if (market !== undefined)
symbol = market['symbol'];
return {
'info': trade,
'id': id,
'order': orderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'type': type,
'side': side,
'price': price,
'amount': amount,
'cost': cost,
'fee': fee,
};
}
async fetchTrades (symbol, since = undefined, limit = 1000, params = {}) {
await this.loadMarkets ();
let market = this.market (symbol);
let request = {
'symbol': market['id'],
};
if (limit !== undefined)
request['size'] = limit;
let response = await this.publicGetHistoryTrade (this.extend (request, params));
return this.parseTrades (response['data']['trade_data'], market, since, limit);
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined)
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
await this.loadMarkets ();
let market = this.market (symbol);
if (limit === undefined)
limit = 100;
let response = await this.privateGetOrderTrade (this.extend ({
'symbol': market['id'],
'offset': 1,
'limit': limit,
}, params));
return this.parseTrades (response['data']['list'], market, since, limit);
}
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
return [
ohlcv['id'] * 1000,
ohlcv['open'],
ohlcv['high'],
ohlcv['low'],
ohlcv['close'],
ohlcv['vol'],
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 1000, params = {}) {
await this.loadMarkets ();
let market = this.market (symbol);
let request = {
'symbol': market['id'],
'period': this.timeframes[timeframe],
};
if (limit !== undefined) {
request['size'] = limit;
}
let response = await this.publicGetHistoryKline (this.extend (request, params));
return this.parseOHLCVs (response['data']['kline_data'], market, timeframe, since, limit);
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
let response = await this.privateGetUserBalance (params);
//
// {
// "code": "0",
// "msg": "suc",
// "data": [{
// "normal": "1813.01144179",
// "lock": "1325.42036785",
// "coin": "btc"
// }, {
// "normal": "9551.96692244",
// "lock": "547.06506717",
// "coin": "eth"
// }]
// }
//
let balances = response['data'];
let result = { 'info': response };
for (let i = 0; i < balances.length; i++) {
let balance = balances[i];
let id = balance['coin'];
let code = id.toUpperCase ();
code = this.commonCurrencyCode (code);
if (id in this.currencies_by_id) {
code = this.currencies_by_id[id]['code'];
}
let account = this.account ();
account['used'] = parseFloat (balance['lock']);
account['free'] = parseFloat (balance['normal']);
account['total'] = this.sum (account['used'], account['free']);
result[code] = account;
}
return this.parseBalance (result);
}
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrderTrades requires a symbol argument');
}
await this.loadMarkets ();
let market = this.market (symbol);
let request = {
'symbol': market['id'],
'order_id': id,
};
let response = await this.v2GetOrderMakeDetail (this.extend (request, params));
//
// the above endpoint often returns an empty array
//
// { code: "0",
// msg: "suc",
// data: [ { volume: "0.014",
// symbol: "ethbtc",
// buy_fee: "0.00001400",
// orderId: 32235710,
// price: "0.06923825",
// created: 1531605169000,
// id: 3785005,
// source: 1,
// type: "buy-limit",
// bid_user_id: 326317 } ] }
//
return this.parseTrades (response['data'], market, since, limit);
}
async fetchOrdersByStatusV1 (status = undefined, symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined)
throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument');
await this.loadMarkets ();
let market = this.market (symbol);
if (limit === undefined)
limit = 100;
let method = (status === 'open') ? 'privateGetOrderNew' : 'privateGetOrderHistory';
let response = await this[method] (this.extend ({
'symbol': market['id'],
'offset': 1,
'limit': limit,
}, params));
let orders = response['data']['list'];
let result = [];
for (let i = 0; i < orders.length; i++) {
let order = this.extend (orders[i], {
'status': status,
});
result.push (this.parseOrder (order, market));
}
return result;
}
async fetchOpenOrdersV1 (symbol = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchOrdersByStatusV1 ('open', symbol, since, limit, params);
}
async fetchOrdersV1 (symbol = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchOrdersByStatusV1 (undefined, symbol, since, limit, params);
}
async fetchOrdersByStatesV2 (states, symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined)
throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument');
await this.loadMarkets ();
let market = this.market (symbol);
if (limit === undefined)
limit = 50;
let response = await this.v2GetOrderOrders (this.extend ({
'symbol': market['id'],
// 'types': 'buy-market,sell-market,buy-limit,sell-limit',
'states': states, // 'new,part_filled,filled,canceled,expired'
// 'from': '0', // id
'direct': 'next', // or 'prev'
'size': limit,
}, params));
return this.parseOrders (response['data'], market, since, limit);
}
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchOrdersByStatesV2 ('new,part_filled,filled,canceled,expired', symbol, since, limit, params);
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchOrdersByStatesV2 ('new,part_filled', symbol, since, limit, params);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchOrdersByStatesV2 ('filled,canceled', symbol, since, limit, params);
}
async fetchOrder (id, symbol = undefined, params = {}) {
//
// { code: "0",
// msg: "suc",
// data: { symbol: "ethbtc",
// fee: "0.00000200",
// avg_price: "0.06863752",
// source: 1,
// type: "buy-limit",
// mtime: 1531340305000,
// volume: "0.002",
// user_id: 326317,
// price: "0.06863752",
// ctime: 1531340304000,
// deal_volume: "0.00200000",
// id: 31920243,
// deal_money: "0.00013727",
// status: 2 } }
//
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
}
await this.loadMarkets ();
let market = this.market (symbol);
let request = {
'symbol': market['id'],
'order_id': id.toString (),
};
let response = await this.v2GetOrderDetails (this.extend (request, params));
return this.parseOrder (response['data'], market);
}
parseOrderStatus (status) {
let statuses = {
'0': 'open', // pending
'1': 'open',
'2': 'closed',
'3': 'open',
'4': 'canceled',
'6': 'error',
};
if (status in statuses)
return statuses[status];
return status;
}
parseOrder (order, market = undefined) {
//
// v1
//
// {
// volume: { "amount": "0.054", "icon": "", "title": "volume" },
// age_price: { "amount": "0.08377697", "icon": "", "title": "Avg price" },
// side: "BUY",
// price: { "amount": "0.00000000", "icon": "", "title": "price" },
// created_at: 1525569480000,
// deal_volume: { "amount": "0.64593598", "icon": "", "title": "Deal volume" },
// "remain_volume": { "amount": "1.00000000", "icon": "", "title": "尚未成交"
// id: 26834207,
// label: { go: "trade", title: "Traded", click: 1 },
// side_msg: "Buy"
// },
//
// v2
//
// { code: "0",
// msg: "suc",
// data: { symbol: "ethbtc",
// fee: "0.00000200",
// avg_price: "0.06863752",
// source: 1,
// type: "buy-limit",
// mtime: 1531340305000,
// volume: "0.002",
// user_id: 326317,
// price: "0.06863752",
// ctime: 1531340304000,
// deal_volume: "0.00200000",
// id: 31920243,
// deal_money: "0.00013727",
// status: 2 } }
//
let id = this.safeString (order, 'id');
let side = this.safeString (order, 'side');
let type = undefined;
let orderType = this.safeString (order, 'type');
let status = this.parseOrderStatus (this.safeString (order, 'status'));
let timestamp = this.safeInteger2 (order, 'created_at', 'ctime');
let lastTradeTimestamp = this.safeInteger2 (order, 'mtime', 'finished-at');
let symbol = undefined;
if (market === undefined) {
let marketId = this.safeString (order, 'symbol');
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
}
}
if (market !== undefined) {
symbol = market['symbol'];
}
let remaining = undefined;
let amount = undefined;
let filled = undefined;
let price = undefined;
let cost = undefined;
let fee = undefined;
let average = undefined;
if (side !== undefined) {
side = side.toLowerCase ();
amount = this.safeFloat (order['volume'], 'amount');
remaining = ('remain_volume' in order) ? this.safeFloat (order['remain_volume'], 'amount') : undefined;
filled = ('deal_volume' in order) ? this.safeFloat (order['deal_volume'], 'amount') : undefined;
price = ('price' in order) ? this.safeFloat (order['price'], 'amount') : undefined;
average = ('age_price' in order) ? this.safeFloat (order['age_price'], 'amount') : undefined;
} else {
if (orderType !== undefined) {
let parts = orderType.split ('-');
side = parts[0];
type = parts[1];
cost = this.safeFloat (order, 'deal_money');
price = this.safeFloat (order, 'price');
average = this.safeFloat (order, 'avg_price');
amount = this.safeFloat2 (order, 'amount', 'volume');
filled = this.safeFloat (order, 'deal_volume');
let feeCost = this.safeFloat (order, 'fee');
if (feeCost !== undefined) {
let feeCurrency = undefined;
if (market !== undefined) {
if (side === 'buy') {
feeCurrency = market['base'];
} else if (side === 'sell') {
feeCurrency = market['quote'];
}
}
fee = {
'cost': feeCost,
'currency': feeCurrency,
};
}
}
}
if (amount !== undefined) {
if (remaining !== undefined) {
if (filled === undefined)
filled = Math.max (0, amount - remaining);
} else if (filled !== undefined) {
cost = filled * price;
if (remaining === undefined)
remaining = Math.max (0, amount - filled);
}
}
if (status === undefined) {
if (remaining !== undefined) {
if (remaining === 0) {
status = 'closed';
}
}
}
let result = {
'info': order,
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
'symbol': symbol,
'type': type,
'side': side,
'price': price,
'average': average,
'cost': cost,
'amount': amount,
'filled': filled,
'remaining': remaining,
'status': status,
'fee': fee,
'trades': undefined,
};
return result;
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
if (!this.password) {
throw new AuthenticationError (this.id + ' createOrder requires exchange.password to be set to user trading password (not login password!)');
}
this.checkRequiredCredentials ();
let market = this.market (symbol);
let orderType = (type === 'limit') ? 1 : 2;
let order = {
'symbol': market['id'],
'side': side.toUpperCase (),
'type': orderType,
'volume': this.amountToPrecision (symbol, amount),
'capital_password': this.password,
};
if ((type === 'market') && (side === 'buy')) {
if (price === undefined) {
throw new InvalidOrder (this.id + ' createOrder requires price argument for market buy orders to calculate total cost according to exchange rules');
}
order['volume'] = this.amountToPrecision (symbol, parseFloat (amount) * parseFloat (price));
}
if (type === 'limit') {
order['price'] = this.priceToPrecision (symbol, price);
} else {
if (price === undefined) {
order['price'] = this.priceToPrecision (symbol, 0);
} else {
order['price'] = this.priceToPrecision (symbol, price);
}
}
let response = await this.privatePostOrder (this.extend (order, params));
//
// { "order_id":34343 }
//
let timestamp = this.milliseconds ();
return {
'info': response,
'id': response['data']['order_id'].toString (),
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
'status': undefined,
'symbol': symbol,
'type': type,
'side': side,
'price': price,
'amount': amount,
'filled': undefined,
'remaining': undefined,
'cost': undefined,
'trades': undefined,
'fee': undefined,
};
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
if (symbol === undefined)
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument');
let market = this.market (symbol);
let response = await this.privateDeleteOrder (this.extend ({
'symbol': market['id'],
'order_id': id,
}, params));
return {
'id': id,
'symbol': symbol,
'info': response,
};
}
async cancelOrders (ids, symbol = undefined, params = {}) {
await this.loadMarkets ();
if (symbol === undefined)
throw new ArgumentsRequired (this.id + ' cancelOrders requires a symbol argument');
let market = this.market (symbol);
let marketId = market['id'];
let orderIdList = {};
orderIdList[marketId] = ids;
let request = {
'orderIdList': this.json (orderIdList),
};
let response = await this.v2PostOrderBatchCancel (this.extend (request, params));
return {
'info': response,
};
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
this.checkRequiredCredentials ();
let url = this.implodeParams (this.urls['api'][api], {
'hostname': this.hostname,
});
url += '/' + this.implodeParams (path, params);
if (api === 'private' || api === 'v2') {
let timestamp = this.milliseconds ().toString ();
let query = this.keysort (this.extend ({
'time': timestamp,
}, params));
let keys = Object.keys (query);
let auth = '';
for (let i = 0; i < keys.length; i++) {
auth += keys[i] + query[keys[i]].toString ();
}
auth += this.secret;
let signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512');
let urlParams = (method === 'POST') ? {} : query;
url += '?' + this.urlencode (this.keysort (this.extend ({
'api_key': this.apiKey,
'time': timestamp,
}, urlParams)));
url += '&sign=' + signature;
if (method === 'POST') {
body = this.urlencode (query);
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
}
} else if (api === 'public' || api === 'v2public') {
url += '?' + this.urlencode (this.extend ({
'api_key': this.apiKey,
}, params));
} else {
if (Object.keys (params).length)
url += '?' + this.urlencode (params);
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors (httpCode, reason, url, method, headers, body, response) {
if (typeof body !== 'string')
return; // fallback to default error handler
if (body.length < 2)
return; // fallback to default error handler
if ((body[0] === '{') || (body[0] === '[')) {
if ('code' in response) {
//
// { "code": "100005", "msg": "request sign illegal", "data": null }
//
let code = this.safeString (response, 'code');
if (code !== undefined) {
const message = this.safeString (response, 'msg');
const feedback = this.id + ' ' + this.json (response);
if (code !== '0') {
const exceptions = this.exceptions;
if (code in exceptions) {
if (code === '1') {
//
// {"code":"1","msg":"系统错误","data":null}
// {“code”:“1",“msg”:“Balance insufficient,余额不足“,”data”:null}
//
if (message.indexOf ('Balance insufficient') >= 0) {
throw new InsufficientFunds (feedback);
}
} else if (code === '2') {
if (message === 'offsetNot Null') {
throw new ExchangeError (feedback);
} else if (message === 'api_keyNot EXIST') {
throw new AuthenticationError (feedback);
} else if (message === 'price precision exceed the limit') {
throw new InvalidOrder (feedback);
} else if (message === 'Parameter error') {
throw new BadRequest (feedback);
}
}
throw new exceptions[code] (feedback);
} else {
throw new ExchangeError (this.id + ' unknown "error" value: ' + this.json (response));
}
} else {
//
// Google Translate:
// 订单状态不能取消,订单取消失败 = Order status cannot be canceled
// 根据订单号没有查询到订单,订单取消失败 = The order was not queried according to the order number
//
// {"code":"0","msg":"suc","data":{"success":[],"failed":[{"err-msg":"订单状态不能取消,订单取消失败","order-id":32857051,"err-code":"8"}]}}
// {"code":"0","msg":"suc","data":{"success":[],"failed":[{"err-msg":"Parameter error","order-id":32857050,"err-code":"2"},{"err-msg":"订单状态不能取消,订单取消失败","order-id":32857050,"err-code":"8"}]}}
// {"code":"0","msg":"suc","data":{"success":[],"failed":[{"err-msg":"Parameter error","order-id":98549677,"err-code":"2"},{"err-msg":"根据订单号没有查询到订单,订单取消失败","order-id":98549677,"err-code":"8"}]}}
//
if (feedback.indexOf ('订单状态不能取消,订单取消失败') >= 0) {
if (feedback.indexOf ('Parameter error') >= 0) {
throw new OrderNotFound (feedback);
} else {
throw new InvalidOrder (feedback);
}
} else if (feedback.indexOf ('根据订单号没有查询到订单,订单取消失败') >= 0) {
throw new OrderNotFound (feedback);
}
}
}
}
}
}
};