Bug Fixes

This commit is contained in:
Luis Fernando Molina committed 2020-02-09 16:35:17 +01:00
1 parent 82bc086e92
commit b1af6e078c
131 files changed
+8597 -1930

No files matched your search

+94
View File
@@ -0,0 +1,94 @@
"use strict"
const { execSync } = require ('child_process')
const log = require ('ololog').noLocate
const { groupBy } = require ('../ccxt.js')
const { values } = Object
const assert = require ('assert')
function cleanupOldTags () {
const tags = execSync ('git tag').toString ().split ('\n').filter (s => s).map (t => {
const [major, minor, patch] = t.replace ('v', '').split ('.').map (Number)
assert (major < 100)
assert (minor < 100)
return {
key: (major * 100) + minor,
tag: t,
major,
minor,
patch,
}
})
const tagsByMajorMinor = values (groupBy (tags, 'key')).sort ((a, b) => a[0].key - b[0].key)
// Preserve all tags for first 3 minor versions
for (let i = 0; i < 3; i++) {
const tags = tagsByMajorMinor.pop ()
if (tags) {
log.green.bright ('Preserving', tags[0].tag, '...', tags[tags.length - 1].tag)
}
}
// For older versions, leave only "round" numbered versions (1/10th)
let tagsToDelete = []
for (const tags of tagsByMajorMinor) {
for (const { tag, patch } of tags) {
if (patch === 1) {
log.green ('Preserving', tag)
} else {
tagsToDelete.push (tag)
}
}
}
log.bright.red ('Deleting', tagsToDelete.length, 'tags...')
if (!process.argv.includes ('--paper')) {
/* If it happens on a CI server, we don't want it to fail the build because of a super
long execution time (one tag deletion takes ~5 sec...), hence that limit here */
if (process.argv.includes ('--limit')) {
tagsToDelete = tagsToDelete.slice (-500)
}
for (const tag of tagsToDelete) {
log.dim ('Deleting', tag)
execSync (`git tag -d ${tag} && git push origin :refs/tags/${tag}`)
}
}
}
// ============================================================================
// main entry point
if (require.main === module) {
// if called directly like `node module`
cleanupOldTags ()
} else {
// do nothing if required as a module
}
// ============================================================================
module.exports = {
cleanupOldTags,
}
+28 -12
View File
@@ -447,15 +447,19 @@ class Transpiler {
return 'class ' + className + '(' + baseClass + '):'
}
createPythonClassHeader (ccxtImports, bodyAsString) {
createPythonClassHeader (imports, bodyAsString) {
return [
"# -*- coding: utf-8 -*-\n",
"# -*- coding: utf-8 -*-",
"",
"# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:",
"# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code\n",
... ccxtImports,
"# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code",
"",
... imports,
// 'from ' + importFrom + ' import ' + baseClass,
... (bodyAsString.match (/basestring/) ? [
"\n# -----------------------------------------------------------------------------\n",
"",
"# -----------------------------------------------------------------------------",
"",
"try:",
" basestring # Python 3",
"except NameError:",
@@ -464,6 +468,21 @@ class Transpiler {
]
}
createPythonClassImports (baseClass, async = false) {
const baseClasses = {
'Exchange': 'base.exchange',
}
async = (async ? '.async_support' : '')
return [
(baseClass.indexOf ('ccxt.') === 0) ?
('import ccxt' + async + ' as ccxt') :
('from ccxt' + async + '.' + safeString (baseClasses, baseClass, baseClass) + ' import ' + baseClass)
]
}
createPythonClass (className, baseClass, body, methods, async = false) {
const pythonStandardLibraries = {
@@ -471,6 +490,7 @@ class Transpiler {
'hashlib': 'hashlib',
'math': 'math',
'json.loads': 'json',
'sys': 'sys',
}
const baseClasses = {
@@ -479,20 +499,16 @@ class Transpiler {
async = (async ? '.async_support' : '')
const ccxtImports = [
(baseClass.indexOf ('ccxt.') === 0) ?
('import ccxt' + async + ' as ccxt') :
('from ccxt' + async + '.' + safeString (baseClasses, baseClass, baseClass) + ' import ' + baseClass)
]
const imports = this.createPythonClassImports (baseClass, async)
let bodyAsString = body.join ("\n")
let header = this.createPythonClassHeader (ccxtImports, bodyAsString)
let header = this.createPythonClassHeader (imports, bodyAsString)
const libraries = []
for (let library in pythonStandardLibraries) {
const regex = new RegExp ("[^\\']" + library + "[^\\'a-zA-Z]")
const regex = new RegExp ("[^\\'a-zA-Z]" + library + "[^\\'a-zA-Z]")
if (bodyAsString.match (regex))
libraries.push ('import ' + pythonStandardLibraries[library])
}
+1 -2
View File
@@ -35,7 +35,7 @@ const Exchange = require ('./js/base/Exchange')
//-----------------------------------------------------------------------------
// this is updated by vss.js when building
const version = '1.21.82'
const version = '1.22.39'
Exchange.ccxtVersion = version
@@ -45,7 +45,6 @@ const exchanges = {
'_1btcxe': require ('./js/_1btcxe.js'),
'acx': require ('./js/acx.js'),
'adara': require ('./js/adara.js'),
'allcoin': require ('./js/allcoin.js'),
'anxpro': require ('./js/anxpro.js'),
'bcex': require ('./js/bcex.js'),
'bequant': require ('./js/bequant.js'),
+710 -539
View File
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -1,6 +1,6 @@
"use strict";
/* ------------------------------------------------------------------------ */
// ----------------------------------------------------------------------------
const functions = require ('./functions')
@@ -411,7 +411,7 @@ module.exports = class Exchange {
}
setSandboxMode (enabled) {
if (!!enabled) {
if (!!enabled) { // eslint-disable-line no-extra-boolean-cast
if ('test' in this.urls) {
if (typeof this.urls['api'] === 'string') {
this.urls['api_backup'] = this.urls['api']
@@ -637,7 +637,7 @@ module.exports = class Exchange {
'limits': this.limits,
'precision': this.precision,
}, this.fees['trading'], market))
this.markets = deepExtend (this.markets, indexBy (values, 'symbol'))
this.markets = indexBy (values, 'symbol')
this.marketsById = indexBy (markets, 'id')
this.markets_by_id = this.marketsById
this.symbols = Object.keys (this.markets).sort ()
@@ -1026,9 +1026,9 @@ module.exports = class Exchange {
return this.markets;
}
filterBySinceLimit (array, since = undefined, limit = undefined) {
filterBySinceLimit (array, since = undefined, limit = undefined, key = 'timestamp') {
if (since !== undefined && since !== null)
array = array.filter (entry => entry.timestamp >= since)
array = array.filter (entry => entry[key] >= since)
if (limit !== undefined && limit !== null)
array = array.slice (0, limit)
return array
+15 -6
View File
@@ -171,8 +171,8 @@ module.exports = class bibox extends Exchange {
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': base,
'quoteId': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': true,
'info': market,
'precision': precision,
@@ -400,7 +400,7 @@ module.exports = class bibox extends Exchange {
for (let i = 0; i < currencies.length; i++) {
const currency = currencies[i];
const id = this.safeString (currency, 'symbol');
const name = this.safeString (currency, 'name');
const name = currency['name']; // contains hieroglyphs causing python ASCII bug
const code = this.safeCurrencyCode (id);
const precision = 8;
const deposit = this.safeValue (currency, 'enable_deposit');
@@ -818,12 +818,21 @@ module.exports = class bibox extends Exchange {
const response = await this.privatePostTransfer (request);
//
// {
// "result":"3Jx6RZ9TNMsAoy9NUzBwZf68QBppDruSKW","cmd":"transfer/transferIn"
// }
//
// {
// "result":"{\"account\":\"PERSONALLY OMITTED\",\"memo\":\"PERSONALLY OMITTED\"}","cmd":"transfer/transferIn"
// }
//
const result = JSON.parse (this.safeString (response, 'result'));
const address = this.safeString (result, 'account');
const tag = this.safeString (result, 'memo');
const result = this.safeString (response, 'result');
let address = result;
let tag = undefined;
if (this.isJsonEncodedObject (result)) {
const parsed = JSON.parse (result);
address = this.safeString (parsed, 'account');
tag = this.safeString (parsed, 'memo');
}
return {
'currency': code,
'address': address,
+51 -14
View File
@@ -85,6 +85,7 @@ module.exports = class binance extends Exchange {
// the API structure below will need 3-layer apidefs
'sapi': {
'get': [
'accountSnapshot',
// these endpoints require this.apiKey
'margin/asset',
'margin/pair',
@@ -108,13 +109,30 @@ module.exports = class binance extends Exchange {
'futures/transfer',
// https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi
'capital/config/getall', // get networks for withdrawing USDT ERC20 vs USDT Omni
'capital/deposit/address',
'capital/deposit/hisrec',
'capital/deposit/subAddress',
'capital/deposit/subHisrec',
'capital/withdraw/history',
'sub-account/futures/account',
'sub-account/futures/accountSummary',
'sub-account/futures/positionRisk',
'sub-account/margin/account',
'sub-account/margin/accountSummary',
'sub-account/status',
],
'post': [
'asset/dust',
'account/disableFastWithdrawSwitch',
'account/enableFastWithdrawSwitch',
'capital/withdraw/apply',
'margin/transfer',
'margin/loan',
'margin/repay',
'margin/order',
'sub-account/margin/enable',
'sub-account/margin/enable',
'sub-account/futures/enable',
'userDataStream',
'futures/transfer',
],
@@ -185,6 +203,7 @@ module.exports = class binance extends Exchange {
],
'delete': [
'order',
'allOpenOrders',
],
},
'v3': {
@@ -849,6 +868,8 @@ module.exports = class binance extends Exchange {
if (this.options['fetchTradesMethod'] === 'publicGetAggTrades') {
if (since !== undefined) {
request['startTime'] = since;
// https://github.com/ccxt/ccxt/issues/6400
// https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list
request['endTime'] = this.sum (since, 3600000);
}
}
@@ -1039,7 +1060,7 @@ module.exports = class binance extends Exchange {
'type': uppercaseType,
'side': side.toUpperCase (),
};
if (type === 'market') {
if (uppercaseType === 'MARKET') {
const quoteOrderQty = this.safeFloat (params, 'quoteOrderQty');
if (quoteOrderQty !== undefined) {
request['quoteOrderQty'] = this.costToPrecision (symbol, quoteOrderQty);
@@ -1707,23 +1728,39 @@ module.exports = class binance extends Exchange {
}
const userDataStream = ((path === 'userDataStream') || (path === 'listenKey'));
if (path === 'historicalTrades') {
headers = {
'X-MBX-APIKEY': this.apiKey,
};
if (this.apiKey) {
headers = {
'X-MBX-APIKEY': this.apiKey,
};
} else {
throw new AuthenticationError (this.id + ' historicalTrades endpoint requires `apiKey` credential');
}
} else if (userDataStream) {
// v1 special case for userDataStream
body = this.urlencode (params);
headers = {
'X-MBX-APIKEY': this.apiKey,
'Content-Type': 'application/x-www-form-urlencoded',
};
if (this.apiKey) {
// v1 special case for userDataStream
body = this.urlencode (params);
headers = {
'X-MBX-APIKEY': this.apiKey,
'Content-Type': 'application/x-www-form-urlencoded',
};
} else {
throw new AuthenticationError (this.id + ' userDataStream endpoint requires `apiKey` credential');
}
}
if ((api === 'private') || (api === 'sapi') || (api === 'wapi' && path !== 'systemStatus') || (api === 'fapiPrivate')) {
this.checkRequiredCredentials ();
let query = this.urlencode (this.extend ({
'timestamp': this.nonce (),
'recvWindow': this.options['recvWindow'],
}, params));
let query = undefined;
if ((api === 'sapi') && (path === 'asset/dust')) {
query = this.urlencodeWithArrayRepeat (this.extend ({
'timestamp': this.nonce (),
'recvWindow': this.options['recvWindow'],
}, params));
} else {
query = this.urlencode (this.extend ({
'timestamp': this.nonce (),
'recvWindow': this.options['recvWindow'],
}, params));
}
const signature = this.hmac (this.encode (query), this.encode (this.secret));
query += '&' + 'signature=' + signature;
headers = {
+9 -1
View File
@@ -543,11 +543,19 @@ module.exports = class bitfinex extends Exchange {
} else {
key = 'base';
}
const code = market[key];
const currency = this.safeValue (this.currencies, code);
if (currency !== undefined) {
const precision = this.safeInteger (currency, 'precision');
if (precision !== undefined) {
cost = parseFloat (this.currencyToPrecision (code, cost));
}
}
return {
'type': takerOrMaker,
'currency': market[key],
'rate': rate,
'cost': parseFloat (this.currencyToPrecision (market[key], cost)),
'cost': cost,
};
}
+1
View File
@@ -33,6 +33,7 @@ module.exports = class bitfinex2 extends bitfinex {
'fetchTickers': true,
'fetchTradingFee': false,
'fetchTradingFees': false,
'fetchTransactions': false,
'withdraw': true,
},
'timeframes': {
+2 -2
View File
@@ -31,7 +31,7 @@ module.exports = class bitforex extends Exchange {
},
'timeframes': {
'1m': '1min',
'5m': 'M5',
'5m': '5min',
'15m': '15min',
'30m': '30min',
'1h': '1hour',
@@ -46,7 +46,7 @@ module.exports = class bitforex extends Exchange {
'logo': 'https://user-images.githubusercontent.com/1294454/44310033-69e9e600-a3d8-11e8-873d-54d74d1bc4e4.jpg',
'api': 'https://api.bitforex.com',
'www': 'https://www.bitforex.com',
'doc': 'https://github.com/bitforexapi/API_Docs/wiki',
'doc': 'https://github.com/githubdev2020/API_Doc_en/wiki',
'fees': 'https://help.bitforex.com/en_us/?cat=13',
'referral': 'https://www.bitforex.com/en/invitationRegister?inviterId=1867438',
},
+13 -2
View File
@@ -165,8 +165,19 @@ module.exports = class btcalpha extends Exchange {
request['limit_sell'] = limit;
request['limit_buy'] = limit;
}
const reponse = await this.publicGetOrderbookPairName (this.extend (request, params));
return this.parseOrderBook (reponse, undefined, 'buy', 'sell', 'price', 'amount');
const response = await this.publicGetOrderbookPairName (this.extend (request, params));
return this.parseOrderBook (response, undefined, 'buy', 'sell', 'price', 'amount');
}
parseBidsAsks (bidasks, priceKey = 0, amountKey = 1) {
const result = [];
for (let i = 0; i < bidasks.length; i++) {
const bidask = bidasks[i];
if (bidask) {
result.push (this.parseBidAsk (bidask, priceKey, amountKey));
}
}
return result;
}
parseTrade (trade, market = undefined) {
+24 -2
View File
@@ -28,7 +28,7 @@ module.exports = class bw extends Exchange {
'editOrder': false,
'fetchBalance': true,
'fetchBidsAsks': false,
'fetchClosedOrders': false,
'fetchClosedOrders': true,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDeposits': true,
@@ -119,6 +119,7 @@ module.exports = class bw extends Exchange {
'exchange/entrust/controller/website/EntrustController/getUserEntrustList',
'exchange/fund/controller/website/fundwebsitecontroller/getwithdrawaddress',
'exchange/fund/controller/website/fundwebsitecontroller/getpayoutcoinrecord',
'exchange/entrust/controller/website/EntrustController/getUserEntrustList',
// the docs say that the following URLs are HTTP POST
// in the docs header and HTTP GET in the docs body
// the docs contradict themselves, a typo most likely
@@ -861,6 +862,27 @@ module.exports = class bw extends Exchange {
return this.parseOrders (orders, market, since, limit);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'marketId': market['id'],
};
if (limit !== undefined) {
request['pageSize'] = limit; // default limit is 20
}
if (since !== undefined) {
request['startDateTime'] = since;
}
const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetUserEntrustList (this.extend (request, params));
const data = this.safeValue (response, 'datas', {});
const orders = this.safeValue (data, 'entrustList', []);
return this.parseOrders (orders, market, since, limit);
}
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument');
@@ -932,7 +954,7 @@ module.exports = class bw extends Exchange {
const keys = Object.keys (sortedParams);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
content += key + sortedParams[key];
content += key + sortedParams[key].toString ();
}
} else {
content = body;
+1 -1
View File
@@ -14,7 +14,7 @@ module.exports = class coinbaseprime extends coinbasepro {
'urls': {
'test': {
'public': 'https://api-public.sandbox.prime.coinbase.com',
'private:': 'https://api-public.sandbox.prime.coinbase.com',
'private': 'https://api-public.sandbox.prime.coinbase.com',
},
'logo': 'https://user-images.githubusercontent.com/1294454/44539184-29f26e00-a70c-11e8-868f-e907fc236a7c.jpg',
'api': {
+90 -16
View File
@@ -3,7 +3,7 @@
// ----------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { InsufficientFunds, ArgumentsRequired, ExchangeError, InvalidOrder, InvalidAddress, AuthenticationError, NotSupported, OrderNotFound } = require ('./base/errors');
const { InsufficientFunds, ArgumentsRequired, ExchangeError, InvalidOrder, InvalidAddress, AuthenticationError, NotSupported, OrderNotFound, OnMaintenance, RateLimitExceeded } = require ('./base/errors');
// ----------------------------------------------------------------------------
@@ -152,12 +152,14 @@ module.exports = class coinbasepro extends Exchange {
'invalid signature': AuthenticationError,
'Invalid Passphrase': AuthenticationError,
'Invalid order id': InvalidOrder,
'Private rate limit exceeded': RateLimitExceeded,
},
'broad': {
'Order already done': OrderNotFound,
'order not found': OrderNotFound,
'price too small': InvalidOrder,
'price too precise': InvalidOrder,
'under maintenance': OnMaintenance,
},
},
});
@@ -295,29 +297,46 @@ module.exports = class coinbasepro extends Exchange {
return orderbook;
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'id': market['id'],
};
const ticker = await this.publicGetProductsIdTicker (this.extend (request, params));
parseTicker (ticker, market = undefined) {
//
// publicGetProductsIdTicker
//
// {
// "trade_id":843439,
// "price":"0.997999",
// "size":"80.29769",
// "time":"2020-01-28T02:13:33.012523Z",
// "bid":"0.997094",
// "ask":"0.998",
// "volume":"1903188.03750000"
// }
//
// publicGetProductsIdStats
//
// {
// "open": "34.19000000",
// "high": "95.70000000",
// "low": "7.06000000",
// "volume": "2.41000000"
// }
//
const timestamp = this.parse8601 (this.safeValue (ticker, 'time'));
const bid = this.safeFloat (ticker, 'bid');
const ask = this.safeFloat (ticker, 'ask');
const last = this.safeFloat (ticker, 'price');
const symbol = (market === undefined) ? undefined : market['symbol'];
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': undefined,
'low': undefined,
'high': this.safeFloat (ticker, 'high'),
'low': this.safeFloat (ticker, 'low'),
'bid': bid,
'bidVolume': undefined,
'ask': ask,
'askVolume': undefined,
'vwap': undefined,
'open': undefined,
'open': this.safeFloat (ticker, 'open'),
'close': last,
'last': last,
'previousClose': undefined,
@@ -330,14 +349,69 @@ module.exports = class coinbasepro extends Exchange {
};
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'id': market['id'],
};
// publicGetProductsIdTicker or publicGetProductsIdStats
const method = this.safeString (this.options, 'fetchTickerMethod', 'publicGetProductsIdTicker');
const response = await this[method] (this.extend (request, params));
//
// publicGetProductsIdTicker
//
// {
// "trade_id":843439,
// "price":"0.997999",
// "size":"80.29769",
// "time":"2020-01-28T02:13:33.012523Z",
// "bid":"0.997094",
// "ask":"0.998",
// "volume":"1903188.03750000"
// }
//
// publicGetProductsIdStats
//
// {
// "open": "34.19000000",
// "high": "95.70000000",
// "low": "7.06000000",
// "volume": "2.41000000"
// }
//
return this.parseTicker (response, market);
}
parseTrade (trade, market = undefined) {
//
// {
// type: 'match',
// trade_id: 82047307,
// maker_order_id: '0f358725-2134-435e-be11-753912a326e0',
// taker_order_id: '252b7002-87a3-425c-ac73-f5b9e23f3caf',
// side: 'sell',
// size: '0.00513192',
// price: '9314.78',
// product_id: 'BTC-USD',
// sequence: 12038915443,
// time: '2020-01-31T20:03:41.158814Z'
// }
//
const timestamp = this.parse8601 (this.safeString2 (trade, 'time', 'created_at'));
let symbol = undefined;
if (market === undefined) {
const marketId = this.safeString (trade, 'product_id');
market = this.safeValue (this.markets_by_id, marketId);
const marketId = this.safeString (trade, 'product_id');
if (marketId !== undefined) {
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
} else {
const [ baseId, quoteId ] = marketId.split ('-');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
symbol = base + '/' + quote;
}
}
if (market) {
if ((symbol === undefined) && (market !== undefined)) {
symbol = market['symbol'];
}
let feeRate = undefined;
@@ -861,7 +935,7 @@ module.exports = class coinbasepro extends Exchange {
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if ((code === 400) || (code === 404)) {
if (body[0] === '{') {
const message = response['message'];
const message = this.safeString (response, 'message');
const feedback = this.id + ' ' + message;
this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback);
this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback);
-3
View File
@@ -315,9 +315,6 @@ module.exports = class crex24 extends Exchange {
// }
// ]
//
// const log = require ('ololog').unlimited.green;
// log (response);
// process.exit ();
const result = { 'info': response };
for (let i = 0; i < response.length; i++) {
const balance = response[i];
+4 -1
View File
@@ -444,7 +444,7 @@ module.exports = class dsx extends Exchange {
return this.parseOrderBook (orderbook);
}
async fetchOrderBooks (symbols = undefined, params = {}) {
async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let ids = undefined;
if (symbols === undefined) {
@@ -461,6 +461,9 @@ module.exports = class dsx extends Exchange {
const request = {
'pair': ids,
};
if (limit !== undefined) {
request['limit'] = limit; // default = 150, max = 2000
}
const response = await this.publicGetDepthPair (this.extend (request, params));
const result = {};
ids = Object.keys (response);
+23 -14
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ArgumentsRequired, ExchangeError, OrderNotFound, AuthenticationError, InsufficientFunds, InvalidOrder, InvalidNonce, NotSupported, OnMaintenance } = require ('./base/errors');
const { ArgumentsRequired, ExchangeError, OrderNotFound, AuthenticationError, InsufficientFunds, InvalidOrder, InvalidNonce, NotSupported, OnMaintenance, RateLimitExceeded } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -384,17 +384,22 @@ module.exports = class exmo extends Exchange {
},
},
'exceptions': {
'40005': AuthenticationError, // Authorization error, incorrect signature
'40009': InvalidNonce, //
'40015': ExchangeError, // API function do not exist
'40016': OnMaintenance, // {"result":false,"error":"Error 40016: Maintenance work in progress"}
'40017': AuthenticationError, // Wrong API Key
'50052': InsufficientFunds,
'50054': InsufficientFunds,
'50304': OrderNotFound, // "Order was not found '123456789'" (fetching order trades for an order that does not have trades yet)
'50173': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order)
'50319': InvalidOrder, // Price by order is less than permissible minimum for this pair
'50321': InvalidOrder, // Price by order is more than permissible maximum for this pair
'exact': {
'40005': AuthenticationError, // Authorization error, incorrect signature
'40009': InvalidNonce, //
'40015': ExchangeError, // API function do not exist
'40016': OnMaintenance, // {"result":false,"error":"Error 40016: Maintenance work in progress"}
'40017': AuthenticationError, // Wrong API Key
'50052': InsufficientFunds,
'50054': InsufficientFunds,
'50304': OrderNotFound, // "Order was not found '123456789'" (fetching order trades for an order that does not have trades yet)
'50173': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order)
'50319': InvalidOrder, // Price by order is less than permissible minimum for this pair
'50321': InvalidOrder, // Price by order is more than permissible maximum for this pair
},
'broad': {
'API rate limit exceeded': RateLimitExceeded, // {"result":false,"error":"API rate limit exceeded for 99.33.55.224. Retry after 60 sec.","history":[],"begin":1579392000,"end":1579478400}
},
},
});
}
@@ -633,7 +638,7 @@ module.exports = class exmo extends Exchange {
return this.parseOrderBook (result, undefined, 'bid', 'ask');
}
async fetchOrderBooks (symbols = undefined, params = {}) {
async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let ids = undefined;
if (symbols === undefined) {
@@ -650,6 +655,9 @@ module.exports = class exmo extends Exchange {
const request = {
'pair': ids,
};
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetOrderBook (this.extend (request, params));
const result = {};
const marketIds = Object.keys (response);
@@ -1396,7 +1404,8 @@ module.exports = class exmo extends Exchange {
code = (numSubParts > 1) ? errorSubParts[1] : errorSubParts[0];
}
const feedback = this.id + ' ' + body;
this.throwExactlyMatchedException (this.exceptions, code, feedback);
this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback);
this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback);
throw new ExchangeError (feedback);
}
}
+17 -14
View File
@@ -34,18 +34,18 @@ module.exports = class gateio extends Exchange {
'fetchMyTrades': true,
},
'timeframes': {
'1m': '60',
'5m': '300',
'10m': '600',
'15m': '900',
'30m': '1800',
'1h': '3600',
'2h': '7200',
'4h': '14400',
'6h': '21600',
'12h': '43200',
'1d': '86400',
'1w': '604800',
'1m': 60,
'5m': 300,
'10m': 600,
'15m': 900,
'30m': 1800,
'1h': 3600,
'2h': 7200,
'4h': 14400,
'6h': 21600,
'12h': 43200,
'1d': 86400,
'1w': 604800,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/31784029-0313c702-b509-11e7-9ccc-bc0da6a0e435.jpg',
@@ -165,7 +165,7 @@ module.exports = class gateio extends Exchange {
for (let i = 0; i < markets.length; i++) {
const market = markets[i];
const keys = Object.keys (market);
const id = keys[0];
const id = this.safeString (keys, 0);
const details = market[id];
// all of their symbols are separated with an underscore
// but not boe_eth_eth (BOE_ETH/ETH) which has two underscores
@@ -731,7 +731,10 @@ module.exports = class gateio extends Exchange {
const id = this.safeString (transaction, 'id');
const txid = this.safeString (transaction, 'txid');
const amount = this.safeFloat (transaction, 'amount');
const address = this.safeString (transaction, 'address');
let address = this.safeString (transaction, 'address');
if (address === 'false') {
address = undefined;
}
const timestamp = this.safeTimestamp (transaction, 'timestamp');
const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
const type = this.parseTransactionType (id[0]);
+17
View File
@@ -87,14 +87,22 @@ module.exports = class huobipro extends Exchange {
],
},
'private': {
// todo add v2 endpoints
// 'GET /v2/account/withdraw/quota
// 'GET /v2/reference/currencies
// 'GET /v2/account/deposit/address
'get': [
'account/accounts', // 查询当前用户的所有账户(即account-id)
'account/accounts/{id}/balance', // 查询指定账户的余额
'account/accounts/{sub-uid}',
'account/history',
'cross-margin/loan-info',
'fee/fee-rate/get',
'order/openOrders',
'order/orders',
'order/orders/{id}', // 查询某个订单详情
'order/orders/{id}/matchresults', // 查询某个订单的成交明细
'order/orders/getClientOrder',
'order/history', // 查询当前委托、历史委托
'order/matchresults', // 查询当前成交、历史成交
'dw/withdraw-virtual/addresses', // 查询虚拟币提现地址
@@ -104,9 +112,17 @@ module.exports = class huobipro extends Exchange {
'points/actions',
'points/orders',
'subuser/aggregate-balance',
'stable-coin/exchange_rate',
'stable-coin/quote',
],
// todo add v2 endpoints
// POST /v2/sub-user/management
'post': [
'futures/transfer',
'order/batch-orders',
'order/orders/place', // 创建并执行一个新订单 (一步下单, 推荐使用)
'order/orders/submitCancelClientOrder',
'order/orders/batchCancelOpenOrders',
'order/orders', // 创建一个新的订单请求 (仅创建订单,不执行下单)
'order/orders/{id}/place', // 执行一个订单 (仅执行已创建的订单)
'order/orders/{id}/submitcancel', // 申请撤销一个订单请求
@@ -120,6 +136,7 @@ module.exports = class huobipro extends Exchange {
'dw/transfer-out/margin', // 借贷账户划出至现货账户
'margin/orders', // 申请借贷
'margin/orders/{id}/repay', // 归还借贷
'stable-coin/exchange',
'subuser/transfer',
],
},
+12 -4
View File
@@ -71,11 +71,13 @@ module.exports = class indodax extends Exchange {
'ATOM/IDR': { 'id': 'atom_idr', 'symbol': 'ATOM/IDR', 'base': 'ATOM', 'quote': 'IDR', 'baseId': 'atom', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}},
'BAT/IDR': { 'id': 'bat_idr', 'symbol': 'BAT/IDR', 'base': 'BAT', 'quote': 'IDR', 'baseId': 'bat', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}},
'BCD/IDR': { 'id': 'bcd_idr', 'symbol': 'BCD/IDR', 'base': 'BCD', 'quote': 'IDR', 'baseId': 'bcd', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}},
'BCH/IDR': { 'id': 'bch_idr', 'symbol': 'BCH/IDR', 'base': 'BCH', 'quote': 'IDR', 'baseId': 'bch', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}},
'BSV/IDR': { 'id': 'bsv_idr', 'symbol': 'BSV/IDR', 'base': 'BSV', 'quote': 'IDR', 'baseId': 'bsv', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}},
'BCH/IDR': { 'id': 'bchabc_idr', 'symbol': 'BCH/IDR', 'base': 'BCH', 'quote': 'IDR', 'baseId': 'bchabc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}},
'BSV/IDR': { 'id': 'bchsv_idr', 'symbol': 'BSV/IDR', 'base': 'BSV', 'quote': 'IDR', 'baseId': 'bchsv', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}},
'BNB/IDR': { 'id': 'bnb_idr', 'symbol': 'BNB/IDR', 'base': 'BNB', 'quote': 'IDR', 'baseId': 'bnb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}},
'BTG/IDR': { 'id': 'btg_idr', 'symbol': 'BTG/IDR', 'base': 'BTG', 'quote': 'IDR', 'baseId': 'btg', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
'BTS/IDR': { 'id': 'bts_idr', 'symbol': 'BTS/IDR', 'base': 'BTS', 'quote': 'IDR', 'baseId': 'bts', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
'BTT/IDR': { 'id': 'btt_idr', 'symbol': 'BTT/IDR', 'base': 'BTT', 'quote': 'IDR', 'baseId': 'btt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1000, 'max': undefined }}},
'COAL/IDR': { 'id': 'coal_idr', 'symbol': 'COAL/IDR', 'base': 'COAL', 'quote': 'IDR', 'baseId': 'coal', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 50, 'max': undefined }}},
'CRO/IDR': { 'id': 'cro_idr', 'symbol': 'CRO/IDR', 'base': 'CRO', 'quote': 'IDR', 'baseId': 'cro', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
'DASH/IDR': { 'id': 'drk_idr', 'symbol': 'DASH/IDR', 'base': 'DASH', 'quote': 'IDR', 'baseId': 'drk', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
'DAX/IDR': { 'id': 'dax_idr', 'symbol': 'DAX/IDR', 'base': 'DAX', 'quote': 'IDR', 'baseId': 'dax', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
@@ -85,15 +87,18 @@ module.exports = class indodax extends Exchange {
'ETC/IDR': { 'id': 'etc_idr', 'symbol': 'ETC/IDR', 'base': 'ETC', 'quote': 'IDR', 'baseId': 'etc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}},
'GARD/IDR': { 'id': 'gard_idr', 'symbol': 'GARD/IDR', 'base': 'GARD', 'quote': 'IDR', 'baseId': 'gard', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}},
'GSC/IDR': { 'id': 'gsc_idr', 'symbol': 'GSC/IDR', 'base': 'GSC', 'quote': 'IDR', 'baseId': 'gsc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}},
'GXC/IDR': { 'id': 'gxc_idr', 'symbol': 'GXC/IDR', 'base': 'GXC', 'quote': 'IDR', 'baseId': 'gxc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}},
'GXC/IDR': { 'id': 'gxs_idr', 'symbol': 'GXC/IDR', 'base': 'GXC', 'quote': 'IDR', 'baseId': 'gxs', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}},
'HPB/IDR': { 'id': 'hpb_idr', 'symbol': 'HPB/IDR', 'base': 'HPB', 'quote': 'IDR', 'baseId': 'hpb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}},
'IGNIS/IDR': { 'id': 'ignis_idr', 'symbol': 'IGNIS/IDR', 'base': 'IGNIS', 'quote': 'IDR', 'baseId': 'ignis', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}},
'INX/IDR': { 'id': 'inx_idr', 'symbol': 'INX/IDR', 'base': 'INX', 'quote': 'IDR', 'baseId': 'inx', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}},
'IOTA/IDR': { 'id': 'iota_idr', 'symbol': 'IOTA/IDR', 'base': 'IOTA', 'quote': 'IDR', 'baseId': 'iota', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}},
'LINK/IDR': { 'id': 'link_idr', 'symbol': 'LINK/IDR', 'base': 'LINK', 'quote': 'IDR', 'baseId': 'link', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}},
'LTC/IDR': { 'id': 'ltc_idr', 'symbol': 'LTC/IDR', 'base': 'LTC', 'quote': 'IDR', 'baseId': 'ltc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
'MBL/IDR': { 'id': 'mbl_idr', 'symbol': 'MBL/IDR', 'base': 'MBL', 'quote': 'IDR', 'baseId': 'mbl', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
'NEO/IDR': { 'id': 'neo_idr', 'symbol': 'NEO/IDR', 'base': 'NEO', 'quote': 'IDR', 'baseId': 'neo', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
'NPXS/IDR': { 'id': 'npxs_idr', 'symbol': 'NPXS/IDR', 'base': 'NPXS', 'quote': 'IDR', 'baseId': 'npxs', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}},
'NXT/IDR': { 'id': 'nxt_idr', 'symbol': 'NXT/IDR', 'base': 'NXT', 'quote': 'IDR', 'baseId': 'nxt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}},
'OKB/IDR': { 'id': 'okb_idr', 'symbol': 'OKB/IDR', 'base': 'OKB', 'quote': 'IDR', 'baseId': 'okb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.5, 'max': undefined }}},
'ONT/IDR': { 'id': 'ont_idr', 'symbol': 'ONT/IDR', 'base': 'ONT', 'quote': 'IDR', 'baseId': 'ont', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}},
'PXG/IDR': { 'id': 'pxg_idr', 'symbol': 'PXG/IDR', 'base': 'PXG', 'quote': 'IDR', 'baseId': 'pxg', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}},
'QTUM/IDR': { 'id': 'qtum_idr', 'symbol': 'QTUM/IDR', 'base': 'QTUM', 'quote': 'IDR', 'baseId': 'qtum', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}},
@@ -105,13 +110,16 @@ module.exports = class indodax extends Exchange {
'USDC/IDR': { 'id': 'usdc_idr', 'symbol': 'USDC/IDR', 'base': 'USDC', 'quote': 'IDR', 'baseId': 'usdc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}},
'USDT/IDR': { 'id': 'usdt_idr', 'symbol': 'USDT/IDR', 'base': 'USDT', 'quote': 'IDR', 'baseId': 'usdt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}},
'VEX/IDR': { 'id': 'vex_idr', 'symbol': 'VEX/IDR', 'base': 'VEX', 'quote': 'IDR', 'baseId': 'vex', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}},
'VIDY/IDR': { 'id': 'vidy_idr', 'symbol': 'VIDY/IDR', 'base': 'VIDY', 'quote': 'IDR', 'baseId': 'vidy', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 100, 'max': undefined }}},
'WAVES/IDR': { 'id': 'waves_idr', 'symbol': 'WAVES/IDR', 'base': 'WAVES', 'quote': 'IDR', 'baseId': 'waves', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}},
'XEM/IDR': { 'id': 'nem_idr', 'symbol': 'XEM/IDR', 'base': 'XEM', 'quote': 'IDR', 'baseId': 'nem', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}},
'XLM/IDR': { 'id': 'str_idr', 'symbol': 'XLM/IDR', 'base': 'XLM', 'quote': 'IDR', 'baseId': 'str', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 20, 'max': undefined }}},
'XDCE/IDR': { 'id': 'xdce_idr', 'symbol': 'XDCE/IDR', 'base': 'XDCE', 'quote': 'IDR', 'baseId': 'xdce', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 10, 'max': undefined }}},
'XMR/IDR': { 'id': 'xmr_idr', 'symbol': 'XMR/IDR', 'base': 'XMR', 'quote': 'IDR', 'baseId': 'xmr', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
'XRP/IDR': { 'id': 'xrp_idr', 'symbol': 'XRP/IDR', 'base': 'XRP', 'quote': 'IDR', 'baseId': 'xrp', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 10, 'max': undefined }}},
'XZC/IDR': { 'id': 'xzc_idr', 'symbol': 'XZC/IDR', 'base': 'XZC', 'quote': 'IDR', 'baseId': 'xzc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}},
'VSYS/IDR': { 'id': 'vsys_idr', 'symbol': 'VSYS/IDR', 'base': 'VSYS', 'quote': 'IDR', 'baseId': 'vsys', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}},
'ZEC/IDR': { 'id': 'zec_idr', 'symbol': 'ZEC/IDR', 'base': 'ZEC', 'quote': 'IDR', 'baseId': 'zec', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
// BTC markets
'BTS/BTC': { 'id': 'bts_btc', 'symbol': 'BTS/BTC', 'base': 'BTS', 'quote': 'BTC', 'baseId': 'bts', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
'DASH/BTC': { 'id': 'drk_btc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC', 'baseId': 'drk', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 6 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}},
@@ -279,7 +287,7 @@ module.exports = class indodax extends Exchange {
let status = this.safeString (order, 'status', 'open');
if (status === 'filled') {
status = 'closed';
} else if (status === 'calcelled') {
} else if (status === 'cancelled') {
status = 'canceled';
}
let symbol = undefined;
+9 -9
View File
@@ -38,15 +38,15 @@ module.exports = class kraken extends Exchange {
},
'marketsByAltname': {},
'timeframes': {
'1m': '1',
'5m': '5',
'15m': '15',
'30m': '30',
'1h': '60',
'4h': '240',
'1d': '1440',
'1w': '10080',
'2w': '21600',
'1m': 1,
'5m': 5,
'15m': 15,
'30m': 30,
'1h': 60,
'4h': 240,
'1d': 1440,
'1w': 10080,
'2w': 21600,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766599-22709304-5ede-11e7-9de1-9f33732e1509.jpg',
+54 -20
View File
@@ -15,7 +15,7 @@ module.exports = class kucoin extends Exchange {
'countries': [ 'SC' ],
'rateLimit': 334,
'version': 'v2',
'certified': true,
'certified': false,
'comment': 'Platform 2.0',
'has': {
'fetchTime': true,
@@ -368,24 +368,47 @@ module.exports = class kucoin extends Exchange {
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'
// symbol: "ETH-BTC",
// high: "0.019518",
// vol: "7997.82836194",
// last: "0.019329",
// low: "0.019",
// buy: "0.019329",
// sell: "0.01933",
// changePrice: "-0.000139",
// time: 1580553706304,
// averagePrice: "0.01926386",
// changeRate: "-0.0071",
// volValue: "154.40791568183474"
// }
//
// {
// "trading": true,
// "symbol": "KCS-BTC",
// "buy": 0.00011,
// "sell": 0.00012,
// "sort": 100,
// "volValue": 3.13851792584, //total
// "baseCurrency": "KCS",
// "market": "BTC",
// "quoteCurrency": "BTC",
// "symbolCode": "KCS-BTC",
// "datetime": 1548388122031,
// "high": 0.00013,
// "vol": 27514.34842,
// "low": 0.0001,
// "changePrice": -1.0e-5,
// "changeRate": -0.0769,
// "lastTradedPrice": 0.00012,
// "board": 0,
// "mark": 0
// }
//
let percentage = this.safeFloat (ticker, 'changeRate');
if (percentage !== undefined) {
percentage = percentage * 100;
}
const last = this.safeFloat (ticker, 'last');
const average = this.safeFloat (ticker, 'averagePrice');
const last = this.safeFloat2 (ticker, 'last', 'lastTradedPrice');
let symbol = undefined;
const marketId = this.safeString (ticker, 'symbol');
if (marketId !== undefined) {
@@ -404,10 +427,11 @@ module.exports = class kucoin extends Exchange {
symbol = market['symbol'];
}
}
const timestamp = this.safeInteger2 (ticker, 'time', 'datetime');
return {
'symbol': symbol,
'timestamp': undefined,
'datetime': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeFloat (ticker, 'high'),
'low': this.safeFloat (ticker, 'low'),
'bid': this.safeFloat (ticker, 'buy'),
@@ -421,7 +445,7 @@ module.exports = class kucoin extends Exchange {
'previousClose': undefined,
'change': this.safeFloat (ticker, 'changePrice'),
'percentage': percentage,
'average': average,
'average': this.safeFloat (ticker, 'averagePrice'),
'baseVolume': this.safeFloat (ticker, 'vol'),
'quoteVolume': this.safeFloat (ticker, 'volValue'),
'info': ticker,
@@ -995,6 +1019,19 @@ module.exports = class kucoin extends Exchange {
// "time":1548848575203567174
// }
//
// {
// sequence: '1568787654360',
// symbol: 'BTC-USDT',
// side: 'buy',
// size: '0.00536577',
// price: '9345',
// takerOrderId: '5e356c4a9f1a790008f8d921',
// time: '1580559434436443257',
// type: 'match',
// makerOrderId: '5e356bffedf0010008fa5d7f',
// tradeId: '5e356c4aeefabd62c62a1ece'
// }
//
// fetchMyTrades (private) v2
//
// {
@@ -1065,10 +1102,7 @@ module.exports = class kucoin extends Exchange {
symbol = market['symbol'];
}
}
let id = this.safeString2 (trade, 'tradeId', 'id');
if (id !== undefined) {
id = id.toString ();
}
const id = this.safeString2 (trade, 'tradeId', 'id');
const orderId = this.safeString (trade, 'orderId');
const takerOrMaker = this.safeString (trade, 'liquidity');
const amount = this.safeFloat2 (trade, 'size', 'amount');
+7 -12
View File
@@ -109,12 +109,9 @@ module.exports = class poloniex extends Exchange {
},
'fees': {
'trading': {
// https://github.com/ccxt/ccxt/issues/6064
// starting from October 21, 2019 17:00 UTC
// all spot trading fees will be reduced to 0.00%
// until December 31, 2019 23:59 UTC
'maker': 0.0,
'taker': 0.0,
// starting from Jan 8 2020
'maker': 0.0009,
'taker': 0.0009,
},
'funding': {},
},
@@ -325,16 +322,14 @@ module.exports = class poloniex extends Exchange {
return orderbook;
}
async fetchOrderBooks (symbols = undefined, params = {}) {
async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'currencyPair': 'all',
};
//
// if (limit !== undefined) {
// request['depth'] = limit; // 100
// }
//
if (limit !== undefined) {
request['depth'] = limit; // 100
}
const response = await this.publicGetReturnOrderBook (this.extend (request, params));
const marketIds = Object.keys (response);
const result = {};
+107 -32
View File
@@ -35,6 +35,7 @@ module.exports = class stex extends Exchange {
'fetchDeposits': true,
'fetchWithdrawals': true,
'withdraw': true,
'fetchFundingFees': true,
},
'version': 'v3',
'urls': {
@@ -499,6 +500,15 @@ module.exports = class stex extends Exchange {
symbol = market['symbol'];
}
const last = this.safeFloat (ticker, 'last');
const open = this.safeFloat (ticker, 'open');
let change = undefined;
let percentage = undefined;
if (last !== undefined) {
if (open !== undefined) {
change = last - open;
percentage = ((100 / open) * last) - 100;
}
}
return {
'symbol': symbol,
'timestamp': timestamp,
@@ -510,12 +520,12 @@ module.exports = class stex extends Exchange {
'ask': this.safeFloat (ticker, 'ask'),
'askVolume': undefined,
'vwap': undefined,
'open': this.safeFloat (ticker, 'open'),
'open': open,
'close': last,
'last': last,
'previousClose': undefined, // previous day close
'change': undefined,
'percentage': undefined,
'change': change,
'percentage': percentage,
'average': undefined,
'baseVolume': this.safeFloat (ticker, 'volumeQuote'),
'quoteVolume': this.safeFloat (ticker, 'volume'),
@@ -1511,7 +1521,7 @@ module.exports = class stex extends Exchange {
if ((code === undefined) && (currency !== undefined)) {
code = currency['code'];
}
const type = ('depositId' in transaction) ? 'deposit' : 'withdrawal';
const type = ('deposit_status_id' in transaction) ? 'deposit' : 'withdrawal';
const amount = this.safeFloat (transaction, 'amount');
const status = this.parseTransactionStatus (this.safeStringLower (transaction, 'status'));
const timestamp = this.safeTimestamp2 (transaction, 'timestamp', 'created_ts');
@@ -1549,21 +1559,20 @@ module.exports = class stex extends Exchange {
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchDeposits() requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'currencyTypeName': currency['name'],
// 'pageSize': limit, // documented as required, but it works without it
// 'pageNum': 0, // also works without it, most likely a typo in the docs
// 'sort': 1, // 1 = asc, 0 = desc
};
if (limit !== undefined) {
request['pageSize'] = limit; // default 50
let currency = undefined;
const request = {};
if (code !== undefined) {
currency = this.currency (code);
request['currencyId'] = currency['id'];
}
const response = await this.privatePostExchangeFundControllerWebsiteFundcontrollerGetPayinCoinRecord (this.extend (request, params));
if (limit !== undefined) {
request['limit'] = limit;
}
if (since !== undefined) {
request['timeStart'] = since;
}
const response = await this.profileGetDeposits (this.extend (request, params));
//
// {
// "success": true,
@@ -1583,31 +1592,35 @@ module.exports = class stex extends Exchange {
// "status_color": "#BC3D51",
// "created_at": "2018-11-28 12:32:08",
// "timestamp": "1543409389",
// "confirmations": "1 of 2"
// "confirmations": "1 of 2",
// "protocol_specific_settings": {
// "protocol_name": "Tether OMNI",
// "protocol_id": 10,
// "block_explorer_url": "https://omniexplorer.info/search/"
// }
// }
// ]
// }
//
const deposits = this.safeValue (response, 'datas', {});
const deposits = this.safeValue (response, 'data', []);
return this.parseTransactions (deposits, code, since, limit);
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'currencyId': currency['id'],
// 'pageSize': limit, // documented as required, but it works without it
// 'pageIndex': 0, // also works without it, most likely a typo in the docs
// 'tab': 'all', // all, wait (submitted, not audited), success (auditing passed), fail (auditing failed), cancel (canceled by user)
};
if (limit !== undefined) {
request['pageSize'] = limit; // default 50
let currency = undefined;
const request = {};
if (code !== undefined) {
currency = this.currency (code);
request['currencyId'] = currency['id'];
}
const response = await this.privateGetExchangeFundControllerWebsiteFundwebsitecontrollerGetpayoutcoinrecord (this.extend (request, params));
if (limit !== undefined) {
request['limit'] = limit;
}
if (since !== undefined) {
request['timeStart'] = since;
}
const response = await this.profileGetWithdrawals (this.extend (request, params));
//
// {
// "success": true,
@@ -1638,6 +1651,11 @@ module.exports = class stex extends Exchange {
// "protocol_id": 10,
// "protocol_name": "Tether OMNI",
// "supports_new_address_creation": false
// },
// "protocol_specific_settings": {
// "protocol_name": "Tether OMNI",
// "protocol_id": 10,
// "block_explorer_url": "https://omniexplorer.info/search/"
// }
// }
// ]
@@ -1699,6 +1717,63 @@ module.exports = class stex extends Exchange {
return this.parseTransaction (data, currency);
}
async fetchFundingFees (codes = undefined, params = {}) {
const response = await this.publicGetCurrencies (params);
//
// {
// "success": true,
// "data": [
// {
// "id": 1,
// "code": "BTC",
// "name": "Bitcoin",
// "active": true,
// "delisted": false,
// "precision": 8,
// "minimum_tx_confirmations": 24,
// "minimum_withdrawal_amount": "0.009",
// "minimum_deposit_amount": "0.000003",
// "deposit_fee_currency_id": 1,
// "deposit_fee_currency_code": "ETH",
// "deposit_fee_const": "0.00001",
// "deposit_fee_percent": "0",
// "withdrawal_fee_currency_id": 1,
// "withdrawal_fee_currency_code": "ETH",
// "withdrawal_fee_const": "0.0015",
// "withdrawal_fee_percent": "0",
// "withdrawal_limit": "string",
// "block_explorer_url": "https://blockchain.info/tx/",
// "protocol_specific_settings": [
// {
// "protocol_name": "Tether OMNI",
// "protocol_id": 10,
// "active": true,
// "withdrawal_fee_currency_id": 1,
// "withdrawal_fee_const": 0.002,
// "withdrawal_fee_percent": 0,
// "block_explorer_url": "https://omniexplorer.info/search/"
// }
// ]
// }
// ]
// }
//
const data = this.safeValue (response, 'data', []);
const withdrawFees = {};
const depositFees = {};
for (let i = 0; i < data.length; i++) {
const id = this.safeString (data[i], 'id');
const code = this.safeCurrencyCode (id);
withdrawFees[code] = this.safeFloat (data[i], 'withdrawal_fee_const');
depositFees[code] = this.safeFloat (data[i], 'deposit_fee_const');
}
return {
'withdraw': withdrawFees,
'deposit': depositFees,
'info': response,
};
}
handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (response === undefined) {
return; // fallback to default error handler
+61
View File
@@ -0,0 +1,61 @@
'use strict'
// ----------------------------------------------------------------------------
const log = require ('ololog')
, chai = require ('chai')
, assert = chai.assert
// ----------------------------------------------------------------------------
module.exports = (exchange, balance, method) => {
let currencies = [
'USD',
'CNY',
'EUR',
'BTC',
'ETH',
'JPY',
'LTC',
'DASH',
'DOGE',
'UAH',
'RUB',
'XRP',
]
// log.yellow (balance)
assert (typeof balance['total'] === 'object')
assert (typeof balance['free'] === 'object')
assert (typeof balance['used'] === 'object')
for (let currency of Object.keys (balance['total'])) {
let total = balance['total'][currency]
let free = balance['free'][currency]
let used = balance['used'][currency]
if (total !== undefined && free !== undefined && used !== undefined) {
assert (total === free + used, 'free and used do not sum to total ' + exchange.id)
}
}
let result = currencies
.filter (currency => (currency in balance) &&
(balance[currency]['total'] !== undefined))
if (result.length > 0) {
result = result.map (currency => currency + ': ' + balance[currency]['total'])
if (exchange.currencies.length > result.length)
result = result.join (', ') + ' + more...'
else
result = result.join (', ')
} else {
result = 'zero balance'
}
log (result)
}
+7 -62
View File
@@ -2,15 +2,12 @@
// ----------------------------------------------------------------------------
const log = require ('ololog')
, ansi = require ('ansicolor').nice
, chai = require ('chai')
, expect = chai.expect
, assert = chai.assert
const log = require ('ololog')
, testBalance = require ('./test.balance.js')
/* ------------------------------------------------------------------------ */
// ----------------------------------------------------------------------------
module.exports = async (exchange, symbol) => {
module.exports = async (exchange) => {
if (!(exchange.has.fetchBalance)) {
log (exchange.id.green, ' does not have fetchBalance')
@@ -19,61 +16,9 @@ module.exports = async (exchange, symbol) => {
log ('fetching balance...')
let balance = await exchange.fetchBalance ()
const response = await exchange.fetchBalance ()
let currencies = [
'USD',
'CNY',
'EUR',
'BTC',
'ETH',
'JPY',
'LTC',
'DASH',
'DOGE',
'UAH',
'RUB',
'XRP',
]
testBalance (exchange, response)
// log.yellow (balance)
assert (typeof balance['total'] === 'object')
assert (typeof balance['free'] === 'object')
assert (typeof balance['used'] === 'object')
if ('info' in balance) {
for (let currency of Object.keys (balance['total'])) {
let total = balance['total'][currency]
let free = balance['free'][currency]
let used = balance['used'][currency]
if (total !== undefined && free !== undefined && used !== undefined) {
assert (total === free + used, 'free and used do not sum to total ' + exchange.id)
}
}
let result = currencies
.filter (currency => (currency in balance) &&
(balance[currency]['total'] !== undefined))
if (result.length > 0) {
result = result.map (currency => currency + ': ' + balance[currency]['total'])
if (exchange.currencies.length > result.length)
result = result.join (', ') + ' + more...'
else
result = result.join (', ')
} else {
result = 'zero balance'
}
log (result)
} else {
log (exchange.omit (balance, 'info'))
}
return balance
return response
}
+1
View File
@@ -14,6 +14,7 @@ const log = require ('ololog')
module.exports = async (exchange, symbol) => {
const skippedExchanges = [
'btcalpha', // issue with 404 on a documented endpoint https://travis-ci.org/ccxt/ccxt/builds/643930431#L2213
'bitmex', // an issue with null values,to be resolved later
'cex',
'okex',
+3 -5
View File
@@ -17,11 +17,9 @@ const printOrderBookOneLiner = (orderbook, method, symbol) => {
log (symbol.toString ().green,
method,
orderbook['datetime'],
'bid: ' + ((bids.length > 0) ? bids[0][0] : 'N/A'),
'bidVolume: ' + ((bids.length > 0) ? bids[0][1] : 'N/A'),
'ask: ' + ((asks.length > 0) ? asks[0][0] : 'N/A'),
'askVolume: ' + ((asks.length > 0) ? asks[0][1] : 'N/A'))
orderbook['nonce'] || orderbook['datetime'],
bids.length, 'bids:', bids[0],
asks.length, 'asks:', asks[0])
}
/* ------------------------------------------------------------------------ */
+1 -1
View File
@@ -42,7 +42,7 @@ module.exports = (exchange, ticker, method, symbol) => {
assert (!('first' in ticker), '`first` field leftover in ' + exchange.id)
assert (ticker['last'] === ticker['close'], '`last` != `close` in ' + exchange.id)
// log (ticker)
log (ticker['datetime'], exchange.id, method, ticker['symbol'].green, ticker['last'])
keys.forEach (key => assert (key in ticker))
+3
View File
@@ -29,6 +29,9 @@ assert (exchange.parse8601 ('1986-04-26T01:23:47.000Z') === 514862627000);
assert (exchange.parse8601 ('1986-04-26T01:23:47.559Z') === 514862627559);
assert (exchange.parse8601 ('1986-04-26T01:23:47.062Z') === 514862627062);
assert (exchange.parse8601 ('1986-04-26T01:23:47.06Z') === 514862627060);
assert (exchange.parse8601 ('1986-04-26T01:23:47.6Z') === 514862627600);
assert (exchange.parse8601 ('1977-13-13T00:00:00.000Z') === undefined);
assert (exchange.parse8601 ('1986-04-26T25:71:47.000Z') === undefined);
+2
View File
@@ -134,6 +134,8 @@ assert (decimalToPrecision ('0.098765', ROUND, 1, SIGNIFICANT_DIGITS, PAD_WITH_Z
assert (decimalToPrecision ('0', ROUND, 0, SIGNIFICANT_DIGITS) === '0');
assert (decimalToPrecision ('-0.123', ROUND, 0, SIGNIFICANT_DIGITS) === '0');
assert (decimalToPrecision ('0.00000044', ROUND, 5, SIGNIFICANT_DIGITS) === '0.00000044');
// ----------------------------------------------------------------------------
// testDecimalToPrecisionRoundingToTickSize
+3 -8
View File
@@ -9,13 +9,11 @@ const debug = process.argv.includes ('--debug') || false
/* ------------------------------------------------------------------------ */
const asTable = require ('as-table')
, util = require ('util')
, log = require ('ololog')
, ansi = require ('ansicolor').nice
, fs = require ('fs')
, ccxt = require ('../../ccxt.js')
, chai = require ('chai')
, expect = chai.expect
, assert = chai.assert
/* ------------------------------------------------------------------------ */
@@ -82,14 +80,12 @@ Object.keys (errors)
const keysGlobal = 'keys.json'
const keysLocal = 'keys.local.json'
let keysFile = fs.existsSync (keysLocal) ? keysLocal : keysGlobal
const keysFile = fs.existsSync (keysLocal) ? keysLocal : keysGlobal
// eslint-disable-next-line import/no-dynamic-require
let settings = require (__dirname + '/../../' + keysFile)[exchangeId]
const settings = require (__dirname + '/../../' + keysFile)[exchangeId]
if (settings) {
const keys = Object.keys (settings)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
for (const key in settings) {
if (settings[key]) {
settings[key] = ccxt.deepExtend (exchange[key] || {}, settings[key])
}
@@ -224,7 +220,6 @@ let testExchange = async exchange => {
await loadExchange (exchange)
let delay = exchange.rateLimit
let symbol = exchange.symbols[0]
const symbols = [
'BTC/USD',
+8 -2
View File
@@ -281,7 +281,7 @@ module.exports = class tidex extends Exchange {
return this.parseOrderBook (orderbook);
}
async fetchOrderBooks (symbols = undefined, params = {}) {
async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let ids = undefined;
if (symbols === undefined) {
@@ -298,6 +298,9 @@ module.exports = class tidex extends Exchange {
const request = {
'pair': ids,
};
if (limit !== undefined) {
request['limit'] = limit; // default = 150, max = 2000
}
const response = await this.publicGetDepthPair (this.extend (request, params));
const result = {};
ids = Object.keys (response);
@@ -324,10 +327,13 @@ module.exports = class tidex extends Exchange {
// sell: 0.03377798,
// updated: 1537522009 }
//
const timestamp = this.safeTimestamp (ticker, 'updated');
let timestamp = this.safeTimestamp (ticker, 'updated');
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
if (!market['active']) {
timestamp = undefined;
}
}
const last = this.safeFloat (ticker, 'last');
return {
+2 -2
View File
@@ -442,7 +442,7 @@ module.exports = class upbit extends Exchange {
return base + '/' + quote;
}
async fetchOrderBooks (symbols = undefined, params = {}) {
async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let ids = undefined;
if (symbols === undefined) {
@@ -505,7 +505,7 @@ module.exports = class upbit extends Exchange {
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
const orderbooks = await this.fetchOrderBooks ([ symbol ], params);
const orderbooks = await this.fetchOrderBooks ([ symbol ], limit, params);
return this.safeValue (orderbooks, symbol);
}
+5 -1
View File
@@ -295,7 +295,7 @@ module.exports = class yobit extends Exchange {
return this.parseOrderBook (orderbook);
}
async fetchOrderBooks (symbols = undefined, params = {}) {
async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let ids = undefined;
if (symbols === undefined) {
@@ -311,7 +311,11 @@ module.exports = class yobit extends Exchange {
}
const request = {
'pair': ids,
// 'ignore_invalid': true,
};
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetDepthPair (this.extend (request, params));
const result = {};
ids = Object.keys (response);
+11 -11
View File
@@ -1,27 +1,27 @@
{
"_from": "ccxt",
"_id": "ccxt@1.21.82",
"_from": "ccxt@1.22.39",
"_id": "ccxt@1.22.39",
"_inBundle": false,
"_integrity": "sha512-+HX/6WK8BM5c9OwhALl9zzMKvueZfHDXVmfrKNmQ4005hc+BtNuNe47XEs6dAylY+ll7n9JpzaoIZhy6Lipiew==",
"_integrity": "sha512-Vq4BzYS/FwRudPKQ6BQGU3SVntYv7EAQGT+2guf+e7qm45CJMhdoVwR1z89kl42fbK39XMnl8sUnRCwsKPs38A==",
"_location": "/ccxt",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"type": "version",
"registry": true,
"raw": "ccxt",
"raw": "ccxt@1.22.39",
"name": "ccxt",
"escapedName": "ccxt",
"rawSpec": "",
"rawSpec": "1.22.39",
"saveSpec": null,
"fetchSpec": "latest"
"fetchSpec": "1.22.39"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/ccxt/-/ccxt-1.21.82.tgz",
"_shasum": "6117f2d436d053c66574eaae195083fb69d64903",
"_spec": "ccxt",
"_resolved": "https://registry.npmjs.org/ccxt/-/ccxt-1.22.39.tgz",
"_shasum": "ca28a2dafd94089238279c9e5f2f4a041348d065",
"_spec": "ccxt@1.22.39",
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer",
"author": {
"name": "Igor Kroitor",
@@ -520,5 +520,5 @@
},
"types": "./ccxt.d.ts",
"unpkg": "dist/ccxt.browser.js",
"version": "1.21.82"
"version": "1.22.39"
}