Beta 7 with some repo names changed.
This commit is contained in:
1 parent
0f7b40cca7
commit
963c19493a
3408 files changed
+462217
-15516
No files matched your search
+43
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
"env": {
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "script",
|
||||
},
|
||||
"extends": ["eslint:recommended"],
|
||||
"rules": {
|
||||
"semi": ["error", "never"],
|
||||
"no-unused-vars": ["off"],
|
||||
"quotes": ["off", "single"],
|
||||
"func-call-spacing": ["error", "always"],
|
||||
"one-var": "off",
|
||||
"indent": ["error", 4],
|
||||
"comma-style": "off",
|
||||
"no-multi-spaces": "off",
|
||||
"comma-dangle": "off",
|
||||
"spaced-comment": "off",
|
||||
"camelcase": "off",
|
||||
"padded-blocks": "off",
|
||||
"multiline-comment-style": "off",
|
||||
"curly": "off",
|
||||
"rest-spread-spacing": "off",
|
||||
"no-multiple-empty-lines": "off",
|
||||
"no-undef-init": "off",
|
||||
"no-useless-return": "off",
|
||||
"no-console": "off",
|
||||
"operator-linebreak": "off",
|
||||
"key-spacing": "off",
|
||||
"brace-style": "off",
|
||||
"padding-line-between-statements": ["off",
|
||||
{ "blankLine": "always", "prev":"function", "next": "*" },
|
||||
{ "blankLine": "always", "prev":"directive", "next": "*" },
|
||||
{ "blankLine": "always", "prev":"*", "next": "cjs-export" },
|
||||
],
|
||||
"import/no-extraneous-dependencies": ["error", { "devDependencies": true }],
|
||||
},
|
||||
}
|
||||
+61
@@ -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)
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = (exchange, currency, method) => {
|
||||
|
||||
const format = {
|
||||
'id': 'btc', // string literal for referencing within an exchange
|
||||
'code': 'BTC', // uppercase string literal of a pair of currencies
|
||||
//----------------------------------------------------------------------
|
||||
// commented temporarily to bring currencies to consistency first
|
||||
// 'name': 'Bitcoin', // uppercase string, base currency, 3 or more letters
|
||||
// 'quote': 'USD', // uppercase string, quote currency, 3 or more letters
|
||||
// 'withdraw': true, // can withdraw
|
||||
// 'deposit': true, // can deposit
|
||||
// 'active': true, // can both withdraw & deposit
|
||||
// 'precision': 8, // number of decimal digits "after the dot"
|
||||
// 'limits': { // value limits when placing orders on this market
|
||||
// 'amount': {
|
||||
// 'min': 0.01, // order amount should be > min
|
||||
// 'max': 1000, // order amount should be < max
|
||||
// },
|
||||
// 'price': {
|
||||
// 'min': 0.01, // order price should be > min
|
||||
// 'max': 1000, // order price should be < max
|
||||
// },
|
||||
// 'cost': { // order cost = price * amount
|
||||
// 'min': 0.01, // order cost should be > min
|
||||
// 'max': 1000, // order cost should be < max
|
||||
// },
|
||||
// },
|
||||
// 'info': {}, // the original unparsed market info from the exchange
|
||||
//----------------------------------------------------------------------
|
||||
}
|
||||
|
||||
expect (currency).to.deep.include.all.keys (format)
|
||||
|
||||
// expect (currency['precision']).to.not.be.undefined
|
||||
|
||||
// expect (currency['limits']['amount']['min']).to.not.be.undefined
|
||||
// expect (currency['limits']['price']['min']).to.not.be.undefined
|
||||
// expect (market['limits']['cost']['min']).to.not.be.undefined
|
||||
|
||||
// log (market)
|
||||
|
||||
return currency
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, testBalance = require ('./test.balance.js')
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
module.exports = async (exchange) => {
|
||||
|
||||
if (!(exchange.has.fetchBalance)) {
|
||||
log (exchange.id.green, ' does not have fetchBalance')
|
||||
return
|
||||
}
|
||||
|
||||
log ('fetching balance...')
|
||||
|
||||
const response = await exchange.fetchBalance ()
|
||||
|
||||
testBalance (exchange, response)
|
||||
|
||||
return response
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testOrder = require ('./test.order.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
if (exchange.has.fetchClosedOrders) {
|
||||
|
||||
// log ('fetching closed orders...')
|
||||
|
||||
let orders = await exchange.fetchClosedOrders (symbol)
|
||||
|
||||
log ('fetched', orders.length.toString ().green, 'closed orders, testing each')
|
||||
|
||||
assert (orders instanceof Array)
|
||||
|
||||
let now = Date.now ()
|
||||
|
||||
for (let i = 0; i < orders.length; i++) {
|
||||
let order = orders[i]
|
||||
testOrder (exchange, order, symbol, now)
|
||||
assert (order.status === 'closed' || order.status === 'canceled')
|
||||
}
|
||||
|
||||
// log (asTable (orders))
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching closed orders not supported')
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testCurrency = require ('./test.currency.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange) => {
|
||||
|
||||
const skippedExchanges = [
|
||||
]
|
||||
|
||||
if (skippedExchanges.includes (exchange.id)) {
|
||||
log (exchange.id, 'found in ignored exchanges, skipping fetchCurrencies...')
|
||||
return
|
||||
}
|
||||
|
||||
if (exchange.has.fetchCurrencies) {
|
||||
|
||||
// log ('fetching currencies...')
|
||||
|
||||
const method = 'fetchCurrencies'
|
||||
const currencies = await exchange[method] ()
|
||||
Object.values (currencies).forEach (currency => testCurrency (exchange, currency, method))
|
||||
return currencies
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching currencies not supported')
|
||||
}
|
||||
}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testTransaction = require ('./test.transaction.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, code) => {
|
||||
|
||||
if (exchange.has.fetchDeposits) {
|
||||
|
||||
// log ('fetching deposits...')
|
||||
|
||||
let transactions = await exchange.fetchDeposits (code)
|
||||
|
||||
log ('fetched', transactions.length.toString ().green, 'deposits, asserting each...')
|
||||
|
||||
assert (transactions instanceof Array)
|
||||
|
||||
let now = Date.now ()
|
||||
|
||||
for (let i = 0; i < transactions.length; i++) {
|
||||
let transaction = transactions[i]
|
||||
testTransaction (exchange, transaction, code, now)
|
||||
}
|
||||
|
||||
// log (asTable (transactions))
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching deposits not supported')
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange) => {
|
||||
|
||||
const skippedExchanges = [
|
||||
'bibox', // fetchFundingFees should be rewritten to fetchFundingFee
|
||||
]
|
||||
|
||||
if (skippedExchanges.includes (exchange.id)) {
|
||||
log (exchange.id, 'found in ignored exchanges, skipping fetchFundingFees...')
|
||||
return
|
||||
}
|
||||
|
||||
if (exchange.has.fetchFundingFees) {
|
||||
|
||||
// log ('fetching funding fees...')
|
||||
|
||||
const method = 'fetchFundingFees'
|
||||
const fees = await exchange[method] ()
|
||||
log.green (fees)
|
||||
return fees
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching funding fees not supported')
|
||||
}
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testOrderBook = require ('./test.orderbook.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
// log (symbol.green, 'fetching order book...')
|
||||
|
||||
const method = 'fetchL2OrderBook'
|
||||
|
||||
if (exchange.has[method]) {
|
||||
|
||||
let orderbook = await exchange[method] (symbol)
|
||||
testOrderBook (exchange, orderbook, method, symbol)
|
||||
return orderbook
|
||||
|
||||
} else {
|
||||
|
||||
log (method + '() not supported')
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testLedgerItem = require ('./test.ledgerItem')
|
||||
|
||||
require ('ansicolor').nice
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, code) => {
|
||||
|
||||
if (exchange.has.fetchLedger) {
|
||||
|
||||
let items = await exchange.fetchLedger (code)
|
||||
|
||||
assert (items instanceof Array)
|
||||
|
||||
log ('fetched', items.length.toString ().green, 'ledger items')
|
||||
|
||||
let now = Date.now ()
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
testLedgerItem (exchange, items[i], code, now)
|
||||
if (i > 0) {
|
||||
assert (items[i].timestamp >= items[i - 1].timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
if (exchange.has.fetchLedgerItem) {
|
||||
let { id } = items.pop ()
|
||||
let item = await exchange.fetchLedgerItem (id)
|
||||
if (Array.isArray (item)) {
|
||||
item = item[0]
|
||||
}
|
||||
testLedgerItem (exchange, item, code, now)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching ledger items not supported')
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testMarket = require ('./test.market.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange) => {
|
||||
|
||||
const skippedExchanges = [
|
||||
'bitforex',
|
||||
]
|
||||
|
||||
if (skippedExchanges.includes (exchange.id)) {
|
||||
log (exchange.id, 'found in ignored exchanges, skipping fetchMarkets...')
|
||||
return
|
||||
}
|
||||
|
||||
if (exchange.has.fetchMarkets) {
|
||||
|
||||
// log ('fetching markets...')
|
||||
|
||||
const method = 'fetchMarkets'
|
||||
const markets = await exchange[method] ()
|
||||
Object.values (markets).forEach (market => testMarket (exchange, market, method))
|
||||
return markets
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching markets not supported')
|
||||
}
|
||||
}
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testTrade = require ('./test.trade.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
const skippedExchanges = [
|
||||
'bitso',
|
||||
]
|
||||
|
||||
if (skippedExchanges.includes (exchange.id)) {
|
||||
log (exchange.id, 'found in ignored exchanges, skipping fetchMyTrades...')
|
||||
return
|
||||
}
|
||||
|
||||
if (exchange.has.fetchMyTrades) {
|
||||
|
||||
// log ('fetching my trades...')
|
||||
|
||||
const since = exchange.milliseconds () - 60 * 60 * 1000
|
||||
|
||||
let trades = await exchange.fetchMyTrades (symbol, since)
|
||||
|
||||
assert (trades instanceof Array)
|
||||
|
||||
log ('fetched', trades.length.toString ().green, 'trades')
|
||||
|
||||
let now = Date.now ()
|
||||
|
||||
for (let i = 0; i < trades.length; i++) {
|
||||
testTrade (exchange, trades[i], symbol, now)
|
||||
if (i > 0) {
|
||||
assert (trades[i].timestamp >= trades[i - 1].timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
// trades.forEach (trade => log.dim ('-'.repeat (80), "\n", trade))
|
||||
// log (asTable (trades))
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching my trades not supported')
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testOHLCV = require ('./test.ohlcv.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
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',
|
||||
'mandala',
|
||||
]
|
||||
|
||||
if (skippedExchanges.includes (exchange.id)) {
|
||||
log (exchange.id, 'found in ignored exchanges, skipping fetchOHLCV...')
|
||||
return
|
||||
}
|
||||
|
||||
if (exchange.has.fetchOHLCV) {
|
||||
|
||||
const timeframe = Object.keys (exchange.timeframes || { '1d': '1d' })[0]
|
||||
const limit = 10
|
||||
const duration = exchange.parseTimeframe (timeframe)
|
||||
const since = exchange.milliseconds () - duration * limit * 1000 - 1000
|
||||
|
||||
// log (symbol.green, 'fetching OHLCV...')
|
||||
|
||||
const ohlcvs = await exchange.fetchOHLCV (symbol, timeframe, since, limit)
|
||||
|
||||
const now = Date.now ()
|
||||
|
||||
for (let i = 0; i < ohlcvs.length; i++) {
|
||||
const ohlcv = ohlcvs[i]
|
||||
testOHLCV (exchange, ohlcv, symbol, now)
|
||||
}
|
||||
|
||||
log (symbol.green, 'fetched', Object.keys (ohlcvs).length.toString ().green, 'OHLCVs')
|
||||
|
||||
return ohlcvs
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching OHLCV not supported')
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testOrder = require ('./test.order.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
if (exchange.has.fetchOpenOrders) {
|
||||
|
||||
// log ('fetching open orders...')
|
||||
|
||||
let orders = await exchange.fetchOpenOrders (symbol)
|
||||
|
||||
assert (orders instanceof Array)
|
||||
|
||||
log ('fetched', orders.length.toString ().green, 'open orders')
|
||||
|
||||
let now = Date.now ()
|
||||
|
||||
for (let i = 0; i < orders.length; i++) {
|
||||
let order = orders[i]
|
||||
testOrder (exchange, order, symbol, now)
|
||||
assert (order.status === 'open')
|
||||
}
|
||||
|
||||
// log (asTable (orders))
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching open orders not supported')
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testOrderBook = require ('./test.orderbook.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
// log (symbol.green, 'fetching order book...')
|
||||
|
||||
const method = 'fetchOrderBook'
|
||||
|
||||
if (exchange.has[method]) {
|
||||
|
||||
let orderbook = await exchange[method] (symbol)
|
||||
|
||||
testOrderBook (exchange, orderbook, method, symbol)
|
||||
|
||||
return orderbook
|
||||
|
||||
} else {
|
||||
|
||||
log (method + '() not supported')
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testOrderBook = require ('./test.orderbook.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange) => {
|
||||
|
||||
const randomSymbols = exchange.symbols.sort (() => 0.5 - Math.random ()).slice (0, 2)
|
||||
const customExchangeParams = ([
|
||||
'yobit',
|
||||
'tidex',
|
||||
'ccex',
|
||||
'liqui',
|
||||
]).reduce ((params, id) => ({ ...params, [id]: [randomSymbols], }), {})
|
||||
|
||||
const args = (exchange.id in customExchangeParams) ? customExchangeParams[exchange.id] : []
|
||||
const method = 'fetchOrderBooks'
|
||||
|
||||
if (exchange.has[method]) {
|
||||
|
||||
// log ('fetching order books...')
|
||||
|
||||
let orderbooks = await exchange[method] (...args)
|
||||
|
||||
// log.green (orderbooks)
|
||||
|
||||
Object.entries (orderbooks).forEach (([symbol, orderbook]) => {
|
||||
testOrderBook (exchange, orderbook, method, symbol)
|
||||
})
|
||||
|
||||
// return orderbooks
|
||||
|
||||
} else {
|
||||
|
||||
log (method + '() not supported')
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testOrder = require ('./test.order.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
const skippedExchanges = [
|
||||
'mandala',
|
||||
'rightbtc',
|
||||
]
|
||||
|
||||
if (skippedExchanges.includes (exchange.id)) {
|
||||
log (exchange.id, 'found in ignored exchanges, skipping fetchMyTrades...')
|
||||
return
|
||||
}
|
||||
|
||||
if (exchange.has.fetchOrders) {
|
||||
|
||||
// log ('fetching orders...')
|
||||
|
||||
const orders = await exchange.fetchOrders (symbol)
|
||||
|
||||
log ('fetched', orders.length.toString ().green, 'orders, asserting each...')
|
||||
|
||||
assert (orders instanceof Array)
|
||||
|
||||
const now = Date.now ()
|
||||
|
||||
for (let i = 0; i < orders.length; i++) {
|
||||
const order = orders[i]
|
||||
testOrder (exchange, order, symbol, now)
|
||||
}
|
||||
|
||||
// log (asTable (orders))
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching orders not supported')
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange) => {
|
||||
|
||||
if (exchange.has.fetchStatus) {
|
||||
|
||||
// log ('fetching status...')
|
||||
|
||||
const method = 'fetchStatus'
|
||||
const status = await exchange[method] ()
|
||||
|
||||
const sampleStatus = {
|
||||
'status': 'ok', // 'ok', 'shutdown', 'error', 'maintenance'
|
||||
'updated': undefined, // integer, last updated timestamp in milliseconds if updated via the API
|
||||
'eta': undefined, // when the maintenance or outage is expected to end
|
||||
'url': undefined, // a link to a GitHub issue or to an exchange post on the subject
|
||||
}
|
||||
|
||||
assert.containsAllKeys (status, sampleStatus)
|
||||
|
||||
return status
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching status not supported')
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testTicker = require ('./test.ticker.js')
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const printTickerOneLiner = (ticker, method, symbol) => {
|
||||
|
||||
log (symbol.toString ().green,
|
||||
method,
|
||||
ticker['datetime'],
|
||||
'bid: ' + (ticker['bid']),
|
||||
'ask: ' + (ticker['ask']))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
const method = 'fetchTicker'
|
||||
|
||||
const skippedExchanges = [
|
||||
'digifinex',
|
||||
]
|
||||
|
||||
if (skippedExchanges.includes (exchange.id)) {
|
||||
log (exchange.id, 'found in ignored exchanges, skipping ' + method + '...')
|
||||
return
|
||||
}
|
||||
|
||||
if (exchange.has[method]) {
|
||||
|
||||
// log (symbol.green, 'fetching ticker...')
|
||||
|
||||
let ticker = await exchange.fetchTicker (symbol)
|
||||
|
||||
testTicker (exchange, ticker, method, symbol)
|
||||
|
||||
printTickerOneLiner (ticker, method, symbol)
|
||||
|
||||
return ticker
|
||||
|
||||
} else {
|
||||
|
||||
log (symbol.green, 'fetchTicker () not supported')
|
||||
}
|
||||
}
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testTicker = require ('./test.ticker.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
const skippedExchanges = [
|
||||
'binance',
|
||||
'digifinex',
|
||||
]
|
||||
|
||||
if (skippedExchanges.includes (exchange.id)) {
|
||||
log (exchange.id, 'found in ignored exchanges, skipping fetch all tickers...')
|
||||
return
|
||||
}
|
||||
|
||||
if (exchange.has.fetchTickers) {
|
||||
|
||||
// log ('fetching all tickers at once...')
|
||||
|
||||
const method = 'fetchTickers'
|
||||
let tickers = undefined
|
||||
|
||||
try {
|
||||
|
||||
tickers = await exchange[method] ()
|
||||
log ('fetched all', Object.keys (tickers).length.toString ().green, 'tickers')
|
||||
|
||||
} catch (e) {
|
||||
|
||||
log ('failed to fetch all tickers, fetching multiple tickers at once...')
|
||||
tickers = await exchange[method] ([ symbol ])
|
||||
log ('fetched', Object.keys (tickers).length.toString ().green, 'tickers')
|
||||
}
|
||||
|
||||
Object.values (tickers).forEach (ticker => testTicker (exchange, ticker, method, symbol))
|
||||
return tickers
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching all tickers at once not supported')
|
||||
}
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testTrade = require ('./test.trade.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
const skippedExchanges = [
|
||||
// 'adara',
|
||||
]
|
||||
|
||||
if (skippedExchanges.includes (exchange.id)) {
|
||||
log (exchange.id, 'found in ignored exchanges, skipping fetchTrades...')
|
||||
return
|
||||
}
|
||||
|
||||
if (exchange.has.fetchTrades) {
|
||||
|
||||
// log (symbol.green, 'fetching trades...')
|
||||
|
||||
let trades = await exchange.fetchTrades (symbol)
|
||||
assert (trades instanceof Array)
|
||||
log (symbol.green, 'fetched', Object.values (trades).length.toString ().green, 'trades')
|
||||
let now = Date.now ()
|
||||
for (let i = 0; i < trades.length; i++) {
|
||||
testTrade (exchange, trades[i], symbol, now)
|
||||
if (i > 0) {
|
||||
if (trades[i].timestamp && trades[i - 1].timestamp) {
|
||||
assert (trades[i].timestamp >= trades[i - 1].timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// log (asTable (trades))
|
||||
|
||||
} else {
|
||||
|
||||
log (symbol.green, 'fetchTrades () not supported'.yellow)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange) => {
|
||||
|
||||
const skippedExchanges = [
|
||||
]
|
||||
|
||||
if (skippedExchanges.includes (exchange.id)) {
|
||||
log (exchange.id, 'found in ignored exchanges, skipping fetchTradingFees...')
|
||||
return
|
||||
}
|
||||
|
||||
if (exchange.has.fetchTradingFees) {
|
||||
|
||||
// log ('fetching trading fees...')
|
||||
|
||||
const method = 'fetchTradingFees'
|
||||
const fees = await exchange[method] ()
|
||||
log.green ({ 'maker': fees['maker'], 'taker': fees['taker'] })
|
||||
return fees
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching trading fees not supported')
|
||||
}
|
||||
}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testTransaction = require ('./test.transaction.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, code) => {
|
||||
|
||||
if (exchange.has.fetchTransactions) {
|
||||
|
||||
// log ('fetching transactions...')
|
||||
|
||||
let transactions = await exchange.fetchTransactions (code)
|
||||
|
||||
log ('fetched', transactions.length.toString ().green, 'transactions, asserting each...')
|
||||
|
||||
assert (transactions instanceof Array)
|
||||
|
||||
let now = Date.now ()
|
||||
|
||||
for (let i = 0; i < transactions.length; i++) {
|
||||
let transaction = transactions[i]
|
||||
testTransaction (exchange, transaction, code, now)
|
||||
}
|
||||
|
||||
// log (asTable (transactions))
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching transactions not supported')
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
, testTransaction = require ('./test.transaction.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, code) => {
|
||||
|
||||
if (exchange.has.fetchWithdrawals) {
|
||||
|
||||
// log ('fetching deposits...')
|
||||
|
||||
let transactions = await exchange.fetchWithdrawals (code)
|
||||
|
||||
log ('fetched', transactions.length.toString ().green, 'withdrawals, asserting each...')
|
||||
|
||||
assert (transactions instanceof Array)
|
||||
|
||||
let now = Date.now ()
|
||||
|
||||
for (let i = 0; i < transactions.length; i++) {
|
||||
let transaction = transactions[i]
|
||||
testTransaction (exchange, transaction, code, now)
|
||||
}
|
||||
|
||||
// log (asTable (transactions))
|
||||
|
||||
} else {
|
||||
|
||||
log ('fetching withdrawals not supported')
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = (exchange, item, code, now) => {
|
||||
expect (item).to.be.an ('object')
|
||||
expect (item).to.have.property ('id')
|
||||
expect (typeof item.id).to.be.oneOf (['string', 'undefined'])
|
||||
expect (item).to.have.property ('direction')
|
||||
expect (item.direction).to.be.oneOf (['in', 'out'])
|
||||
expect (item).to.have.property ('account')
|
||||
expect (typeof item.account).to.be.oneOf (['string', 'undefined'])
|
||||
expect (item).to.have.property ('referenceId')
|
||||
expect (typeof item.referenceId).to.be.oneOf (['string', 'undefined'])
|
||||
expect (item).to.have.property ('referenceAccount')
|
||||
expect (typeof item.referenceAccount).to.be.oneOf (['string', 'undefined'])
|
||||
expect (item).to.have.property ('type')
|
||||
// expect (item.type).to.be.oneOf (['trade', 'transaction', 'margin', 'cashback', 'referral', 'transfer', 'fee', /* TODO: add more types here */ ])
|
||||
expect (item).to.have.property ('currency')
|
||||
expect (exchange.currencies).to.have.property (item.currency)
|
||||
expect (item).to.have.property ('amount')
|
||||
expect (typeof item.amount).to.be.oneOf (['number', 'undefined'])
|
||||
expect (item).to.have.property ('before')
|
||||
expect (typeof item.balanceBefore).to.be.oneOf (['number', 'undefined'])
|
||||
expect (item).to.have.property ('after')
|
||||
expect (typeof item.balanceAfter).to.be.oneOf (['number', 'undefined'])
|
||||
expect (item).to.have.property ('timestamp')
|
||||
expect (typeof item.timestamp).to.be.oneOf (['number', 'undefined'])
|
||||
expect (item.timestamp).to.be.gt (1230940800000)
|
||||
expect (item.timestamp).to.be.lt (now)
|
||||
expect (item).to.have.property ('datetime')
|
||||
expect (item.datetime).to.be.equal (exchange.iso8601 (item.timestamp))
|
||||
expect (item).to.have.property ('fee')
|
||||
if (item.fee !== undefined) {
|
||||
expect (item.fee).to.be.an ('object')
|
||||
expect (item.fee).to.have.property ('cost')
|
||||
expect (typeof item.fee.cost).to.be.oneOf (['number', 'undefined'])
|
||||
expect (item.fee).to.have.property ('currency')
|
||||
expect (item.fee.currency).to.be.equal (item.currency)
|
||||
}
|
||||
expect (item).to.have.property ('info').that.is.an ('object')
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = (exchange, market, method) => {
|
||||
|
||||
const format = {
|
||||
'id': 'btcusd', // string literal for referencing within an exchange
|
||||
'symbol': 'BTC/USD', // uppercase string literal of a pair of currencies
|
||||
'base': 'BTC', // uppercase string, base currency, 3 or more letters
|
||||
'quote': 'USD', // uppercase string, quote currency, 3 or more letters
|
||||
//---------------------------------------------------------------------
|
||||
// commented temporarily to bring markets to consistency first
|
||||
// 'active': true, // boolean, market status
|
||||
// 'precision': { // number of decimal digits "after the dot"
|
||||
// 'price': 8, // integer
|
||||
// 'amount': 8, // integer
|
||||
// 'cost': 8, // integer
|
||||
// },
|
||||
// 'limits': { // value limits when placing orders on this market
|
||||
// 'amount': {
|
||||
// 'min': 0.01, // order amount should be > min
|
||||
// 'max': 1000, // order amount should be < max
|
||||
// },
|
||||
// 'price': {
|
||||
// 'min': 0.01, // order price should be > min
|
||||
// 'max': 1000, // order price should be < max
|
||||
// },
|
||||
// 'cost': { // order cost = price * amount
|
||||
// 'min': 0.01, // order cost should be > min
|
||||
// 'max': 1000, // order cost should be < max
|
||||
// },
|
||||
// },
|
||||
// 'info': {}, // the original unparsed market info from the exchange
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
expect (market).to.deep.include.all.keys (format)
|
||||
expect (market).to.not.have.key ('lot')
|
||||
|
||||
// expect (market['precision']['amount']).to.not.be.undefined
|
||||
// expect (market['precision']['price']).to.not.be.undefined
|
||||
|
||||
// expect (market['limits']['amount']['min']).to.not.be.undefined
|
||||
// expect (market['limits']['price']['min']).to.not.be.undefined
|
||||
// expect (market['limits']['cost']['min']).to.not.be.undefined
|
||||
|
||||
// log (market)
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = (exchange, ohlcv, symbol, now) => {
|
||||
assert.isOk (ohlcv)
|
||||
assert (Array.isArray (ohlcv))
|
||||
assert (ohlcv.length >= 6)
|
||||
for (let i = 0; i < ohlcv.length; i++) {
|
||||
assert (ohlcv[i] === undefined || typeof ohlcv[i] === 'number')
|
||||
}
|
||||
|
||||
assert (ohlcv[0] > 1230940800000) // 03 Jan 2009 - first block
|
||||
assert (ohlcv[0] < 2147483648000) // 19 Jan 2038 - int32 overflows
|
||||
|
||||
assert (ohlcv[1] === undefined || ohlcv[2] === undefined || ohlcv[1] <= ohlcv[2], 'open > high! ' + (ohlcv[1].toString () + ' > ' + ohlcv[2].toString ())) // open <= high
|
||||
assert (ohlcv[3] === undefined || ohlcv[2] === undefined || ohlcv[3] <= ohlcv[2], 'low > high! ' + (ohlcv[3].toString () + ' > ' + ohlcv[2].toString ())) // low <= high
|
||||
assert (ohlcv[3] === undefined || ohlcv[4] === undefined || ohlcv[3] <= ohlcv[4], 'low > close! ' + (ohlcv[3].toString () + ' > ' + ohlcv[4].toString ())) // low <= close
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = (exchange, order, symbol, now) => {
|
||||
assert.isOk (order)
|
||||
assert (typeof order.id === 'string')
|
||||
assert (typeof order.timestamp === 'number')
|
||||
assert (order.timestamp > 1230940800000) // 03 Jan 2009 - first block
|
||||
assert (order.timestamp < now)
|
||||
assert ('lastTradeTimestamp' in order)
|
||||
assert (order.datetime === exchange.iso8601 (order.timestamp))
|
||||
assert (order.status === 'open' || order.status === 'closed' || order.status === 'canceled')
|
||||
assert (order.symbol === symbol)
|
||||
assert (typeof order.type === 'string')
|
||||
assert (order.side === 'buy' || order.side === 'sell')
|
||||
assert (typeof order.price === 'number')
|
||||
assert (order.price > 0)
|
||||
assert (typeof order.amount === 'number')
|
||||
assert (order.amount >= 0)
|
||||
if (order.filled) {
|
||||
assert (typeof order.filled === 'number')
|
||||
assert (order.filled >= 0 && order.filled <= order.amount)
|
||||
}
|
||||
if (order.remaining) {
|
||||
assert (typeof order.remaining === 'number')
|
||||
assert (order.remaining >= 0 && order.remaining <= order.amount)
|
||||
}
|
||||
if (order.trades) {
|
||||
assert (order.trades instanceof Array)
|
||||
}
|
||||
if (order.fee) {
|
||||
assert (typeof order.fee.cost === 'number')
|
||||
if (order.fee.cost !== 0)
|
||||
assert (typeof order.fee.currency === 'string')
|
||||
}
|
||||
assert.isOk (order.info)
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const printOrderBookOneLiner = (orderbook, method, symbol) => {
|
||||
|
||||
const bids = orderbook.bids
|
||||
const asks = orderbook.asks
|
||||
|
||||
log (symbol.toString ().green,
|
||||
method,
|
||||
orderbook['nonce'] || orderbook['datetime'],
|
||||
bids.length, 'bids:', bids[0],
|
||||
asks.length, 'asks:', asks[0])
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = (exchange, orderbook, method, symbol) => {
|
||||
|
||||
const format = {
|
||||
'bids': [],
|
||||
'asks': [],
|
||||
'timestamp': 1234567890,
|
||||
'datetime': '2017-09-01T00:00:00',
|
||||
'nonce': 134234234,
|
||||
// 'info': {},
|
||||
}
|
||||
|
||||
expect (orderbook).to.have.all.keys (format)
|
||||
|
||||
const bids = orderbook.bids
|
||||
const asks = orderbook.asks
|
||||
|
||||
for (let i = 0; i < bids.length; i++) {
|
||||
if (bids.length > (i + 1)) {
|
||||
assert (bids[i][0] >= bids[i + 1][0])
|
||||
}
|
||||
assert (typeof bids[i][0] === 'number')
|
||||
assert (typeof bids[i][1] === 'number')
|
||||
}
|
||||
|
||||
for (let i = 0; i < asks.length; i++) {
|
||||
if (asks.length > (i + 1)) {
|
||||
assert (asks[i][0] <= asks[i + 1][0])
|
||||
}
|
||||
assert (typeof asks[i][0] === 'number')
|
||||
assert (typeof asks[i][1] === 'number')
|
||||
}
|
||||
|
||||
if (![
|
||||
|
||||
'coinmarketcap',
|
||||
'xbtce',
|
||||
'upbit', // an orderbook might have a 0-price ask occasionally
|
||||
|
||||
].includes (exchange.id)) {
|
||||
|
||||
if (bids.length && asks.length)
|
||||
assert (bids[0][0] <= asks[0][0],
|
||||
`bids[0][0]: ${bids[0][0]} (of ${bids.length}); asks[0][0]:${asks[0][0]} (of ${asks.length})`)
|
||||
|
||||
}
|
||||
|
||||
printOrderBookOneLiner (orderbook, method, symbol)
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = (exchange, ticker, method, symbol) => {
|
||||
|
||||
const format = {
|
||||
'symbol': 'ETH/BTC',
|
||||
'info': {},
|
||||
'timestamp': 1234567890,
|
||||
'datetime': '2017-09-01T00:00:00',
|
||||
'high': 1.234, // highest price
|
||||
'low': 1.234, // lowest price
|
||||
'bid': 1.234, // current best bid (buy) price
|
||||
'bidVolume': 1.234, // current best bid (buy) amount (may be missing or undefined)
|
||||
'ask': 1.234, // current best ask (sell) price
|
||||
'askVolume': 1.234, // current best ask (sell) amount (may be missing or undefined)
|
||||
'vwap': 1.234, // volume weighed average price
|
||||
'open': 1.234, // opening price
|
||||
'close': 1.234, // price of last trade (closing price for current period)
|
||||
'last': 1.234, // same as `close`, duplicated for convenience
|
||||
'previousClose': 1.234, // closing price for the previous period
|
||||
'change': 1.234, // absolute change, `last - open`
|
||||
'percentage': 1.234, // relative change, `(change/open) * 100`
|
||||
'average': 1.234, // average price, `(last + open) / 2`
|
||||
'baseVolume': 1.234, // volume of base currency
|
||||
'quoteVolume': 1.234, // volume of quote currency
|
||||
}
|
||||
|
||||
expect (ticker).to.have.all.keys (format)
|
||||
|
||||
const keys = [ 'datetime', 'timestamp', 'high', 'low', 'bid', 'ask', 'baseVolume', 'quoteVolume', 'vwap' ]
|
||||
|
||||
assert (!('first' in ticker), '`first` field leftover in ' + exchange.id)
|
||||
assert (ticker['last'] === ticker['close'], '`last` != `close` in ' + exchange.id)
|
||||
|
||||
log (ticker['datetime'], exchange.id, method, ticker['symbol'].green, ticker['last'])
|
||||
|
||||
keys.forEach (key => assert (key in ticker))
|
||||
|
||||
const { high, low, vwap, baseVolume, quoteVolume } = ticker
|
||||
|
||||
// this assert breaks QuadrigaCX sometimes... still investigating
|
||||
// if (vwap)
|
||||
// assert (vwap >= low && vwap <= high)
|
||||
|
||||
/*
|
||||
if (baseVolume && quoteVolume && high && low) {
|
||||
assert (quoteVolume >= baseVolume * low) // this assertion breaks therock
|
||||
assert (quoteVolume <= baseVolume * high)
|
||||
}
|
||||
*/
|
||||
|
||||
if (baseVolume && vwap)
|
||||
assert (quoteVolume)
|
||||
|
||||
if (quoteVolume && vwap)
|
||||
assert (baseVolume)
|
||||
|
||||
// log (symbol.green, 'ticker',
|
||||
// ticker['datetime'],
|
||||
// ... (keys.map (key =>
|
||||
// key + ': ' + ticker[key])))
|
||||
|
||||
if (![
|
||||
|
||||
'coinmarketcap',
|
||||
'xbtce',
|
||||
'coss',
|
||||
'idex',
|
||||
'mandala',
|
||||
'mercado',
|
||||
'okex',
|
||||
'southxchange', // https://user-images.githubusercontent.com/1294454/59953532-314bea80-9489-11e9-85b3-2a711ca49aa7.png
|
||||
'bitmart',
|
||||
|
||||
].includes (exchange.id)) {
|
||||
|
||||
if (ticker['baseVolume'] || ticker['quoteVolume']) {
|
||||
if (ticker['bid'] && ticker['ask']) {
|
||||
assert (ticker['bid'] <= ticker['ask'], 'ticker bid is greater than ticker ask!')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ticker
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = (exchange, trade, symbol, now) => {
|
||||
|
||||
assert.isOk (trade)
|
||||
|
||||
const sampleTrade = {
|
||||
'info': { a: 1, b: 2, c: 3 }, // the original decoded JSON as is
|
||||
'id': '12345-67890:09876/54321', // string trade id
|
||||
'timestamp': 1502962946216, // Unix timestamp in milliseconds
|
||||
'datetime': '2017-08-17 12:42:48.000', // ISO8601 datetime with milliseconds
|
||||
'symbol': 'ETH/BTC', // symbol
|
||||
'order': '12345-67890:09876/54321', // string order id or undefined/None/null
|
||||
'type': 'limit', // order type, 'market', 'limit' or undefined/None/null
|
||||
'side': 'buy', // direction of the trade, 'buy' or 'sell'
|
||||
'takerOrMaker': 'taker', // string, 'taker' or 'maker'
|
||||
'price': 0.06917684, // float price in quote currency
|
||||
'amount': 1.5, // amount of base currency
|
||||
'cost': 0.10376526, // total cost (including fees), `price * amount`
|
||||
}
|
||||
|
||||
assert.containsAllKeys (trade, sampleTrade)
|
||||
assert.hasAnyKeys (trade, [ 'fee', 'fees' ])
|
||||
|
||||
const feeKeys = [ 'cost', 'currency' ]
|
||||
|
||||
if (trade.fee !== undefined) {
|
||||
assert (typeof trade.fee === 'object')
|
||||
assert.containsAllKeys (trade.fee, feeKeys)
|
||||
}
|
||||
|
||||
if (trade.fees !== undefined) {
|
||||
assert (typeof trade.fees === 'object')
|
||||
assert (Array.isArray (trade.fees))
|
||||
trade.fees.forEach (fee => assert.containsAllKeys (fee, feeKeys))
|
||||
}
|
||||
|
||||
assert (trade.id === undefined || typeof trade.id === 'string')
|
||||
assert (typeof trade.timestamp === 'number' || trade.timestamp === undefined)
|
||||
|
||||
if (trade.timestamp) {
|
||||
assert (trade.timestamp > 1230940800000) // 03 Jan 2009 - first block
|
||||
assert (trade.timestamp < 2147483648000) // 19 Jan 2038 - int32 overflows
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
const adjustedNow = now + 60000
|
||||
|
||||
const exchangesExcludedFromTimestampCheck = [
|
||||
'coinbasepro',
|
||||
'coinbaseprime', // ... as well, probably
|
||||
'kucoin2',
|
||||
]
|
||||
|
||||
if (trade.timestamp) {
|
||||
if (!exchangesExcludedFromTimestampCheck.includes (exchange.id)) {
|
||||
assert (trade.timestamp < adjustedNow, 'trade.timestamp is greater than or equal to current time: trade: ' + exchange.iso8601 (trade.timestamp) + ' now: ' + exchange.iso8601 (now))
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
assert (trade.datetime === exchange.iso8601 (trade.timestamp))
|
||||
|
||||
const isExchangeLackingFilteringTradesBySymbol = [
|
||||
'kraken', // override for kraken and possibly other exchanges as well, can't return private trades per symbol at all
|
||||
].includes (exchange.id)
|
||||
|
||||
if (!isExchangeLackingFilteringTradesBySymbol)
|
||||
assert (trade.symbol === symbol, 'trade symbol is not equal to requested symbol: trade: ' + trade.symbol + ' reqeusted: ' + symbol)
|
||||
|
||||
assert (trade.type === undefined || typeof trade.type === 'string')
|
||||
assert (trade.side === undefined || trade.side === 'buy' || trade.side === 'sell')
|
||||
assert (trade.order === undefined || typeof trade.order === 'string')
|
||||
assert (typeof trade.price === 'number', 'trade.price is not a number')
|
||||
assert (trade.price > 0)
|
||||
assert (typeof trade.amount === 'number', 'trade.amount is not a number')
|
||||
assert (trade.amount >= 0)
|
||||
assert (trade.takerOrMaker === undefined || trade.takerOrMaker === 'taker' || trade.takerOrMaker === 'maker')
|
||||
assert.isOk (trade.info)
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = (exchange, transaction, code, now) => {
|
||||
assert.isOk (transaction)
|
||||
assert (typeof transaction.id === 'string')
|
||||
assert (typeof transaction.timestamp === 'number')
|
||||
assert (transaction.timestamp > 1230940800000) // 03 Jan 2009 - first block
|
||||
assert (transaction.timestamp < now)
|
||||
assert ('updated' in transaction)
|
||||
assert ('address' in transaction)
|
||||
assert ('tag' in transaction)
|
||||
assert ('txid' in transaction)
|
||||
assert (transaction.datetime === exchange.iso8601 (transaction.timestamp))
|
||||
assert (transaction.status === 'ok' || transaction.status === 'pending' || transaction.status === 'canceled')
|
||||
assert (transaction.currency === code)
|
||||
assert (typeof transaction.type === 'string')
|
||||
assert (transaction.type === 'deposit' || transaction.type === 'withdrawal')
|
||||
assert (typeof transaction.amount === 'number')
|
||||
assert (transaction.amount >= 0)
|
||||
if (transaction.fee) {
|
||||
assert (typeof transaction.fee.cost === 'number')
|
||||
if (transaction.fee.cost !== 0) {
|
||||
assert (typeof transaction.fee.currency === 'string')
|
||||
}
|
||||
}
|
||||
assert.isOk (transaction.info)
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
const { hash, ecdsa, jwt } = require ('../../../base/functions/crypto')
|
||||
const { encode } = require ('../../../base/functions/encode')
|
||||
const Exchange = require ('../../../base/Exchange');
|
||||
const assert = require ('assert')
|
||||
|
||||
function equals (a, b) {
|
||||
// does not check if b has more properties than a
|
||||
for (const prop of Object.keys (a)) {
|
||||
if (a[prop] !== b[prop]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const exchange = new Exchange ();
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
assert (hash (encode (''), 'sha256', 'hex') === 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
|
||||
assert (hash (encode ('cheese'), 'sha256', 'hex') === '873ac9ffea4dd04fa719e8920cd6938f0c23cd678af330939cff53c3d2855f34');
|
||||
|
||||
assert (hash (encode (''), 'md5', 'hex') === 'd41d8cd98f00b204e9800998ecf8427e');
|
||||
assert (hash (encode ('sexyfish'), 'md5', 'hex') === 'c8a35464aa9d5683585786f44d5889f8');
|
||||
|
||||
assert (hash (encode (''), 'sha1', 'hex') === 'da39a3ee5e6b4b0d3255bfef95601890afd80709');
|
||||
assert (hash (encode ('nutella'), 'sha1', 'hex') === 'b3d60a34b744159793c483b067c56d8affc5111a');
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
const privateKey = '1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a';
|
||||
|
||||
assert (equals (ecdsa ('1a', privateKey, 'p256', 'sha256'), {
|
||||
'r': '3f4537ef9e72240cdefaea19d68fd483b5e10227e89747c58a3b543bfaf9cc46',
|
||||
's': '278d65adebd8d52dac34820c02cee0a0e3348f9aee91a21b8e9bfa0a23b40001',
|
||||
'v': 1
|
||||
}));
|
||||
|
||||
|
||||
assert (equals (ecdsa (privateKey, privateKey, 'p256', undefined), {
|
||||
'r': '9aadbfe7feca28a8b665731abd8fdd19f8b1be5fcdd1264d111b291a01c8dea2',
|
||||
's': '5084765e08e169fbba470ed1f573c9fed8d68a9d883049054b601f66a6648912',
|
||||
'v': 0,
|
||||
}));
|
||||
|
||||
assert (equals (ecdsa ('1a', privateKey, 'secp256k1', 'sha256'), {
|
||||
'r': '23dcb2a2a3728a35eb1a35cc01743c4609550d9cceaf2083550f13a9eb135f9f',
|
||||
's': '317963fcac18e4ec9f7921b97d7ea0c82a873dd6299cbfb6af016e08ef5ed667',
|
||||
'v': 0,
|
||||
}));
|
||||
|
||||
|
||||
assert (equals (ecdsa (privateKey, privateKey, 'secp256k1', undefined), {
|
||||
'r': 'b84a36a6fbabd5277ede578448b93d48e70b38efb5b15b1d4e2a298accf938b1',
|
||||
's': '66ebfb8221cda925526e699a59cd221bb4cc84bdc563024b1802c4d9e1d8bbe9',
|
||||
'v': 1,
|
||||
}));
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
assert (exchange.hashMessage (privateKey) === '0x59ea5d98c3500c3729f95cf98aa91663f498518cc401360df2912742c232207f');
|
||||
|
||||
assert (equals (exchange.signHash ('0x59ea5d98c3500c3729f95cf98aa91663f498518cc401360df2912742c232207f', privateKey), {
|
||||
'r': '0x6f684aa41c02da83dac3039d8805ddbe79a03b1297e247c7742cab8dfc19d341',
|
||||
's': '0x62473881674550563cb028ff40a7846fd53620ddf40a20cc1003b8484a109a4a',
|
||||
'v': 27
|
||||
}));
|
||||
|
||||
assert (equals (exchange.signMessage (privateKey, privateKey), {
|
||||
'r': '0x6f684aa41c02da83dac3039d8805ddbe79a03b1297e247c7742cab8dfc19d341',
|
||||
's': '0x62473881674550563cb028ff40a7846fd53620ddf40a20cc1003b8484a109a4a',
|
||||
'v': 27
|
||||
}));
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
const pemKeyArray = [
|
||||
'-----BEGIN RSA PRIVATE KEY-----',
|
||||
'MIIEpAIBAAKCAQEAqUtXQXv2uSm9zPvdJTRpu5/65rBjAoHmIiowAs+u7fY0QP9O',
|
||||
'+T8CZRQjvZrfPomBiccjCxDo8mm7GkmL67rs9s7VOMQFvpTTtMBeglNSEbXJ3wTt',
|
||||
'7WM5gD3ZAbhfGtwWdGpcOMX0hV5/d/fiw1zQk0+AooayTryZ7HV+DQcD1sGTYYir',
|
||||
'LePUGkKV2NCvKtGOQfRAtuo0ccigMxMvEhWuExCc9Fhd077OpXMc0n7OiFqA2JWN',
|
||||
'jlocM9S71Srvw/lDos8a7lGaFUV8gP1CsQY/4qTju6hhsRd4lFujVr5J9FqmDbnv',
|
||||
'wQ79SSu6+lT0+ToFdDHiYOorBK2ESFR7EzlyLwIDAQABAoIBAEobQMbZjNbg/sSM',
|
||||
'O/HdT6tiDGKPM8gVNLgf34RbhSeFbrpFCDzy6Al3F24YLUEi0CGPmjdt34q93blU',
|
||||
'GHvIB5LCV3PR2vHiFAo7ayOBdZtrCEMn1T7lAHaynBu0qW0IiovLQzNW9AKtqv7I',
|
||||
'8+qw5lyVoKmEbOkqhfaMN/Fb8MJAo3yEVyhyp4EjtFhqDQ9DuHEjp1jGthnMgm+4',
|
||||
'qFyELf0DXZSAN+R9IGDqWPyo78lOcPW4pgeLuoLQ6Nn/2JAEGHFt1a7zMEw5yYbR',
|
||||
'XB89Bh0dheSmopBN6Heay4YuhnNKua37OlL1/nhKpLGUNE3z/UOXeiUiMeE1C2i+',
|
||||
'GjK+xgECgYEA4XH63rJ3VYDHHWUGlWffFWkEpgR4BDEkZ8MyVYQoUjQA2TpCvQ2+',
|
||||
'bR7aAIGXhBnRTrIBNu9m4+am0aUfi1hVmVHqt1b2vFgEb9uKuO4U62tCFpeji+Rz',
|
||||
'8C8QJyu5v5OUWdQZ4EA0d7ljeoTl50g8tpcGsDhnImLt+/jvJuFt8dECgYEAwD0n',
|
||||
'rhlkEjKVHxu1xwKeczxYCqqcUV9Quq8HnPh4VJQ77ljfZ57V7sAKrFDi5XfVD9jS',
|
||||
'oJPwxjw6za134VobhdvCM6WNs6AJmbR8E/b5kDMbGaZn27vt+/8JfnGaWBqpDi6W',
|
||||
'a5xoJOqmcMBQBK1YleZDoY4PLbk+onVKLMfGI/8CgYEAkyUIv8euGdGWpHnm5SdJ',
|
||||
'tLi5vv4Vs267uzntJWG/y3+DukTLgIdy7dgAI+pxkVgkg/+syUVSW5eU9CqZPyLl',
|
||||
'o8+Sqh2Jp36vTq71iSRj5RA5r3ND3K+8eFzPZzGj6AWUA1lrljFxzV7kLfiF8gH1',
|
||||
'FpvWUrhNoGT/vcFJno/uabECgYAecRC5hxfLserfVDoC261PvjyK492BHUDhbxob',
|
||||
'h1U2v4qGAdjOxd5Gwm5uPxjPEZzRt5oTB5pXKe5953xWWTiGh/hGyW6ZBTy/9E65',
|
||||
'sqBub0lZVHqZ1zamcwqD1WWFkiM3NbVoMQpk3iuhKzMAqpqekiofiSlqKi16+GvY',
|
||||
'j4IW7QKBgQCrIUckPZ6IY3ERIN1IL4TIcK2gOJcznp7fLWpC+sv35ya3OhtDXKFt',
|
||||
'GjIHmwLuiUNc0iPzA9Rw89W0zIkKWmWcxM88/ithdxh3MEeNDUGSnd4hQ9SECwxB',
|
||||
'Wem3eBT7I4VtFYoaTE3/bX1SKfgBdTzIRqWKSDpgBNZg/P2Tc+s11g==',
|
||||
'-----END RSA PRIVATE KEY-----',
|
||||
];
|
||||
|
||||
const pemKey = pemKeyArray.join ("\n");
|
||||
|
||||
assert (jwt ({'chicken': 'salad'}, encode (pemKey), 'RS256') === 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaGlja2VuIjoic2FsYWQifQ.FSKD5Y6RNzkHTuHdvG3753U7QNZ-u-GUSPfP1FMjEaK0Rr_iyQTSSmHhkdYSFFnmBvrrN_l-UwKwir52WlsgmQm9HYm0kidxbj7fWwrK2E1oe0P7OjupFjv1BZxc5W69WeaHtOPWe28tiHiON1LCnax6HgfI5lcIBsESGIIBZMVeaioQn9gDVwea7JxJvAlrhDIWZowIHTIdCQocXip7g5jREWHeEIuJNug67mwnfAFxCjvTRiTd0Bw6oBwjM3FLya-RyEyWrejQOWSuC8CNWVUHISaSmEyZ7uM6wTi2m_58TaE9mQwlef32OPErPvvBpgL5pZIyQ4ymwrCIFQLBQQ');
|
||||
assert (jwt ({'lil': 'xan'}, encode ('betrayed'), 'HS256') === 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsaWwiOiJ4YW4ifQ.md-oFvZagA-NXmZoRNyJOQ7zwK-PWUMmMQ_LI9ZOKaM');
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
assert (exchange.soliditySha3 (['0x63581b9abf2b661da0ba247e0dda1b723dcff5e3', 100, 'lulwat']) === '0x1d3e95c10fc64aee6628ea59284503a4eafc6ff13c541f00753fbd2a66cea0f5');
|
||||
assert (exchange.soliditySha3 ([234]) === '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2');
|
||||
assert (exchange.soliditySha3 (['234']) === '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2');
|
||||
assert (exchange.soliditySha3 (['Hello!%']) === '0x661136a4267dba9ccdf6bfddb7c00e714de936674c4bdb065a531cf1cb15c7fc');
|
||||
assert (exchange.soliditySha3 (['0x407D73d8a49eeb85D32Cf465507dd71d507100c1']) === '0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b');
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
'use strict'
|
||||
|
||||
const ccxt = require ('../../../../ccxt');
|
||||
const assert = require ('assert');
|
||||
const { ROUND_UP, ROUND_DOWN } = require ('../../../base/functions/misc');
|
||||
|
||||
const exchange = new ccxt.Exchange ({
|
||||
'id': 'regirock',
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
assert (exchange.iso8601 (514862627000) === '1986-04-26T01:23:47.000Z');
|
||||
assert (exchange.iso8601 (514862627559) === '1986-04-26T01:23:47.559Z');
|
||||
assert (exchange.iso8601 (514862627062) === '1986-04-26T01:23:47.062Z');
|
||||
|
||||
assert (exchange.iso8601 (0) === '1970-01-01T00:00:00.000Z');
|
||||
|
||||
assert (exchange.iso8601 (-1) === undefined);
|
||||
assert (exchange.iso8601 () === undefined);
|
||||
assert (exchange.iso8601 (undefined) === undefined);
|
||||
assert (exchange.iso8601 ('') === undefined);
|
||||
assert (exchange.iso8601 ('a') === undefined);
|
||||
assert (exchange.iso8601 ({}) === undefined);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
|
||||
assert (exchange.parse8601 ('3333') === undefined);
|
||||
assert (exchange.parse8601 ('Sr90') === undefined);
|
||||
assert (exchange.parse8601 ('') === undefined);
|
||||
assert (exchange.parse8601 () === undefined);
|
||||
assert (exchange.parse8601 (undefined) === undefined);
|
||||
assert (exchange.parse8601 ({}) === undefined);
|
||||
assert (exchange.parse8601 (33) === undefined);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
assert (exchange.parseDate ('1986-04-26 00:00:00') === 514857600000);
|
||||
assert (exchange.parseDate ('1986-04-26T01:23:47.000Z') === 514862627000);
|
||||
assert (exchange.parseDate ('1986-13-13 00:00:00') === undefined);
|
||||
|
||||
|
||||
assert (exchange.roundTimeframe('5m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_DOWN) === exchange.parse8601('2019-08-12 13:20:00'));
|
||||
assert (exchange.roundTimeframe('10m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_DOWN) === exchange.parse8601('2019-08-12 13:20:00'));
|
||||
assert (exchange.roundTimeframe('30m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_DOWN) === exchange.parse8601('2019-08-12 13:00:00'));
|
||||
assert (exchange.roundTimeframe('1d', exchange.parse8601('2019-08-12 13:22:08'), ROUND_DOWN) === exchange.parse8601('2019-08-12 00:00:00'));
|
||||
|
||||
assert (exchange.roundTimeframe('5m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_UP) === exchange.parse8601('2019-08-12 13:25:00'));
|
||||
assert (exchange.roundTimeframe('10m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_UP) === exchange.parse8601('2019-08-12 13:30:00'));
|
||||
assert (exchange.roundTimeframe('30m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_UP) === exchange.parse8601('2019-08-12 13:30:00'));
|
||||
assert (exchange.roundTimeframe('1h', exchange.parse8601('2019-08-12 13:22:08'), ROUND_UP) === exchange.parse8601('2019-08-12 14:00:00'));
|
||||
assert (exchange.roundTimeframe('1d', exchange.parse8601('2019-08-12 13:22:08'), ROUND_UP) === exchange.parse8601('2019-08-13 00:00:00'));
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
'use strict'
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const { deepExtend, groupBy, filterBy, omit, sum, sortBy } = require ('../../../../ccxt')
|
||||
const { strictEqual: equal, deepEqual } = require ('assert')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('deepExtend() works', () => {
|
||||
|
||||
let count = 0
|
||||
|
||||
const values = [{
|
||||
a: 1,
|
||||
b: 2,
|
||||
d: {
|
||||
a: 1,
|
||||
b: [],
|
||||
c: { test1: 123, test2: 321 }},
|
||||
f: 5,
|
||||
g: 123,
|
||||
i: 321,
|
||||
j: [1, 2],
|
||||
},
|
||||
{
|
||||
b: 3,
|
||||
c: 5,
|
||||
d: {
|
||||
b: { first: 'one', second: 'two' },
|
||||
c: { test2: 222 }},
|
||||
e: { one: 1, two: 2 },
|
||||
f: [{ 'foo': 'bar' }],
|
||||
g: (void 0),
|
||||
h: /abc/g,
|
||||
i: null,
|
||||
j: [3, 4]
|
||||
}]
|
||||
|
||||
const extended = deepExtend (...values)
|
||||
deepEqual ({
|
||||
a: 1,
|
||||
b: 3,
|
||||
d: {
|
||||
a: 1,
|
||||
b: { first: 'one', second: 'two' },
|
||||
c: { test1: 123, test2: 222 }
|
||||
},
|
||||
f: [{ 'foo': 'bar' }],
|
||||
g: undefined,
|
||||
c: 5,
|
||||
e: { one: 1, two: 2 },
|
||||
h: /abc/g,
|
||||
i: null,
|
||||
j: [3, 4]
|
||||
}, extended)
|
||||
|
||||
deepEqual (deepExtend (undefined, undefined, {'foo': 'bar' }), { 'foo': 'bar' })
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('groupBy() works', () => {
|
||||
|
||||
const array = [
|
||||
{ 'foo': 'a' },
|
||||
{ 'foo': 'b' },
|
||||
{ 'foo': 'c' },
|
||||
{ 'foo': 'b' },
|
||||
{ 'foo': 'c' },
|
||||
{ 'foo': 'c' },
|
||||
]
|
||||
|
||||
deepEqual (groupBy (array, 'foo'), {
|
||||
'a': [ { 'foo': 'a' } ],
|
||||
'b': [ { 'foo': 'b' }, { 'foo': 'b' } ],
|
||||
'c': [ { 'foo': 'c' }, { 'foo': 'c' }, { 'foo': 'c' } ],
|
||||
})
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('filterBy() works', () => {
|
||||
|
||||
const array = [
|
||||
{ 'foo': 'a' },
|
||||
{ 'foo': undefined },
|
||||
{ 'foo': 'b' },
|
||||
{ },
|
||||
{ 'foo': 'a', 'bar': 'b' },
|
||||
{ 'foo': 'c' },
|
||||
{ 'foo': 'd' },
|
||||
{ 'foo': 'b' },
|
||||
{ 'foo': 'c' },
|
||||
{ 'foo': 'c' },
|
||||
]
|
||||
|
||||
deepEqual (filterBy (array, 'foo', 'a'), [
|
||||
{ 'foo': 'a' },
|
||||
{ 'foo': 'a', 'bar': 'b' },
|
||||
])
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('omit works', () => {
|
||||
|
||||
deepEqual (omit ({ }, 'foo'), {})
|
||||
deepEqual (omit ({ foo: 2 }, 'foo'), { })
|
||||
deepEqual (omit ({ foo: 2, bar: 3 }, 'foo'), { bar: 3 })
|
||||
deepEqual (omit ({ foo: 2, bar: 3 }, ['foo']), { bar: 3 })
|
||||
deepEqual (omit ({ foo: 2, bar: 3 }), { foo: 2, bar: 3 })
|
||||
deepEqual (omit ({ foo: 2, bar: 3 }, 'foo', 'bar'), {})
|
||||
deepEqual (omit ({ foo: 2, bar: 3 }, ['foo'], 'bar'), {})
|
||||
deepEqual (omit ({ 5: 2, bar: 3 }, [5]), { bar: 3 })
|
||||
deepEqual (omit ({ 5: 2, bar: 3 }, 5), { bar: 3 })
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('sum works', () => {
|
||||
|
||||
equal (undefined, sum ())
|
||||
equal (2, sum (2))
|
||||
equal (432, sum (2,30,400))
|
||||
equal (432, sum (2, undefined, [88], 30, '7', 400, null))
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('sortBy works', () => {
|
||||
|
||||
const arr = [{ x: 5 }, { x: 2 }, { x: 4 }, { x: 0 },{ x: 1 },{ x: 3 }]
|
||||
sortBy (arr, 'x')
|
||||
|
||||
deepEqual (arr
|
||||
[ { x: 0 },
|
||||
{ x: 1 },
|
||||
{ x: 2 },
|
||||
{ x: 3 },
|
||||
{ x: 4 },
|
||||
{ x: 5 } ])
|
||||
|
||||
deepEqual (sortBy (arr, 'x', true),
|
||||
[ { x: 5 },
|
||||
{ x: 4 },
|
||||
{ x: 3 },
|
||||
{ x: 2 },
|
||||
{ x: 1 },
|
||||
{ x: 0 } ])
|
||||
|
||||
deepEqual (sortBy ([], 'x'), [])
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
'use strict'
|
||||
|
||||
const { numberToString, decimalToPrecision, ROUND, TRUNCATE, DECIMAL_PLACES, TICK_SIZE, PAD_WITH_ZERO, SIGNIFICANT_DIGITS, toWei, fromWei } = require ('../../../../ccxt');
|
||||
const assert = require ('assert');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// toWei / fromWei
|
||||
|
||||
assert (toWei (1, 18) === '1000000000000000000');
|
||||
assert (toWei (1, 17) === '100000000000000000');
|
||||
assert (toWei (1, 16) === '10000000000000000');
|
||||
assert (toWei ('1', 18) === '1000000000000000000');
|
||||
assert (toWei ('1', 17) === '100000000000000000');
|
||||
assert (toWei ('1', 16) === '10000000000000000');
|
||||
assert (toWei (0, 18) === '0');
|
||||
assert (toWei (1, 0) === '1');
|
||||
assert (toWei (1, 1) === '10');
|
||||
assert (toWei (1.3, 18) === '1300000000000000000');
|
||||
assert (toWei ('1.3', 18) === '1300000000000000000');
|
||||
assert (toWei (1.999, 17) === '199900000000000000');
|
||||
assert (toWei ('1.999', 17) === '199900000000000000');
|
||||
assert (toWei ('0.1', 18) === '100000000000000000');
|
||||
assert (toWei ('0.01', 18) === '10000000000000000');
|
||||
assert (toWei ('0.001', 18) === '1000000000000000');
|
||||
assert (toWei (0.1, 18) === '100000000000000000');
|
||||
assert (toWei (0.01, 18) === '10000000000000000');
|
||||
assert (toWei (0.001, 18) === '1000000000000000');
|
||||
assert (toWei ('0.3323340739', 18) === '332334073900000000');
|
||||
assert (toWei (0.3323340739, 18) === '332334073900000000');
|
||||
|
||||
// let us test that we get the inverse for all these test
|
||||
assert (fromWei ('1000000000000000000', 18) === 1.0);
|
||||
assert (fromWei ('100000000000000000', 17) === 1.0);
|
||||
assert (fromWei ('10000000000000000', 16) === 1.0);
|
||||
assert (fromWei (1000000000000000000, 18) === 1.0);
|
||||
assert (fromWei (100000000000000000, 17) === 1.0);
|
||||
assert (fromWei (10000000000000000, 16) === 1.0);
|
||||
assert (fromWei ('1300000000000000000', 18) === 1.3);
|
||||
assert (fromWei (1300000000000000000, 18) === 1.3);
|
||||
assert (fromWei ('199900000000000000', 17) === 1.999);
|
||||
assert (fromWei (199900000000000000, 17) === 1.999);
|
||||
assert (fromWei ('100000000000000000', 18) === 0.1);
|
||||
assert (fromWei ('10000000000000000', 18) === 0.01);
|
||||
assert (fromWei ('1000000000000000', 18) === 0.001);
|
||||
assert (fromWei (100000000000000000, 18) === 0.1);
|
||||
assert (fromWei (10000000000000000, 18) === 0.01);
|
||||
assert (fromWei (1000000000000000, 18) === 0.001);
|
||||
assert (fromWei ('332334073900000000', 18) === 0.3323340739);
|
||||
assert (fromWei (332334073900000000, 18) === 0.3323340739);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// numberToString
|
||||
|
||||
assert (numberToString (-7.8e-7) === '-0.00000078');
|
||||
assert (numberToString (7.8e-7) === '0.00000078');
|
||||
assert (numberToString (-17.805e-7) === '-0.0000017805');
|
||||
assert (numberToString (17.805e-7) === '0.0000017805');
|
||||
assert (numberToString (-7.0005e27) === '-7000500000000000000000000000');
|
||||
assert (numberToString (7.0005e27) === '7000500000000000000000000000');
|
||||
assert (numberToString (-7.9e27) === '-7900000000000000000000000000');
|
||||
assert (numberToString (7.9e27) === '7900000000000000000000000000');
|
||||
assert (numberToString (-12.345) === '-12.345');
|
||||
assert (numberToString (12.345) === '12.345');
|
||||
assert (numberToString (0) === '0');
|
||||
assert (numberToString (7.35946e21) === '7359460000000000000000');
|
||||
// the following line breaks the test
|
||||
// see https://github.com/ccxt/ccxt/issues/5744
|
||||
// assert (numberToString (0.00000001) === '0.00000001');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// testDecimalToPrecisionTruncationToNDigitsAfterDot
|
||||
|
||||
assert (decimalToPrecision ('12.3456000', TRUNCATE, 100, DECIMAL_PLACES) === '12.3456');
|
||||
assert (decimalToPrecision ('12.3456', TRUNCATE, 100, DECIMAL_PLACES) === '12.3456');
|
||||
assert (decimalToPrecision ('12.3456', TRUNCATE, 4, DECIMAL_PLACES) === '12.3456');
|
||||
assert (decimalToPrecision ('12.3456', TRUNCATE, 3, DECIMAL_PLACES) === '12.345');
|
||||
assert (decimalToPrecision ('12.3456', TRUNCATE, 2, DECIMAL_PLACES) === '12.34');
|
||||
assert (decimalToPrecision ('12.3456', TRUNCATE, 1, DECIMAL_PLACES) === '12.3');
|
||||
assert (decimalToPrecision ('12.3456', TRUNCATE, 0, DECIMAL_PLACES) === '12');
|
||||
|
||||
assert (decimalToPrecision ('0.0000001', TRUNCATE, 8, DECIMAL_PLACES) === '0.0000001');
|
||||
assert (decimalToPrecision ('0.00000001', TRUNCATE, 8, DECIMAL_PLACES) === '0.00000001');
|
||||
|
||||
assert (decimalToPrecision ('0.000000000', TRUNCATE, 9, DECIMAL_PLACES, PAD_WITH_ZERO) === '0.000000000');
|
||||
assert (decimalToPrecision ('0.000000001', TRUNCATE, 9, DECIMAL_PLACES, PAD_WITH_ZERO) === '0.000000001');
|
||||
|
||||
assert (decimalToPrecision ('12.3456', TRUNCATE, -1, DECIMAL_PLACES) === '10');
|
||||
assert (decimalToPrecision ('123.456', TRUNCATE, -1, DECIMAL_PLACES) === '120');
|
||||
assert (decimalToPrecision ('123.456', TRUNCATE, -2, DECIMAL_PLACES) === '100');
|
||||
assert (decimalToPrecision ('9.99999', TRUNCATE, -1, DECIMAL_PLACES) === '0');
|
||||
assert (decimalToPrecision ('99.9999', TRUNCATE, -1, DECIMAL_PLACES) === '90');
|
||||
assert (decimalToPrecision ('99.9999', TRUNCATE, -2, DECIMAL_PLACES) === '0');
|
||||
|
||||
assert (decimalToPrecision ('0', TRUNCATE, 0, DECIMAL_PLACES) === '0');
|
||||
assert (decimalToPrecision ('-0.9', TRUNCATE, 0, DECIMAL_PLACES) === '0');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// testDecimalToPrecisionTruncationToNSignificantDigits
|
||||
|
||||
assert (decimalToPrecision ('0.000123456700', TRUNCATE, 100, SIGNIFICANT_DIGITS) === '0.0001234567');
|
||||
assert (decimalToPrecision ('0.0001234567', TRUNCATE, 100, SIGNIFICANT_DIGITS) === '0.0001234567');
|
||||
assert (decimalToPrecision ('0.0001234567', TRUNCATE, 7, SIGNIFICANT_DIGITS) === '0.0001234567');
|
||||
|
||||
assert (decimalToPrecision ('0.000123456', TRUNCATE, 6, SIGNIFICANT_DIGITS) === '0.000123456');
|
||||
assert (decimalToPrecision ('0.000123456', TRUNCATE, 5, SIGNIFICANT_DIGITS) === '0.00012345');
|
||||
assert (decimalToPrecision ('0.000123456', TRUNCATE, 2, SIGNIFICANT_DIGITS) === '0.00012');
|
||||
assert (decimalToPrecision ('0.000123456', TRUNCATE, 1, SIGNIFICANT_DIGITS) === '0.0001');
|
||||
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 10, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '123.0000987');
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 8, SIGNIFICANT_DIGITS) === '123.00009');
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 7, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '123.0000');
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 6, SIGNIFICANT_DIGITS) === '123');
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 5, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '123.00');
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 4, SIGNIFICANT_DIGITS) === '123');
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 4, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '123.0');
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 3, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '123');
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 2, SIGNIFICANT_DIGITS) === '120');
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 1, SIGNIFICANT_DIGITS) === '100');
|
||||
assert (decimalToPrecision ('123.0000987654', TRUNCATE, 1, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '100');
|
||||
|
||||
assert (decimalToPrecision ('1234', TRUNCATE, 5, SIGNIFICANT_DIGITS) === '1234');
|
||||
assert (decimalToPrecision ('1234', TRUNCATE, 5, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '1234.0');
|
||||
assert (decimalToPrecision ('1234', TRUNCATE, 4, SIGNIFICANT_DIGITS) === '1234');
|
||||
assert (decimalToPrecision ('1234', TRUNCATE, 4, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '1234');
|
||||
assert (decimalToPrecision ('1234.69', TRUNCATE, 0, SIGNIFICANT_DIGITS) === '0');
|
||||
assert (decimalToPrecision ('1234.69', TRUNCATE, 0, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '0');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// testDecimalToPrecisionRoundingToNDigitsAfterDot
|
||||
|
||||
assert (decimalToPrecision ('12.3456000', ROUND, 100, DECIMAL_PLACES) === '12.3456');
|
||||
assert (decimalToPrecision ('12.3456', ROUND, 100, DECIMAL_PLACES) === '12.3456');
|
||||
assert (decimalToPrecision ('12.3456', ROUND, 4, DECIMAL_PLACES) === '12.3456');
|
||||
assert (decimalToPrecision ('12.3456', ROUND, 3, DECIMAL_PLACES) === '12.346');
|
||||
assert (decimalToPrecision ('12.3456', ROUND, 2, DECIMAL_PLACES) === '12.35');
|
||||
assert (decimalToPrecision ('12.3456', ROUND, 1, DECIMAL_PLACES) === '12.3');
|
||||
assert (decimalToPrecision ('12.3456', ROUND, 0, DECIMAL_PLACES) === '12');
|
||||
|
||||
// a problematic case in PHP
|
||||
assert (decimalToPrecision ('10000', ROUND, 6, DECIMAL_PLACES) === '10000');
|
||||
assert (decimalToPrecision ('0.00003186', ROUND, 8, DECIMAL_PLACES) === '0.00003186');
|
||||
|
||||
assert (decimalToPrecision ('12.3456', ROUND, -1, DECIMAL_PLACES) === '10');
|
||||
assert (decimalToPrecision ('123.456', ROUND, -1, DECIMAL_PLACES) === '120');
|
||||
assert (decimalToPrecision ('123.456', ROUND, -2, DECIMAL_PLACES) === '100');
|
||||
assert (decimalToPrecision ('9.99999', ROUND, -1, DECIMAL_PLACES) === '10');
|
||||
assert (decimalToPrecision ('99.9999', ROUND, -1, DECIMAL_PLACES) === '100');
|
||||
assert (decimalToPrecision ('99.9999', ROUND, -2, DECIMAL_PLACES) === '100');
|
||||
|
||||
assert (decimalToPrecision ('9.999', ROUND, 3, DECIMAL_PLACES) === '9.999');
|
||||
assert (decimalToPrecision ('9.999', ROUND, 2, DECIMAL_PLACES) === '10');
|
||||
assert (decimalToPrecision ('9.999', ROUND, 2, DECIMAL_PLACES, PAD_WITH_ZERO) === '10.00');
|
||||
assert (decimalToPrecision ('99.999', ROUND, 2, DECIMAL_PLACES, PAD_WITH_ZERO) === '100.00');
|
||||
assert (decimalToPrecision ('-99.999', ROUND, 2, DECIMAL_PLACES, PAD_WITH_ZERO) === '-100.00');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// testDecimalToPrecisionRoundingToNSignificantDigits
|
||||
|
||||
assert (decimalToPrecision ('0.000123456700', ROUND, 100, SIGNIFICANT_DIGITS) === '0.0001234567');
|
||||
assert (decimalToPrecision ('0.0001234567', ROUND, 100, SIGNIFICANT_DIGITS) === '0.0001234567');
|
||||
assert (decimalToPrecision ('0.0001234567', ROUND, 7, SIGNIFICANT_DIGITS) === '0.0001234567');
|
||||
|
||||
assert (decimalToPrecision ('0.000123456', ROUND, 6, SIGNIFICANT_DIGITS) === '0.000123456');
|
||||
assert (decimalToPrecision ('0.000123456', ROUND, 5, SIGNIFICANT_DIGITS) === '0.00012346');
|
||||
assert (decimalToPrecision ('0.000123456', ROUND, 4, SIGNIFICANT_DIGITS) === '0.0001235');
|
||||
assert (decimalToPrecision ('0.00012', ROUND, 2, SIGNIFICANT_DIGITS) === '0.00012');
|
||||
assert (decimalToPrecision ('0.0001', ROUND, 1, SIGNIFICANT_DIGITS) === '0.0001');
|
||||
|
||||
assert (decimalToPrecision ('123.0000987654', ROUND, 7, SIGNIFICANT_DIGITS) === '123.0001');
|
||||
assert (decimalToPrecision ('123.0000987654', ROUND, 6, SIGNIFICANT_DIGITS) === '123');
|
||||
|
||||
assert (decimalToPrecision ('0.00098765', ROUND, 2, SIGNIFICANT_DIGITS) === '0.00099');
|
||||
assert (decimalToPrecision ('0.00098765', ROUND, 2, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '0.00099');
|
||||
|
||||
assert (decimalToPrecision ('0.00098765', ROUND, 1, SIGNIFICANT_DIGITS) === '0.001');
|
||||
assert (decimalToPrecision ('0.00098765', ROUND, 10, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '0.0009876500000');
|
||||
|
||||
assert (decimalToPrecision ('0.098765', ROUND, 1, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '0.1');
|
||||
|
||||
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
|
||||
|
||||
assert (decimalToPrecision ('0.000123456700', ROUND, 0.00012, TICK_SIZE) === '0.00012');
|
||||
assert (decimalToPrecision ('0.0001234567', ROUND, 0.00013, TICK_SIZE) === '0.00013');
|
||||
assert (decimalToPrecision ('0.0001234567', TRUNCATE, 0.00013, TICK_SIZE) === '0');
|
||||
assert (decimalToPrecision ('101.000123456700', ROUND, 100, TICK_SIZE) === '100');
|
||||
assert (decimalToPrecision ('0.000123456700', ROUND, 100, TICK_SIZE) === '0');
|
||||
assert (decimalToPrecision ('165', TRUNCATE, 110, TICK_SIZE) === '110');
|
||||
assert (decimalToPrecision ('3210', TRUNCATE, 1110, TICK_SIZE) === '2220');
|
||||
assert (decimalToPrecision ('165', ROUND, 110, TICK_SIZE) === '220');
|
||||
assert (decimalToPrecision ('0.000123456789', ROUND, 0.00000012, TICK_SIZE) === '0.00012348');
|
||||
assert (decimalToPrecision ('0.000123456789', TRUNCATE, 0.00000012, TICK_SIZE) === '0.00012336');
|
||||
|
||||
assert (decimalToPrecision ('0.01', ROUND, 0.0001, TICK_SIZE, PAD_WITH_ZERO) === '0.0100');
|
||||
assert (decimalToPrecision ('0.01', TRUNCATE, 0.0001, TICK_SIZE, PAD_WITH_ZERO) === '0.0100');
|
||||
|
||||
assert (decimalToPrecision ('-0.000123456789', ROUND, 0.00000012, TICK_SIZE) === '-0.00012348');
|
||||
assert (decimalToPrecision ('-0.000123456789', TRUNCATE, 0.00000012, TICK_SIZE) === '-0.00012336');
|
||||
assert (decimalToPrecision ('-165', TRUNCATE, 110, TICK_SIZE) === '-110');
|
||||
assert (decimalToPrecision ('-165', ROUND, 110, TICK_SIZE) === '-220');
|
||||
assert (decimalToPrecision ('-1650', TRUNCATE, 1100, TICK_SIZE) === '-1100');
|
||||
assert (decimalToPrecision ('-1650', ROUND, 1100, TICK_SIZE) === '-2200');
|
||||
|
||||
assert (decimalToPrecision ('0.0006', TRUNCATE, 0.0001, TICK_SIZE) === '0.0006');
|
||||
assert (decimalToPrecision ('-0.0006', TRUNCATE, 0.0001, TICK_SIZE) === '-0.0006');
|
||||
assert (decimalToPrecision ('0.6', TRUNCATE, 0.2, TICK_SIZE) === '0.6');
|
||||
assert (decimalToPrecision ('-0.6', TRUNCATE, 0.2, TICK_SIZE) === '-0.6');
|
||||
assert (decimalToPrecision ('1.2', ROUND, 0.4, TICK_SIZE) === '1.2');
|
||||
assert (decimalToPrecision ('-1.2', ROUND, 0.4, TICK_SIZE) === '-1.2');
|
||||
assert (decimalToPrecision ('1.2', ROUND, 0.02, TICK_SIZE) === '1.2');
|
||||
assert (decimalToPrecision ('-1.2', ROUND, 0.02, TICK_SIZE) === '-1.2');
|
||||
assert (decimalToPrecision ('44', ROUND, 4.4, TICK_SIZE) === '44');
|
||||
assert (decimalToPrecision ('-44', ROUND, 4.4, TICK_SIZE) === '-44');
|
||||
assert (decimalToPrecision ('44.00000001', ROUND, 4.4, TICK_SIZE) === '44');
|
||||
assert (decimalToPrecision ('-44.00000001', ROUND, 4.4, TICK_SIZE) === '-44');
|
||||
|
||||
// https://github.com/ccxt/ccxt/issues/6731
|
||||
assert (decimalToPrecision ('20', TRUNCATE, 0.00000001, TICK_SIZE) === '20');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// testDecimalToPrecisionNegativeNumbers
|
||||
|
||||
assert (decimalToPrecision ('-0.123456', TRUNCATE, 5, DECIMAL_PLACES) === '-0.12345');
|
||||
assert (decimalToPrecision ('-0.123456', ROUND, 5, DECIMAL_PLACES) === '-0.12346');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// decimalToPrecision: without dot / trailing dot
|
||||
|
||||
assert (decimalToPrecision ('123', TRUNCATE, 0) === '123');
|
||||
|
||||
assert (decimalToPrecision ('123', TRUNCATE, 5, DECIMAL_PLACES) === '123');
|
||||
assert (decimalToPrecision ('123', TRUNCATE, 5, DECIMAL_PLACES, PAD_WITH_ZERO) === '123.00000');
|
||||
|
||||
assert (decimalToPrecision ('123.', TRUNCATE, 0, DECIMAL_PLACES) === '123');
|
||||
assert (decimalToPrecision ('123.', TRUNCATE, 5, DECIMAL_PLACES, PAD_WITH_ZERO) === '123.00000');
|
||||
|
||||
assert (decimalToPrecision ('0.', TRUNCATE, 0) === '0');
|
||||
assert (decimalToPrecision ('0.', TRUNCATE, 5, DECIMAL_PLACES, PAD_WITH_ZERO) === '0.00000');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// decimalToPrecision: rounding for equidistant digits
|
||||
|
||||
assert (decimalToPrecision ('1.44', ROUND, 1, DECIMAL_PLACES) === '1.4');
|
||||
assert (decimalToPrecision ('1.45', ROUND, 1, DECIMAL_PLACES) === '1.5');
|
||||
assert (decimalToPrecision ('1.45', ROUND, 0, DECIMAL_PLACES) === '1'); // not 2
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// negative precision only implemented so far in python
|
||||
// pretty useless for decimal applications as anything |x| < 5 === 0
|
||||
// NO_PADDING and PAD_WITH_ZERO are ignored
|
||||
|
||||
assert (decimalToPrecision ('5', ROUND, -1, DECIMAL_PLACES) === '10');
|
||||
assert (decimalToPrecision ('4.999', ROUND, -1, DECIMAL_PLACES) === '0');
|
||||
assert (decimalToPrecision ('0.0431531423', ROUND, -1, DECIMAL_PLACES) === '0');
|
||||
assert (decimalToPrecision ('-69.3', ROUND, -1, DECIMAL_PLACES) === '-70');
|
||||
assert (decimalToPrecision ('5001', ROUND, -4, DECIMAL_PLACES) === '10000');
|
||||
assert (decimalToPrecision ('4999.999', ROUND, -4, DECIMAL_PLACES) === '0');
|
||||
|
||||
assert (decimalToPrecision ('69.3', TRUNCATE, -2, DECIMAL_PLACES) === '0');
|
||||
assert (decimalToPrecision ('-69.3', TRUNCATE, -2, DECIMAL_PLACES) === '0');
|
||||
assert (decimalToPrecision ('69.3', TRUNCATE, -1, SIGNIFICANT_DIGITS) === '60');
|
||||
assert (decimalToPrecision ('-69.3', TRUNCATE, -1, SIGNIFICANT_DIGITS) === '-60');
|
||||
assert (decimalToPrecision ('69.3', TRUNCATE, -2, SIGNIFICANT_DIGITS) === '0');
|
||||
assert (decimalToPrecision ('1602000000000000000000', TRUNCATE, 3, SIGNIFICANT_DIGITS) === '1600000000000000000000');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// testDecimalToPrecisionErrorHandling (todo)
|
||||
//
|
||||
// throws (() =>
|
||||
// decimalToPrecision ('123456.789', TRUNCATE, -2, DECIMAL_PLACES),
|
||||
// 'negative precision is not yet supported')
|
||||
//
|
||||
// throws (() =>
|
||||
// decimalToPrecision ('foo'),
|
||||
// "invalid number (contains an illegal character 'f')")
|
||||
//
|
||||
// throws (() =>
|
||||
// decimalToPrecision ('0.01', TRUNCATE, -1, TICK_SIZE),
|
||||
// "TICK_SIZE cant be used with negative numPrecisionDigits")
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
'use strict'
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const { setTimeout_safe
|
||||
, timeout
|
||||
, now
|
||||
, isWindows } = require ('../../../../ccxt')
|
||||
|
||||
const { strictEqual: equal } = require ('assert')
|
||||
, approxEqual = (a, b) => equal (true, Math.abs (a - b) <= 20)
|
||||
, rejectsWith = (msg, x) => x.then (() => { throw new Error ('not rejected!') })
|
||||
.catch (e => { equal (e.message, msg) })
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('setTimeout_safe is working', (done) => {
|
||||
|
||||
// TODO: make a more robust test that is not failing on certain machines under certain conditions
|
||||
|
||||
if (isWindows) {
|
||||
|
||||
// temporarily skip this test with appveyor
|
||||
// todo: refix timer functions for windows properly
|
||||
|
||||
done ()
|
||||
|
||||
} else {
|
||||
|
||||
const start = now ()
|
||||
const calls = []
|
||||
|
||||
const brokenSetTimeout = (done, ms) => {
|
||||
calls.push ({ when: now () - start, ms_asked: ms })
|
||||
return setTimeout (done, 100) // simulates a defect setTimeout implementation that sleeps wrong time (100ms always in this test)
|
||||
}
|
||||
|
||||
// ask to sleep 250ms
|
||||
setTimeout_safe (() => {
|
||||
approxEqual (calls[0].ms_asked, 250)
|
||||
approxEqual (calls[1].ms_asked, 150)
|
||||
approxEqual (calls[2].ms_asked, 50)
|
||||
done ()
|
||||
}, 250, brokenSetTimeout)
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('setTimeout_safe canceling is working', (done) => {
|
||||
|
||||
const brokenSetTimeout = (done, ms) => setTimeout (done, 100) // simulates a defect setTimeout implementation that sleeps wrong time (100ms always in this test)
|
||||
|
||||
const clear = setTimeout_safe (() => { throw new Error ('shouldnt happen!') }, 250, brokenSetTimeout)
|
||||
|
||||
setTimeout (() => { clear () }, 200)
|
||||
setTimeout (() => { done () }, 400)
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('timeout() is working', async () => {
|
||||
|
||||
equal ('foo', await timeout (200, new Promise (resolve => setTimeout (() => resolve ('foo'), 100))))
|
||||
|
||||
await rejectsWith ('foo', timeout (100, Promise.reject (new Error ('foo'))))
|
||||
await rejectsWith ('timed out', timeout (100, new Promise ((resolve, reject) => setTimeout (() => reject (new Error ('foo')), 200))))
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
'use strict'
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const { safeFloat, safeInteger, safeValue } = require ('../../../../ccxt')
|
||||
const { strictEqual: equal, deepEqual } = require ('assert')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('safeFloat/safeInteger is robust', async () => {
|
||||
|
||||
const $default = {}
|
||||
|
||||
const fns = { safeFloat, safeInteger }
|
||||
|
||||
for (const fn of ['safeFloat', 'safeInteger']) {
|
||||
|
||||
equal (fns[fn] ({ 'x': false }, 'x', $default), $default)
|
||||
equal (fns[fn] ({ 'x': true }, 'x', $default), $default)
|
||||
equal (fns[fn] ({ 'x': [] }, 'x', $default), $default)
|
||||
equal (fns[fn] ({ 'x': [0] }, 'x', $default), $default)
|
||||
equal (fns[fn] ({ 'x': [1] }, 'x', $default), $default)
|
||||
equal (fns[fn] ({ 'x': {} }, 'x', $default), $default)
|
||||
equal (fns[fn] ({ 'x': Number.NaN }, 'x'), undefined)
|
||||
equal (fns[fn] ({ 'x': Number.POSITIVE_INFINITY }, 'x'), undefined)
|
||||
equal (fns[fn] ({ 'x': null }, 'x', undefined), undefined)
|
||||
equal (fns[fn] ({ 'x': null }, 'x', $default), $default)
|
||||
equal (fns[fn] ({ 'x': '1.0' }, 'x'), 1.0)
|
||||
equal (fns[fn] ({ 'x': '-1.0' }, 'x'), -1.0)
|
||||
equal (fns[fn] ({ 'x': 1.0 }, 'x'), 1.0)
|
||||
equal (fns[fn] ({ 'x': 0 }, 'x'), 0)
|
||||
equal (fns[fn] ({ 'x': undefined }, 'x', $default), $default)
|
||||
equal (fns[fn] ({ 'x': "" }, 'x'), undefined)
|
||||
equal (fns[fn] ({ 'x': "" }, 'x', 0), 0)
|
||||
equal (fns[fn] ({}, 'x'), undefined)
|
||||
equal (fns[fn] ({}, 'x', 0), 0)
|
||||
}
|
||||
|
||||
equal (safeFloat ({ 'x': 1.59999999 }, 'x'), 1.59999999)
|
||||
equal (safeInteger ({ 'x': 1.59999999 }, 'x'), 1)
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('safeValue works', () => {
|
||||
|
||||
equal (safeValue ({}, 'foo'), undefined)
|
||||
equal (safeValue ({}, 'foo', 'bar'), 'bar')
|
||||
equal (safeValue ({ 'foo': 'bar' }, 'foo'), 'bar')
|
||||
equal (safeValue ({ 'foo': '' }, 'foo'), '')
|
||||
equal (safeValue ({ 'foo': 0 }, 'foo'), 0)
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
'use strict'
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
global.log = require ('ololog') // for easier debugging
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const { Exchange, keys, values, unique, index, aggregate } = require ('../../../ccxt')
|
||||
const { strictEqual: equal, deepEqual } = require ('assert')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
describe ('ccxt base code', () => {
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
require ('./functions/test.generic')
|
||||
require ('./functions/test.time')
|
||||
require ('./functions/test.type')
|
||||
// Some test files are transpiled to other languages and must be kept very simple so we put `it` call externally to them
|
||||
// Some mocha reporters will swallow `assert` failures if they are outside of `it` clause so we must call `it`
|
||||
it ('decimalToPrecision() works', () => {
|
||||
require ('./functions/test.number')
|
||||
})
|
||||
it ('date&time functions work', () => {
|
||||
require('./functions/test.datetime')
|
||||
})
|
||||
it ('cryptography code works', () => {
|
||||
require('./functions/test.crypto')
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('calculateFee() works', () => {
|
||||
|
||||
const price = 100.00
|
||||
const amount = 10.00
|
||||
const taker = 0.0025
|
||||
const maker = 0.0010
|
||||
const fees = { taker, maker }
|
||||
const market = {
|
||||
'id': 'foobar',
|
||||
'symbol': 'FOO/BAR',
|
||||
'base': 'FOO',
|
||||
'quote': 'BAR',
|
||||
'taker': taker,
|
||||
'maker': maker,
|
||||
'precision': {
|
||||
'amount': 8,
|
||||
'price': 8,
|
||||
},
|
||||
}
|
||||
|
||||
const exchange = new Exchange ({
|
||||
'id': 'mock',
|
||||
'markets': {
|
||||
'FOO/BAR': market,
|
||||
},
|
||||
})
|
||||
|
||||
Object.keys (fees).forEach (takerOrMaker => {
|
||||
|
||||
const result = exchange.calculateFee (market['symbol'], 'limit', 'sell', amount, price, takerOrMaker, {})
|
||||
|
||||
deepEqual (result, {
|
||||
'type': takerOrMaker,
|
||||
'currency': 'BAR',
|
||||
'rate': fees[takerOrMaker],
|
||||
'cost': fees[takerOrMaker] * amount * price,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it.skip ('exchange config extension works', () => {
|
||||
|
||||
|
||||
cost = { min: 0.001, max: 1000 }
|
||||
precision = { amount: 3 }
|
||||
const exchange = new binance ({
|
||||
'markets': {
|
||||
'ETH/BTC': { limits: { cost }, precision },
|
||||
},
|
||||
})
|
||||
|
||||
deepEqual (exchange.markets['ETH/BTC'].limits.cost, cost)
|
||||
deepEqual (exchange.markets['ETH/BTC'].precision, { price: 6, amount: 3 })
|
||||
deepEqual (exchange.markets['ETH/BTC'].symbol, 'ETH/BTC')
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('aggregate() works', () => {
|
||||
|
||||
const bids = [
|
||||
[ 789.1, 123.0 ],
|
||||
[ 789.100, 123.0 ],
|
||||
[ 123.0, 456.0 ],
|
||||
[ 789.0, 123.0 ],
|
||||
[ 789.10, 123.0 ],
|
||||
]
|
||||
|
||||
const asks = [
|
||||
[ 123.0, 456.0 ],
|
||||
[ 789.0, 123.0 ],
|
||||
[ 789.10, 123.0 ],
|
||||
]
|
||||
|
||||
deepEqual (aggregate (bids.sort ()), [
|
||||
[ 123.0, 456.0 ],
|
||||
[ 789.0, 123.0 ],
|
||||
[ 789.1, 369.0 ],
|
||||
])
|
||||
|
||||
deepEqual (aggregate (asks.sort ()), [
|
||||
[ 123.0, 456.0 ],
|
||||
[ 789.0, 123.0 ],
|
||||
[ 789.10, 123.0 ],
|
||||
])
|
||||
|
||||
deepEqual (aggregate ([]), [])
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('parseBalance() works', () => {
|
||||
|
||||
const exchange = new Exchange ({
|
||||
'markets': {
|
||||
'ETH/BTC': { 'id': 'ETH/BTC', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', }
|
||||
}
|
||||
})
|
||||
|
||||
const input = {
|
||||
'ETH': { 'free': 10, 'used': 10, 'total': 20 },
|
||||
'ZEC': { 'free': 0, 'used': 0, 'total': 0 },
|
||||
}
|
||||
|
||||
const expected = {
|
||||
'ETH': { 'free': 10, 'used': 10, 'total': 20 },
|
||||
'ZEC': { 'free': 0, 'used': 0, 'total': 0 },
|
||||
'free': {
|
||||
'ETH': 10,
|
||||
'ZEC': 0,
|
||||
},
|
||||
'used': {
|
||||
'ETH': 10,
|
||||
'ZEC': 0,
|
||||
},
|
||||
'total': {
|
||||
'ETH': 20,
|
||||
'ZEC': 0,
|
||||
},
|
||||
}
|
||||
|
||||
const actual = exchange.parseBalance (input)
|
||||
|
||||
deepEqual (actual, expected)
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('camelCase/camel_case property conversion works', () => {
|
||||
|
||||
const exchange = new Exchange ({ 'id': 'mock' })
|
||||
|
||||
const propsSeenBefore = index (
|
||||
["isNode", "empty", "keys", "values", "extend", "clone", "index", "ordered", "unique", "keysort", "indexBy", "groupBy", "filterBy", "sortBy", "flatten", "pluck", "omit", "sum", "deepExtend", "uuid", "unCamelCase", "capitalize", "isNumber", "isArray", "isObject", "isString", "isStringCoercible", "isDictionary", "hasProps", "prop", "asFloat", "asInteger", "safeFloat", "safeInteger", "safeValue", "safeString", "decimal", "toFixed", "truncate", "truncateToString", "precisionFromString", "stringToBinary", "stringToBase64", "utf16ToBase64", "base64ToBinary", "base64ToString", "binaryToString", "binaryConcat", "urlencode", "rawencode", "urlencodeBase64", "hash", "hmac", "jwt", "time", "setTimeout_safe", "sleep", "TimedOut", "timeout", "throttle", "json", "unjson", "aggregate", "is_node", "index_by", "group_by", "filter_by", "sort_by", "deep_extend", "un_camel_case", "is_number", "is_array", "is_object", "is_string", "is_string_coercible", "is_dictionary", "has_props", "as_float", "as_integer", "safe_float", "safe_integer", "safe_value", "safe_string", "to_fixed", "truncate_to_string", "precision_from_string", "string_to_binary", "string_to_base64", "utf16To_base64", "base64To_binary", "base64To_string", "binary_to_string", "binary_concat", "urlencode_base64", "set_timeout_safe", "Timed_out", "encode", "decode", "userAgents", "headers", "proxy", "origin", "iso8601", "parse8601", "milliseconds", "microseconds", "seconds", "id", "enableRateLimit", "rateLimit", "parseJsonResponse", "substituteCommonCurrencyCodes", "parseBalanceFromOpenOrders", "verbose", "debug", "journal", "userAgent", "twofa", "timeframes", "hasPublicAPI", "hasPrivateAPI", "hasCORS", "hasDeposit", "hasFetchBalance", "hasFetchClosedOrders", "hasFetchCurrencies", "hasFetchMyTrades", "hasFetchOHLCV", "hasFetchOpenOrders", "hasFetchOrder", "hasFetchOrderBook", "hasFetchOrders", "hasFetchTicker", "hasFetchTickers", "hasFetchBidsAsks", "hasFetchTrades", "hasWithdraw", "hasCreateOrder", "hasCancelOrder", "apiKey", "secret", "uid", "login", "password", "requiredCredentials", "exceptions", "balance", "orderbooks", "tickers", "fees", "orders", "trades", "currencies", "last_http_response", "last_json_response", "arrayConcat", "market_id", "market_ids", "array_concat", "implode_params", "extract_params", "fetch_balance", "fetch_free_balance", "fetch_used_balance","fetch_total_balance", "fetch_l2_order_book", "fetch_order_book", "fetch_bids_asks", "fetch_tickers", "fetch_ticker", "fetch_trades", "fetch_order", "fetch_orders", "fetch_open_orders", "fetch_closed_orders", "fetch_order_status", "fetch_markets", "load_markets", "set_markets", "parse_balance", "parse_bid_ask", "parse_bids_asks", "parse_order_book", "parse_trades", "parse_orders", "parse_ohlcv", "parse_ohlcvs", "edit_limit_buy_order", "edit_limit_sell_order", "edit_limit_order", "edit_order", "create_limit_buy_order", "create_limit_sell_order", "create_market_buy_order", "create_market_sell_order", "create_order", "calculate_fee", "common_currency_code", "price_to_precision", "amount_to_precision", "amount_to_string", "fee_to_precision", "cost_to_precision", "constructor", "getMarket", "describe", "defaults", "nonce", "encodeURIComponent", "checkRequiredCredentials", "initRestRateLimiter", "defineRestApi", "fetch", "fetch2", "request", "handleErrors", "defaultErrorHandler", "handleRestErrors", "handleRestResponse", "setMarkets", "loadMarkets", "fetchBidsAsks", "fetchTickers", "fetchOrder", "fetchOrders", "fetchOpenOrders", "fetchClosedOrders", "fetchMyTrades", "fetchCurrencies", "fetchMarkets", "fetchOrderStatus", "account", "commonCurrencyCode", "currency", "market", "marketId", "marketIds", "symbol", "extractParams", "implodeParams", "url", "parseBidAsk", "parseBidsAsks", "fetchL2OrderBook", "parseOrderBook", "parseBalance", "fetchPartialBalance", "fetchFreeBalance", "fetchUsedBalance", "fetchTotalBalance", "filterBySinceLimit", "parseTrades", "parseOrders", "filterOrdersBySymbol", "parseOHLCV", "parseOHLCVs", "editLimitBuyOrder", "editLimitSellOrder", "editLimitOrder", "editOrder", "createLimitBuyOrder", "createLimitSellOrder", "createMarketBuyOrder", "createMarketSellOrder", "costToPrecision", "priceToPrecision", "amountToPrecision", "amountToLots", "feeToPrecision", "calculateFee", "Ymd", "YmdHMS"]
|
||||
)
|
||||
|
||||
const props = index (Object.getOwnPropertyNames (exchange).concat (
|
||||
Object.getOwnPropertyNames (exchange.constructor.prototype)))
|
||||
|
||||
for (const k of Array.from (propsSeenBefore))
|
||||
if (this[k] && !props.has (k))
|
||||
throw new Error (`missing prop: ${k}`)
|
||||
|
||||
for (const k of Array.from (props))
|
||||
if (!propsSeenBefore.has (k))
|
||||
log.magenta.noLocate (`+ ${k}`)
|
||||
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('camelCase/camel_case property conversion works #2', () => {
|
||||
|
||||
class Derived extends Exchange {}
|
||||
const derived = new Derived ()
|
||||
equal (typeof derived.load_markets, 'function')
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
it ('legacy .hasSomething maps to has.something automatically', () => {
|
||||
|
||||
const exchange = new Exchange ({
|
||||
id: 'mock',
|
||||
has: {
|
||||
CORS: true,
|
||||
publicAPI: false,
|
||||
fetchDepositAddress: 'emulated'
|
||||
}
|
||||
})
|
||||
|
||||
equal (exchange.hasCORS, true)
|
||||
equal (exchange.hasPublicAPI, false)
|
||||
equal (exchange.hasFetchDepositAddress, true)
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
if (Exchange.hasWeb3 ()) {
|
||||
it ('getZeroExOrderHashV2 return valid signature for example order', () => {
|
||||
// based on https://github.com/0xProject/0x-monorepo/blob/development/python-packages/order_utils/test/test_generate_order_hash_hex.py
|
||||
const exampleOrder = {
|
||||
"makerAddress": "0x0000000000000000000000000000000000000000",
|
||||
"takerAddress": "0x0000000000000000000000000000000000000000",
|
||||
"senderAddress": "0x0000000000000000000000000000000000000000",
|
||||
"feeRecipientAddress": "0x0000000000000000000000000000000000000000",
|
||||
"makerAssetData": "0x0000000000000000000000000000000000000000",
|
||||
"takerAssetData": "0x0000000000000000000000000000000000000000",
|
||||
"salt": "0",
|
||||
"makerFee": "0",
|
||||
"takerFee": "0",
|
||||
"makerAssetAmount": "0",
|
||||
"takerAssetAmount": "0",
|
||||
"expirationTimeSeconds": "0",
|
||||
"exchangeAddress": "0x0000000000000000000000000000000000000000",
|
||||
}
|
||||
const expectedOrderHash = '0xfaa49b35faeb9197e9c3ba7a52075e6dad19739549f153b77dfcf59408a4b422';
|
||||
class Derived extends Exchange {}
|
||||
const derived = new Derived ()
|
||||
const orderHash = derived.getZeroExOrderHashV2(exampleOrder)
|
||||
equal (orderHash, expectedOrderHash)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, ccxt = require ('../../../ccxt.js')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
// will try to place a buy order at the minimum price level on minimum amount possible
|
||||
// will skip if balance is positive or market limits are not set
|
||||
|
||||
module.exports = async (exchange, symbol, balance) => {
|
||||
|
||||
if (!exchange.has.createOrder) {
|
||||
log ('createOrder not supported')
|
||||
return
|
||||
}
|
||||
|
||||
const markets = await exchange.loadMarkets ()
|
||||
const market = markets[symbol]
|
||||
if (market.limits === undefined) {
|
||||
log ('market.limits property is not set, will not test order creation')
|
||||
return
|
||||
}
|
||||
|
||||
const { price, amount, cost } = market.limits
|
||||
|
||||
if (price === undefined || amount === undefined || cost === undefined) {
|
||||
log ('market.limits.[price|amount|cost] property is not set, will not test order creation')
|
||||
return
|
||||
}
|
||||
|
||||
let minPrice = price.min
|
||||
let minAmount = amount.min // will be adjusted co cover minCost if needed
|
||||
const minCost = cost.min
|
||||
|
||||
if (minPrice === undefined || minAmount === undefined || minCost === undefined) {
|
||||
log ('min limits are not set, will not test order creation')
|
||||
return
|
||||
}
|
||||
|
||||
if (minCost > minPrice * minAmount) {
|
||||
minAmount = minCost / minPrice
|
||||
}
|
||||
|
||||
minPrice = exchange.priceToPrecision (symbol, minPrice)
|
||||
minAmount = exchange.amountToPrecision (symbol, minAmount)
|
||||
|
||||
if (balance === undefined) {
|
||||
log ('balance is not set, cannot ensure safety, will not test order creation')
|
||||
return
|
||||
}
|
||||
|
||||
const { base, quote } = market
|
||||
if (balance[quote] !== undefined && balance[quote].total > 0) {
|
||||
log ('balance is not empty, will not test order creation')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
log ('creating limit buy order...', symbol, minAmount, minPrice)
|
||||
|
||||
let order = await exchange.createLimitBuyOrder (symbol, minAmount, minPrice)
|
||||
|
||||
log ('order created although it should not had to - cleaning up')
|
||||
|
||||
log (order)
|
||||
|
||||
await exchange.cancelOrder (order.id, symbol)
|
||||
|
||||
assert.fail ()
|
||||
|
||||
} catch (e) {
|
||||
|
||||
if (e instanceof ccxt.InsufficientFunds) {
|
||||
|
||||
log ('InsufficientFunds thrown as expected')
|
||||
|
||||
} else {
|
||||
|
||||
log ('InsufficientFunds failed, exception follows:')
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, ccxt = require ('../../../ccxt.js')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
log.green ('AuthenticationError (bad nonce) test...')
|
||||
|
||||
const hasFetchBalance = exchange.has.fetchBalance
|
||||
const hasFetchMyTrades = exchange.has.fetchMyTrades
|
||||
const hasFetchOrders = exchange.has.fetchOrders
|
||||
|
||||
if (hasFetchBalance || hasFetchMyTrades || hasFetchOrders) {
|
||||
|
||||
// save the nonce temporarily and replace it with a fake one
|
||||
const nonce = exchange.nonce
|
||||
exchange.nonce = () => 1
|
||||
|
||||
try {
|
||||
|
||||
// check if handleErrors() throws AuthenticationError if an exchange
|
||||
// responds with an error on a bad nonce
|
||||
// (still, some exchanges that require nonce silently eat bad nonce w/o an error)
|
||||
|
||||
if (hasFetchBalance)
|
||||
await exchange.fetchBalance ()
|
||||
else if (hasFetchMyTrades)
|
||||
await exchange.fetchMyTrades (symbol, 0)
|
||||
else
|
||||
await exchange.fetchOrders (symbol)
|
||||
|
||||
// restore the nonce so the caller may proceed in case bad nonce was accepted by an exchange
|
||||
exchange.nonce = nonce
|
||||
log.warn (exchange.id + ': AuthenticationError: bad nonce swallowed')
|
||||
|
||||
} catch (e) {
|
||||
|
||||
// restore the nonce so the caller may proceed in case the test failed
|
||||
exchange.nonce = nonce
|
||||
|
||||
if (e instanceof ccxt.AuthenticationError || e instanceof ccxt.InvalidNonce) {
|
||||
|
||||
// it has thrown the exception as expected
|
||||
log.green ('AuthenticationError test passed')
|
||||
|
||||
} else {
|
||||
|
||||
// rethrow an unexpected error if any
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
log (exchange.id + ' has no means of testing for bad nonce')
|
||||
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, ccxt = require ('../../../ccxt.js')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
if (!exchange.has.createOrder) {
|
||||
log ('createOrder not supported')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
await exchange.createLimitBuyOrder (symbol, 0, 0)
|
||||
assert.fail ()
|
||||
|
||||
} catch (e) {
|
||||
|
||||
if (e instanceof ccxt.InvalidOrder) {
|
||||
|
||||
log ('InvalidOrder thrown as expected')
|
||||
return
|
||||
|
||||
} else {
|
||||
|
||||
log ('InvalidOrder failed, exception follows:')
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
'use strict'
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, chai = require ('chai')
|
||||
, ccxt = require ('../../../ccxt.js')
|
||||
, expect = chai.expect
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = async (exchange, symbol) => {
|
||||
|
||||
if (!exchange.has.createOrder) {
|
||||
log ('createOrder not supported -> test skipped')
|
||||
return
|
||||
}
|
||||
|
||||
let id = 1
|
||||
|
||||
try {
|
||||
|
||||
await exchange.cancelOrder (id, symbol)
|
||||
|
||||
log ('test failed')
|
||||
|
||||
assert.fail ()
|
||||
|
||||
} catch (e) {
|
||||
|
||||
if (e instanceof ccxt.OrderNotFound) {
|
||||
|
||||
log ('OrderNotFound thrown as expected')
|
||||
|
||||
} else {
|
||||
|
||||
log ('OrderNotFound test failed')
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
'use strict'
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const [processPath, , exchangeId = null, exchangeSymbol = null] = process.argv.filter (x => !x.startsWith ('--'))
|
||||
const verbose = process.argv.includes ('--verbose') || false
|
||||
const debug = process.argv.includes ('--debug') || false
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const asTable = require ('as-table')
|
||||
, log = require ('ololog')
|
||||
, ansi = require ('ansicolor').nice
|
||||
, fs = require ('fs')
|
||||
, ccxt = require ('../../ccxt.js')
|
||||
, chai = require ('chai')
|
||||
, assert = chai.assert
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const warn = log.bright.yellow.error // .error → stderr
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
process.on ('uncaughtException', e => { log.bright.red.error (e); process.exit (1) })
|
||||
process.on ('unhandledRejection', e => { log.bright.red.error (e); process.exit (1) })
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
log.bright ('\nTESTING', { exchange: exchangeId, symbol: exchangeSymbol || 'all' }, '\n')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
let proxies = [
|
||||
'',
|
||||
'https://cors-anywhere.herokuapp.com/'
|
||||
]
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const enableRateLimit = true
|
||||
|
||||
const { Agent } = require ('https')
|
||||
|
||||
const agent = new Agent ({
|
||||
ecdhCurve: 'auto',
|
||||
})
|
||||
|
||||
const exchange = new (ccxt)[exchangeId] ({
|
||||
agent,
|
||||
verbose,
|
||||
enableRateLimit,
|
||||
debug,
|
||||
timeout: 20000,
|
||||
})
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const tests = {}
|
||||
const properties = Object.keys (exchange.has)
|
||||
properties
|
||||
.filter (property => fs.existsSync (__dirname + '/Exchange/test.' + property + '.js'))
|
||||
.forEach (property => {
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
tests[property] = require (__dirname + '/Exchange/test.' + property + '.js')
|
||||
})
|
||||
|
||||
const errors = require ('../base/errors.js')
|
||||
|
||||
Object.keys (errors)
|
||||
.filter (error => fs.existsSync (__dirname + '/errors/test.' + error + '.js'))
|
||||
.forEach (error => {
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
tests[error] = require (__dirname + '/errors/test.' + error + '.js')
|
||||
})
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const keysGlobal = 'keys.json'
|
||||
const keysLocal = 'keys.local.json'
|
||||
|
||||
const keysFile = fs.existsSync (keysLocal) ? keysLocal : keysGlobal
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
const settings = require (__dirname + '/../../' + keysFile)[exchangeId]
|
||||
|
||||
if (settings) {
|
||||
for (const key in settings) {
|
||||
if (settings[key]) {
|
||||
settings[key] = ccxt.deepExtend (exchange[key] || {}, settings[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign (exchange, settings)
|
||||
|
||||
if (settings && settings.skip) {
|
||||
log.error.bright ('[Skipped]', { exchange: exchangeId, symbol: exchangeSymbol || 'all' })
|
||||
process.exit ()
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
let testSymbol = async (exchange, symbol) => {
|
||||
|
||||
if (exchange.id !== 'coinmarketcap') {
|
||||
await tests['fetchMarkets'] (exchange)
|
||||
await tests['fetchCurrencies'] (exchange)
|
||||
}
|
||||
|
||||
await tests['fetchTicker'] (exchange, symbol)
|
||||
await tests['fetchTickers'] (exchange, symbol)
|
||||
await tests['fetchOHLCV'] (exchange, symbol)
|
||||
await tests['fetchTrades'] (exchange, symbol)
|
||||
|
||||
if (exchange.id === 'coinmarketcap') {
|
||||
|
||||
log (await exchange.fetchTickers ())
|
||||
log (await exchange.fetchGlobal ())
|
||||
|
||||
} else if (exchange.id === 'coinbase') {
|
||||
|
||||
// nothing for now
|
||||
|
||||
} else {
|
||||
|
||||
await tests['fetchOrderBook'] (exchange, symbol)
|
||||
await tests['fetchL2OrderBook'] (exchange, symbol)
|
||||
await tests['fetchOrderBooks'] (exchange)
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
let loadExchange = async exchange => {
|
||||
|
||||
let markets = await exchange.loadMarkets ()
|
||||
|
||||
assert (typeof exchange.markets === 'object', '.markets is not an object')
|
||||
assert (Array.isArray (exchange.symbols), '.symbols is not an array')
|
||||
assert (exchange.symbols.length > 0, '.symbols.length <= 0 (less than or equal to zero)')
|
||||
assert (Object.keys (exchange.markets).length > 0, 'Object.keys (.markets).length <= 0 (less than or equal to zero)')
|
||||
assert (exchange.symbols.length === Object.keys (exchange.markets).length, 'number of .symbols is not equal to the number of .markets')
|
||||
|
||||
let symbols = [
|
||||
'BTC/CNY',
|
||||
'BTC/USD',
|
||||
'BTC/USDT',
|
||||
'BTC/EUR',
|
||||
'BTC/ETH',
|
||||
'ETH/BTC',
|
||||
'BTC/JPY',
|
||||
'ETH/EUR',
|
||||
'ETH/JPY',
|
||||
'ETH/CNY',
|
||||
'LTC/CNY',
|
||||
'DASH/BTC',
|
||||
'DOGE/BTC',
|
||||
'BTC/AUD',
|
||||
'BTC/PLN',
|
||||
'USD/SLL',
|
||||
'BTC/RUB',
|
||||
'BTC/UAH',
|
||||
'LTC/BTC',
|
||||
]
|
||||
|
||||
let result = exchange.symbols.filter (symbol => symbols.indexOf (symbol) >= 0)
|
||||
|
||||
if (result.length > 0)
|
||||
if (exchange.symbols.length > result.length)
|
||||
result = result.join (', ') + ' + more...'
|
||||
else
|
||||
result = result.join (', ')
|
||||
|
||||
log (exchange.symbols.length.toString ().bright.green, 'symbols', result)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
let testExchange = async exchange => {
|
||||
|
||||
const codes = [
|
||||
'BTC',
|
||||
'ETH',
|
||||
'XRP',
|
||||
'LTC',
|
||||
'BCH',
|
||||
'EOS',
|
||||
'BNB',
|
||||
'BSV',
|
||||
'USDT',
|
||||
'ATOM',
|
||||
'BAT',
|
||||
'BTG',
|
||||
'DASH',
|
||||
'DOGE',
|
||||
'ETC',
|
||||
'IOTA',
|
||||
'LSK',
|
||||
'MKR',
|
||||
'NEO',
|
||||
'PAX',
|
||||
'QTUM',
|
||||
'TRX',
|
||||
'TUSD',
|
||||
'USD',
|
||||
'USDC',
|
||||
'WAVES',
|
||||
'XEM',
|
||||
'XMR',
|
||||
'ZEC',
|
||||
'ZRX',
|
||||
]
|
||||
let code = codes[0]
|
||||
for (let c in codes) {
|
||||
if (c in exchange.currencies) {
|
||||
code = c
|
||||
}
|
||||
}
|
||||
|
||||
await loadExchange (exchange)
|
||||
|
||||
let symbol = exchange.symbols[0]
|
||||
const symbols = [
|
||||
'BTC/USD',
|
||||
'BTC/USDT',
|
||||
'BTC/CNY',
|
||||
'BTC/EUR',
|
||||
'BTC/ETH',
|
||||
'ETH/BTC',
|
||||
'BTC/JPY',
|
||||
'LTC/BTC',
|
||||
'ZRX/WETH',
|
||||
]
|
||||
for (let s in symbols) {
|
||||
if (exchange.symbols.includes (symbols[s]) &&
|
||||
(('active' in exchange.markets[symbols[s]]) ? exchange.markets[symbols[s]]['active'] : true)) {
|
||||
symbol = symbols[s]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (exchange.id === 'okex') {
|
||||
// okex has different order creation params for spot and futures markets
|
||||
// this will stick okex to spot market until there is a way to test
|
||||
// several markets per exchange
|
||||
symbol = 'BTC/USDT'
|
||||
}
|
||||
|
||||
log.green ('SYMBOL:', symbol)
|
||||
if ((symbol.indexOf ('.d') < 0)) {
|
||||
await testSymbol (exchange, symbol)
|
||||
}
|
||||
|
||||
if (!exchange.privateKey && (!exchange.apiKey || (exchange.apiKey.length < 1)))
|
||||
return true
|
||||
|
||||
exchange.checkRequiredCredentials ()
|
||||
|
||||
// move to testnet/sandbox if possible before accessing the balance
|
||||
// if (exchange.urls['test'])
|
||||
// exchange.urls['api'] = exchange.urls['test']
|
||||
|
||||
let balance = await tests['fetchBalance'] (exchange)
|
||||
|
||||
await tests['fetchFundingFees'] (exchange)
|
||||
await tests['fetchTradingFees'] (exchange)
|
||||
await tests['fetchStatus'] (exchange)
|
||||
|
||||
await tests['fetchOrders'] (exchange, symbol)
|
||||
await tests['fetchOpenOrders'] (exchange, symbol)
|
||||
await tests['fetchClosedOrders'] (exchange, symbol)
|
||||
await tests['fetchMyTrades'] (exchange, symbol)
|
||||
|
||||
if ('fetchLedger' in tests) {
|
||||
await tests['fetchLedger'] (exchange, code)
|
||||
}
|
||||
|
||||
// const code = exchange.markets[symbol]['quote']
|
||||
// await tests['fetchTransactions'] (exchange, code)
|
||||
// await tests['fetchDeposits'] (exchange, code)
|
||||
// await tests['fetchWithdrawals'] (exchange, code)
|
||||
|
||||
if (exchange.extendedTest) {
|
||||
|
||||
await tests['InvalidNonce'] (exchange, symbol)
|
||||
await tests['OrderNotFound'] (exchange, symbol)
|
||||
await tests['InvalidOrder'] (exchange, symbol)
|
||||
await tests['InsufficientFunds'] (exchange, symbol, balance) // danger zone - won't execute with non-empty balance
|
||||
}
|
||||
|
||||
// try {
|
||||
// let marketSellOrder =
|
||||
// await exchange.createMarketSellOrder (exchange.symbols[0], 1)
|
||||
// console.log (exchange.id, 'ok', marketSellOrder)
|
||||
// } catch (e) {
|
||||
// console.log (exchange.id, 'error', 'market sell', e)
|
||||
// }
|
||||
|
||||
// try {
|
||||
// let marketBuyOrder = await exchange.createMarketBuyOrder (exchange.symbols[0], 1)
|
||||
// console.log (exchange.id, 'ok', marketBuyOrder)
|
||||
// } catch (e) {
|
||||
// console.log (exchange.id, 'error', 'market buy', e)
|
||||
// }
|
||||
|
||||
// try {
|
||||
// let limitSellOrder = await exchange.createLimitSellOrder (exchange.symbols[0], 1, 3000)
|
||||
// console.log (exchange.id, 'ok', limitSellOrder)
|
||||
// } catch (e) {
|
||||
// console.log (exchange.id, 'error', 'limit sell', e)
|
||||
// }
|
||||
|
||||
// try {
|
||||
// let limitBuyOrder = await exchange.createLimitBuyOrder (exchange.symbols[0], 1, 3000)
|
||||
// console.log (exchange.id, 'ok', limitBuyOrder)
|
||||
// } catch (e) {
|
||||
// console.log (exchange.id, 'error', 'limit buy', e)
|
||||
// }
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
let tryAllProxies = async function (exchange, proxies) {
|
||||
|
||||
let currentProxy = 0
|
||||
let maxRetries = proxies.length
|
||||
|
||||
if (settings && ('proxy' in settings))
|
||||
currentProxy = proxies.indexOf (settings.proxy)
|
||||
|
||||
for (let numRetries = 0; numRetries < maxRetries; numRetries++) {
|
||||
|
||||
try {
|
||||
|
||||
exchange.proxy = proxies[currentProxy]
|
||||
|
||||
// add random origin for proxies
|
||||
if (exchange.proxy.length > 0) {
|
||||
exchange.origin = exchange.uuid ()
|
||||
}
|
||||
|
||||
await testExchange (exchange)
|
||||
|
||||
break
|
||||
|
||||
} catch (e) {
|
||||
|
||||
currentProxy = ++currentProxy % proxies.length
|
||||
if (e instanceof ccxt.DDoSProtection) {
|
||||
warn ('[DDoS Protection]' + e.message.slice (0, 200))
|
||||
} else if (e instanceof ccxt.RequestTimeout) {
|
||||
warn ('[Request Timeout] ' + e.message.slice (0, 200))
|
||||
} else if (e instanceof ccxt.ExchangeNotAvailable) {
|
||||
warn ('[Exchange Not Available] ' + e.message.slice (0, 200))
|
||||
} else if (e instanceof ccxt.AuthenticationError) {
|
||||
warn ('[Authentication Error] ' + e.message.slice (0, 200))
|
||||
return
|
||||
} else if (e instanceof ccxt.InvalidNonce) {
|
||||
warn ('[Invalid Nonce] ' + e.message.slice (0, 200))
|
||||
return
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
;(async function test () {
|
||||
|
||||
if (exchangeSymbol) {
|
||||
await loadExchange (exchange)
|
||||
await testSymbol (exchange, exchangeSymbol)
|
||||
} else {
|
||||
await tryAllProxies (exchange, proxies)
|
||||
}
|
||||
|
||||
}) ()
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// run with `node test_timeout_hang`
|
||||
// TODO: integrate with CI tests somehow...
|
||||
|
||||
const { timeout } = require ('../base/functions')
|
||||
|
||||
;(async function () {
|
||||
|
||||
await timeout (10000, Promise.resolve ('foo'))
|
||||
|
||||
console.log ('Look ma, no hangs!') // should terminate the process immediately..
|
||||
|
||||
}) ()
|
||||
Reference in new issue
Block a user