@jmparsons/ccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 100+ exchanges
233 lines (177 loc) • 11.9 kB
JavaScript
/* ------------------------------------------------------------------------ */
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.number')
require ('./functions/test.time')
require ('./functions/test.type')
/* ------------------------------------------------------------------------ */
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 ('getCurrencyUsedOnOpenOrders() works', () => {
const calls = []
const rateLimit = 100
const exchange = new Exchange ({
'orders': [
{ status: 'open', symbol: 'ETH/BTC', side: 'sell', price: 200.0, amount: 21.0, remaining: 20.0 },
{ status: 'open', symbol: 'ETH/BTC', side: 'buy', price: 200.0, amount: 22.0, remaining: 20.0 },
{ status: 'open', symbol: 'ETH/BTC', side: 'sell', price: 200.0, amount: 23.0, remaining: 20.0 },
{ status: 'closed', symbol: 'BTC/USD', side: 'sell', price: 10.0, amount: 11.0, remaining: 10.0 },
{ status: 'open', symbol: 'BTC/USD', side: 'buy', price: 10.0, amount: 12.0, remaining: 10.0 },
{ status: 'open', symbol: 'BTC/USD', side: 'sell', price: 10.0, amount: 13.0, remaining: 10.0 },
],
'markets': {
'ETH/BTC': { id: 'ETH/BTC', symbol: 'ETH/BTC', base: 'ETH', quote: 'BTC' },
'BTC/USD': { id: 'BTC/USD', symbol: 'BTC/USD', base: 'BTC', quote: 'USD' },
},
})
equal (exchange.getCurrencyUsedOnOpenOrders ('LTC'), 0)
equal (exchange.getCurrencyUsedOnOpenOrders ('ETH'), 40)
equal (exchange.getCurrencyUsedOnOpenOrders ('USD'), 100)
equal (exchange.getCurrencyUsedOnOpenOrders ('BTC'), 4010)
})
/* ------------------------------------------------------------------------ */
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", "nodeVersion", "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", "getCurrencyUsedOnOpenOrders", "parseBalance", "fetchPartialBalance", "fetchFreeBalance", "fetchUsedBalance", "fetchTotalBalance", "filterBySinceLimit", "parseTrades", "parseOrders", "filterOrdersBySymbol", "parseOHLCV", "parseOHLCVs", "editLimitBuyOrder", "editLimitSellOrder", "editLimitOrder", "editOrder", "createLimitBuyOrder", "createLimitSellOrder", "createMarketBuyOrder", "createMarketSellOrder", "costToPrecision", "priceToPrecision", "amountToPrecision", "amountToString", "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)
})
})
/* ------------------------------------------------------------------------ */