From a9ef60dac6486de987a1ce4b77dd4ccbaa1d5a6c Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 10:49:18 +0100 Subject: [PATCH 01/13] chore: add create-order-example --- examples/create-order.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 examples/create-order.js diff --git a/examples/create-order.js b/examples/create-order.js new file mode 100644 index 00000000..8cd49c48 --- /dev/null +++ b/examples/create-order.js @@ -0,0 +1,33 @@ +import Binance from 'index' + +const client = Binance({ + apiKey: 'your_api_key_here', + apiSecret: 'your_api_secret_here', +}) + + +async function main() { + try { + const order = await client.order({ + symbol: 'LTCUSDT', + side: 'BUY', + type: 'LIMIT', + quantity: 0.1, + price:90, + timeInForce: 'GTC' + }) + console.log(order.id) + + // will cancel the order + const result = await client.cancelOrder({ + symbol: 'LTCUSDT', + orderId: order.id, + }) + console.log(result) + } catch (error) { + console.error(error) + } +} + +main() +// node --require @babel/register examples/create-order.js \ No newline at end of file From 120db27606127bde17789d12f6df6804431e88d8 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 11:40:18 +0100 Subject: [PATCH 02/13] feat(client): add demoTrading urls --- src/http-client.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/http-client.js b/src/http-client.js index 3355cf5d..be99b4c0 100644 --- a/src/http-client.js +++ b/src/http-client.js @@ -6,7 +6,17 @@ import JSONbig from 'json-bigint' import 'isomorphic-fetch' const getEndpoint = (endpoints, path, testnet) => { - if (testnet) return 'https://testnet.binancefuture.com' + + const isFutures = path.includes('/fapi') || path.includes('/futures'); + const isDelivery = path.includes('/dapi'); + const isPortfolioMargin = path.includes('/papi'); + + if (testnet) { + if (isFutures) return "https://demo-fapi.binance.com" + if (isDelivery) return "https://demo-dapi.binance.com" + if (isPortfolioMargin) return "https://demo-papi.binance.com" + return "https://demo-api.binance.com" + } if (path.includes('/fapi') || path.includes('/futures')) return endpoints.futures || 'https://fapi.binance.com' From ff0afa0c1ecff0536b882cd94498d08569f887f5 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 12:06:52 +0100 Subject: [PATCH 03/13] init static tests --- test/static-tests.js | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/static-tests.js diff --git a/test/static-tests.js b/test/static-tests.js new file mode 100644 index 00000000..f139e57d --- /dev/null +++ b/test/static-tests.js @@ -0,0 +1,40 @@ +import test from 'ava' +import nock from 'nock'; +import Binance, { ErrorCodes } from 'index' + +const binance = Binance() + +let interceptedUrl = null; +let interceptedBody = null; + +test.beforeEach(t => { + + interceptedUrl = null; + interceptedBody = null; + nock(/.*/) + .get(/.*/) + .reply(200, function (uri, requestBody) { + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedBody = requestBody; + return { success: true }; + }); + nock(/.*/) + .post(/.*/) + .reply(200, function (uri, requestBody) { + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedBody = requestBody; + return { success: true }; + }); + nock(/.*/) + .delete(/.*/) + .reply(200, function (uri, requestBody) { + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedBody = requestBody; + return { success: true }; + }); +}); + +test('[REST] Prices no symbol', async t => { + await binance.prices(); + t.is(interceptedUrl, 'https://api.binance.com/api/v3/ticker/price') +}) \ No newline at end of file From 4d77186c05c0e92295cbd34f5916f486fea157c1 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 12:07:09 +0100 Subject: [PATCH 04/13] add nock dependency --- package-lock.json | 94 ++++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 031f750b..5886fc73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "eslint": "^6.7.1", "eslint-config-prettier": "^6.7.0", "eslint-config-zavatta": "^6.0.3", + "nock": "^14.0.10", "nyc": "^14.1.1", "prettier": "^3.5.3", "ts-node": "^10.9.1", @@ -1497,6 +1498,24 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mswjs/interceptors": { + "version": "0.39.8", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.39.8.tgz", + "integrity": "sha512-2+BzZbjRO7Ct61k8fMNHEtoKjeWI9pIlHFTqBwZ5icHpqszIgEZbjb1MW5Z0+bITTCTl3gk4PDBxs9tA/csXvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "dev": true, @@ -1535,6 +1554,31 @@ "node": ">= 8" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "dev": true, @@ -2220,7 +2264,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001707", + "version": "1.0.30001751", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", + "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", "dev": true, "funding": [ { @@ -4000,6 +4046,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "dev": true, @@ -4563,6 +4616,21 @@ "dev": true, "license": "MIT" }, + "node_modules/nock": { + "version": "14.0.10", + "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.10.tgz", + "integrity": "sha512-Q7HjkpyPeLa0ZVZC5qpxBt5EyLczFJ91MEewQiIi9taWuA0KB/MDJlUWtON+7dGouVdADTQsf9RA7TZk6D8VMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mswjs/interceptors": "^0.39.5", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">=18.20.0 <20 || >=20.12.1" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -4848,6 +4916,13 @@ "node": ">=0.10.0" } }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, "node_modules/p-defer": { "version": "1.0.0", "dev": true, @@ -5267,6 +5342,16 @@ "node": ">=0.4.0" } }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/pseudomap": { "version": "1.0.2", "dev": true, @@ -5889,6 +5974,13 @@ "node": ">=8" } }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "dev": true, diff --git a/package.json b/package.json index bf412259..57ec97e7 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "eslint": "^6.7.1", "eslint-config-prettier": "^6.7.0", "eslint-config-zavatta": "^6.0.3", + "nock": "^14.0.10", "nyc": "^14.1.1", "prettier": "^3.5.3", "ts-node": "^10.9.1", From 0c2960d5af52a54c320d76354b671e60219ffe22 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 12:22:16 +0100 Subject: [PATCH 05/13] add more static prices --- test/static-tests.js | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/test/static-tests.js b/test/static-tests.js index f139e57d..590a2e9d 100644 --- a/test/static-tests.js +++ b/test/static-tests.js @@ -2,13 +2,13 @@ import test from 'ava' import nock from 'nock'; import Binance, { ErrorCodes } from 'index' +// Warning: For now these tests can't run in parallel due to nock interceptors const binance = Binance() let interceptedUrl = null; let interceptedBody = null; -test.beforeEach(t => { - +test.serial.beforeEach(t => { interceptedUrl = null; interceptedBody = null; nock(/.*/) @@ -34,7 +34,31 @@ test.beforeEach(t => { }); }); -test('[REST] Prices no symbol', async t => { +test.serial('[REST] Prices no symbol', async t => { await binance.prices(); t.is(interceptedUrl, 'https://api.binance.com/api/v3/ticker/price') +}) + +test.serial('[REST] Futures Prices no symbol', async t => { + await binance.futuresPrices(); + t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/ticker/price') +}) + + +test.serial('[REST] Orderbook', async t => { + try { + await binance.book({ symbol: 'BTCUSDT' }); + } catch (e) { + // it can throw an error because of the mocked response + } + t.is(interceptedUrl, 'https://api.binance.com/api/v3/depth?symbol=BTCUSDT') +}) + +test.serial('[REST] Futures Orderbook', async t => { + try { + await binance.futuresBook({ symbol: 'BTCUSDT' }); + } catch (e) { + // it can throw an error because of the mocked response + } + t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/depth?symbol=BTCUSDT') }) \ No newline at end of file From e86fb8fddbcffd8baf3f2556be9bd436a0378e75 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 12:24:08 +0100 Subject: [PATCH 06/13] add more examples --- examples/fetch-orderbook.js | 16 ++++++++++++++++ examples/fetch-prices.js | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 examples/fetch-orderbook.js create mode 100644 examples/fetch-prices.js diff --git a/examples/fetch-orderbook.js b/examples/fetch-orderbook.js new file mode 100644 index 00000000..6fef3516 --- /dev/null +++ b/examples/fetch-orderbook.js @@ -0,0 +1,16 @@ +import Binance from 'index' + + +const client = Binance({ +}) + +async function main() { + const spotOb = await client.book({ symbol: 'BTCUSDT' }) + console.log('Spot Orderbook:', spotOb) + + const futuresOb = await client.futuresBook({ symbol: 'BTCUSDT' }) + console.log('Futures Orderbook:', futuresOb) +} + +main() +// node --require @babel/register examples/fetch-orderbook.js \ No newline at end of file diff --git a/examples/fetch-prices.js b/examples/fetch-prices.js new file mode 100644 index 00000000..47ac213c --- /dev/null +++ b/examples/fetch-prices.js @@ -0,0 +1,16 @@ +import Binance from 'index' + + +const client = Binance({ +}) + +async function main() { + const spotPrices = await client.prices() + console.log('Spot Prices:', spotPrices) + + const futuresPrices = await client.futuresPrices() + console.log('Futures Prices:', futuresPrices) +} + +main() +// node --require @babel/register examples/fetch-prices.js \ No newline at end of file From caac710dcc4798d0d23b6c513119c0ed5258414b Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 12:44:58 +0100 Subject: [PATCH 07/13] more tests --- test/static-tests.js | 206 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 204 insertions(+), 2 deletions(-) diff --git a/test/static-tests.js b/test/static-tests.js index 590a2e9d..c8750bbd 100644 --- a/test/static-tests.js +++ b/test/static-tests.js @@ -3,7 +3,20 @@ import nock from 'nock'; import Binance, { ErrorCodes } from 'index' // Warning: For now these tests can't run in parallel due to nock interceptors -const binance = Binance() +const binance = Binance({ + apiKey: 'testkey', + apiSecret: 'test' +}) +const demoBinance = Binance({ + testnet: true, +}); + +function urlToObject(queryString) { + const params = new URLSearchParams(queryString); + const obj = Object.fromEntries(params.entries()); + return obj; +} + let interceptedUrl = null; let interceptedBody = null; @@ -34,6 +47,16 @@ test.serial.beforeEach(t => { }); }); +test.serial('[Rest] Spot demo url', async t => { + await demoBinance.time(); + t.is(interceptedUrl, 'https://demo-api.binance.com/api/v3/time') +}) + +test.serial('[Rest] Futures demo url', async t => { + await demoBinance.futuresTime(); + t.is(interceptedUrl, 'https://demo-fapi.binance.com/fapi/v1/time') +}) + test.serial('[REST] Prices no symbol', async t => { await binance.prices(); t.is(interceptedUrl, 'https://api.binance.com/api/v3/ticker/price') @@ -61,4 +84,183 @@ test.serial('[REST] Futures Orderbook', async t => { // it can throw an error because of the mocked response } t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/depth?symbol=BTCUSDT') -}) \ No newline at end of file +}) + +test.serial('[REST] OHLCVS', async t => { + try { + await binance.candles({ symbol: 'BTCUSDT' }) + } catch (e) { + // it can throw an error because of the mocked response + } + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/klines?interval=5m&symbol=BTCUSDT')) +}) + +test.serial('[REST] Futures OHLCVS', async t => { + try { + await binance.futuresCandles({ symbol: 'BTCUSDT', interval: '30m' }) + } catch (e) { + // it can throw an error because of the mocked response + } + t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/klines?interval=30m&symbol=BTCUSDT') +}) + +test.serial('[REST] Recent Trades', async t => { + await binance.trades({ symbol: 'BTCUSDT', limit: 500 }) + t.is(interceptedUrl, 'https://api.binance.com/api/v3/trades?symbol=BTCUSDT&limit=500') +}) + +test.serial('[REST] Agg Trades', async t => { + try { + await binance.aggTrades({ symbol: 'BTCUSDT' }) + } catch (e) { + // it can throw an error because of the mocked response + } + t.is(interceptedUrl, 'https://api.binance.com/api/v3/aggTrades?symbol=BTCUSDT') +}) + +test.serial('[REST] FuturesTrades', async t => { + try { + await binance.futuresTrades({ symbol: 'BTCUSDT' }) + } catch (e) { + // it can throw an error because of the mocked response + } + t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/trades?symbol=BTCUSDT') +}) + +test.serial('[REST] FuturesAggTrades', async t => { + try { + await binance.futuresAggTrades({ symbol: 'BTCUSDT' }) + } catch (e) { + // it can throw an error because of the mocked response + } + t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/aggTrades?symbol=BTCUSDT') +}) + +test.serial('[REST] PositionRisk V2', async t => { + await binance.futuresPositionRisk() + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v2/positionRisk')) +}) + + +test.serial('[REST] CancelOrder', async t => { + await binance.cancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) + const obj = urlToObject(interceptedUrl.replace('https://api.binance.com/api/v3/order', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.orderId, '34234234') +}) + +test.serial('[REST] Futures CancelOrder', async t => { + await binance.futuresCancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.orderId, '34234234') +}) + + +test.serial('[REST] MarketBuy', async t => { + await binance.order({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) + const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') +}) + +test.serial('[REST] MarketSell', async t => { + await binance.order({ symbol: 'LTCUSDT', side: 'SELL', type: 'MARKET', quantity: 0.5 }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) + const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'SELL') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') +}) + +test.serial('[REST] LimitBuy', async t => { + await binance.order({ symbol: 'LTCUSDT', side: 'BUY', type: 'LIMIT', quantity: 0.5, price: 100 }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) + const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'LIMIT') + t.is(obj.quantity, '0.5') +}) + +test.serial('[REST] LimitSell', async t => { + await binance.order({ symbol: 'LTCUSDT', side: 'SELL', type: 'LIMIT', quantity: 0.5, price: 100 }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) + const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'SELL') + t.is(obj.type, 'LIMIT') + t.is(obj.quantity, '0.5') +}) + + +test.serial('[REST] Futures MarketBuy', async t => { + await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') +}) + +test.serial('[REST] Futures MarketSell', async t => { + await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'SELL', type: 'MARKET', quantity: 0.5 }) + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'SELL') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') +}) + +test.serial('[REST] Futures LimitBuy', async t => { + await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'BUY', type: 'LIMIT', quantity: 0.5, price: 100 }) + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'LIMIT') + t.is(obj.quantity, '0.5') +}) + +test.serial('[REST] Futures LimitSell', async t => { + await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'SELL', type: 'LIMIT', quantity: 0.5, price: 100 }) + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'SELL') + t.is(obj.type, 'LIMIT') + t.is(obj.quantity, '0.5') +}) + + +test.serial('[REST] Futures cancel order', async t => { + await binance.futuresCancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) + const url = 'https://fapi.binance.com/fapi/v1/order' + t.true(interceptedUrl.startsWith(url)) + const obj = urlToObject(interceptedUrl.replace(url, '')) + t.is(obj.orderId, '34234234') + t.is(obj.symbol, 'LTCUSDT') +}) + +test.serial('[REST] MarketBuy test', async t => { + await binance.orderTest({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order/test')) + const body = interceptedUrl.replace('https://api.binance.com/api/v3/order/test', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') +}) From a7cd06305cd607c81b7595adaec3fa99646f7e75 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 12:46:28 +0100 Subject: [PATCH 08/13] format document --- test/static-tests.js | 245 +++++++++++++++++++++++-------------------- 1 file changed, 133 insertions(+), 112 deletions(-) diff --git a/test/static-tests.js b/test/static-tests.js index c8750bbd..eb2ee858 100644 --- a/test/static-tests.js +++ b/test/static-tests.js @@ -27,24 +27,24 @@ test.serial.beforeEach(t => { nock(/.*/) .get(/.*/) .reply(200, function (uri, requestBody) { - interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; interceptedBody = requestBody; return { success: true }; }); nock(/.*/) .post(/.*/) .reply(200, function (uri, requestBody) { - interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; interceptedBody = requestBody; return { success: true }; }); nock(/.*/) .delete(/.*/) .reply(200, function (uri, requestBody) { - interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; interceptedBody = requestBody; return { success: true }; - }); + }); }); test.serial('[Rest] Spot demo url', async t => { @@ -87,35 +87,35 @@ test.serial('[REST] Futures Orderbook', async t => { }) test.serial('[REST] OHLCVS', async t => { - try { - await binance.candles({ symbol: 'BTCUSDT' }) - } catch (e) { - // it can throw an error because of the mocked response - } - t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/klines?interval=5m&symbol=BTCUSDT')) + try { + await binance.candles({ symbol: 'BTCUSDT' }) + } catch (e) { + // it can throw an error because of the mocked response + } + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/klines?interval=5m&symbol=BTCUSDT')) }) test.serial('[REST] Futures OHLCVS', async t => { - try { - await binance.futuresCandles({ symbol: 'BTCUSDT', interval: '30m' }) - } catch (e) { - // it can throw an error because of the mocked response - } - t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/klines?interval=30m&symbol=BTCUSDT') + try { + await binance.futuresCandles({ symbol: 'BTCUSDT', interval: '30m' }) + } catch (e) { + // it can throw an error because of the mocked response + } + t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/klines?interval=30m&symbol=BTCUSDT') }) test.serial('[REST] Recent Trades', async t => { - await binance.trades({ symbol: 'BTCUSDT', limit: 500 }) - t.is(interceptedUrl, 'https://api.binance.com/api/v3/trades?symbol=BTCUSDT&limit=500') + await binance.trades({ symbol: 'BTCUSDT', limit: 500 }) + t.is(interceptedUrl, 'https://api.binance.com/api/v3/trades?symbol=BTCUSDT&limit=500') }) test.serial('[REST] Agg Trades', async t => { - try { - await binance.aggTrades({ symbol: 'BTCUSDT' }) - } catch (e) { + try { + await binance.aggTrades({ symbol: 'BTCUSDT' }) + } catch (e) { // it can throw an error because of the mocked response - } - t.is(interceptedUrl, 'https://api.binance.com/api/v3/aggTrades?symbol=BTCUSDT') + } + t.is(interceptedUrl, 'https://api.binance.com/api/v3/aggTrades?symbol=BTCUSDT') }) test.serial('[REST] FuturesTrades', async t => { @@ -128,139 +128,160 @@ test.serial('[REST] FuturesTrades', async t => { }) test.serial('[REST] FuturesAggTrades', async t => { - try { + try { await binance.futuresAggTrades({ symbol: 'BTCUSDT' }) - } catch (e) { - // it can throw an error because of the mocked response - } - t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/aggTrades?symbol=BTCUSDT') + } catch (e) { + // it can throw an error because of the mocked response + } + t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/aggTrades?symbol=BTCUSDT') }) test.serial('[REST] PositionRisk V2', async t => { - await binance.futuresPositionRisk() - t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v2/positionRisk')) + await binance.futuresPositionRisk() + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v2/positionRisk')) }) test.serial('[REST] CancelOrder', async t => { - await binance.cancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) - t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) - const obj = urlToObject(interceptedUrl.replace('https://api.binance.com/api/v3/order', '')) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.orderId, '34234234') + await binance.cancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) + const obj = urlToObject(interceptedUrl.replace('https://api.binance.com/api/v3/order', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.orderId, '34234234') }) test.serial('[REST] Futures CancelOrder', async t => { - await binance.futuresCancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) - t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) - const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order', '')) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.orderId, '34234234') + await binance.futuresCancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.orderId, '34234234') }) test.serial('[REST] MarketBuy', async t => { - await binance.order({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) - t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) - const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') - const obj = urlToObject(body) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.side, 'BUY') - t.is(obj.type, 'MARKET') - t.is(obj.quantity, '0.5') + await binance.order({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) + const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') }) test.serial('[REST] MarketSell', async t => { - await binance.order({ symbol: 'LTCUSDT', side: 'SELL', type: 'MARKET', quantity: 0.5 }) + await binance.order({ symbol: 'LTCUSDT', side: 'SELL', type: 'MARKET', quantity: 0.5 }) t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') - const obj = urlToObject(body) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.side, 'SELL') - t.is(obj.type, 'MARKET') - t.is(obj.quantity, '0.5') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'SELL') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') }) test.serial('[REST] LimitBuy', async t => { - await binance.order({ symbol: 'LTCUSDT', side: 'BUY', type: 'LIMIT', quantity: 0.5, price: 100 }) - t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) - const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') - const obj = urlToObject(body) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.side, 'BUY') - t.is(obj.type, 'LIMIT') - t.is(obj.quantity, '0.5') + await binance.order({ symbol: 'LTCUSDT', side: 'BUY', type: 'LIMIT', quantity: 0.5, price: 100 }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) + const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'LIMIT') + t.is(obj.quantity, '0.5') }) test.serial('[REST] LimitSell', async t => { - await binance.order({ symbol: 'LTCUSDT', side: 'SELL', type: 'LIMIT', quantity: 0.5, price: 100 }) - t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) - const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') - const obj = urlToObject(body) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.side, 'SELL') - t.is(obj.type, 'LIMIT') - t.is(obj.quantity, '0.5') + await binance.order({ symbol: 'LTCUSDT', side: 'SELL', type: 'LIMIT', quantity: 0.5, price: 100 }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) + const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'SELL') + t.is(obj.type, 'LIMIT') + t.is(obj.quantity, '0.5') }) test.serial('[REST] Futures MarketBuy', async t => { - await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) - t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) - const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.side, 'BUY') - t.is(obj.type, 'MARKET') - t.is(obj.quantity, '0.5') + await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') }) test.serial('[REST] Futures MarketSell', async t => { - await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'SELL', type: 'MARKET', quantity: 0.5 }) - t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) - const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.side, 'SELL') - t.is(obj.type, 'MARKET') - t.is(obj.quantity, '0.5') + await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'SELL', type: 'MARKET', quantity: 0.5 }) + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'SELL') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') }) test.serial('[REST] Futures LimitBuy', async t => { - await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'BUY', type: 'LIMIT', quantity: 0.5, price: 100 }) - t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) - const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.side, 'BUY') - t.is(obj.type, 'LIMIT') - t.is(obj.quantity, '0.5') + await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'BUY', type: 'LIMIT', quantity: 0.5, price: 100 }) + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'LIMIT') + t.is(obj.quantity, '0.5') }) test.serial('[REST] Futures LimitSell', async t => { - await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'SELL', type: 'LIMIT', quantity: 0.5, price: 100 }) - t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) - const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.side, 'SELL') - t.is(obj.type, 'LIMIT') - t.is(obj.quantity, '0.5') + await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'SELL', type: 'LIMIT', quantity: 0.5, price: 100 }) + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'SELL') + t.is(obj.type, 'LIMIT') + t.is(obj.quantity, '0.5') }) test.serial('[REST] Futures cancel order', async t => { - await binance.futuresCancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) - const url = 'https://fapi.binance.com/fapi/v1/order' - t.true(interceptedUrl.startsWith(url)) - const obj = urlToObject(interceptedUrl.replace(url, '')) - t.is(obj.orderId, '34234234') - t.is(obj.symbol, 'LTCUSDT') + await binance.futuresCancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) + const url = 'https://fapi.binance.com/fapi/v1/order' + t.true(interceptedUrl.startsWith(url)) + const obj = urlToObject(interceptedUrl.replace(url, '')) + t.is(obj.orderId, '34234234') + t.is(obj.symbol, 'LTCUSDT') }) test.serial('[REST] MarketBuy test', async t => { - await binance.orderTest({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) - t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order/test')) - const body = interceptedUrl.replace('https://api.binance.com/api/v3/order/test', '') - const obj = urlToObject(body) - t.is(obj.symbol, 'LTCUSDT') - t.is(obj.side, 'BUY') - t.is(obj.type, 'MARKET') - t.is(obj.quantity, '0.5') + await binance.orderTest({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order/test')) + const body = interceptedUrl.replace('https://api.binance.com/api/v3/order/test', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') +}) + +test.serial('[REST] spot open orders', async t => { + await binance.openOrders({ symbol: 'LTCUSDT' }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/openOrders')) +}) + +test.serial('[REST] margin open orders', async t => { + await binance.marginOpenOrders({ symbol: 'LTCUSDT' }) + t.true(interceptedUrl.startsWith('https://api.binance.com/sapi/v1/margin/openOrders')) +}) + +test.serial('[REST] Margin MarketBuy order', async t => { + await binance.marginOrder({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) + t.true(interceptedUrl.startsWith('https://api.binance.com/sapi/v1/margin/order')) + const body = interceptedUrl.replace('https://api.binance.com/sapi/v1/margin/order', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.5') }) From f4852e0ea60e3287e31afbd4e6b271a24e88f7ee Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:45:55 +0100 Subject: [PATCH 09/13] more tests and other fixes --- src/http-client.js | 33 +++++++++++++++++++++++ test/static-tests.js | 63 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/src/http-client.js b/src/http-client.js index be99b4c0..5f4df11d 100644 --- a/src/http-client.js +++ b/src/http-client.js @@ -33,6 +33,10 @@ const getDomainName = url => { const defaultGetTime = () => Date.now() +const uuid22 = (a) => { + return a ? (a ^ Math.random() * 16 >> a / 4).toString(16) : (([1e7]) + 1e3 + 4e3 + 8e5).replace(/[018]/g, uuid22); +} + // Singleton holding header data like rate limits. const info = {} @@ -122,6 +126,14 @@ const checkParams = (name, payload, requires = []) => { return true } +const spotP = () => { + return `x-B3AUXNYV${uuid22()}` +} + +const futuresP = () => { + return `x-ftGmvgAN${uuid22()}` +} + /** * Make public calls against the api * @@ -250,6 +262,13 @@ const candles = (pubCall, payload, endpoint = '/api/v3/klines') => ), ) +const isContractURL = (path) => { + const isFutures = path.includes('/fapi') || path.includes('/futures'); + const isDelivery = path.includes('/dapi'); + const isPortfolioMargin = path.includes('/papi'); + return isFutures || isDelivery || isPortfolioMargin; +} + /** * Create a new order wrapper for market order simplicity */ @@ -274,6 +293,16 @@ const order = (privCall, payload = {}, url) => { requires.push('callbackRate') } + if (!newPayload.newClientOrderId) { + // check if this is spot or futures/delivery + const isContract = isContractURL(url); + if (isContract) { + newPayload.newClientOrderId = futuresP(); + } else { + newPayload.newClientOrderId = spotP(); + } + } + return ( checkParams('order', newPayload, requires) && privCall(url, { type: 'LIMIT', ...newPayload }, 'POST') @@ -286,6 +315,10 @@ const orderOco = (privCall, payload = {}, url) => { ? { stopLimitTimeInForce: 'GTC', ...payload } : payload + if (!newPayload.listClientOrderId) { + newPayload.listClientOrderId = spotP(); + } + return ( checkParams('order', newPayload, ['symbol', 'side', 'quantity', 'price', 'stopPrice']) && privCall(url, newPayload, 'POST') diff --git a/test/static-tests.js b/test/static-tests.js index eb2ee858..72f458d9 100644 --- a/test/static-tests.js +++ b/test/static-tests.js @@ -158,6 +158,8 @@ test.serial('[REST] Futures CancelOrder', async t => { t.is(obj.orderId, '34234234') }) +const CONTRACT_PREFIX = "x-ftGmvgAN" +const SPOT_PREFIX = "x-B3AUXNYV" test.serial('[REST] MarketBuy', async t => { await binance.order({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) @@ -168,6 +170,7 @@ test.serial('[REST] MarketBuy', async t => { t.is(obj.side, 'BUY') t.is(obj.type, 'MARKET') t.is(obj.quantity, '0.5') + t.true(obj.newClientOrderId.startsWith(SPOT_PREFIX)) }) test.serial('[REST] MarketSell', async t => { @@ -179,6 +182,8 @@ test.serial('[REST] MarketSell', async t => { t.is(obj.side, 'SELL') t.is(obj.type, 'MARKET') t.is(obj.quantity, '0.5') + t.true(obj.newClientOrderId.startsWith(SPOT_PREFIX)) + }) test.serial('[REST] LimitBuy', async t => { @@ -190,6 +195,7 @@ test.serial('[REST] LimitBuy', async t => { t.is(obj.side, 'BUY') t.is(obj.type, 'LIMIT') t.is(obj.quantity, '0.5') + t.true(obj.newClientOrderId.startsWith(SPOT_PREFIX)) }) test.serial('[REST] LimitSell', async t => { @@ -201,6 +207,7 @@ test.serial('[REST] LimitSell', async t => { t.is(obj.side, 'SELL') t.is(obj.type, 'LIMIT') t.is(obj.quantity, '0.5') + t.true(obj.newClientOrderId.startsWith(SPOT_PREFIX)) }) @@ -212,6 +219,7 @@ test.serial('[REST] Futures MarketBuy', async t => { t.is(obj.side, 'BUY') t.is(obj.type, 'MARKET') t.is(obj.quantity, '0.5') + t.true(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) }) test.serial('[REST] Futures MarketSell', async t => { @@ -222,6 +230,7 @@ test.serial('[REST] Futures MarketSell', async t => { t.is(obj.side, 'SELL') t.is(obj.type, 'MARKET') t.is(obj.quantity, '0.5') + t.true(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) }) test.serial('[REST] Futures LimitBuy', async t => { @@ -242,6 +251,7 @@ test.serial('[REST] Futures LimitSell', async t => { t.is(obj.side, 'SELL') t.is(obj.type, 'LIMIT') t.is(obj.quantity, '0.5') + t.true(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) }) @@ -263,6 +273,7 @@ test.serial('[REST] MarketBuy test', async t => { t.is(obj.side, 'BUY') t.is(obj.type, 'MARKET') t.is(obj.quantity, '0.5') + t.true(obj.newClientOrderId.startsWith(SPOT_PREFIX)) }) test.serial('[REST] spot open orders', async t => { @@ -284,4 +295,56 @@ test.serial('[REST] Margin MarketBuy order', async t => { t.is(obj.side, 'BUY') t.is(obj.type, 'MARKET') t.is(obj.quantity, '0.5') + t.true(obj.newClientOrderId.startsWith(SPOT_PREFIX)) +}) + +test.serial('[REST] spot order with custom clientorderId', async t => { + await binance.order({ + symbol: 'LTCUSDT', + side: 'BUY', + type: 'LIMIT', + quantity: 0.5, + price: 100, + newClientOrderId: 'myid' + }) + t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) + const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') + const obj = urlToObject(body) + t.is(obj.symbol, 'LTCUSDT') + t.is(obj.side, 'BUY') + t.is(obj.type, 'LIMIT') + t.is(obj.quantity, '0.5') + t.is(obj.price, '100') + t.is(obj.newClientOrderId, 'myid') }) + + +test.serial('[REST] delivery OrderBook', async t => { + try { + await binance.deliveryBook({ symbol: 'BTCUSD_PERP' }) + } catch (e) { + // it can throw an error because of the mocked response + } + t.is(interceptedUrl, 'https://dapi.binance.com/dapi/v1/depth?symbol=BTCUSD_PERP') +}) + +test.serial('[REST] futures set leverage', async t => { + try { + await binance.futuresLeverage({ symbol: 'BTCUSDT', leverage: 5 }) + } catch (e) { + // it can throw an error because of the mocked response + } + t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/leverage?symbol=BTCUSDT&leverage=5')) +}) + + +test.serial('[REST] delivery MarketBuy', async t => { + await binance.deliveryOrder({ symbol: 'BTCUSD_PERP', side: 'BUY', type: 'MARKET', quantity: 0.1 }) + t.true(interceptedUrl.startsWith('https://dapi.binance.com/dapi/v1/order')) + const obj = urlToObject(interceptedUrl.replace('https://dapi.binance.com/dapi/v1/order', '')) + t.is(obj.symbol, 'BTCUSD_PERP') + t.is(obj.side, 'BUY') + t.is(obj.type, 'MARKET') + t.is(obj.quantity, '0.1') + t.true(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) +}) \ No newline at end of file From 1f066c1ffc5a28c98735454ef78242f9d7d8a736 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:53:23 +0100 Subject: [PATCH 10/13] split ci steps --- .github/workflows/ci.yml | 18 ++++++++++++------ package.json | 4 +++- publish.sh | 21 +++++++++++++++++++++ 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 publish.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df6aee08..6c048a95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/cache@v4 with: - path: '**/node_modules' + path: "**/node_modules" key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }} - name: Install dependencies @@ -24,12 +24,12 @@ jobs: run: | sudo apt-get update sudo apt-get install -y openvpn - + - name: Download IVPN config run: | wget -O ivpn-config.zip "https://api.ivpn.net/v5/config/ivpn-openvpn-config.zip?country=NL&city=Amsterdam&proto=udp&port=2049" unzip ivpn-config.zip - + - name: Start IVPN connection run: | echo "${{ secrets.IVPN_ACCOUNT_NUMBER }}" > auth.txt @@ -39,12 +39,18 @@ jobs: - name: Run typecheck run: npm run typecheck + - name: Lint and Prettier + run: npm run lint && npm run prettier:check + - name: Static Tests + run: npm run static-tests + - name: All Tests + run: npm test - - name: Run checks - run: npm run ci + # - name: Run checks + # run: npm run ci - name: Cleanup VPN if: always() run: | sudo killall openvpn || true - rm -f auth.txt \ No newline at end of file + rm -f auth.txt diff --git a/package.json b/package.json index 57ec97e7..0eb1d463 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,12 @@ "cover": "nyc ava", "report": "npm run cover && nyc report --reporter=text-lcov | coveralls", "lint": "eslint src", + "static-tests": "ava test/static-tests.js", "prettier": "prettier --write '{src,test}/**/*.{ts,js}'", "prettier:check": "prettier -l '{src,test}/**/*.{ts,js}'", "ci": "npm run lint && npm run prettier:check && npm run test", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "publishPackage": "npm run build && sh publish.sh && git push && git push --tags && npm publish" }, "dependencies": { "https-proxy-agent": "^5.0.0", diff --git a/publish.sh b/publish.sh new file mode 100644 index 00000000..e91db6e1 --- /dev/null +++ b/publish.sh @@ -0,0 +1,21 @@ + +#!/bin/bash +set -e + +# release defaults to patch (last number in semver) +RELEASE="patch" && [ -n "$1" ] && RELEASE=$1 + +# cut the release +VERSION=$(npm --no-git-tag-version version $RELEASE | sed 's/v//') + +git add package.json +git commit -m "release: cut the $VERSION release" + +# tag the release +git tag $VERSION +git tag -l + +echo -e "\033[1;92m You are ready to publish!" +echo -e "\033[1;95m git push" +echo -e "\033[1;95m git push --tags" +echo -e "\033[1;95m npm publish" \ No newline at end of file From cc2f62eabe6be7b6238faa9096941a2554c992c5 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:04:21 +0100 Subject: [PATCH 11/13] fix formatting --- .prettierrc | 2 +- src/http-client.js | 1130 ++++++++++--------- src/index.js | 170 +-- src/open-websocket.js | 38 +- src/websocket.js | 1640 ++++++++++++++-------------- test/auth.js | 420 +++---- test/index.js | 1402 ++++++++++++------------ test/static-tests.js | 125 ++- test/types.ts | 1168 ++++++++++---------- test/utils.js | 30 +- test/websockets/bookTicker.js | 146 +-- test/websockets/candles.js | 174 +-- test/websockets/customSubStream.js | 182 +-- test/websockets/depth.js | 258 ++--- test/websockets/liquidations.js | 194 ++-- test/websockets/markPrices.js | 224 ++-- test/websockets/ticker.js | 401 +++---- test/websockets/trades.js | 254 ++--- test/websockets/user.js | 590 +++++----- 19 files changed, 4380 insertions(+), 4168 deletions(-) diff --git a/.prettierrc b/.prettierrc index e0fd805c..e385d5fa 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,7 +3,7 @@ "semi": false, "singleQuote": true, "trailingComma": "all", - "tabWidth": 2, + "tabWidth": 4, "arrowParens": "avoid", "overrides": [ { diff --git a/src/http-client.js b/src/http-client.js index 5f4df11d..a3b8793f 100644 --- a/src/http-client.js +++ b/src/http-client.js @@ -6,35 +6,36 @@ import JSONbig from 'json-bigint' import 'isomorphic-fetch' const getEndpoint = (endpoints, path, testnet) => { + const isFutures = path.includes('/fapi') || path.includes('/futures') + const isDelivery = path.includes('/dapi') + const isPortfolioMargin = path.includes('/papi') + + if (testnet) { + if (isFutures) return 'https://demo-fapi.binance.com' + if (isDelivery) return 'https://demo-dapi.binance.com' + if (isPortfolioMargin) return 'https://demo-papi.binance.com' + return 'https://demo-api.binance.com' + } - const isFutures = path.includes('/fapi') || path.includes('/futures'); - const isDelivery = path.includes('/dapi'); - const isPortfolioMargin = path.includes('/papi'); - - if (testnet) { - if (isFutures) return "https://demo-fapi.binance.com" - if (isDelivery) return "https://demo-dapi.binance.com" - if (isPortfolioMargin) return "https://demo-papi.binance.com" - return "https://demo-api.binance.com" - } - - if (path.includes('/fapi') || path.includes('/futures')) - return endpoints.futures || 'https://fapi.binance.com' - if (path.includes('/dapi')) return endpoints.delivery || 'https://dapi.binance.com' - if (path.includes('/papi')) return endpoints.portfolioMargin || 'https://papi.binance.com' + if (path.includes('/fapi') || path.includes('/futures')) + return endpoints.futures || 'https://fapi.binance.com' + if (path.includes('/dapi')) return endpoints.delivery || 'https://dapi.binance.com' + if (path.includes('/papi')) return endpoints.portfolioMargin || 'https://papi.binance.com' - return endpoints.base || 'https://api.binance.com' + return endpoints.base || 'https://api.binance.com' } const getDomainName = url => { - const match = url.match(/^(?:https?:\/\/)?(?:www\.)?([\w-]+(\.[\w-]+)+)/) - return match ? match[1] : null + const match = url.match(/^(?:https?:\/\/)?(?:www\.)?([\w-]+(\.[\w-]+)+)/) + return match ? match[1] : null } const defaultGetTime = () => Date.now() -const uuid22 = (a) => { - return a ? (a ^ Math.random() * 16 >> a / 4).toString(16) : (([1e7]) + 1e3 + 4e3 + 8e5).replace(/[018]/g, uuid22); +const uuid22 = a => { + return a + ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) + : ([1e7] + 1e3 + 4e3 + 8e5).replace(/[018]/g, uuid22) } // Singleton holding header data like rate limits. @@ -44,94 +45,94 @@ const info = {} * Build query string for uri encoded url based on json object */ const makeQueryString = q => - q - ? `?${Object.keys(q) - .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(q[k])}`) - .join('&')}` - : '' + q + ? `?${Object.keys(q) + .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(q[k])}`) + .join('&')}` + : '' /** * Get API limits info from headers */ const headersMapping = { - 'x-mbx-used-weight-1m': 'usedWeight1m', - 'x-mbx-order-count-10s': 'orderCount10s', - 'x-mbx-order-count-1m': 'orderCount1m', - 'x-mbx-order-count-1h': 'orderCount1h', - 'x-mbx-order-count-1d': 'orderCount1d', - 'x-response-time': 'responseTime', + 'x-mbx-used-weight-1m': 'usedWeight1m', + 'x-mbx-order-count-10s': 'orderCount10s', + 'x-mbx-order-count-1m': 'orderCount1m', + 'x-mbx-order-count-1h': 'orderCount1h', + 'x-mbx-order-count-1d': 'orderCount1d', + 'x-response-time': 'responseTime', } const responseHandler = res => { - if (!res.headers || !res.url) return + if (!res.headers || !res.url) return - const domain = getDomainName(res.url) - if (!info[domain]) info[domain] = {} + const domain = getDomainName(res.url) + if (!info[domain]) info[domain] = {} - for (const key of Object.keys(headersMapping)) { - const outKey = headersMapping[key] + for (const key of Object.keys(headersMapping)) { + const outKey = headersMapping[key] - if (res.headers.has(key)) { - info[domain][outKey] = res.headers.get(key) + if (res.headers.has(key)) { + info[domain][outKey] = res.headers.get(key) + } } - } } /** * Finalize API response */ const sendResult = call => - call.then(res => { - // Get API limits info from headers - responseHandler(res) - - // If response is ok, we can safely assume it is valid JSON - if (res.ok) return res.text().then(text => JSONbig.parse(text)) - - // Errors might come from the API itself or the proxy Binance is using. - // For API errors the response will be valid JSON,but for proxy errors - // it will be HTML - return res.text().then(text => { - let error - try { - const json = JSONbig.parse(text) - // The body was JSON parseable, assume it is an API response error - error = new Error(json.msg || `${res.status} ${res.statusText}`) - error.code = json.code - error.url = res.url - } catch (e) { - // The body was not JSON parseable, assume it is proxy error - error = new Error(`${res.status} ${res.statusText} ${text}`) - error.response = res - error.responseText = text - } - throw error + call.then(res => { + // Get API limits info from headers + responseHandler(res) + + // If response is ok, we can safely assume it is valid JSON + if (res.ok) return res.text().then(text => JSONbig.parse(text)) + + // Errors might come from the API itself or the proxy Binance is using. + // For API errors the response will be valid JSON,but for proxy errors + // it will be HTML + return res.text().then(text => { + let error + try { + const json = JSONbig.parse(text) + // The body was JSON parseable, assume it is an API response error + error = new Error(json.msg || `${res.status} ${res.statusText}`) + error.code = json.code + error.url = res.url + } catch (e) { + // The body was not JSON parseable, assume it is proxy error + error = new Error(`${res.status} ${res.statusText} ${text}`) + error.response = res + error.responseText = text + } + throw error + }) }) - }) /** * Util to validate existence of required parameter(s) */ const checkParams = (name, payload, requires = []) => { - if (!payload) { - throw new Error('You need to pass a payload object.') - } - - requires.forEach(r => { - if (!payload[r] && isNaN(payload[r])) { - throw new Error(`Method ${name} requires ${r} parameter.`) + if (!payload) { + throw new Error('You need to pass a payload object.') } - }) - return true + requires.forEach(r => { + if (!payload[r] && isNaN(payload[r])) { + throw new Error(`Method ${name} requires ${r} parameter.`) + } + }) + + return true } const spotP = () => { - return `x-B3AUXNYV${uuid22()}` + return `x-B3AUXNYV${uuid22()}` } const futuresP = () => { - return `x-ftGmvgAN${uuid22()}` + return `x-ftGmvgAN${uuid22()}` } /** @@ -144,17 +145,17 @@ const futuresP = () => { * @returns {object} The api response */ const publicCall = - ({ proxy, endpoints, testnet }) => - (path, data, method = 'GET', headers = {}) => { - return sendResult( - fetch(`${getEndpoint(endpoints, path, testnet)}${path}${makeQueryString(data)}`, { - method, - json: true, - headers, - ...(proxy ? { agent: new HttpsProxyAgent(proxy) } : {}), - }), - ) - } + ({ proxy, endpoints, testnet }) => + (path, data, method = 'GET', headers = {}) => { + return sendResult( + fetch(`${getEndpoint(endpoints, path, testnet)}${path}${makeQueryString(data)}`, { + method, + json: true, + headers, + ...(proxy ? { agent: new HttpsProxyAgent(proxy) } : {}), + }), + ) + } /** * Factory method for partial private calls against the api @@ -165,17 +166,17 @@ const publicCall = * @returns {object} The api response */ const keyCall = - ({ apiKey, pubCall }) => - (path, data, method = 'GET') => { - if (!apiKey) { - throw new Error('You need to pass an API key to make this call.') + ({ apiKey, pubCall }) => + (path, data, method = 'GET') => { + if (!apiKey) { + throw new Error('You need to pass an API key to make this call.') + } + + return pubCall(path, data, method, { + 'X-MBX-APIKEY': apiKey, + }) } - return pubCall(path, data, method, { - 'X-MBX-APIKEY': apiKey, - }) - } - /** * Factory method for private calls against the api * @@ -186,68 +187,68 @@ const keyCall = * @returns {object} The api response */ const privateCall = - ({ apiKey, apiSecret, proxy, endpoints, getTime = defaultGetTime, pubCall, testnet }) => - (path, data = {}, method = 'GET', noData, noExtra) => { - if (!apiKey || !apiSecret) { - throw new Error('You need to pass an API key and secret to make authenticated calls.') + ({ apiKey, apiSecret, proxy, endpoints, getTime = defaultGetTime, pubCall, testnet }) => + (path, data = {}, method = 'GET', noData, noExtra) => { + if (!apiKey || !apiSecret) { + throw new Error('You need to pass an API key and secret to make authenticated calls.') + } + + return ( + data && data.useServerTime + ? pubCall('/api/v3/time').then(r => r.serverTime) + : Promise.resolve(getTime()) + ).then(timestamp => { + if (data) { + delete data.useServerTime + } + + const signature = crypto + .createHmac('sha256', apiSecret) + .update(makeQueryString({ ...data, timestamp }).substr(1)) + .digest('hex') + + const newData = noExtra ? data : { ...data, timestamp, signature } + + return sendResult( + fetch( + `${getEndpoint(endpoints, path, testnet)}${path}${noData ? '' : makeQueryString(newData)}`, + { + method, + headers: { 'X-MBX-APIKEY': apiKey }, + json: true, + ...(proxy ? { agent: new HttpsProxyAgent(proxy) } : {}), + }, + ), + ) + }) } - return ( - data && data.useServerTime - ? pubCall('/api/v3/time').then(r => r.serverTime) - : Promise.resolve(getTime()) - ).then(timestamp => { - if (data) { - delete data.useServerTime - } - - const signature = crypto - .createHmac('sha256', apiSecret) - .update(makeQueryString({ ...data, timestamp }).substr(1)) - .digest('hex') - - const newData = noExtra ? data : { ...data, timestamp, signature } - - return sendResult( - fetch( - `${getEndpoint(endpoints, path, testnet)}${path}${noData ? '' : makeQueryString(newData)}`, - { - method, - headers: { 'X-MBX-APIKEY': apiKey }, - json: true, - ...(proxy ? { agent: new HttpsProxyAgent(proxy) } : {}), - }, - ), - ) - }) - } - export const candleFields = [ - 'openTime', - 'open', - 'high', - 'low', - 'close', - 'volume', - 'closeTime', - 'quoteVolume', - 'trades', - 'baseAssetVolume', - 'quoteAssetVolume', + 'openTime', + 'open', + 'high', + 'low', + 'close', + 'volume', + 'closeTime', + 'quoteVolume', + 'trades', + 'baseAssetVolume', + 'quoteAssetVolume', ] export const deliveryCandleFields = [ - 'openTime', - 'open', - 'high', - 'low', - 'close', - 'volume', - 'closeTime', - 'baseVolume', - 'trades', - 'quoteAssetVolume', - 'baseAssetVolume', + 'openTime', + 'open', + 'high', + 'low', + 'close', + 'volume', + 'closeTime', + 'baseVolume', + 'trades', + 'quoteAssetVolume', + 'baseAssetVolume', ] /** @@ -255,421 +256,458 @@ export const deliveryCandleFields = [ * to a user friendly collection. */ const candles = (pubCall, payload, endpoint = '/api/v3/klines') => - checkParams('candles', payload, endpoint.includes('indexPrice') ? ['pair'] : ['symbol']) && - pubCall(endpoint, { interval: '5m', ...payload }).then(candles => - candles.map(candle => - zip(!endpoint.includes('dapi') ? candleFields : deliveryCandleFields, candle), - ), - ) - -const isContractURL = (path) => { - const isFutures = path.includes('/fapi') || path.includes('/futures'); - const isDelivery = path.includes('/dapi'); - const isPortfolioMargin = path.includes('/papi'); - return isFutures || isDelivery || isPortfolioMargin; + checkParams('candles', payload, endpoint.includes('indexPrice') ? ['pair'] : ['symbol']) && + pubCall(endpoint, { interval: '5m', ...payload }).then(candles => + candles.map(candle => + zip(!endpoint.includes('dapi') ? candleFields : deliveryCandleFields, candle), + ), + ) + +const isContractURL = path => { + const isFutures = path.includes('/fapi') || path.includes('/futures') + const isDelivery = path.includes('/dapi') + const isPortfolioMargin = path.includes('/papi') + return isFutures || isDelivery || isPortfolioMargin } /** * Create a new order wrapper for market order simplicity */ const order = (privCall, payload = {}, url) => { - const newPayload = - ['LIMIT', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT_LIMIT'].includes(payload.type) || !payload.type - ? { timeInForce: 'GTC', ...payload } - : payload - - const requires = ['symbol', 'side'] - - if ( - !(newPayload.type === 'MARKET' && newPayload.quoteOrderQty) && - !(newPayload.type === 'STOP_MARKET') && - !(newPayload.type === 'TAKE_PROFIT_MARKET') && - !(newPayload.type === 'TRAILING_STOP_MARKET') - ) { - requires.push('quantity') - } - - if (newPayload.type === 'TRAILING_STOP_MARKET') { - requires.push('callbackRate') - } - - if (!newPayload.newClientOrderId) { - // check if this is spot or futures/delivery - const isContract = isContractURL(url); - if (isContract) { - newPayload.newClientOrderId = futuresP(); - } else { - newPayload.newClientOrderId = spotP(); - } - } - - return ( - checkParams('order', newPayload, requires) && - privCall(url, { type: 'LIMIT', ...newPayload }, 'POST') - ) + const newPayload = + ['LIMIT', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT_LIMIT'].includes(payload.type) || !payload.type + ? { timeInForce: 'GTC', ...payload } + : payload + + const requires = ['symbol', 'side'] + + if ( + !(newPayload.type === 'MARKET' && newPayload.quoteOrderQty) && + !(newPayload.type === 'STOP_MARKET') && + !(newPayload.type === 'TAKE_PROFIT_MARKET') && + !(newPayload.type === 'TRAILING_STOP_MARKET') + ) { + requires.push('quantity') + } + + if (newPayload.type === 'TRAILING_STOP_MARKET') { + requires.push('callbackRate') + } + + if (!newPayload.newClientOrderId) { + // check if this is spot or futures/delivery + const isContract = isContractURL(url) + if (isContract) { + newPayload.newClientOrderId = futuresP() + } else { + newPayload.newClientOrderId = spotP() + } + } + + return ( + checkParams('order', newPayload, requires) && + privCall(url, { type: 'LIMIT', ...newPayload }, 'POST') + ) } const orderOco = (privCall, payload = {}, url) => { - const newPayload = - payload.stopLimitPrice && !payload.stopLimitTimeInForce - ? { stopLimitTimeInForce: 'GTC', ...payload } - : payload + const newPayload = + payload.stopLimitPrice && !payload.stopLimitTimeInForce + ? { stopLimitTimeInForce: 'GTC', ...payload } + : payload if (!newPayload.listClientOrderId) { - newPayload.listClientOrderId = spotP(); + newPayload.listClientOrderId = spotP() } - return ( - checkParams('order', newPayload, ['symbol', 'side', 'quantity', 'price', 'stopPrice']) && - privCall(url, newPayload, 'POST') - ) + return ( + checkParams('order', newPayload, ['symbol', 'side', 'quantity', 'price', 'stopPrice']) && + privCall(url, newPayload, 'POST') + ) } /** * Zip asks and bids reponse from order book */ const book = (pubCall, payload, endpoint = '/api/v3/depth') => - checkParams('book', payload, ['symbol']) && - pubCall(endpoint, payload).then(({ lastUpdateId, asks, bids }) => ({ - lastUpdateId, - asks: asks.map(a => zip(['price', 'quantity'], a)), - bids: bids.map(b => zip(['price', 'quantity'], b)), - })) + checkParams('book', payload, ['symbol']) && + pubCall(endpoint, payload).then(({ lastUpdateId, asks, bids }) => ({ + lastUpdateId, + asks: asks.map(a => zip(['price', 'quantity'], a)), + bids: bids.map(b => zip(['price', 'quantity'], b)), + })) const aggTrades = (pubCall, payload, endpoint = '/api/v3/aggTrades') => - checkParams('aggTrades', payload, ['symbol']) && - pubCall(endpoint, payload).then(trades => - trades.map(trade => { - const transformed = { - aggId: trade.a, - symbol: payload.symbol, - price: trade.p, - quantity: trade.q, - firstId: trade.f, - lastId: trade.l, - timestamp: trade.T, - isBuyerMaker: trade.m, - } - if (trade.M) transformed.wasBestPrice = trade.M - - return transformed - }), - ) + checkParams('aggTrades', payload, ['symbol']) && + pubCall(endpoint, payload).then(trades => + trades.map(trade => { + const transformed = { + aggId: trade.a, + symbol: payload.symbol, + price: trade.p, + quantity: trade.q, + firstId: trade.f, + lastId: trade.l, + timestamp: trade.T, + isBuyerMaker: trade.m, + } + if (trade.M) transformed.wasBestPrice = trade.M + + return transformed + }), + ) export default opts => { - const endpoints = { - base: opts && opts.httpBase, - futures: opts && opts.httpFutures, - delivery: opts && opts.httpDelivery, - portfolioMargin: opts && opts.httpPortfolioMargin, - } - - const pubCall = publicCall({ ...opts, endpoints }) - const deliveryPubCall = publicCall({ - ...opts, - endpoints: { futures: endpoints.delivery }, - }) - const privCall = privateCall({ ...opts, endpoints, pubCall }) - const kCall = keyCall({ ...opts, pubCall }) - - return { - // Generic endpoints - getInfo: () => info, - ping: () => pubCall('/api/v3/ping').then(() => true), - time: () => pubCall('/api/v3/time').then(r => r.serverTime), - exchangeInfo: payload => pubCall('/api/v3/exchangeInfo', payload), - - // Market Data endpoints - book: payload => book(pubCall, payload), - aggTrades: payload => aggTrades(pubCall, payload), - candles: payload => candles(pubCall, payload), - trades: payload => - checkParams('trades', payload, ['symbol']) && pubCall('/api/v3/trades', payload), - tradesHistory: payload => - checkParams('tradesHitory', payload, ['symbol']) && - kCall('/api/v3/historicalTrades', payload), - dailyStats: payload => pubCall('/api/v3/ticker/24hr', payload), - prices: payload => - pubCall('/api/v3/ticker/price', payload).then(r => - (Array.isArray(r) ? r : [r]).reduce((out, cur) => ((out[cur.symbol] = cur.price), out), {}), - ), - avgPrice: payload => pubCall('/api/v3/avgPrice', payload), - allBookTickers: () => - pubCall('/api/v3/ticker/bookTicker').then(r => - (Array.isArray(r) ? r : [r]).reduce((out, cur) => ((out[cur.symbol] = cur), out), {}), - ), - - // Order endpoints - order: payload => order(privCall, payload, '/api/v3/order'), - orderOco: payload => orderOco(privCall, payload, '/api/v3/order/oco'), - orderTest: payload => order(privCall, payload, '/api/v3/order/test'), - getOrder: payload => privCall('/api/v3/order', payload), - getOrderOco: payload => privCall('/api/v3/orderList', payload), - cancelOrder: payload => privCall('/api/v3/order', payload, 'DELETE'), - cancelOrderOco: payload => privCall('/api/v3/orderList', payload, 'DELETE'), - cancelOpenOrders: payload => privCall('/api/v3/openOrders', payload, 'DELETE'), - openOrders: payload => privCall('/api/v3/openOrders', payload), - allOrders: payload => privCall('/api/v3/allOrders', payload), - allOrdersOCO: payload => privCall('/api/v3/allOrderList', payload), - - // Account endpoints - accountInfo: payload => privCall('/api/v3/account', payload), - myTrades: payload => privCall('/api/v3/myTrades', payload), - withdraw: payload => privCall('/sapi/v1/capital/withdraw/apply', payload, 'POST'), - withdrawHistory: payload => privCall('/sapi/v1/capital/withdraw/history', payload), - depositHistory: payload => privCall('/sapi/v1/capital/deposit/hisrec', payload), - depositAddress: payload => privCall('/sapi/v1/capital/deposit/address', payload), - tradeFee: payload => privCall('/sapi/v1/asset/tradeFee', payload), - assetDetail: payload => privCall('/sapi/v1/asset/assetDetail', payload), - accountSnapshot: payload => privCall('/sapi/v1/accountSnapshot', payload), - universalTransfer: payload => privCall('/sapi/v1/asset/transfer', payload, 'POST'), - universalTransferHistory: payload => privCall('/sapi/v1/asset/transfer', payload), - dustLog: payload => privCall('/sapi/v1/asset/dribblet', payload), - dustTransfer: payload => privCall('/sapi/v1/asset/dust', payload, 'POST'), - accountCoins: payload => privCall('/sapi/v1/capital/config/getall', payload), - getBnbBurn: payload => privCall('/sapi/v1/bnbBurn', payload), - setBnbBurn: payload => privCall('/sapi/v1/bnbBurn', payload, 'POST'), - capitalConfigs: () => privCall('/sapi/v1/capital/config/getall'), - - // User Data Stream endpoints - getDataStream: () => privCall('/api/v3/userDataStream', null, 'POST', true), - keepDataStream: payload => privCall('/api/v3/userDataStream', payload, 'PUT', false, true), - closeDataStream: payload => privCall('/api/v3/userDataStream', payload, 'DELETE', false, true), - marginGetDataStream: () => privCall('/sapi/v1/userDataStream', null, 'POST', true), - marginKeepDataStream: payload => - privCall('/sapi/v1/userDataStream', payload, 'PUT', false, true), - marginCloseDataStream: payload => - privCall('/sapi/v1/userDataStream', payload, 'DELETE', false, true), - futuresGetDataStream: () => privCall('/fapi/v1/listenKey', null, 'POST', true), - futuresKeepDataStream: payload => privCall('/fapi/v1/listenKey', payload, 'PUT', false, true), - futuresCloseDataStream: payload => - privCall('/fapi/v1/listenKey', payload, 'DELETE', false, true), - deliveryGetDataStream: () => privCall('/dapi/v1/listenKey', null, 'POST', true), - deliveryKeepDataStream: payload => privCall('/dapi/v1/listenKey', payload, 'PUT', false, true), - deliveryCloseDataStream: payload => - privCall('/dapi/v1/listenKey', payload, 'DELETE', false, true), - - // Futures endpoints - futuresPing: () => pubCall('/fapi/v1/ping').then(() => true), - futuresTime: () => pubCall('/fapi/v1/time').then(r => r.serverTime), - futuresExchangeInfo: () => pubCall('/fapi/v1/exchangeInfo'), - futuresBook: payload => book(pubCall, payload, '/fapi/v1/depth'), - futuresAggTrades: payload => aggTrades(pubCall, payload, '/fapi/v1/aggTrades'), - futuresMarkPrice: payload => pubCall('/fapi/v1/premiumIndex', payload), - futuresAllForceOrders: payload => pubCall('/fapi/v1/allForceOrders', payload), - futuresLongShortRatio: payload => pubCall('/futures/data/globalLongShortAccountRatio', payload), - futuresCandles: payload => candles(pubCall, payload, '/fapi/v1/klines'), - futuresMarkPriceCandles: payload => candles(pubCall, payload, '/fapi/v1/markPriceKlines'), - futuresIndexPriceCandles: payload => candles(pubCall, payload, '/fapi/v1/indexPriceKlines'), - futuresTrades: payload => - checkParams('trades', payload, ['symbol']) && pubCall('/fapi/v1/trades', payload), - futuresDailyStats: payload => pubCall('/fapi/v1/ticker/24hr', payload), - futuresPrices: payload => - pubCall('/fapi/v1/ticker/price', payload).then(r => - (Array.isArray(r) ? r : [r]).reduce((out, cur) => ((out[cur.symbol] = cur.price), out), {}), - ), - futuresAllBookTickers: () => - pubCall('/fapi/v1/ticker/bookTicker').then(r => - (Array.isArray(r) ? r : [r]).reduce((out, cur) => ((out[cur.symbol] = cur), out), {}), - ), - futuresFundingRate: payload => - checkParams('fundingRate', payload, ['symbol']) && pubCall('/fapi/v1/fundingRate', payload), - futuresOrder: payload => order(privCall, payload, '/fapi/v1/order'), - futuresBatchOrders: payload => privCall('/fapi/v1/batchOrders', payload, 'POST'), - futuresGetOrder: payload => privCall('/fapi/v1/order', payload), - futuresCancelOrder: payload => privCall('/fapi/v1/order', payload, 'DELETE'), - futuresCancelAllOpenOrders: payload => privCall('/fapi/v1/allOpenOrders', payload, 'DELETE'), - futuresCancelBatchOrders: payload => privCall('/fapi/v1/batchOrders', payload, 'DELETE'), - futuresOpenOrders: payload => privCall('/fapi/v1/openOrders', payload), - futuresAllOrders: payload => privCall('/fapi/v1/allOrders', payload), - futuresPositionRisk: payload => privCall('/fapi/v2/positionRisk', payload), - futuresLeverageBracket: payload => privCall('/fapi/v1/leverageBracket', payload), - futuresAccountBalance: payload => privCall('/fapi/v2/balance', payload), - futuresAccountInfo: payload => privCall('/fapi/v2/account', payload), - futuresUserTrades: payload => privCall('/fapi/v1/userTrades', payload), - futuresPositionMode: payload => privCall('/fapi/v1/positionSide/dual', payload), - futuresPositionModeChange: payload => privCall('/fapi/v1/positionSide/dual', payload, 'POST'), - futuresLeverage: payload => privCall('/fapi/v1/leverage', payload, 'POST'), - futuresMarginType: payload => privCall('/fapi/v1/marginType', payload, 'POST'), - futuresPositionMargin: payload => privCall('/fapi/v1/positionMargin', payload, 'POST'), - futuresMarginHistory: payload => privCall('/fapi/v1/positionMargin/history', payload), - futuresIncome: payload => privCall('/fapi/v1/income', payload), - getMultiAssetsMargin: payload => privCall('/fapi/v1/multiAssetsMargin', payload), - setMultiAssetsMargin: payload => privCall('/fapi/v1/multiAssetsMargin', payload, 'POST'), - - // Delivery endpoints - deliveryPing: () => pubCall('/dapi/v1/ping').then(() => true), - deliveryTime: () => pubCall('/dapi/v1/time').then(r => r.serverTime), - deliveryExchangeInfo: () => pubCall('/dapi/v1/exchangeInfo'), - deliveryBook: payload => book(pubCall, payload, '/dapi/v1/depth'), - deliveryAggTrades: payload => aggTrades(pubCall, payload, '/dapi/v1/aggTrades'), - deliveryMarkPrice: payload => pubCall('/dapi/v1/premiumIndex', payload), - deliveryAllForceOrders: payload => pubCall('/dapi/v1/allForceOrders', payload), - deliveryLongShortRatio: payload => - deliveryPubCall('/futures/data/globalLongShortAccountRatio', payload), - deliveryCandles: payload => candles(pubCall, payload, '/dapi/v1/klines'), - deliveryMarkPriceCandles: payload => candles(pubCall, payload, '/dapi/v1/markPriceKlines'), - deliveryIndexPriceCandles: payload => candles(pubCall, payload, '/dapi/v1/indexPriceKlines'), - deliveryTrades: payload => - checkParams('trades', payload, ['symbol']) && pubCall('/dapi/v1/trades', payload), - deliveryDailyStats: payload => pubCall('/dapi/v1/ticker/24hr', payload), - deliveryPrices: () => - pubCall('/dapi/v1/ticker/price').then(r => - (Array.isArray(r) ? r : [r]).reduce((out, cur) => ((out[cur.symbol] = cur.price), out), {}), - ), - deliveryAllBookTickers: () => - pubCall('/dapi/v1/ticker/bookTicker').then(r => - (Array.isArray(r) ? r : [r]).reduce((out, cur) => ((out[cur.symbol] = cur), out), {}), - ), - deliveryFundingRate: payload => - checkParams('fundingRate', payload, ['symbol']) && pubCall('/dapi/v1/fundingRate', payload), - deliveryOrder: payload => order(privCall, payload, '/dapi/v1/order'), - deliveryBatchOrders: payload => privCall('/dapi/v1/batchOrders', payload, 'POST'), - deliveryGetOrder: payload => privCall('/dapi/v1/order', payload), - deliveryCancelOrder: payload => privCall('/dapi/v1/order', payload, 'DELETE'), - deliveryCancelAllOpenOrders: payload => privCall('/dapi/v1/allOpenOrders', payload, 'DELETE'), - deliveryCancelBatchOrders: payload => privCall('/dapi/v1/batchOrders', payload, 'DELETE'), - deliveryOpenOrders: payload => privCall('/dapi/v1/openOrders', payload), - deliveryAllOrders: payload => privCall('/dapi/v1/allOrders', payload), - deliveryPositionRisk: payload => privCall('/dapi/v1/positionRisk', payload), - deliveryLeverageBracket: payload => privCall('/dapi/v1/leverageBracket', payload), - deliveryAccountBalance: payload => privCall('/dapi/v1/balance', payload), - deliveryAccountInfo: payload => privCall('/dapi/v1/account', payload), - deliveryUserTrades: payload => privCall('/dapi/v1/userTrades', payload), - deliveryPositionMode: payload => privCall('/dapi/v1/positionSide/dual', payload), - deliveryPositionModeChange: payload => privCall('/dapi/v1/positionSide/dual', payload, 'POST'), - deliveryLeverage: payload => privCall('/dapi/v1/leverage', payload, 'POST'), - deliveryMarginType: payload => privCall('/dapi/v1/marginType', payload, 'POST'), - deliveryPositionMargin: payload => privCall('/dapi/v1/positionMargin', payload, 'POST'), - deliveryMarginHistory: payload => privCall('/dapi/v1/positionMargin/history', payload), - deliveryIncome: payload => privCall('/dapi/v1/income', payload), - - // PAPI endpoints - papiPing: () => privCall('/papi/v1/ping'), - papiAccount: () => privCall('/papi/v1/account'), - papiBalance: payload => privCall('/papi/v1/balance', payload), - papiUmOrder: payload => privCall('/papi/v1/um/order', payload), - papiUmConditionalOrder: payload => privCall('/papi/v1/um/conditional/order', payload, 'POST'), - papiCmOrder: payload => privCall('/papi/v1/cm/order', payload, 'POST'), - papiCmConditionalOrder: payload => privCall('/papi/v1/cm/conditional/order', payload, 'POST'), - papiMarginOrder: payload => privCall('/papi/v1/margin/order', payload, 'POST'), - papiMarginLoan: payload => privCall('/papi/v1/marginLoan', payload, 'POST'), - papiRepayLoan: payload => privCall('/papi/v1/repayLoan', payload, 'POST'), - papiMarginOrderOco: payload => privCall('/papi/v1/margin/order/oco', payload, 'POST'), - papiUmCancelOrder: payload => privCall('/papi/v1/um/order', payload, 'DELETE'), - papiUmCancelAllOpenOrders: payload => privCall('/papi/v1/um/allOpenOrders', payload, 'DELETE'), - papiUmCancelConditionalOrder: payload => - privCall('/papi/v1/um/conditional/order', payload, 'DELETE'), - papiUmCancelConditionalAllOpenOrders: payload => - privCall('/papi/v1/um/conditional/allOpenOrders', payload, 'DELETE'), - papiCmCancelOrder: payload => privCall('/papi/v1/cm/order', payload, 'DELETE'), - papiCmCancelAllOpenOrders: payload => privCall('/papi/v1/cm/allOpenOrders', payload, 'DELETE'), - papiCmCancelConditionalOrder: payload => - privCall('/papi/v1/cm/conditional/order', payload, 'DELETE'), - papiCmCancelConditionalAllOpenOrders: payload => - privCall('/papi/v1/cm/conditional/allOpenOrders', payload, 'DELETE'), - papiMarginCancelOrder: payload => privCall('/papi/v1/margin/order', payload, 'DELETE'), - papiMarginCancelOrderList: payload => privCall('/papi/v1/margin/orderList', payload, 'DELETE'), - papiMarginCancelAllOpenOrders: payload => - privCall('/papi/v1/margin/allOpenOrders', payload, 'DELETE'), - papiUmUpdateOrder: payload => privCall('/papi/v1/um/order', payload, 'PUT'), - papiCmUpdateOrder: payload => privCall('/papi/v1/cm/order', payload, 'PUT'), - papiUmGetOrder: payload => privCall('/papi/v1/um/order', payload), - papiUmGetAllOrders: payload => privCall('/papi/v1/um/allOrders', payload), - papiUmGetOpenOrder: payload => privCall('/papi/v1/um/openOrder', payload), - papiUmGetOpenOrders: payload => privCall('/papi/v1/um/openOrders', payload), - papiUmGetConditionalAllOrders: payload => - privCall('/papi/v1/um/conditional/allOrders', payload), - papiUmGetConditionalOpenOrders: payload => - privCall('/papi/v1/um/conditional/openOrders', payload), - papiUmGetConditionalOpenOrder: payload => - privCall('/papi/v1/um/conditional/openOrder', payload), - papiUmGetConditionalOrderHistory: payload => - privCall('/papi/v1/um/conditional/orderHistory', payload), - papiCmGetOrder: payload => privCall('/papi/v1/cm/order', payload), - papiCmGetAllOrders: payload => privCall('/papi/v1/cm/allOrders', payload), - papiCmGetOpenOrder: payload => privCall('/papi/v1/cm/openOrder', payload), - papiCmGetOpenOrders: payload => privCall('/papi/v1/cm/openOrders', payload), - papiCmGetConditionalOpenOrders: payload => - privCall('/papi/v1/cm/conditional/openOrders', payload), - papiCmGetConditionalOpenOrder: payload => - privCall('/papi/v1/cm/conditional/openOrder', payload), - papiCmGetConditionalAllOrders: payload => - privCall('/papi/v1/cm/conditional/allOrders', payload), - papiCmGetConditionalOrderHistory: payload => - privCall('/papi/v1/cm/conditional/orderHistory', payload), - papiUmGetForceOrders: payload => privCall('/papi/v1/um/forceOrders', payload), - papiCmGetForceOrders: payload => privCall('/papi/v1/cm/forceOrders', payload), - papiUmGetOrderAmendment: payload => privCall('/papi/v1/um/orderAmendment', payload), - papiCmGetOrderAmendment: payload => privCall('/papi/v1/cm/orderAmendment', payload), - papiMarginGetForceOrders: payload => privCall('/papi/v1/margin/forceOrders', payload), - papiUmGetUserTrades: payload => privCall('/papi/v1/um/userTrades', payload), - papiCmGetUserTrades: payload => privCall('/papi/v1/cm/userTrades', payload), - papiUmGetAdlQuantile: payload => privCall('/papi/v1/um/adlQuantile', payload), - papiCmGetAdlQuantile: payload => privCall('/papi/v1/cm/adlQuantile', payload), - papiUmFeeBurn: payload => privCall('/papi/v1/um/feeBurn', payload, 'POST'), - papiUmGetFeeBurn: payload => privCall('/papi/v1/um/feeBurn', payload), - papiMarginGetOrder: payload => privCall('/papi/v1/margin/order', payload), - papiMarginGetOpenOrders: payload => privCall('/papi/v1/margin/openOrders', payload), - papiMarginGetAllOrders: payload => privCall('/papi/v1/margin/allOrders', payload), - papiMarginGetOrderList: payload => privCall('/papi/v1/margin/orderList', payload), - papiMarginGetAllOrderList: payload => privCall('/papi/v1/margin/allOrderList', payload), - papiMarginGetOpenOrderList: payload => privCall('/papi/v1/margin/openOrderList', payload), - papiMarginGetMyTrades: payload => privCall('/papi/v1/margin/myTrades', payload), - papiMarginRepayDebt: payload => privCall('/papi/v1/margin/repay-debt', payload, 'POST'), - - // Margin endpoints - marginAllOrders: payload => privCall('/sapi/v1/margin/allOrders', payload), - marginOrder: payload => order(privCall, payload, '/sapi/v1/margin/order'), - marginOrderOco: payload => orderOco(privCall, payload, '/sapi/v1/margin/order/oco'), - marginGetOrder: payload => privCall('/sapi/v1/margin/order', payload), - marginGetOrderOco: payload => privCall('/sapi/v1/margin/orderList', payload), - marginCancelOrder: payload => privCall('/sapi/v1/margin/order', payload, 'DELETE'), - marginOpenOrders: payload => privCall('/sapi/v1/margin/openOrders', payload), - marginCancelOpenOrders: payload => privCall('/sapi/v1/margin/openOrders', payload, 'DELETE'), - marginAccountInfo: payload => privCall('/sapi/v1/margin/account', payload), - marginMyTrades: payload => privCall('/sapi/v1/margin/myTrades', payload), - marginRepay: payload => privCall('/sapi/v1/margin/repay', payload, 'POST'), - marginLoan: payload => privCall('/sapi/v1/margin/loan', payload, 'POST'), - marginIsolatedAccount: payload => privCall('/sapi/v1/margin/isolated/account', payload), - marginMaxBorrow: payload => privCall('/sapi/v1/margin/maxBorrowable', payload), - marginCreateIsolated: payload => privCall('/sapi/v1/margin/isolated/create', payload, 'POST'), - marginIsolatedTransfer: payload => - privCall('/sapi/v1/margin/isolated/transfer', payload, 'POST'), - marginIsolatedTransferHistory: payload => - privCall('/sapi/v1/margin/isolated/transfer', payload), - disableMarginAccount: payload => - privCall('/sapi/v1/margin/isolated/account', payload, 'DELETE'), - enableMarginAccount: payload => privCall('/sapi/v1/margin/isolated/account', payload, 'POST'), - marginAccount: () => privCall('/sapi/v1/margin/account'), - - // Portfolio Margin endpoints - portfolioMarginAccountInfo: () => privCall('/sapi/v1/portfolio/account'), - portfolioMarginCollateralRate: () => privCall('/sapi/v1/portfolio/collateralRate'), - portfolioMarginLoan: payload => privCall('/sapi/v1/portfolio/pmLoan', payload), - portfolioMarginLoanRepay: payload => privCall('/sapi/v1/portfolio/repay', payload, 'POST'), - portfolioMarginInterestHistory: payload => - privCall('/sapi/v1/portfolio/interest-history', payload), - - // Savings endpoints - savingsAccount: payload => privCall('/sapi/v1/lending/union/account', payload), - savingsPurchase: payload => privCall('/sapi/v1/lending/union/purchase', payload, 'POST'), - savingsRedeem: payload => privCall('/sapi/v1/lending/union/redeem', payload, 'POST'), - fundingWallet: payload => privCall('/sapi/v1/asset/get-funding-asset', payload, 'POST'), - convertTradeFlow: payload => privCall('/sapi/v1/convert/tradeFlow', payload), - rebateTaxQuery: () => privCall('/sapi/v1/rebate/taxQuery'), - payTradeHistory: payload => privCall('/sapi/v1/pay/transactions', payload), - apiRestrictions: payload => privCall('/sapi/v1/account/apiRestrictions', payload), - - // Mining endpoints - miningHashrateResaleRequest: payload => - privCall('/sapi/v1/mining/hash-transfer/config', payload, 'POST'), - miningHashrateResaleCancel: payload => - privCall('/sapi/v1/mining/hash-transfer/config/cancel', payload, 'POST'), - miningStatistics: payload => privCall('/sapi/v1/mining/statistics/user/status', payload), - - // Utility endpoints - privateRequest: (method, url, payload) => privCall(url, payload, method), - publicRequest: (method, url, payload) => pubCall(url, payload, method), - } + const endpoints = { + base: opts && opts.httpBase, + futures: opts && opts.httpFutures, + delivery: opts && opts.httpDelivery, + portfolioMargin: opts && opts.httpPortfolioMargin, + } + + const pubCall = publicCall({ ...opts, endpoints }) + const deliveryPubCall = publicCall({ + ...opts, + endpoints: { futures: endpoints.delivery }, + }) + const privCall = privateCall({ ...opts, endpoints, pubCall }) + const kCall = keyCall({ ...opts, pubCall }) + + return { + // Generic endpoints + getInfo: () => info, + ping: () => pubCall('/api/v3/ping').then(() => true), + time: () => pubCall('/api/v3/time').then(r => r.serverTime), + exchangeInfo: payload => pubCall('/api/v3/exchangeInfo', payload), + + // Market Data endpoints + book: payload => book(pubCall, payload), + aggTrades: payload => aggTrades(pubCall, payload), + candles: payload => candles(pubCall, payload), + trades: payload => + checkParams('trades', payload, ['symbol']) && pubCall('/api/v3/trades', payload), + tradesHistory: payload => + checkParams('tradesHitory', payload, ['symbol']) && + kCall('/api/v3/historicalTrades', payload), + dailyStats: payload => pubCall('/api/v3/ticker/24hr', payload), + prices: payload => + pubCall('/api/v3/ticker/price', payload).then(r => + (Array.isArray(r) ? r : [r]).reduce( + (out, cur) => ((out[cur.symbol] = cur.price), out), + {}, + ), + ), + avgPrice: payload => pubCall('/api/v3/avgPrice', payload), + allBookTickers: () => + pubCall('/api/v3/ticker/bookTicker').then(r => + (Array.isArray(r) ? r : [r]).reduce( + (out, cur) => ((out[cur.symbol] = cur), out), + {}, + ), + ), + + // Order endpoints + order: payload => order(privCall, payload, '/api/v3/order'), + orderOco: payload => orderOco(privCall, payload, '/api/v3/order/oco'), + orderTest: payload => order(privCall, payload, '/api/v3/order/test'), + getOrder: payload => privCall('/api/v3/order', payload), + getOrderOco: payload => privCall('/api/v3/orderList', payload), + cancelOrder: payload => privCall('/api/v3/order', payload, 'DELETE'), + cancelOrderOco: payload => privCall('/api/v3/orderList', payload, 'DELETE'), + cancelOpenOrders: payload => privCall('/api/v3/openOrders', payload, 'DELETE'), + openOrders: payload => privCall('/api/v3/openOrders', payload), + allOrders: payload => privCall('/api/v3/allOrders', payload), + allOrdersOCO: payload => privCall('/api/v3/allOrderList', payload), + + // Account endpoints + accountInfo: payload => privCall('/api/v3/account', payload), + myTrades: payload => privCall('/api/v3/myTrades', payload), + withdraw: payload => privCall('/sapi/v1/capital/withdraw/apply', payload, 'POST'), + withdrawHistory: payload => privCall('/sapi/v1/capital/withdraw/history', payload), + depositHistory: payload => privCall('/sapi/v1/capital/deposit/hisrec', payload), + depositAddress: payload => privCall('/sapi/v1/capital/deposit/address', payload), + tradeFee: payload => privCall('/sapi/v1/asset/tradeFee', payload), + assetDetail: payload => privCall('/sapi/v1/asset/assetDetail', payload), + accountSnapshot: payload => privCall('/sapi/v1/accountSnapshot', payload), + universalTransfer: payload => privCall('/sapi/v1/asset/transfer', payload, 'POST'), + universalTransferHistory: payload => privCall('/sapi/v1/asset/transfer', payload), + dustLog: payload => privCall('/sapi/v1/asset/dribblet', payload), + dustTransfer: payload => privCall('/sapi/v1/asset/dust', payload, 'POST'), + accountCoins: payload => privCall('/sapi/v1/capital/config/getall', payload), + getBnbBurn: payload => privCall('/sapi/v1/bnbBurn', payload), + setBnbBurn: payload => privCall('/sapi/v1/bnbBurn', payload, 'POST'), + capitalConfigs: () => privCall('/sapi/v1/capital/config/getall'), + + // User Data Stream endpoints + getDataStream: () => privCall('/api/v3/userDataStream', null, 'POST', true), + keepDataStream: payload => privCall('/api/v3/userDataStream', payload, 'PUT', false, true), + closeDataStream: payload => + privCall('/api/v3/userDataStream', payload, 'DELETE', false, true), + marginGetDataStream: () => privCall('/sapi/v1/userDataStream', null, 'POST', true), + marginKeepDataStream: payload => + privCall('/sapi/v1/userDataStream', payload, 'PUT', false, true), + marginCloseDataStream: payload => + privCall('/sapi/v1/userDataStream', payload, 'DELETE', false, true), + futuresGetDataStream: () => privCall('/fapi/v1/listenKey', null, 'POST', true), + futuresKeepDataStream: payload => + privCall('/fapi/v1/listenKey', payload, 'PUT', false, true), + futuresCloseDataStream: payload => + privCall('/fapi/v1/listenKey', payload, 'DELETE', false, true), + deliveryGetDataStream: () => privCall('/dapi/v1/listenKey', null, 'POST', true), + deliveryKeepDataStream: payload => + privCall('/dapi/v1/listenKey', payload, 'PUT', false, true), + deliveryCloseDataStream: payload => + privCall('/dapi/v1/listenKey', payload, 'DELETE', false, true), + + // Futures endpoints + futuresPing: () => pubCall('/fapi/v1/ping').then(() => true), + futuresTime: () => pubCall('/fapi/v1/time').then(r => r.serverTime), + futuresExchangeInfo: () => pubCall('/fapi/v1/exchangeInfo'), + futuresBook: payload => book(pubCall, payload, '/fapi/v1/depth'), + futuresAggTrades: payload => aggTrades(pubCall, payload, '/fapi/v1/aggTrades'), + futuresMarkPrice: payload => pubCall('/fapi/v1/premiumIndex', payload), + futuresAllForceOrders: payload => pubCall('/fapi/v1/allForceOrders', payload), + futuresLongShortRatio: payload => + pubCall('/futures/data/globalLongShortAccountRatio', payload), + futuresCandles: payload => candles(pubCall, payload, '/fapi/v1/klines'), + futuresMarkPriceCandles: payload => candles(pubCall, payload, '/fapi/v1/markPriceKlines'), + futuresIndexPriceCandles: payload => candles(pubCall, payload, '/fapi/v1/indexPriceKlines'), + futuresTrades: payload => + checkParams('trades', payload, ['symbol']) && pubCall('/fapi/v1/trades', payload), + futuresDailyStats: payload => pubCall('/fapi/v1/ticker/24hr', payload), + futuresPrices: payload => + pubCall('/fapi/v1/ticker/price', payload).then(r => + (Array.isArray(r) ? r : [r]).reduce( + (out, cur) => ((out[cur.symbol] = cur.price), out), + {}, + ), + ), + futuresAllBookTickers: () => + pubCall('/fapi/v1/ticker/bookTicker').then(r => + (Array.isArray(r) ? r : [r]).reduce( + (out, cur) => ((out[cur.symbol] = cur), out), + {}, + ), + ), + futuresFundingRate: payload => + checkParams('fundingRate', payload, ['symbol']) && + pubCall('/fapi/v1/fundingRate', payload), + futuresOrder: payload => order(privCall, payload, '/fapi/v1/order'), + futuresBatchOrders: payload => privCall('/fapi/v1/batchOrders', payload, 'POST'), + futuresGetOrder: payload => privCall('/fapi/v1/order', payload), + futuresCancelOrder: payload => privCall('/fapi/v1/order', payload, 'DELETE'), + futuresCancelAllOpenOrders: payload => + privCall('/fapi/v1/allOpenOrders', payload, 'DELETE'), + futuresCancelBatchOrders: payload => privCall('/fapi/v1/batchOrders', payload, 'DELETE'), + futuresOpenOrders: payload => privCall('/fapi/v1/openOrders', payload), + futuresAllOrders: payload => privCall('/fapi/v1/allOrders', payload), + futuresPositionRisk: payload => privCall('/fapi/v2/positionRisk', payload), + futuresLeverageBracket: payload => privCall('/fapi/v1/leverageBracket', payload), + futuresAccountBalance: payload => privCall('/fapi/v2/balance', payload), + futuresAccountInfo: payload => privCall('/fapi/v2/account', payload), + futuresUserTrades: payload => privCall('/fapi/v1/userTrades', payload), + futuresPositionMode: payload => privCall('/fapi/v1/positionSide/dual', payload), + futuresPositionModeChange: payload => + privCall('/fapi/v1/positionSide/dual', payload, 'POST'), + futuresLeverage: payload => privCall('/fapi/v1/leverage', payload, 'POST'), + futuresMarginType: payload => privCall('/fapi/v1/marginType', payload, 'POST'), + futuresPositionMargin: payload => privCall('/fapi/v1/positionMargin', payload, 'POST'), + futuresMarginHistory: payload => privCall('/fapi/v1/positionMargin/history', payload), + futuresIncome: payload => privCall('/fapi/v1/income', payload), + getMultiAssetsMargin: payload => privCall('/fapi/v1/multiAssetsMargin', payload), + setMultiAssetsMargin: payload => privCall('/fapi/v1/multiAssetsMargin', payload, 'POST'), + + // Delivery endpoints + deliveryPing: () => pubCall('/dapi/v1/ping').then(() => true), + deliveryTime: () => pubCall('/dapi/v1/time').then(r => r.serverTime), + deliveryExchangeInfo: () => pubCall('/dapi/v1/exchangeInfo'), + deliveryBook: payload => book(pubCall, payload, '/dapi/v1/depth'), + deliveryAggTrades: payload => aggTrades(pubCall, payload, '/dapi/v1/aggTrades'), + deliveryMarkPrice: payload => pubCall('/dapi/v1/premiumIndex', payload), + deliveryAllForceOrders: payload => pubCall('/dapi/v1/allForceOrders', payload), + deliveryLongShortRatio: payload => + deliveryPubCall('/futures/data/globalLongShortAccountRatio', payload), + deliveryCandles: payload => candles(pubCall, payload, '/dapi/v1/klines'), + deliveryMarkPriceCandles: payload => candles(pubCall, payload, '/dapi/v1/markPriceKlines'), + deliveryIndexPriceCandles: payload => + candles(pubCall, payload, '/dapi/v1/indexPriceKlines'), + deliveryTrades: payload => + checkParams('trades', payload, ['symbol']) && pubCall('/dapi/v1/trades', payload), + deliveryDailyStats: payload => pubCall('/dapi/v1/ticker/24hr', payload), + deliveryPrices: () => + pubCall('/dapi/v1/ticker/price').then(r => + (Array.isArray(r) ? r : [r]).reduce( + (out, cur) => ((out[cur.symbol] = cur.price), out), + {}, + ), + ), + deliveryAllBookTickers: () => + pubCall('/dapi/v1/ticker/bookTicker').then(r => + (Array.isArray(r) ? r : [r]).reduce( + (out, cur) => ((out[cur.symbol] = cur), out), + {}, + ), + ), + deliveryFundingRate: payload => + checkParams('fundingRate', payload, ['symbol']) && + pubCall('/dapi/v1/fundingRate', payload), + deliveryOrder: payload => order(privCall, payload, '/dapi/v1/order'), + deliveryBatchOrders: payload => privCall('/dapi/v1/batchOrders', payload, 'POST'), + deliveryGetOrder: payload => privCall('/dapi/v1/order', payload), + deliveryCancelOrder: payload => privCall('/dapi/v1/order', payload, 'DELETE'), + deliveryCancelAllOpenOrders: payload => + privCall('/dapi/v1/allOpenOrders', payload, 'DELETE'), + deliveryCancelBatchOrders: payload => privCall('/dapi/v1/batchOrders', payload, 'DELETE'), + deliveryOpenOrders: payload => privCall('/dapi/v1/openOrders', payload), + deliveryAllOrders: payload => privCall('/dapi/v1/allOrders', payload), + deliveryPositionRisk: payload => privCall('/dapi/v1/positionRisk', payload), + deliveryLeverageBracket: payload => privCall('/dapi/v1/leverageBracket', payload), + deliveryAccountBalance: payload => privCall('/dapi/v1/balance', payload), + deliveryAccountInfo: payload => privCall('/dapi/v1/account', payload), + deliveryUserTrades: payload => privCall('/dapi/v1/userTrades', payload), + deliveryPositionMode: payload => privCall('/dapi/v1/positionSide/dual', payload), + deliveryPositionModeChange: payload => + privCall('/dapi/v1/positionSide/dual', payload, 'POST'), + deliveryLeverage: payload => privCall('/dapi/v1/leverage', payload, 'POST'), + deliveryMarginType: payload => privCall('/dapi/v1/marginType', payload, 'POST'), + deliveryPositionMargin: payload => privCall('/dapi/v1/positionMargin', payload, 'POST'), + deliveryMarginHistory: payload => privCall('/dapi/v1/positionMargin/history', payload), + deliveryIncome: payload => privCall('/dapi/v1/income', payload), + + // PAPI endpoints + papiPing: () => privCall('/papi/v1/ping'), + papiAccount: () => privCall('/papi/v1/account'), + papiBalance: payload => privCall('/papi/v1/balance', payload), + papiUmOrder: payload => privCall('/papi/v1/um/order', payload), + papiUmConditionalOrder: payload => + privCall('/papi/v1/um/conditional/order', payload, 'POST'), + papiCmOrder: payload => privCall('/papi/v1/cm/order', payload, 'POST'), + papiCmConditionalOrder: payload => + privCall('/papi/v1/cm/conditional/order', payload, 'POST'), + papiMarginOrder: payload => privCall('/papi/v1/margin/order', payload, 'POST'), + papiMarginLoan: payload => privCall('/papi/v1/marginLoan', payload, 'POST'), + papiRepayLoan: payload => privCall('/papi/v1/repayLoan', payload, 'POST'), + papiMarginOrderOco: payload => privCall('/papi/v1/margin/order/oco', payload, 'POST'), + papiUmCancelOrder: payload => privCall('/papi/v1/um/order', payload, 'DELETE'), + papiUmCancelAllOpenOrders: payload => + privCall('/papi/v1/um/allOpenOrders', payload, 'DELETE'), + papiUmCancelConditionalOrder: payload => + privCall('/papi/v1/um/conditional/order', payload, 'DELETE'), + papiUmCancelConditionalAllOpenOrders: payload => + privCall('/papi/v1/um/conditional/allOpenOrders', payload, 'DELETE'), + papiCmCancelOrder: payload => privCall('/papi/v1/cm/order', payload, 'DELETE'), + papiCmCancelAllOpenOrders: payload => + privCall('/papi/v1/cm/allOpenOrders', payload, 'DELETE'), + papiCmCancelConditionalOrder: payload => + privCall('/papi/v1/cm/conditional/order', payload, 'DELETE'), + papiCmCancelConditionalAllOpenOrders: payload => + privCall('/papi/v1/cm/conditional/allOpenOrders', payload, 'DELETE'), + papiMarginCancelOrder: payload => privCall('/papi/v1/margin/order', payload, 'DELETE'), + papiMarginCancelOrderList: payload => + privCall('/papi/v1/margin/orderList', payload, 'DELETE'), + papiMarginCancelAllOpenOrders: payload => + privCall('/papi/v1/margin/allOpenOrders', payload, 'DELETE'), + papiUmUpdateOrder: payload => privCall('/papi/v1/um/order', payload, 'PUT'), + papiCmUpdateOrder: payload => privCall('/papi/v1/cm/order', payload, 'PUT'), + papiUmGetOrder: payload => privCall('/papi/v1/um/order', payload), + papiUmGetAllOrders: payload => privCall('/papi/v1/um/allOrders', payload), + papiUmGetOpenOrder: payload => privCall('/papi/v1/um/openOrder', payload), + papiUmGetOpenOrders: payload => privCall('/papi/v1/um/openOrders', payload), + papiUmGetConditionalAllOrders: payload => + privCall('/papi/v1/um/conditional/allOrders', payload), + papiUmGetConditionalOpenOrders: payload => + privCall('/papi/v1/um/conditional/openOrders', payload), + papiUmGetConditionalOpenOrder: payload => + privCall('/papi/v1/um/conditional/openOrder', payload), + papiUmGetConditionalOrderHistory: payload => + privCall('/papi/v1/um/conditional/orderHistory', payload), + papiCmGetOrder: payload => privCall('/papi/v1/cm/order', payload), + papiCmGetAllOrders: payload => privCall('/papi/v1/cm/allOrders', payload), + papiCmGetOpenOrder: payload => privCall('/papi/v1/cm/openOrder', payload), + papiCmGetOpenOrders: payload => privCall('/papi/v1/cm/openOrders', payload), + papiCmGetConditionalOpenOrders: payload => + privCall('/papi/v1/cm/conditional/openOrders', payload), + papiCmGetConditionalOpenOrder: payload => + privCall('/papi/v1/cm/conditional/openOrder', payload), + papiCmGetConditionalAllOrders: payload => + privCall('/papi/v1/cm/conditional/allOrders', payload), + papiCmGetConditionalOrderHistory: payload => + privCall('/papi/v1/cm/conditional/orderHistory', payload), + papiUmGetForceOrders: payload => privCall('/papi/v1/um/forceOrders', payload), + papiCmGetForceOrders: payload => privCall('/papi/v1/cm/forceOrders', payload), + papiUmGetOrderAmendment: payload => privCall('/papi/v1/um/orderAmendment', payload), + papiCmGetOrderAmendment: payload => privCall('/papi/v1/cm/orderAmendment', payload), + papiMarginGetForceOrders: payload => privCall('/papi/v1/margin/forceOrders', payload), + papiUmGetUserTrades: payload => privCall('/papi/v1/um/userTrades', payload), + papiCmGetUserTrades: payload => privCall('/papi/v1/cm/userTrades', payload), + papiUmGetAdlQuantile: payload => privCall('/papi/v1/um/adlQuantile', payload), + papiCmGetAdlQuantile: payload => privCall('/papi/v1/cm/adlQuantile', payload), + papiUmFeeBurn: payload => privCall('/papi/v1/um/feeBurn', payload, 'POST'), + papiUmGetFeeBurn: payload => privCall('/papi/v1/um/feeBurn', payload), + papiMarginGetOrder: payload => privCall('/papi/v1/margin/order', payload), + papiMarginGetOpenOrders: payload => privCall('/papi/v1/margin/openOrders', payload), + papiMarginGetAllOrders: payload => privCall('/papi/v1/margin/allOrders', payload), + papiMarginGetOrderList: payload => privCall('/papi/v1/margin/orderList', payload), + papiMarginGetAllOrderList: payload => privCall('/papi/v1/margin/allOrderList', payload), + papiMarginGetOpenOrderList: payload => privCall('/papi/v1/margin/openOrderList', payload), + papiMarginGetMyTrades: payload => privCall('/papi/v1/margin/myTrades', payload), + papiMarginRepayDebt: payload => privCall('/papi/v1/margin/repay-debt', payload, 'POST'), + + // Margin endpoints + marginAllOrders: payload => privCall('/sapi/v1/margin/allOrders', payload), + marginOrder: payload => order(privCall, payload, '/sapi/v1/margin/order'), + marginOrderOco: payload => orderOco(privCall, payload, '/sapi/v1/margin/order/oco'), + marginGetOrder: payload => privCall('/sapi/v1/margin/order', payload), + marginGetOrderOco: payload => privCall('/sapi/v1/margin/orderList', payload), + marginCancelOrder: payload => privCall('/sapi/v1/margin/order', payload, 'DELETE'), + marginOpenOrders: payload => privCall('/sapi/v1/margin/openOrders', payload), + marginCancelOpenOrders: payload => + privCall('/sapi/v1/margin/openOrders', payload, 'DELETE'), + marginAccountInfo: payload => privCall('/sapi/v1/margin/account', payload), + marginMyTrades: payload => privCall('/sapi/v1/margin/myTrades', payload), + marginRepay: payload => privCall('/sapi/v1/margin/repay', payload, 'POST'), + marginLoan: payload => privCall('/sapi/v1/margin/loan', payload, 'POST'), + marginIsolatedAccount: payload => privCall('/sapi/v1/margin/isolated/account', payload), + marginMaxBorrow: payload => privCall('/sapi/v1/margin/maxBorrowable', payload), + marginCreateIsolated: payload => + privCall('/sapi/v1/margin/isolated/create', payload, 'POST'), + marginIsolatedTransfer: payload => + privCall('/sapi/v1/margin/isolated/transfer', payload, 'POST'), + marginIsolatedTransferHistory: payload => + privCall('/sapi/v1/margin/isolated/transfer', payload), + disableMarginAccount: payload => + privCall('/sapi/v1/margin/isolated/account', payload, 'DELETE'), + enableMarginAccount: payload => + privCall('/sapi/v1/margin/isolated/account', payload, 'POST'), + marginAccount: () => privCall('/sapi/v1/margin/account'), + + // Portfolio Margin endpoints + portfolioMarginAccountInfo: () => privCall('/sapi/v1/portfolio/account'), + portfolioMarginCollateralRate: () => privCall('/sapi/v1/portfolio/collateralRate'), + portfolioMarginLoan: payload => privCall('/sapi/v1/portfolio/pmLoan', payload), + portfolioMarginLoanRepay: payload => privCall('/sapi/v1/portfolio/repay', payload, 'POST'), + portfolioMarginInterestHistory: payload => + privCall('/sapi/v1/portfolio/interest-history', payload), + + // Savings endpoints + savingsAccount: payload => privCall('/sapi/v1/lending/union/account', payload), + savingsPurchase: payload => privCall('/sapi/v1/lending/union/purchase', payload, 'POST'), + savingsRedeem: payload => privCall('/sapi/v1/lending/union/redeem', payload, 'POST'), + fundingWallet: payload => privCall('/sapi/v1/asset/get-funding-asset', payload, 'POST'), + convertTradeFlow: payload => privCall('/sapi/v1/convert/tradeFlow', payload), + rebateTaxQuery: () => privCall('/sapi/v1/rebate/taxQuery'), + payTradeHistory: payload => privCall('/sapi/v1/pay/transactions', payload), + apiRestrictions: payload => privCall('/sapi/v1/account/apiRestrictions', payload), + + // Mining endpoints + miningHashrateResaleRequest: payload => + privCall('/sapi/v1/mining/hash-transfer/config', payload, 'POST'), + miningHashrateResaleCancel: payload => + privCall('/sapi/v1/mining/hash-transfer/config/cancel', payload, 'POST'), + miningStatistics: payload => privCall('/sapi/v1/mining/statistics/user/status', payload), + + // Utility endpoints + privateRequest: (method, url, payload) => privCall(url, payload, method), + publicRequest: (method, url, payload) => pubCall(url, payload, method), + } } diff --git a/src/index.js b/src/index.js index c35198ce..5e371549 100644 --- a/src/index.js +++ b/src/index.js @@ -2,119 +2,119 @@ import httpMethods from 'http-client' import wsMethods from 'websocket' export default (opts = {}) => ({ - ...httpMethods(opts), - ws: wsMethods(opts), + ...httpMethods(opts), + ws: wsMethods(opts), }) export const ErrorCodes = { - UNKNOWN: -1000, - DISCONNECTED: -1001, - UNAUTHORIZED: -1002, - TOO_MANY_REQUESTS: -1003, - UNEXPECTED_RESP: -1006, - TIMEOUT: -1007, - INVALID_MESSAGE: -1013, - UNKNOWN_ORDER_COMPOSITION: -1014, - TOO_MANY_ORDERS: -1015, - SERVICE_SHUTTING_DOWN: -1016, - UNSUPPORTED_OPERATION: -1020, - INVALID_TIMESTAMP: -1021, - INVALID_SIGNATURE: -1022, - ILLEGAL_CHARS: -1100, - TOO_MANY_PARAMETERS: -1101, - MANDATORY_PARAM_EMPTY_OR_MALFORMED: -1102, // eslint-disable-line id-length - UNKNOWN_PARAM: -1103, - UNREAD_PARAMETERS: -1104, - PARAM_EMPTY: -1105, - PARAM_NOT_REQUIRED: -1106, - NO_DEPTH: -1112, - TIF_NOT_REQUIRED: -1114, - INVALID_TIF: -1115, - INVALID_ORDER_TYPE: -1116, - INVALID_SIDE: -1117, - EMPTY_NEW_CL_ORD_ID: -1118, - EMPTY_ORG_CL_ORD_ID: -1119, - BAD_INTERVAL: -1120, - BAD_SYMBOL: -1121, - INVALID_LISTEN_KEY: -1125, - MORE_THAN_XX_HOURS: -1127, - OPTIONAL_PARAMS_BAD_COMBO: -1128, - INVALID_PARAMETER: -1130, - BAD_API_ID: -2008, - DUPLICATE_API_KEY_DESC: -2009, - INSUFFICIENT_BALANCE: -2010, - CANCEL_REJECTED: -2011, - CANCEL_ALL_FAIL: -2012, - NO_SUCH_ORDER: -2013, - BAD_API_KEY_FMT: -2014, - REJECTED_MBX_KEY: -2015, + UNKNOWN: -1000, + DISCONNECTED: -1001, + UNAUTHORIZED: -1002, + TOO_MANY_REQUESTS: -1003, + UNEXPECTED_RESP: -1006, + TIMEOUT: -1007, + INVALID_MESSAGE: -1013, + UNKNOWN_ORDER_COMPOSITION: -1014, + TOO_MANY_ORDERS: -1015, + SERVICE_SHUTTING_DOWN: -1016, + UNSUPPORTED_OPERATION: -1020, + INVALID_TIMESTAMP: -1021, + INVALID_SIGNATURE: -1022, + ILLEGAL_CHARS: -1100, + TOO_MANY_PARAMETERS: -1101, + MANDATORY_PARAM_EMPTY_OR_MALFORMED: -1102, // eslint-disable-line id-length + UNKNOWN_PARAM: -1103, + UNREAD_PARAMETERS: -1104, + PARAM_EMPTY: -1105, + PARAM_NOT_REQUIRED: -1106, + NO_DEPTH: -1112, + TIF_NOT_REQUIRED: -1114, + INVALID_TIF: -1115, + INVALID_ORDER_TYPE: -1116, + INVALID_SIDE: -1117, + EMPTY_NEW_CL_ORD_ID: -1118, + EMPTY_ORG_CL_ORD_ID: -1119, + BAD_INTERVAL: -1120, + BAD_SYMBOL: -1121, + INVALID_LISTEN_KEY: -1125, + MORE_THAN_XX_HOURS: -1127, + OPTIONAL_PARAMS_BAD_COMBO: -1128, + INVALID_PARAMETER: -1130, + BAD_API_ID: -2008, + DUPLICATE_API_KEY_DESC: -2009, + INSUFFICIENT_BALANCE: -2010, + CANCEL_REJECTED: -2011, + CANCEL_ALL_FAIL: -2012, + NO_SUCH_ORDER: -2013, + BAD_API_KEY_FMT: -2014, + REJECTED_MBX_KEY: -2015, } export const CandleChartInterval = { - ONE_MINUTE: '1m', - THREE_MINUTES: '3m', - FIVE_MINUTES: '5m', - FIFTEEN_MINUTES: '15m', - THIRTY_MINUTES: '30m', - ONE_HOUR: '1h', - TWO_HOURS: '2h', - FOUR_HOURS: '4h', - SIX_HOURS: '6h', - EIGHT_HOURS: '8h', - TWELVE_HOURS: '12h', - ONE_DAY: '1d', - THREE_DAYS: '3d', - ONE_WEEK: '1w', - ONE_MONTH: '1M', + ONE_MINUTE: '1m', + THREE_MINUTES: '3m', + FIVE_MINUTES: '5m', + FIFTEEN_MINUTES: '15m', + THIRTY_MINUTES: '30m', + ONE_HOUR: '1h', + TWO_HOURS: '2h', + FOUR_HOURS: '4h', + SIX_HOURS: '6h', + EIGHT_HOURS: '8h', + TWELVE_HOURS: '12h', + ONE_DAY: '1d', + THREE_DAYS: '3d', + ONE_WEEK: '1w', + ONE_MONTH: '1M', } export const DepositStatus = { - PENDING: 0, - SUCCESS: 1, + PENDING: 0, + SUCCESS: 1, } export const WithdrawStatus = { - EMAIL_SENT: 0, - CANCELLED: 1, - AWAITING_APPROVAL: 2, - REJECTED: 3, - PROCESSING: 4, - FAILURE: 5, - COMPLETED: 6, + EMAIL_SENT: 0, + CANCELLED: 1, + AWAITING_APPROVAL: 2, + REJECTED: 3, + PROCESSING: 4, + FAILURE: 5, + COMPLETED: 6, } export const SavingsStatus = { - HOLDING: 'HOLDING', - REDEEMED: 'REDEEMED', - TRANSFERRED: 'TRANSFERRED', + HOLDING: 'HOLDING', + REDEEMED: 'REDEEMED', + TRANSFERRED: 'TRANSFERRED', } export const SavingsType = { - FAST: 'FAST', - NORMAL: 'NORMAL', + FAST: 'FAST', + NORMAL: 'NORMAL', } export const MiningAlgo = { - SHA256: 'sha256', - SCRYPT: 'scrypt', - ETHASH: 'ethash', - X11: 'x11', + SHA256: 'sha256', + SCRYPT: 'scrypt', + ETHASH: 'ethash', + X11: 'x11', } export const MiningStatus = { - HASH_RATE: 'hash_rate', - REJECTED: 'rejected', - EARNINGS: 'earnings', + HASH_RATE: 'hash_rate', + REJECTED: 'rejected', + EARNINGS: 'earnings', } export const ConvertStatus = { - PROCESSING: 'PROCESSING', - SUCCESS: 'SUCCESS', - FAILURE: 'FAILURE', + PROCESSING: 'PROCESSING', + SUCCESS: 'SUCCESS', + FAILURE: 'FAILURE', } export const PayStatus = { - PENDING: 'PENDING', - SUCCESS: 'SUCCESS', - FAILED: 'FAILED', + PENDING: 'PENDING', + SUCCESS: 'SUCCESS', + FAILED: 'FAILED', } diff --git a/src/open-websocket.js b/src/open-websocket.js index 274cc53b..4a65ec4e 100644 --- a/src/open-websocket.js +++ b/src/open-websocket.js @@ -2,27 +2,27 @@ import ws from 'isomorphic-ws' import ReconnectingWebSocket from 'reconnecting-websocket' export default (url, opts) => { - const rws = new ReconnectingWebSocket(url, [], { - WebSocket: ws, - connectionTimeout: 4e3, - debug: false, - maxReconnectionDelay: 10e3, - maxRetries: Infinity, - minReconnectionDelay: 4e3, - ...opts, - }) + const rws = new ReconnectingWebSocket(url, [], { + WebSocket: ws, + connectionTimeout: 4e3, + debug: false, + maxReconnectionDelay: 10e3, + maxRetries: Infinity, + minReconnectionDelay: 4e3, + ...opts, + }) - // TODO Maybe we have to pass the proxy to this line - // https://github.com/pladaria/reconnecting-websocket/blob/05a2f7cb0e31f15dff5ff35ad53d07b1bec5e197/reconnecting-websocket.ts#L383 + // TODO Maybe we have to pass the proxy to this line + // https://github.com/pladaria/reconnecting-websocket/blob/05a2f7cb0e31f15dff5ff35ad53d07b1bec5e197/reconnecting-websocket.ts#L383 - const pong = () => rws._ws.pong(() => null) + const pong = () => rws._ws.pong(() => null) - rws.addEventListener('open', () => { - // .on only works in node env, not in browser. https://github.com/Ashlar/binance-api-node/issues/404#issuecomment-833668033 - if (rws._ws.on) { - rws._ws.on('ping', pong) - } - }) + rws.addEventListener('open', () => { + // .on only works in node env, not in browser. https://github.com/Ashlar/binance-api-node/issues/404#issuecomment-833668033 + if (rws._ws.on) { + rws._ws.on('ping', pong) + } + }) - return rws + return rws } diff --git a/src/websocket.js b/src/websocket.js index 2f777fd5..443cf64b 100644 --- a/src/websocket.js +++ b/src/websocket.js @@ -5,985 +5,1011 @@ import httpMethods from 'http-client' import _openWebSocket from 'open-websocket' const endpoints = { - base: 'wss://stream.binance.com:9443/ws', - futures: 'wss://fstream.binance.com/ws', - delivery: 'wss://dstream.binance.com/ws', + base: 'wss://stream.binance.com:9443/ws', + futures: 'wss://fstream.binance.com/ws', + delivery: 'wss://dstream.binance.com/ws', } const wsOptions = {} function openWebSocket(url) { - return _openWebSocket(url, wsOptions) + return _openWebSocket(url, wsOptions) } const depthTransform = m => ({ - eventType: m.e, - eventTime: m.E, - symbol: m.s, - firstUpdateId: m.U, - finalUpdateId: m.u, - bidDepth: m.b.map(b => zip(['price', 'quantity'], b)), - askDepth: m.a.map(a => zip(['price', 'quantity'], a)), + eventType: m.e, + eventTime: m.E, + symbol: m.s, + firstUpdateId: m.U, + finalUpdateId: m.u, + bidDepth: m.b.map(b => zip(['price', 'quantity'], b)), + askDepth: m.a.map(a => zip(['price', 'quantity'], a)), }) const futuresDepthTransform = m => ({ - eventType: m.e, - eventTime: m.E, - transactionTime: m.T, - symbol: m.s, - firstUpdateId: m.U, - finalUpdateId: m.u, - prevFinalUpdateId: m.pu, - bidDepth: m.b.map(b => zip(['price', 'quantity'], b)), - askDepth: m.a.map(a => zip(['price', 'quantity'], a)), + eventType: m.e, + eventTime: m.E, + transactionTime: m.T, + symbol: m.s, + firstUpdateId: m.U, + finalUpdateId: m.u, + prevFinalUpdateId: m.pu, + bidDepth: m.b.map(b => zip(['price', 'quantity'], b)), + askDepth: m.a.map(a => zip(['price', 'quantity'], a)), }) const deliveryDepthTransform = m => ({ - eventType: m.e, - eventTime: m.E, - transactionTime: m.T, - symbol: m.s, - pair: m.ps, - firstUpdateId: m.U, - finalUpdateId: m.u, - prevFinalUpdateId: m.pu, - bidDepth: m.b.map(b => zip(['price', 'quantity'], b)), - askDepth: m.a.map(a => zip(['price', 'quantity'], a)), + eventType: m.e, + eventTime: m.E, + transactionTime: m.T, + symbol: m.s, + pair: m.ps, + firstUpdateId: m.U, + finalUpdateId: m.u, + prevFinalUpdateId: m.pu, + bidDepth: m.b.map(b => zip(['price', 'quantity'], b)), + askDepth: m.a.map(a => zip(['price', 'quantity'], a)), }) const depth = (payload, cb, transform = true, variator) => { - const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { - const [symbolName, updateSpeed] = symbol.toLowerCase().split('@') - const w = openWebSocket( - `${variator ? endpoints[variator] : endpoints.base}/${symbolName}@depth${ - updateSpeed ? `@${updateSpeed}` : '' - }`, - ) - w.onmessage = msg => { - const obj = JSONbig.parse(msg.data) - - cb( - transform - ? variator === 'futures' - ? futuresDepthTransform(obj) - : variator === 'delivery' - ? deliveryDepthTransform(obj) - : depthTransform(obj) - : obj, - ) - } + const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { + const [symbolName, updateSpeed] = symbol.toLowerCase().split('@') + const w = openWebSocket( + `${variator ? endpoints[variator] : endpoints.base}/${symbolName}@depth${ + updateSpeed ? `@${updateSpeed}` : '' + }`, + ) + w.onmessage = msg => { + const obj = JSONbig.parse(msg.data) + + cb( + transform + ? variator === 'futures' + ? futuresDepthTransform(obj) + : variator === 'delivery' + ? deliveryDepthTransform(obj) + : depthTransform(obj) + : obj, + ) + } - return w - }) + return w + }) - return options => - cache.forEach(w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options })) + return options => + cache.forEach(w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }), + ) } const partialDepthTransform = (symbol, level, m) => ({ - symbol, - level, - lastUpdateId: m.lastUpdateId, - bids: m.bids.map(b => zip(['price', 'quantity'], b)), - asks: m.asks.map(a => zip(['price', 'quantity'], a)), + symbol, + level, + lastUpdateId: m.lastUpdateId, + bids: m.bids.map(b => zip(['price', 'quantity'], b)), + asks: m.asks.map(a => zip(['price', 'quantity'], a)), }) const futuresPartDepthTransform = (level, m) => ({ - level, - eventType: m.e, - eventTime: m.E, - transactionTime: m.T, - symbol: m.s, - firstUpdateId: m.U, - finalUpdateId: m.u, - prevFinalUpdateId: m.pu, - bidDepth: m.b.map(b => zip(['price', 'quantity'], b)), - askDepth: m.a.map(a => zip(['price', 'quantity'], a)), + level, + eventType: m.e, + eventTime: m.E, + transactionTime: m.T, + symbol: m.s, + firstUpdateId: m.U, + finalUpdateId: m.u, + prevFinalUpdateId: m.pu, + bidDepth: m.b.map(b => zip(['price', 'quantity'], b)), + askDepth: m.a.map(a => zip(['price', 'quantity'], a)), }) const deliveryPartDepthTransform = (level, m) => ({ - level, - eventType: m.e, - eventTime: m.E, - transactionTime: m.T, - symbol: m.s, - pair: m.ps, - firstUpdateId: m.U, - finalUpdateId: m.u, - prevFinalUpdateId: m.pu, - bidDepth: m.b.map(b => zip(['price', 'quantity'], b)), - askDepth: m.a.map(a => zip(['price', 'quantity'], a)), + level, + eventType: m.e, + eventTime: m.E, + transactionTime: m.T, + symbol: m.s, + pair: m.ps, + firstUpdateId: m.U, + finalUpdateId: m.u, + prevFinalUpdateId: m.pu, + bidDepth: m.b.map(b => zip(['price', 'quantity'], b)), + askDepth: m.a.map(a => zip(['price', 'quantity'], a)), }) const partialDepth = (payload, cb, transform = true, variator) => { - const cache = (Array.isArray(payload) ? payload : [payload]).map(({ symbol, level }) => { - const [symbolName, updateSpeed] = symbol.toLowerCase().split('@') - const w = openWebSocket( - `${variator ? endpoints[variator] : endpoints.base}/${symbolName}@depth${level}${ - updateSpeed ? `@${updateSpeed}` : '' - }`, - ) - w.onmessage = msg => { - const obj = JSONbig.parse(msg.data) - - cb( - transform - ? variator === 'futures' - ? futuresPartDepthTransform(level, obj) - : variator === 'delivery' - ? deliveryPartDepthTransform(level, obj) - : partialDepthTransform(symbol, level, obj) - : obj, - ) - } + const cache = (Array.isArray(payload) ? payload : [payload]).map(({ symbol, level }) => { + const [symbolName, updateSpeed] = symbol.toLowerCase().split('@') + const w = openWebSocket( + `${variator ? endpoints[variator] : endpoints.base}/${symbolName}@depth${level}${ + updateSpeed ? `@${updateSpeed}` : '' + }`, + ) + w.onmessage = msg => { + const obj = JSONbig.parse(msg.data) + + cb( + transform + ? variator === 'futures' + ? futuresPartDepthTransform(level, obj) + : variator === 'delivery' + ? deliveryPartDepthTransform(level, obj) + : partialDepthTransform(symbol, level, obj) + : obj, + ) + } - return w - }) + return w + }) - return options => - cache.forEach(w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options })) + return options => + cache.forEach(w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }), + ) } const candleTransform = m => ({ - startTime: m.t, - closeTime: m.T, - firstTradeId: m.f, - lastTradeId: m.L, - open: m.o, - high: m.h, - low: m.l, - close: m.c, - volume: m.v, - trades: m.n, - interval: m.i, - isFinal: m.x, - quoteVolume: m.q, - buyVolume: m.V, - quoteBuyVolume: m.Q, + startTime: m.t, + closeTime: m.T, + firstTradeId: m.f, + lastTradeId: m.L, + open: m.o, + high: m.h, + low: m.l, + close: m.c, + volume: m.v, + trades: m.n, + interval: m.i, + isFinal: m.x, + quoteVolume: m.q, + buyVolume: m.V, + quoteBuyVolume: m.Q, }) const deliveryCandleTransform = m => ({ - startTime: m.t, - closeTime: m.T, - firstTradeId: m.f, - lastTradeId: m.L, - open: m.o, - high: m.h, - low: m.l, - close: m.c, - volume: m.v, - trades: m.n, - interval: m.i, - isFinal: m.x, - baseVolume: m.q, - buyVolume: m.V, - baseBuyVolume: m.Q, + startTime: m.t, + closeTime: m.T, + firstTradeId: m.f, + lastTradeId: m.L, + open: m.o, + high: m.h, + low: m.l, + close: m.c, + volume: m.v, + trades: m.n, + interval: m.i, + isFinal: m.x, + baseVolume: m.q, + buyVolume: m.V, + baseBuyVolume: m.Q, }) const candles = (payload, interval, cb, transform = true, variator) => { - if (!interval || !cb) { - throw new Error('Please pass a symbol, interval and callback.') - } - - const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { - const w = openWebSocket( - `${ - variator ? endpoints[variator] : endpoints.base - }/${symbol.toLowerCase()}@kline_${interval}`, - ) - w.onmessage = msg => { - const obj = JSONbig.parse(msg.data) - const { e: eventType, E: eventTime, s: symbol, k: tick } = obj - - cb( - transform - ? { - eventType, - eventTime, - symbol, - ...(variator === 'delivery' ? deliveryCandleTransform(tick) : candleTransform(tick)), - } - : obj, - ) + if (!interval || !cb) { + throw new Error('Please pass a symbol, interval and callback.') } - return w - }) + const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { + const w = openWebSocket( + `${ + variator ? endpoints[variator] : endpoints.base + }/${symbol.toLowerCase()}@kline_${interval}`, + ) + w.onmessage = msg => { + const obj = JSONbig.parse(msg.data) + const { e: eventType, E: eventTime, s: symbol, k: tick } = obj + + cb( + transform + ? { + eventType, + eventTime, + symbol, + ...(variator === 'delivery' + ? deliveryCandleTransform(tick) + : candleTransform(tick)), + } + : obj, + ) + } + + return w + }) - return options => - cache.forEach(w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options })) + return options => + cache.forEach(w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }), + ) } const bookTickerTransform = m => ({ - updateId: m.u, - symbol: m.s, - bestBid: m.b, - bestBidQnt: m.B, - bestAsk: m.a, - bestAskQnt: m.A, + updateId: m.u, + symbol: m.s, + bestBid: m.b, + bestBidQnt: m.B, + bestAsk: m.a, + bestAskQnt: m.A, }) const miniTickerTransform = m => ({ - eventType: m.e, - eventTime: m.E, - symbol: m.s, - curDayClose: m.c, - open: m.o, - high: m.h, - low: m.l, - volume: m.v, - volumeQuote: m.q, + eventType: m.e, + eventTime: m.E, + symbol: m.s, + curDayClose: m.c, + open: m.o, + high: m.h, + low: m.l, + volume: m.v, + volumeQuote: m.q, }) const deliveryMiniTickerTransform = m => ({ - eventType: m.e, - eventTime: m.E, - symbol: m.s, - pair: m.ps, - curDayClose: m.c, - open: m.o, - high: m.h, - low: m.l, - volume: m.v, - volumeBase: m.q, + eventType: m.e, + eventTime: m.E, + symbol: m.s, + pair: m.ps, + curDayClose: m.c, + open: m.o, + high: m.h, + low: m.l, + volume: m.v, + volumeBase: m.q, }) const tickerTransform = m => ({ - eventType: m.e, - eventTime: m.E, - symbol: m.s, - priceChange: m.p, - priceChangePercent: m.P, - weightedAvg: m.w, - prevDayClose: m.x, - curDayClose: m.c, - closeTradeQuantity: m.Q, - bestBid: m.b, - bestBidQnt: m.B, - bestAsk: m.a, - bestAskQnt: m.A, - open: m.o, - high: m.h, - low: m.l, - volume: m.v, - volumeQuote: m.q, - openTime: m.O, - closeTime: m.C, - firstTradeId: m.F, - lastTradeId: m.L, - totalTrades: m.n, + eventType: m.e, + eventTime: m.E, + symbol: m.s, + priceChange: m.p, + priceChangePercent: m.P, + weightedAvg: m.w, + prevDayClose: m.x, + curDayClose: m.c, + closeTradeQuantity: m.Q, + bestBid: m.b, + bestBidQnt: m.B, + bestAsk: m.a, + bestAskQnt: m.A, + open: m.o, + high: m.h, + low: m.l, + volume: m.v, + volumeQuote: m.q, + openTime: m.O, + closeTime: m.C, + firstTradeId: m.F, + lastTradeId: m.L, + totalTrades: m.n, }) const futuresTickerTransform = m => ({ - eventType: m.e, - eventTime: m.E, - symbol: m.s, - priceChange: m.p, - priceChangePercent: m.P, - weightedAvg: m.w, - curDayClose: m.c, - closeTradeQuantity: m.Q, - open: m.o, - high: m.h, - low: m.l, - volume: m.v, - volumeQuote: m.q, - openTime: m.O, - closeTime: m.C, - firstTradeId: m.F, - lastTradeId: m.L, - totalTrades: m.n, + eventType: m.e, + eventTime: m.E, + symbol: m.s, + priceChange: m.p, + priceChangePercent: m.P, + weightedAvg: m.w, + curDayClose: m.c, + closeTradeQuantity: m.Q, + open: m.o, + high: m.h, + low: m.l, + volume: m.v, + volumeQuote: m.q, + openTime: m.O, + closeTime: m.C, + firstTradeId: m.F, + lastTradeId: m.L, + totalTrades: m.n, }) const deliveryTickerTransform = m => ({ - eventType: m.e, - eventTime: m.E, - symbol: m.s, - pair: m.ps, - priceChange: m.p, - priceChangePercent: m.P, - weightedAvg: m.w, - curDayClose: m.c, - closeTradeQuantity: m.Q, - open: m.o, - high: m.h, - low: m.l, - volume: m.v, - volumeBase: m.q, - openTime: m.O, - closeTime: m.C, - firstTradeId: m.F, - lastTradeId: m.L, - totalTrades: m.n, + eventType: m.e, + eventTime: m.E, + symbol: m.s, + pair: m.ps, + priceChange: m.p, + priceChangePercent: m.P, + weightedAvg: m.w, + curDayClose: m.c, + closeTradeQuantity: m.Q, + open: m.o, + high: m.h, + low: m.l, + volume: m.v, + volumeBase: m.q, + openTime: m.O, + closeTime: m.C, + firstTradeId: m.F, + lastTradeId: m.L, + totalTrades: m.n, }) const bookTicker = (payload, cb, transform = true) => { - const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { - const w = openWebSocket(`${endpoints.base}/${symbol.toLowerCase()}@bookTicker`) + const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { + const w = openWebSocket(`${endpoints.base}/${symbol.toLowerCase()}@bookTicker`) - w.onmessage = msg => { - const obj = JSONbig.parse(msg.data) - cb(transform ? bookTickerTransform(obj) : obj) - } + w.onmessage = msg => { + const obj = JSONbig.parse(msg.data) + cb(transform ? bookTickerTransform(obj) : obj) + } - return w - }) + return w + }) - return options => - cache.forEach(w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options })) + return options => + cache.forEach(w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }), + ) } const ticker = (payload, cb, transform = true, variator) => { - const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { - const w = openWebSocket( - `${ - variator === 'futures' - ? endpoints.futures - : variator === 'delivery' - ? endpoints.delivery - : endpoints.base - }/${symbol.toLowerCase()}@ticker`, - ) - - w.onmessage = msg => { - const obj = JSONbig.parse(msg.data) - cb( - transform - ? variator === 'futures' - ? futuresTickerTransform(obj) - : variator === 'delivery' - ? deliveryTickerTransform(obj) - : tickerTransform(obj) - : obj, - ) - } + const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { + const w = openWebSocket( + `${ + variator === 'futures' + ? endpoints.futures + : variator === 'delivery' + ? endpoints.delivery + : endpoints.base + }/${symbol.toLowerCase()}@ticker`, + ) + + w.onmessage = msg => { + const obj = JSONbig.parse(msg.data) + cb( + transform + ? variator === 'futures' + ? futuresTickerTransform(obj) + : variator === 'delivery' + ? deliveryTickerTransform(obj) + : tickerTransform(obj) + : obj, + ) + } - return w - }) + return w + }) - return options => - cache.forEach(w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options })) + return options => + cache.forEach(w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }), + ) } const allTickers = (cb, transform = true, variator) => { - const w = new openWebSocket( - `${ - variator === 'futures' - ? endpoints.futures - : variator === 'delivery' - ? endpoints.delivery - : endpoints.base - }/!ticker@arr`, - ) - - w.onmessage = msg => { - const arr = JSONbig.parse(msg.data) - cb( - transform - ? variator === 'futures' - ? arr.map(m => futuresTickerTransform(m)) - : variator === 'delivery' - ? arr.map(m => deliveryTickerTransform(m)) - : arr.map(m => tickerTransform(m)) - : arr, + const w = new openWebSocket( + `${ + variator === 'futures' + ? endpoints.futures + : variator === 'delivery' + ? endpoints.delivery + : endpoints.base + }/!ticker@arr`, ) - } - return options => w.close(1000, 'Close handle was called', { keepClosed: true, ...options }) + w.onmessage = msg => { + const arr = JSONbig.parse(msg.data) + cb( + transform + ? variator === 'futures' + ? arr.map(m => futuresTickerTransform(m)) + : variator === 'delivery' + ? arr.map(m => deliveryTickerTransform(m)) + : arr.map(m => tickerTransform(m)) + : arr, + ) + } + + return options => w.close(1000, 'Close handle was called', { keepClosed: true, ...options }) } const miniTicker = (payload, cb, transform = true, variator) => { - const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { - const w = openWebSocket(`${endpoints.base}/${symbol.toLowerCase()}@miniTicker`) - - w.onmessage = msg => { - const obj = JSONbig.parse(msg.data) - cb( - transform - ? variator === 'delivery' - ? deliveryMiniTickerTransform(obj) - : miniTickerTransform(obj) - : obj, - ) - } + const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { + const w = openWebSocket(`${endpoints.base}/${symbol.toLowerCase()}@miniTicker`) + + w.onmessage = msg => { + const obj = JSONbig.parse(msg.data) + cb( + transform + ? variator === 'delivery' + ? deliveryMiniTickerTransform(obj) + : miniTickerTransform(obj) + : obj, + ) + } - return w - }) + return w + }) - return options => - cache.forEach(w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options })) + return options => + cache.forEach(w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }), + ) } const allMiniTickers = (cb, transform = true, variator) => { - const w = openWebSocket(`${endpoints.base}/!miniTicker@arr`) - - w.onmessage = msg => { - const arr = JSONbig.parse(msg.data) - cb( - transform - ? arr.map(variator === 'delivery' ? deliveryMiniTickerTransform : miniTickerTransform) - : arr, - ) - } + const w = openWebSocket(`${endpoints.base}/!miniTicker@arr`) + + w.onmessage = msg => { + const arr = JSONbig.parse(msg.data) + cb( + transform + ? arr.map( + variator === 'delivery' ? deliveryMiniTickerTransform : miniTickerTransform, + ) + : arr, + ) + } - return options => w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options }) + return options => w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }) } const customSubStream = (payload, cb, variator) => { - const cache = (Array.isArray(payload) ? payload : [payload]).map(sub => { - const w = openWebSocket( - `${ - variator === 'futures' - ? endpoints.futures - : variator === 'delivery' - ? endpoints.delivery - : endpoints.base - }/${sub}`, - ) - - w.onmessage = msg => { - const data = JSONbig.parse(msg.data) - cb(data) - } + const cache = (Array.isArray(payload) ? payload : [payload]).map(sub => { + const w = openWebSocket( + `${ + variator === 'futures' + ? endpoints.futures + : variator === 'delivery' + ? endpoints.delivery + : endpoints.base + }/${sub}`, + ) + + w.onmessage = msg => { + const data = JSONbig.parse(msg.data) + cb(data) + } - return w - }) + return w + }) - return options => - cache.forEach(w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options })) + return options => + cache.forEach(w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }), + ) } const aggTradesTransform = m => ({ - eventType: m.e, - eventTime: m.E, - timestamp: m.T, - symbol: m.s, - price: m.p, - quantity: m.q, - isBuyerMaker: m.m, - wasBestPrice: m.M, - aggId: m.a, - firstId: m.f, - lastId: m.l, + eventType: m.e, + eventTime: m.E, + timestamp: m.T, + symbol: m.s, + price: m.p, + quantity: m.q, + isBuyerMaker: m.m, + wasBestPrice: m.M, + aggId: m.a, + firstId: m.f, + lastId: m.l, }) const futuresAggTradesTransform = m => ({ - eventType: m.e, - eventTime: m.E, - symbol: m.s, - aggId: m.a, - price: m.p, - quantity: m.q, - firstId: m.f, - lastId: m.l, - timestamp: m.T, - isBuyerMaker: m.m, + eventType: m.e, + eventTime: m.E, + symbol: m.s, + aggId: m.a, + price: m.p, + quantity: m.q, + firstId: m.f, + lastId: m.l, + timestamp: m.T, + isBuyerMaker: m.m, }) const aggTrades = (payload, cb, transform = true, variator) => { - const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { - const w = openWebSocket( - `${ - variator === 'futures' - ? endpoints.futures - : variator === 'delivery' - ? endpoints.delivery - : endpoints.base - }/${symbol.toLowerCase()}@aggTrade`, - ) - w.onmessage = msg => { - const obj = JSONbig.parse(msg.data) - - cb( - transform - ? variator === 'futures' || variator === 'delivery' - ? futuresAggTradesTransform(obj) - : aggTradesTransform(obj) - : obj, - ) - } + const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { + const w = openWebSocket( + `${ + variator === 'futures' + ? endpoints.futures + : variator === 'delivery' + ? endpoints.delivery + : endpoints.base + }/${symbol.toLowerCase()}@aggTrade`, + ) + w.onmessage = msg => { + const obj = JSONbig.parse(msg.data) + + cb( + transform + ? variator === 'futures' || variator === 'delivery' + ? futuresAggTradesTransform(obj) + : aggTradesTransform(obj) + : obj, + ) + } - return w - }) + return w + }) - return options => - cache.forEach(w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options })) + return options => + cache.forEach(w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }), + ) } const futuresLiqsTransform = m => ({ - symbol: m.s, - price: m.p, - origQty: m.q, - lastFilledQty: m.l, - accumulatedQty: m.z, - averagePrice: m.ap, - status: m.X, - timeInForce: m.f, - type: m.o, - side: m.S, - time: m.T, + symbol: m.s, + price: m.p, + origQty: m.q, + lastFilledQty: m.l, + accumulatedQty: m.z, + averagePrice: m.ap, + status: m.X, + timeInForce: m.f, + type: m.o, + side: m.S, + time: m.T, }) const futuresLiquidations = (payload, cb, transform = true) => { - const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { - const w = openWebSocket(`${endpoints.futures}/${symbol.toLowerCase()}@forceOrder`) - w.onmessage = msg => { - const obj = JSONbig.parse(msg.data) + const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { + const w = openWebSocket(`${endpoints.futures}/${symbol.toLowerCase()}@forceOrder`) + w.onmessage = msg => { + const obj = JSONbig.parse(msg.data) - cb(transform ? futuresLiqsTransform(obj.o) : obj) - } + cb(transform ? futuresLiqsTransform(obj.o) : obj) + } - return w - }) + return w + }) - return options => - cache.forEach(w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options })) + return options => + cache.forEach(w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }), + ) } const futuresAllLiquidations = (cb, transform = true) => { - const w = new openWebSocket(`${endpoints.futures}/!forceOrder@arr`) + const w = new openWebSocket(`${endpoints.futures}/!forceOrder@arr`) - w.onmessage = msg => { - const obj = JSONbig.parse(msg.data) - cb(transform ? futuresLiqsTransform(obj.o) : obj) - } + w.onmessage = msg => { + const obj = JSONbig.parse(msg.data) + cb(transform ? futuresLiqsTransform(obj.o) : obj) + } - return options => w.close(1000, 'Close handle was called', { keepClosed: true, ...options }) + return options => w.close(1000, 'Close handle was called', { keepClosed: true, ...options }) } const tradesTransform = m => ({ - eventType: m.e, - eventTime: m.E, - tradeTime: m.T, - symbol: m.s, - price: m.p, - quantity: m.q, - isBuyerMaker: m.m, - maker: m.M, - tradeId: m.t, - buyerOrderId: m.b, - sellerOrderId: m.a, + eventType: m.e, + eventTime: m.E, + tradeTime: m.T, + symbol: m.s, + price: m.p, + quantity: m.q, + isBuyerMaker: m.m, + maker: m.M, + tradeId: m.t, + buyerOrderId: m.b, + sellerOrderId: m.a, }) const trades = (payload, cb, transform = true) => { - const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { - const w = openWebSocket(`${endpoints.base}/${symbol.toLowerCase()}@trade`) - w.onmessage = msg => { - const obj = JSONbig.parse(msg.data) - cb(transform ? tradesTransform(obj) : obj) - } + const cache = (Array.isArray(payload) ? payload : [payload]).map(symbol => { + const w = openWebSocket(`${endpoints.base}/${symbol.toLowerCase()}@trade`) + w.onmessage = msg => { + const obj = JSONbig.parse(msg.data) + cb(transform ? tradesTransform(obj) : obj) + } - return w - }) + return w + }) - return options => - cache.forEach(w => w.close(1000, 'Close handle was called', { keepClosed: true, ...options })) + return options => + cache.forEach(w => + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }), + ) } const userTransforms = { - // https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#balance-update - balanceUpdate: m => ({ - asset: m.a, - balanceDelta: m.d, - clearTime: m.T, - eventTime: m.E, - eventType: 'balanceUpdate', - }), - // https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#account-update - outboundAccountInfo: m => ({ - eventType: 'account', - eventTime: m.E, - makerCommissionRate: m.m, - takerCommissionRate: m.t, - buyerCommissionRate: m.b, - sellerCommissionRate: m.s, - canTrade: m.T, - canWithdraw: m.W, - canDeposit: m.D, - lastAccountUpdate: m.u, - balances: m.B.reduce((out, cur) => { - out[cur.a] = { available: cur.f, locked: cur.l } - return out - }, {}), - }), - // https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#account-update - outboundAccountPosition: m => ({ - balances: m.B.map(({ a, f, l }) => ({ asset: a, free: f, locked: l })), - eventTime: m.E, - eventType: 'outboundAccountPosition', - lastAccountUpdate: m.u, - }), - // https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#order-update - executionReport: m => ({ - eventType: 'executionReport', - eventTime: m.E, - symbol: m.s, - newClientOrderId: m.c, - originalClientOrderId: m.C, - side: m.S, - orderType: m.o, - timeInForce: m.f, - quantity: m.q, - price: m.p, - executionType: m.x, - stopPrice: m.P, - trailingDelta: m.d, - icebergQuantity: m.F, - orderStatus: m.X, - orderRejectReason: m.r, - orderId: m.i, - orderTime: m.T, - lastTradeQuantity: m.l, - totalTradeQuantity: m.z, - priceLastTrade: m.L, - commission: m.n, - commissionAsset: m.N, - tradeId: m.t, - isOrderWorking: m.w, - isBuyerMaker: m.m, - creationTime: m.O, - totalQuoteTradeQuantity: m.Z, - orderListId: m.g, - quoteOrderQuantity: m.Q, - lastQuoteTransacted: m.Y, - trailingTime: m.D, - }), - listStatus: m => ({ - eventType: 'listStatus', - eventTime: m.E, - symbol: m.s, - orderListId: m.g, - contingencyType: m.c, - listStatusType: m.l, - listOrderStatus: m.L, - listRejectReason: m.r, - listClientOrderId: m.C, - transactionTime: m.T, - orders: m.O.map(o => ({ - symbol: o.s, - orderId: o.i, - clientOrderId: o.c, - })), - }), + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#balance-update + balanceUpdate: m => ({ + asset: m.a, + balanceDelta: m.d, + clearTime: m.T, + eventTime: m.E, + eventType: 'balanceUpdate', + }), + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#account-update + outboundAccountInfo: m => ({ + eventType: 'account', + eventTime: m.E, + makerCommissionRate: m.m, + takerCommissionRate: m.t, + buyerCommissionRate: m.b, + sellerCommissionRate: m.s, + canTrade: m.T, + canWithdraw: m.W, + canDeposit: m.D, + lastAccountUpdate: m.u, + balances: m.B.reduce((out, cur) => { + out[cur.a] = { available: cur.f, locked: cur.l } + return out + }, {}), + }), + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#account-update + outboundAccountPosition: m => ({ + balances: m.B.map(({ a, f, l }) => ({ asset: a, free: f, locked: l })), + eventTime: m.E, + eventType: 'outboundAccountPosition', + lastAccountUpdate: m.u, + }), + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#order-update + executionReport: m => ({ + eventType: 'executionReport', + eventTime: m.E, + symbol: m.s, + newClientOrderId: m.c, + originalClientOrderId: m.C, + side: m.S, + orderType: m.o, + timeInForce: m.f, + quantity: m.q, + price: m.p, + executionType: m.x, + stopPrice: m.P, + trailingDelta: m.d, + icebergQuantity: m.F, + orderStatus: m.X, + orderRejectReason: m.r, + orderId: m.i, + orderTime: m.T, + lastTradeQuantity: m.l, + totalTradeQuantity: m.z, + priceLastTrade: m.L, + commission: m.n, + commissionAsset: m.N, + tradeId: m.t, + isOrderWorking: m.w, + isBuyerMaker: m.m, + creationTime: m.O, + totalQuoteTradeQuantity: m.Z, + orderListId: m.g, + quoteOrderQuantity: m.Q, + lastQuoteTransacted: m.Y, + trailingTime: m.D, + }), + listStatus: m => ({ + eventType: 'listStatus', + eventTime: m.E, + symbol: m.s, + orderListId: m.g, + contingencyType: m.c, + listStatusType: m.l, + listOrderStatus: m.L, + listRejectReason: m.r, + listClientOrderId: m.C, + transactionTime: m.T, + orders: m.O.map(o => ({ + symbol: o.s, + orderId: o.i, + clientOrderId: o.c, + })), + }), } const futuresUserTransforms = { - // https://binance-docs.github.io/apidocs/futures/en/#close-user-data-stream-user_stream - listenKeyExpired: function USER_DATA_STREAM_EXPIRED(m) { - return { - eventTime: m.E, - eventType: 'USER_DATA_STREAM_EXPIRED', - } - }, - // https://binance-docs.github.io/apidocs/futures/en/#event-margin-call - MARGIN_CALL: m => ({ - eventTime: m.E, - crossWalletBalance: m.cw, - eventType: 'MARGIN_CALL', - positions: m.p.map(cur => ({ - symbol: cur.s, - positionSide: cur.ps, - positionAmount: cur.pa, - marginType: cur.mt, - isolatedWallet: cur.iw, - markPrice: cur.mp, - unrealizedPnL: cur.up, - maintenanceMarginRequired: cur.mm, - })), - }), - // https://binance-docs.github.io/apidocs/futures/en/#event-balance-and-position-update - ACCOUNT_UPDATE: m => ({ - eventTime: m.E, - transactionTime: m.T, - eventType: 'ACCOUNT_UPDATE', - eventReasonType: m.a.m, - balances: m.a.B.map(b => ({ - asset: b.a, - walletBalance: b.wb, - crossWalletBalance: b.cw, - balanceChange: b.bc, - })), - positions: m.a.P.map(p => ({ - symbol: p.s, - positionAmount: p.pa, - entryPrice: p.ep, - accumulatedRealized: p.cr, - unrealizedPnL: p.up, - marginType: p.mt, - isolatedWallet: p.iw, - positionSide: p.ps, - })), - }), - // https://binance-docs.github.io/apidocs/futures/en/#event-order-update - ORDER_TRADE_UPDATE: m => ({ - eventType: 'ORDER_TRADE_UPDATE', - eventTime: m.E, - transactionTime: m.T, - symbol: m.o.s, - clientOrderId: m.o.c, - side: m.o.S, - orderType: m.o.o, - timeInForce: m.o.f, - quantity: m.o.q, - price: m.o.p, - averagePrice: m.o.ap, - stopPrice: m.o.sp, - executionType: m.o.x, - orderStatus: m.o.X, - orderId: m.o.i, - lastTradeQuantity: m.o.l, - totalTradeQuantity: m.o.z, - priceLastTrade: m.o.L, - commissionAsset: m.o.N, - commission: m.o.n, - orderTime: m.o.T, - tradeId: m.o.t, - bidsNotional: m.o.b, - asksNotional: m.o.a, - isMaker: m.o.m, - isReduceOnly: m.o.R, - workingType: m.o.wt, - originalOrderType: m.o.ot, - positionSide: m.o.ps, - closePosition: m.o.cp, - activationPrice: m.o.AP, - callbackRate: m.o.cr, - realizedProfit: m.o.rp, - }), - // https://binance-docs.github.io/apidocs/futures/en/#event-account-configuration-update-previous-leverage-update - ACCOUNT_CONFIG_UPDATE: m => ({ - eventType: 'ACCOUNT_CONFIG_UPDATE', - eventTime: m.E, - transactionTime: m.T, - type: m.ac ? 'ACCOUNT_CONFIG' : 'MULTI_ASSETS', - ...(m.ac - ? { - symbol: m.ac.s, - leverage: m.ac.l, + // https://binance-docs.github.io/apidocs/futures/en/#close-user-data-stream-user_stream + listenKeyExpired: function USER_DATA_STREAM_EXPIRED(m) { + return { + eventTime: m.E, + eventType: 'USER_DATA_STREAM_EXPIRED', } - : { - multiAssets: m.ai.j, - }), - }), + }, + // https://binance-docs.github.io/apidocs/futures/en/#event-margin-call + MARGIN_CALL: m => ({ + eventTime: m.E, + crossWalletBalance: m.cw, + eventType: 'MARGIN_CALL', + positions: m.p.map(cur => ({ + symbol: cur.s, + positionSide: cur.ps, + positionAmount: cur.pa, + marginType: cur.mt, + isolatedWallet: cur.iw, + markPrice: cur.mp, + unrealizedPnL: cur.up, + maintenanceMarginRequired: cur.mm, + })), + }), + // https://binance-docs.github.io/apidocs/futures/en/#event-balance-and-position-update + ACCOUNT_UPDATE: m => ({ + eventTime: m.E, + transactionTime: m.T, + eventType: 'ACCOUNT_UPDATE', + eventReasonType: m.a.m, + balances: m.a.B.map(b => ({ + asset: b.a, + walletBalance: b.wb, + crossWalletBalance: b.cw, + balanceChange: b.bc, + })), + positions: m.a.P.map(p => ({ + symbol: p.s, + positionAmount: p.pa, + entryPrice: p.ep, + accumulatedRealized: p.cr, + unrealizedPnL: p.up, + marginType: p.mt, + isolatedWallet: p.iw, + positionSide: p.ps, + })), + }), + // https://binance-docs.github.io/apidocs/futures/en/#event-order-update + ORDER_TRADE_UPDATE: m => ({ + eventType: 'ORDER_TRADE_UPDATE', + eventTime: m.E, + transactionTime: m.T, + symbol: m.o.s, + clientOrderId: m.o.c, + side: m.o.S, + orderType: m.o.o, + timeInForce: m.o.f, + quantity: m.o.q, + price: m.o.p, + averagePrice: m.o.ap, + stopPrice: m.o.sp, + executionType: m.o.x, + orderStatus: m.o.X, + orderId: m.o.i, + lastTradeQuantity: m.o.l, + totalTradeQuantity: m.o.z, + priceLastTrade: m.o.L, + commissionAsset: m.o.N, + commission: m.o.n, + orderTime: m.o.T, + tradeId: m.o.t, + bidsNotional: m.o.b, + asksNotional: m.o.a, + isMaker: m.o.m, + isReduceOnly: m.o.R, + workingType: m.o.wt, + originalOrderType: m.o.ot, + positionSide: m.o.ps, + closePosition: m.o.cp, + activationPrice: m.o.AP, + callbackRate: m.o.cr, + realizedProfit: m.o.rp, + }), + // https://binance-docs.github.io/apidocs/futures/en/#event-account-configuration-update-previous-leverage-update + ACCOUNT_CONFIG_UPDATE: m => ({ + eventType: 'ACCOUNT_CONFIG_UPDATE', + eventTime: m.E, + transactionTime: m.T, + type: m.ac ? 'ACCOUNT_CONFIG' : 'MULTI_ASSETS', + ...(m.ac + ? { + symbol: m.ac.s, + leverage: m.ac.l, + } + : { + multiAssets: m.ai.j, + }), + }), } export const userEventHandler = - (cb, transform = true, variator) => - msg => { - const { e: type, ...rest } = JSONbig.parse(msg.data) - - cb( - variator === 'futures' || variator === 'delivery' - ? transform && futuresUserTransforms[type] - ? futuresUserTransforms[type](rest) - : { type, ...rest } - : transform && userTransforms[type] - ? userTransforms[type](rest) - : { type, ...rest }, - ) - } + (cb, transform = true, variator) => + msg => { + const { e: type, ...rest } = JSONbig.parse(msg.data) + + cb( + variator === 'futures' || variator === 'delivery' + ? transform && futuresUserTransforms[type] + ? futuresUserTransforms[type](rest) + : { type, ...rest } + : transform && userTransforms[type] + ? userTransforms[type](rest) + : { type, ...rest }, + ) + } const userOpenHandler = - (cb, transform = true) => - () => { - cb({ [transform ? 'eventType' : 'type']: 'open' }) - } + (cb, transform = true) => + () => { + cb({ [transform ? 'eventType' : 'type']: 'open' }) + } const userErrorHandler = - (cb, transform = true) => - error => { - cb({ [transform ? 'eventType' : 'type']: 'error', error }) - } + (cb, transform = true) => + error => { + cb({ [transform ? 'eventType' : 'type']: 'error', error }) + } const STREAM_METHODS = ['get', 'keep', 'close'] const capitalize = (str, check) => (check ? `${str[0].toUpperCase()}${str.slice(1)}` : str) const getStreamMethods = (opts, variator = '') => { - const methods = httpMethods(opts) + const methods = httpMethods(opts) - return STREAM_METHODS.reduce( - (acc, key) => [...acc, methods[`${variator}${capitalize(`${key}DataStream`, !!variator)}`]], - [], - ) + return STREAM_METHODS.reduce( + (acc, key) => [...acc, methods[`${variator}${capitalize(`${key}DataStream`, !!variator)}`]], + [], + ) } export const keepStreamAlive = (method, listenKey) => method({ listenKey }) const user = (opts, variator) => (cb, transform) => { - const [getDataStream, keepDataStream, closeDataStream] = getStreamMethods(opts, variator) - - let currentListenKey = null - let int = null - let w = null - let keepClosed = false - const errorHandler = userErrorHandler(cb, transform) - - const keepAlive = isReconnecting => { - if (currentListenKey) { - keepStreamAlive(keepDataStream, currentListenKey).catch(err => { - closeStream({}, true) - - if (isReconnecting) { - setTimeout(() => makeStream(true), 30e3) - } else { - makeStream(true) + const [getDataStream, keepDataStream, closeDataStream] = getStreamMethods(opts, variator) + + let currentListenKey = null + let int = null + let w = null + let keepClosed = false + const errorHandler = userErrorHandler(cb, transform) + + const keepAlive = isReconnecting => { + if (currentListenKey) { + keepStreamAlive(keepDataStream, currentListenKey).catch(err => { + closeStream({}, true) + + if (isReconnecting) { + setTimeout(() => makeStream(true), 30e3) + } else { + makeStream(true) + } + + if (opts.emitStreamErrors) { + errorHandler(err) + } + }) } - - if (opts.emitStreamErrors) { - errorHandler(err) - } - }) } - } - const closeStream = (options, catchErrors, setKeepClosed = false) => { - keepClosed = setKeepClosed - - if (!currentListenKey) { - return Promise.resolve() - } + const closeStream = (options, catchErrors, setKeepClosed = false) => { + keepClosed = setKeepClosed - clearInterval(int) + if (!currentListenKey) { + return Promise.resolve() + } - const p = closeDataStream({ listenKey: currentListenKey }) + clearInterval(int) - if (catchErrors) { - p.catch(f => f) - } + const p = closeDataStream({ listenKey: currentListenKey }) - w.close(1000, 'Close handle was called', { keepClosed: true, ...options }) - currentListenKey = null + if (catchErrors) { + p.catch(f => f) + } - return p - } + w.close(1000, 'Close handle was called', { keepClosed: true, ...options }) + currentListenKey = null - const makeStream = isReconnecting => { - return ( - !keepClosed && - getDataStream() - .then(({ listenKey }) => { - if (keepClosed) { - return closeDataStream({ listenKey }).catch(f => f) - } + return p + } - w = openWebSocket( - `${ - variator === 'futures' - ? endpoints.futures - : variator === 'delivery' - ? endpoints.delivery - : endpoints.base - }/${listenKey}`, - ) - - w.onmessage = msg => userEventHandler(cb, transform, variator)(msg) - if (opts.emitSocketOpens) { - w.onopen = () => userOpenHandler(cb, transform)() - } - if (opts.emitSocketErrors) { - w.onerror = ({ error }) => errorHandler(error) - } - - currentListenKey = listenKey - - int = setInterval(() => keepAlive(false), 50e3) - - keepAlive(true) - - return options => closeStream(options, false, true) - }) - .catch(err => { - if (isReconnecting) { - if (!keepClosed) { - setTimeout(() => makeStream(true), 30e3) - } - - if (opts.emitStreamErrors) { - errorHandler(err) - } - } else { - throw err - } - }) - ) - } + const makeStream = isReconnecting => { + return ( + !keepClosed && + getDataStream() + .then(({ listenKey }) => { + if (keepClosed) { + return closeDataStream({ listenKey }).catch(f => f) + } + + w = openWebSocket( + `${ + variator === 'futures' + ? endpoints.futures + : variator === 'delivery' + ? endpoints.delivery + : endpoints.base + }/${listenKey}`, + ) + + w.onmessage = msg => userEventHandler(cb, transform, variator)(msg) + if (opts.emitSocketOpens) { + w.onopen = () => userOpenHandler(cb, transform)() + } + if (opts.emitSocketErrors) { + w.onerror = ({ error }) => errorHandler(error) + } + + currentListenKey = listenKey + + int = setInterval(() => keepAlive(false), 50e3) + + keepAlive(true) + + return options => closeStream(options, false, true) + }) + .catch(err => { + if (isReconnecting) { + if (!keepClosed) { + setTimeout(() => makeStream(true), 30e3) + } + + if (opts.emitStreamErrors) { + errorHandler(err) + } + } else { + throw err + } + }) + ) + } - return makeStream(false) + return makeStream(false) } const futuresAllMarkPricesTransform = m => - m.map(x => ({ - eventType: x.e, - eventTime: x.E, - symbol: x.s, - markPrice: x.p, - indexPrice: x.i, - settlePrice: x.P, - fundingRate: x.r, - nextFundingRate: x.T, - })) + m.map(x => ({ + eventType: x.e, + eventTime: x.E, + symbol: x.s, + markPrice: x.p, + indexPrice: x.i, + settlePrice: x.P, + fundingRate: x.r, + nextFundingRate: x.T, + })) const futuresAllMarkPrices = (payload, cb, transform = true) => { - const variant = payload.updateSpeed === '1s' ? '!markPrice@arr@1s' : '!markPrice@arr' + const variant = payload.updateSpeed === '1s' ? '!markPrice@arr@1s' : '!markPrice@arr' - const w = openWebSocket(`${endpoints.futures}/${variant}`) + const w = openWebSocket(`${endpoints.futures}/${variant}`) - w.onmessage = msg => { - const arr = JSONbig.parse(msg.data) - cb(transform ? futuresAllMarkPricesTransform(arr) : arr) - } + w.onmessage = msg => { + const arr = JSONbig.parse(msg.data) + cb(transform ? futuresAllMarkPricesTransform(arr) : arr) + } - return options => w.close(1000, 'Close handle was called', { keepClosed: true, ...options }) + return options => w.close(1000, 'Close handle was called', { keepClosed: true, ...options }) } export default opts => { - if (opts && opts.wsBase) endpoints.base = opts.wsBase - if (opts && opts.wsFutures) endpoints.futures = opts.wsFutures - if (opts && opts.wsDelivery) endpoints.delivery = opts.wsDelivery - - if (opts && opts.proxy) { - wsOptions.proxy = opts.proxy - } - - return { - depth, - partialDepth, - candles, - trades, - aggTrades, - bookTicker, - ticker, - allTickers, - miniTicker, - allMiniTickers, - customSubStream, - user: user(opts), - - marginUser: user(opts, 'margin'), - - futuresDepth: (payload, cb, transform) => depth(payload, cb, transform, 'futures'), - deliveryDepth: (payload, cb, transform) => depth(payload, cb, transform, 'delivery'), - futuresPartialDepth: (payload, cb, transform) => - partialDepth(payload, cb, transform, 'futures'), - deliveryPartialDepth: (payload, cb, transform) => - partialDepth(payload, cb, transform, 'delivery'), - futuresCandles: (payload, interval, cb, transform) => - candles(payload, interval, cb, transform, 'futures'), - deliveryCandles: (payload, interval, cb, transform) => - candles(payload, interval, cb, transform, 'delivery'), - futuresTicker: (payload, cb, transform) => ticker(payload, cb, transform, 'futures'), - deliveryTicker: (payload, cb, transform) => ticker(payload, cb, transform, 'delivery'), - futuresAllTickers: (cb, transform) => allTickers(cb, transform, 'futures'), - deliveryAllTickers: (cb, transform) => allTickers(cb, transform, 'delivery'), - futuresAggTrades: (payload, cb, transform) => aggTrades(payload, cb, transform, 'futures'), - deliveryAggTrades: (payload, cb, transform) => aggTrades(payload, cb, transform, 'delivery'), - futuresLiquidations, - futuresAllLiquidations, - futuresUser: user(opts, 'futures'), - deliveryUser: user(opts, 'delivery'), - futuresCustomSubStream: (payload, cb) => customSubStream(payload, cb, 'futures'), - deliveryCustomSubStream: (payload, cb) => customSubStream(payload, cb, 'delivery'), - futuresAllMarkPrices: (payload, cb) => futuresAllMarkPrices(payload, cb), - } + if (opts && opts.wsBase) endpoints.base = opts.wsBase + if (opts && opts.wsFutures) endpoints.futures = opts.wsFutures + if (opts && opts.wsDelivery) endpoints.delivery = opts.wsDelivery + + if (opts && opts.proxy) { + wsOptions.proxy = opts.proxy + } + + return { + depth, + partialDepth, + candles, + trades, + aggTrades, + bookTicker, + ticker, + allTickers, + miniTicker, + allMiniTickers, + customSubStream, + user: user(opts), + + marginUser: user(opts, 'margin'), + + futuresDepth: (payload, cb, transform) => depth(payload, cb, transform, 'futures'), + deliveryDepth: (payload, cb, transform) => depth(payload, cb, transform, 'delivery'), + futuresPartialDepth: (payload, cb, transform) => + partialDepth(payload, cb, transform, 'futures'), + deliveryPartialDepth: (payload, cb, transform) => + partialDepth(payload, cb, transform, 'delivery'), + futuresCandles: (payload, interval, cb, transform) => + candles(payload, interval, cb, transform, 'futures'), + deliveryCandles: (payload, interval, cb, transform) => + candles(payload, interval, cb, transform, 'delivery'), + futuresTicker: (payload, cb, transform) => ticker(payload, cb, transform, 'futures'), + deliveryTicker: (payload, cb, transform) => ticker(payload, cb, transform, 'delivery'), + futuresAllTickers: (cb, transform) => allTickers(cb, transform, 'futures'), + deliveryAllTickers: (cb, transform) => allTickers(cb, transform, 'delivery'), + futuresAggTrades: (payload, cb, transform) => aggTrades(payload, cb, transform, 'futures'), + deliveryAggTrades: (payload, cb, transform) => + aggTrades(payload, cb, transform, 'delivery'), + futuresLiquidations, + futuresAllLiquidations, + futuresUser: user(opts, 'futures'), + deliveryUser: user(opts, 'delivery'), + futuresCustomSubStream: (payload, cb) => customSubStream(payload, cb, 'futures'), + deliveryCustomSubStream: (payload, cb) => customSubStream(payload, cb, 'delivery'), + futuresAllMarkPrices: (payload, cb) => futuresAllMarkPrices(payload, cb), + } } diff --git a/test/auth.js b/test/auth.js index b842da47..7d8fd2ed 100644 --- a/test/auth.js +++ b/test/auth.js @@ -8,243 +8,243 @@ import { checkFields } from './utils' dotenv.config() const main = () => { - if (!process.env.API_KEY || !process.env.API_SECRET) { - return test('[AUTH] ⚠️ Skipping tests.', t => { - t.log('Provide an API_KEY and API_SECRET to run them.') - t.pass() - }) - } - - const client = Binance({ - apiKey: process.env.API_KEY, - apiSecret: process.env.API_SECRET, - }) - - test('[REST] order', async t => { - try { - await client.orderTest({ - symbol: 'ETHBTC', - side: 'BUY', - quantity: 1, - price: 1, - }) - } catch (e) { - t.is(e.message, 'Filter failure: PERCENT_PRICE') + if (!process.env.API_KEY || !process.env.API_SECRET) { + return test('[AUTH] ⚠️ Skipping tests.', t => { + t.log('Provide an API_KEY and API_SECRET to run them.') + t.pass() + }) } - await client.orderTest({ - symbol: 'ETHBTC', - side: 'BUY', - quantity: 1, - type: 'MARKET', - }) - - t.pass() - }) - - test('[REST] make a MARKET order with quoteOrderQty', async t => { - try { - await client.orderTest({ - symbol: 'ETHBTC', - side: 'BUY', - quoteOrderQty: 10, - type: 'MARKET', - }) - } catch (e) { - t.is(e.message, 'Filter failure: PERCENT_PRICE') - } + const client = Binance({ + apiKey: process.env.API_KEY, + apiSecret: process.env.API_SECRET, + }) - await client.orderTest({ - symbol: 'ETHBTC', - side: 'BUY', - quantity: 1, - type: 'MARKET', + test('[REST] order', async t => { + try { + await client.orderTest({ + symbol: 'ETHBTC', + side: 'BUY', + quantity: 1, + price: 1, + }) + } catch (e) { + t.is(e.message, 'Filter failure: PERCENT_PRICE') + } + + await client.orderTest({ + symbol: 'ETHBTC', + side: 'BUY', + quantity: 1, + type: 'MARKET', + }) + + t.pass() }) - t.pass() - }) + test('[REST] make a MARKET order with quoteOrderQty', async t => { + try { + await client.orderTest({ + symbol: 'ETHBTC', + side: 'BUY', + quoteOrderQty: 10, + type: 'MARKET', + }) + } catch (e) { + t.is(e.message, 'Filter failure: PERCENT_PRICE') + } + + await client.orderTest({ + symbol: 'ETHBTC', + side: 'BUY', + quantity: 1, + type: 'MARKET', + }) + + t.pass() + }) - test('[REST] allOrders / getOrder', async t => { - try { - await client.getOrder({ symbol: 'ASTETH' }) - } catch (e) { - t.is( - e.message, - "Param 'origClientOrderId' or 'orderId' must be sent, but both were empty/null!", - ) - } + test('[REST] allOrders / getOrder', async t => { + try { + await client.getOrder({ symbol: 'ASTETH' }) + } catch (e) { + t.is( + e.message, + "Param 'origClientOrderId' or 'orderId' must be sent, but both were empty/null!", + ) + } + + try { + await client.getOrder({ symbol: 'ASTETH', orderId: 1 }) + } catch (e) { + t.is(e.message, 'Order does not exist.') + } + + // Note that this test will fail if you don't have any ETH/BTC order in your account + const orders = await client.allOrders({ + symbol: 'ETHBTC', + }) + + t.true(Array.isArray(orders)) + t.truthy(orders.length) + + const [order] = orders + + checkFields(t, order, ['orderId', 'symbol', 'price', 'type', 'side']) + + const res = await client.getOrder({ + symbol: 'ETHBTC', + orderId: order.orderId, + }) + + t.truthy(res) + checkFields(t, res, ['orderId', 'symbol', 'price', 'type', 'side']) + }) - try { - await client.getOrder({ symbol: 'ASTETH', orderId: 1 }) - } catch (e) { - t.is(e.message, 'Order does not exist.') - } + test('[REST] allOrdersOCO', async t => { + const orderLists = await client.allOrdersOCO({ + timestamp: new Date().getTime(), + }) + + t.true(Array.isArray(orderLists)) + + if (orderLists.length) { + const [orderList] = orderLists + checkFields(t, orderList, [ + 'orderListId', + 'symbol', + 'transactionTime', + 'listStatusType', + 'orders', + ]) + } + }) + + test('[REST] getOrder with useServerTime', async t => { + const orders = await client.allOrders({ + symbol: 'ETHBTC', + useServerTime: true, + }) - // Note that this test will fail if you don't have any ETH/BTC order in your account - const orders = await client.allOrders({ - symbol: 'ETHBTC', + t.true(Array.isArray(orders)) + t.truthy(orders.length) }) - t.true(Array.isArray(orders)) - t.truthy(orders.length) + test('[REST] openOrders', async t => { + const orders = await client.openOrders({ + symbol: 'ETHBTC', + }) - const [order] = orders + t.true(Array.isArray(orders)) + }) - checkFields(t, order, ['orderId', 'symbol', 'price', 'type', 'side']) + test('[REST] cancelOrder', async t => { + try { + await client.cancelOrder({ symbol: 'ETHBTC', orderId: 1 }) + } catch (e) { + t.is(e.message, 'Unknown order sent.') + } + }) - const res = await client.getOrder({ - symbol: 'ETHBTC', - orderId: order.orderId, + test('[REST] cancelOpenOrders', async t => { + try { + await client.cancelOpenOrders({ symbol: 'ETHBTC' }) + } catch (e) { + t.is(e.message, 'Unknown order sent.') + } }) - t.truthy(res) - checkFields(t, res, ['orderId', 'symbol', 'price', 'type', 'side']) - }) + test('[REST] accountInfo', async t => { + const account = await client.accountInfo() + t.truthy(account) + checkFields(t, account, ['makerCommission', 'takerCommission', 'balances']) + t.truthy(account.balances.length) + }) - test('[REST] allOrdersOCO', async t => { - const orderLists = await client.allOrdersOCO({ - timestamp: new Date().getTime(), + test('[REST] tradeFee', async t => { + const tfee = (await client.tradeFee()).tradeFee + t.truthy(tfee) + t.truthy(tfee.length) + checkFields(t, tfee[0], ['symbol', 'maker', 'taker']) }) - t.true(Array.isArray(orderLists)) + test('[REST] depositHistory', async t => { + const history = await client.depositHistory() + t.true(history.success) + t.truthy(Array.isArray(history.depositList)) + }) - if (orderLists.length) { - const [orderList] = orderLists - checkFields(t, orderList, [ - 'orderListId', - 'symbol', - 'transactionTime', - 'listStatusType', - 'orders', - ]) - } - }) + test('[REST] withdrawHistory', async t => { + const history = await client.withdrawHistory() + t.true(history.success) + t.is(typeof history.withdrawList.length, 'number') + }) - test('[REST] getOrder with useServerTime', async t => { - const orders = await client.allOrders({ - symbol: 'ETHBTC', - useServerTime: true, + test('[REST] depositAddress', async t => { + const out = await client.depositAddress({ asset: 'ETH' }) + t.true(out.success) + t.is(out.asset, 'ETH') + t.truthy(out.address) }) - t.true(Array.isArray(orders)) - t.truthy(orders.length) - }) + test('[REST] myTrades', async t => { + const trades = await client.myTrades({ symbol: 'ETHBTC' }) + t.true(Array.isArray(trades)) + const [trade] = trades + checkFields(t, trade, ['id', 'orderId', 'qty', 'commission', 'time']) + }) - test('[REST] openOrders', async t => { - const orders = await client.openOrders({ - symbol: 'ETHBTC', + test('[REST] tradesHistory', async t => { + const trades = await client.tradesHistory({ symbol: 'ETHBTC', fromId: 28457 }) + t.is(trades.length, 500) }) - t.true(Array.isArray(orders)) - }) + test('[REST] error code', async t => { + try { + await client.orderTest({ + symbol: 'TRXETH', + side: 'SELL', + type: 'LIMIT', + quantity: '-1337.00000000', + price: '1.00000000', + }) + } catch (e) { + t.is(e.code, -1100) + } + }) - test('[REST] cancelOrder', async t => { - try { - await client.cancelOrder({ symbol: 'ETHBTC', orderId: 1 }) - } catch (e) { - t.is(e.message, 'Unknown order sent.') - } - }) + test('[WS] user', async t => { + const clean = await client.ws.user() + t.truthy(clean) + t.true(typeof clean === 'function') + }) - test('[REST] cancelOpenOrders', async t => { - try { - await client.cancelOpenOrders({ symbol: 'ETHBTC' }) - } catch (e) { - t.is(e.message, 'Unknown order sent.') - } - }) - - test('[REST] accountInfo', async t => { - const account = await client.accountInfo() - t.truthy(account) - checkFields(t, account, ['makerCommission', 'takerCommission', 'balances']) - t.truthy(account.balances.length) - }) - - test('[REST] tradeFee', async t => { - const tfee = (await client.tradeFee()).tradeFee - t.truthy(tfee) - t.truthy(tfee.length) - checkFields(t, tfee[0], ['symbol', 'maker', 'taker']) - }) - - test('[REST] depositHistory', async t => { - const history = await client.depositHistory() - t.true(history.success) - t.truthy(Array.isArray(history.depositList)) - }) - - test('[REST] withdrawHistory', async t => { - const history = await client.withdrawHistory() - t.true(history.success) - t.is(typeof history.withdrawList.length, 'number') - }) - - test('[REST] depositAddress', async t => { - const out = await client.depositAddress({ asset: 'ETH' }) - t.true(out.success) - t.is(out.asset, 'ETH') - t.truthy(out.address) - }) - - test('[REST] myTrades', async t => { - const trades = await client.myTrades({ symbol: 'ETHBTC' }) - t.true(Array.isArray(trades)) - const [trade] = trades - checkFields(t, trade, ['id', 'orderId', 'qty', 'commission', 'time']) - }) - - test('[REST] tradesHistory', async t => { - const trades = await client.tradesHistory({ symbol: 'ETHBTC', fromId: 28457 }) - t.is(trades.length, 500) - }) - - test('[REST] error code', async t => { - try { - await client.orderTest({ - symbol: 'TRXETH', - side: 'SELL', - type: 'LIMIT', - quantity: '-1337.00000000', - price: '1.00000000', - }) - } catch (e) { - t.is(e.code, -1100) - } - }) - - test('[WS] user', async t => { - const clean = await client.ws.user() - t.truthy(clean) - t.true(typeof clean === 'function') - }) - - test('[FUTURES-REST] walletBalance', async t => { - const walletBalance = await client.futuresAccountBalance() - t.truthy(walletBalance) - checkFields(t, walletBalance[0], [ - 'asset', - 'balance', - 'crossWalletBalance', - 'crossUnPnl', - 'availableBalance', - 'maxWithdrawAmount', - ]) - }) - - test('[DELIVERY-REST] walletBalance', async t => { - const walletBalance = await client.deliveryAccountBalance() - t.truthy(walletBalance) - checkFields(t, walletBalance[0], [ - 'asset', - 'balance', - 'withdrawAvailable', - 'crossWalletBalance', - 'crossUnPnl', - 'availableBalance', - 'maxWithdrawAmount', - ]) - }) + test('[FUTURES-REST] walletBalance', async t => { + const walletBalance = await client.futuresAccountBalance() + t.truthy(walletBalance) + checkFields(t, walletBalance[0], [ + 'asset', + 'balance', + 'crossWalletBalance', + 'crossUnPnl', + 'availableBalance', + 'maxWithdrawAmount', + ]) + }) + + test('[DELIVERY-REST] walletBalance', async t => { + const walletBalance = await client.deliveryAccountBalance() + t.truthy(walletBalance) + checkFields(t, walletBalance[0], [ + 'asset', + 'balance', + 'withdrawAvailable', + 'crossWalletBalance', + 'crossUnPnl', + 'availableBalance', + 'maxWithdrawAmount', + ]) + }) } main() diff --git a/test/index.js b/test/index.js index 6d3e6a07..eea45135 100644 --- a/test/index.js +++ b/test/index.js @@ -9,933 +9,941 @@ import { checkFields, createHttpServer } from './utils' const client = Binance() test('[MISC] Some error codes are defined', t => { - t.truthy(ErrorCodes, 'The map is there') - t.truthy(ErrorCodes.TOO_MANY_ORDERS, 'And we have this') + t.truthy(ErrorCodes, 'The map is there') + t.truthy(ErrorCodes.TOO_MANY_ORDERS, 'And we have this') }) test('[REST] ping', async t => { - t.truthy(await client.ping(), 'A simple ping should work') + t.truthy(await client.ping(), 'A simple ping should work') }) test('[REST] time', async t => { - const ts = await client.time() - t.truthy(new Date(ts).getTime() > 0, 'The returned timestamp should be valid') + const ts = await client.time() + t.truthy(new Date(ts).getTime() > 0, 'The returned timestamp should be valid') }) test('[REST] exchangeInfo', async t => { - const res = await client.exchangeInfo() - checkFields(t, res, ['timezone', 'serverTime', 'rateLimits', 'symbols']) + const res = await client.exchangeInfo() + checkFields(t, res, ['timezone', 'serverTime', 'rateLimits', 'symbols']) }) test('[REST] book', async t => { - try { - await client.book() - } catch (e) { - t.is(e.message, 'You need to pass a payload object.') - } + try { + await client.book() + } catch (e) { + t.is(e.message, 'You need to pass a payload object.') + } - try { - await client.book({}) - } catch (e) { - t.is(e.message, 'Method book requires symbol parameter.') - } + try { + await client.book({}) + } catch (e) { + t.is(e.message, 'Method book requires symbol parameter.') + } - const book = await client.book({ symbol: 'ETHBTC' }) - t.truthy(book.lastUpdateId) - t.truthy(book.asks.length) - t.truthy(book.bids.length) + const book = await client.book({ symbol: 'ETHBTC' }) + t.truthy(book.lastUpdateId) + t.truthy(book.asks.length) + t.truthy(book.bids.length) - const [bid] = book.bids - t.truthy(typeof bid.price === 'string') - t.truthy(typeof bid.quantity === 'string') + const [bid] = book.bids + t.truthy(typeof bid.price === 'string') + t.truthy(typeof bid.quantity === 'string') }) test('[REST] candles', async t => { - try { - await client.candles({}) - } catch (e) { - t.is(e.message, 'Method candles requires symbol parameter.') - } + try { + await client.candles({}) + } catch (e) { + t.is(e.message, 'Method candles requires symbol parameter.') + } - const candles = await client.candles({ symbol: 'ETHBTC' }) + const candles = await client.candles({ symbol: 'ETHBTC' }) - t.truthy(candles.length) + t.truthy(candles.length) - const [candle] = candles - checkFields(t, candle, candleFields) + const [candle] = candles + checkFields(t, candle, candleFields) }) test('[REST] aggTrades', async t => { - try { - await client.aggTrades({}) - } catch (e) { - t.is(e.message, 'Method aggTrades requires symbol parameter.') - } + try { + await client.aggTrades({}) + } catch (e) { + t.is(e.message, 'Method aggTrades requires symbol parameter.') + } - const trades = await client.aggTrades({ symbol: 'ETHBTC' }) - t.truthy(trades.length) + const trades = await client.aggTrades({ symbol: 'ETHBTC' }) + t.truthy(trades.length) - const [trade] = trades - t.truthy(trade.aggId) - t.truthy(trade.symbol) + const [trade] = trades + t.truthy(trade.aggId) + t.truthy(trade.symbol) }) test('[REST] trades', async t => { - const trades = await client.trades({ symbol: 'ETHBTC' }) - t.is(trades.length, 500) + const trades = await client.trades({ symbol: 'ETHBTC' }) + t.is(trades.length, 500) }) test('[REST] dailyStats', async t => { - const res = await client.dailyStats({ symbol: 'ETHBTC' }) - t.truthy(res) - checkFields(t, res, ['highPrice', 'lowPrice', 'volume', 'priceChange']) + const res = await client.dailyStats({ symbol: 'ETHBTC' }) + t.truthy(res) + checkFields(t, res, ['highPrice', 'lowPrice', 'volume', 'priceChange']) }) test('[REST] prices', async t => { - const prices = await client.prices() - t.truthy(prices) - t.truthy(prices.ETHBTC) + const prices = await client.prices() + t.truthy(prices) + t.truthy(prices.ETHBTC) }) test('[REST] individual price', async t => { - const prices = await client.prices({ symbol: 'ETHUSDT' }) - t.truthy(prices) - t.truthy(prices.ETHUSDT) + const prices = await client.prices({ symbol: 'ETHUSDT' }) + t.truthy(prices) + t.truthy(prices.ETHUSDT) }) test('[REST] avgPrice', async t => { - const res = await client.avgPrice({ symbol: 'ETHBTC' }) - t.truthy(res) - checkFields(t, res, ['mins', 'price']) + const res = await client.avgPrice({ symbol: 'ETHBTC' }) + t.truthy(res) + checkFields(t, res, ['mins', 'price']) }) test('[REST] allBookTickers', async t => { - const tickers = await client.allBookTickers() - t.truthy(tickers) - t.truthy(tickers.ETHBTC) + const tickers = await client.allBookTickers() + t.truthy(tickers) + t.truthy(tickers.ETHBTC) }) test('[REST] Signed call without creds', async t => { - try { - await client.accountInfo() - } catch (e) { - t.is(e.message, 'You need to pass an API key and secret to make authenticated calls.') - } + try { + await client.accountInfo() + } catch (e) { + t.is(e.message, 'You need to pass an API key and secret to make authenticated calls.') + } }) test('[REST] Signed call without creds - attempt getting tradeFee', async t => { - try { - await client.tradeFee() - } catch (e) { - t.is(e.message, 'You need to pass an API key and secret to make authenticated calls.') - } + try { + await client.tradeFee() + } catch (e) { + t.is(e.message, 'You need to pass an API key and secret to make authenticated calls.') + } }) test('[REST] Server-side JSON error', async t => { - const server = createHttpServer((req, res) => { - res.statusCode = 500 - res.write( - JSON.stringify({ - msg: 'Server unkown error', - code: -1337, - }), - ) - res.end() - }) - const localClient = Binance({ httpBase: server.url }) - - try { - await server.start() - await localClient.ping() - t.fail('did not throw') - } catch (e) { - t.is(e.message, 'Server unkown error') - t.is(e.code, -1337) - } finally { - await server.stop() - } + const server = createHttpServer((req, res) => { + res.statusCode = 500 + res.write( + JSON.stringify({ + msg: 'Server unkown error', + code: -1337, + }), + ) + res.end() + }) + const localClient = Binance({ httpBase: server.url }) + + try { + await server.start() + await localClient.ping() + t.fail('did not throw') + } catch (e) { + t.is(e.message, 'Server unkown error') + t.is(e.code, -1337) + } finally { + await server.stop() + } }) test('[REST] Server-side HTML error', async t => { - const serverReponse = 'Server Internal Error' - const server = createHttpServer((req, res) => { - res.statusCode = 500 - res.write(serverReponse) - res.end() - }) - const localClient = Binance({ httpBase: server.url }) - - try { - await server.start() - await localClient.ping() - t.fail('did not throw') - } catch (e) { - t.is(e.message, `500 Internal Server Error ${serverReponse}`) - t.truthy(e.response) - t.is(e.responseText, serverReponse) - } finally { - await server.stop() - } + const serverReponse = 'Server Internal Error' + const server = createHttpServer((req, res) => { + res.statusCode = 500 + res.write(serverReponse) + res.end() + }) + const localClient = Binance({ httpBase: server.url }) + + try { + await server.start() + await localClient.ping() + t.fail('did not throw') + } catch (e) { + t.is(e.message, `500 Internal Server Error ${serverReponse}`) + t.truthy(e.response) + t.is(e.responseText, serverReponse) + } finally { + await server.stop() + } }) test('[WS] depth', t => { - return new Promise(resolve => { - client.ws.depth('ETHBTC', depth => { - checkFields(t, depth, [ - 'eventType', - 'eventTime', - 'firstUpdateId', - 'finalUpdateId', - 'symbol', - 'bidDepth', - 'askDepth', - ]) - resolve() + return new Promise(resolve => { + client.ws.depth('ETHBTC', depth => { + checkFields(t, depth, [ + 'eventType', + 'eventTime', + 'firstUpdateId', + 'finalUpdateId', + 'symbol', + 'bidDepth', + 'askDepth', + ]) + resolve() + }) }) - }) }) test('[WS] depth with update speed', t => { - return new Promise(resolve => { - client.ws.depth('ETHBTC@100ms', depth => { - checkFields(t, depth, [ - 'eventType', - 'eventTime', - 'firstUpdateId', - 'finalUpdateId', - 'symbol', - 'bidDepth', - 'askDepth', - ]) - resolve() + return new Promise(resolve => { + client.ws.depth('ETHBTC@100ms', depth => { + checkFields(t, depth, [ + 'eventType', + 'eventTime', + 'firstUpdateId', + 'finalUpdateId', + 'symbol', + 'bidDepth', + 'askDepth', + ]) + resolve() + }) }) - }) }) test('[WS] partial depth', t => { - return new Promise(resolve => { - client.ws.partialDepth({ symbol: 'ETHBTC', level: 10 }, depth => { - checkFields(t, depth, ['lastUpdateId', 'bids', 'asks']) - resolve() + return new Promise(resolve => { + client.ws.partialDepth({ symbol: 'ETHBTC', level: 10 }, depth => { + checkFields(t, depth, ['lastUpdateId', 'bids', 'asks']) + resolve() + }) }) - }) }) test('[WS] partial depth with update speed', t => { - return new Promise(resolve => { - client.ws.partialDepth({ symbol: 'ETHBTC@100ms', level: 10 }, depth => { - checkFields(t, depth, ['lastUpdateId', 'bids', 'asks']) - resolve() + return new Promise(resolve => { + client.ws.partialDepth({ symbol: 'ETHBTC@100ms', level: 10 }, depth => { + checkFields(t, depth, ['lastUpdateId', 'bids', 'asks']) + resolve() + }) }) - }) }) test('[WS] ticker', t => { - return new Promise(resolve => { - client.ws.ticker('ETHBTC', ticker => { - checkFields(t, ticker, ['open', 'high', 'low', 'eventTime', 'symbol', 'volume']) - resolve() + return new Promise(resolve => { + client.ws.ticker('ETHBTC', ticker => { + checkFields(t, ticker, ['open', 'high', 'low', 'eventTime', 'symbol', 'volume']) + resolve() + }) }) - }) }) test('[WS] allTicker', t => { - return new Promise(resolve => { - client.ws.allTickers(tickers => { - t.truthy(Array.isArray(tickers)) - t.is(tickers[0].eventType, '24hrTicker') - checkFields(t, tickers[0], ['symbol', 'priceChange', 'priceChangePercent']) - resolve() + return new Promise(resolve => { + client.ws.allTickers(tickers => { + t.truthy(Array.isArray(tickers)) + t.is(tickers[0].eventType, '24hrTicker') + checkFields(t, tickers[0], ['symbol', 'priceChange', 'priceChangePercent']) + resolve() + }) }) - }) }) test('[WS] miniTicker', t => { - return new Promise(resolve => { - client.ws.miniTicker('ETHBTC', ticker => { - checkFields(t, ticker, [ - 'open', - 'high', - 'low', - 'curDayClose', - 'eventTime', - 'symbol', - 'volume', - 'volumeQuote', - ]) - resolve() + return new Promise(resolve => { + client.ws.miniTicker('ETHBTC', ticker => { + checkFields(t, ticker, [ + 'open', + 'high', + 'low', + 'curDayClose', + 'eventTime', + 'symbol', + 'volume', + 'volumeQuote', + ]) + resolve() + }) }) - }) }) test('[WS] allMiniTickers', t => { - return new Promise(resolve => { - client.ws.allMiniTickers(tickers => { - t.truthy(Array.isArray(tickers)) - t.is(tickers[0].eventType, '24hrMiniTicker') - checkFields(t, tickers[0], [ - 'open', - 'high', - 'low', - 'curDayClose', - 'eventTime', - 'symbol', - 'volume', - 'volumeQuote', - ]) - resolve() + return new Promise(resolve => { + client.ws.allMiniTickers(tickers => { + t.truthy(Array.isArray(tickers)) + t.is(tickers[0].eventType, '24hrMiniTicker') + checkFields(t, tickers[0], [ + 'open', + 'high', + 'low', + 'curDayClose', + 'eventTime', + 'symbol', + 'volume', + 'volumeQuote', + ]) + resolve() + }) }) - }) }) test('[WS] candles', t => { - try { - client.ws.candles('ETHBTC', d => d) - } catch (e) { - t.is(e.message, 'Please pass a symbol, interval and callback.') - } - - return new Promise(resolve => { - client.ws.candles(['ETHBTC', 'BNBBTC', 'BNTBTC'], '5m', candle => { - checkFields(t, candle, ['open', 'high', 'low', 'close', 'volume', 'trades', 'quoteVolume']) - resolve() + try { + client.ws.candles('ETHBTC', d => d) + } catch (e) { + t.is(e.message, 'Please pass a symbol, interval and callback.') + } + + return new Promise(resolve => { + client.ws.candles(['ETHBTC', 'BNBBTC', 'BNTBTC'], '5m', candle => { + checkFields(t, candle, [ + 'open', + 'high', + 'low', + 'close', + 'volume', + 'trades', + 'quoteVolume', + ]) + resolve() + }) }) - }) }) test('[WS] trades', t => { - return new Promise(resolve => { - client.ws.trades(['BNBBTC', 'ETHBTC', 'BNTBTC'], trade => { - checkFields(t, trade, [ - 'eventType', - 'tradeId', - 'tradeTime', - 'quantity', - 'price', - 'symbol', - // 'buyerOrderId', - // 'sellerOrderId', - ]) - resolve() + return new Promise(resolve => { + client.ws.trades(['BNBBTC', 'ETHBTC', 'BNTBTC'], trade => { + checkFields(t, trade, [ + 'eventType', + 'tradeId', + 'tradeTime', + 'quantity', + 'price', + 'symbol', + // 'buyerOrderId', + // 'sellerOrderId', + ]) + resolve() + }) }) - }) }) test('[WS] aggregate trades', t => { - return new Promise(resolve => { - client.ws.aggTrades(['BNBBTC', 'ETHBTC', 'BNTBTC'], trade => { - checkFields(t, trade, [ - 'eventType', - 'aggId', - 'timestamp', - 'quantity', - 'price', - 'symbol', - 'firstId', - 'lastId', - ]) - resolve() + return new Promise(resolve => { + client.ws.aggTrades(['BNBBTC', 'ETHBTC', 'BNTBTC'], trade => { + checkFields(t, trade, [ + 'eventType', + 'aggId', + 'timestamp', + 'quantity', + 'price', + 'symbol', + 'firstId', + 'lastId', + ]) + resolve() + }) }) - }) }) test.skip('[WS] liquidations', t => { - return new Promise(resolve => { - client.ws.futuresLiquidations('ETHBTC', liquidation => { - checkFields(t, liquidation, [ - 'symbol', - 'price', - 'origQty', - 'lastFilledQty', - 'accumulatedQty', - 'averagePrice', - 'status', - 'timeInForce', - 'type', - 'side', - 'time', - ]) - resolve() + return new Promise(resolve => { + client.ws.futuresLiquidations('ETHBTC', liquidation => { + checkFields(t, liquidation, [ + 'symbol', + 'price', + 'origQty', + 'lastFilledQty', + 'accumulatedQty', + 'averagePrice', + 'status', + 'timeInForce', + 'type', + 'side', + 'time', + ]) + resolve() + }) }) - }) }) test.skip('[FUTURES-WS] all liquidations', t => { - return new Promise(resolve => { - client.ws.futuresAllLiquidations(liquidation => { - checkFields(t, liquidation, [ - 'symbol', - 'price', - 'origQty', - 'lastFilledQty', - 'accumulatedQty', - 'averagePrice', - 'status', - 'timeInForce', - 'type', - 'side', - 'time', - ]) - resolve() + return new Promise(resolve => { + client.ws.futuresAllLiquidations(liquidation => { + checkFields(t, liquidation, [ + 'symbol', + 'price', + 'origQty', + 'lastFilledQty', + 'accumulatedQty', + 'averagePrice', + 'status', + 'timeInForce', + 'type', + 'side', + 'time', + ]) + resolve() + }) }) - }) }) test('[WS] userEvents', t => { - const accountPayload = { - e: 'outboundAccountInfo', - E: 1499405658849, - m: 0, - t: 0, - b: 0, - s: 0, - T: true, - W: true, - D: true, - u: 1499405658849, - B: [ - { - a: 'LTC', - f: '17366.18538083', - l: '0.00000000', - }, - { - a: 'BTC', - f: '10537.85314051', - l: '2.19464093', - }, - { - a: 'ETH', - f: '17902.35190619', - l: '0.00000000', - }, - { - a: 'BNC', - f: '1114503.29769312', + const accountPayload = { + e: 'outboundAccountInfo', + E: 1499405658849, + m: 0, + t: 0, + b: 0, + s: 0, + T: true, + W: true, + D: true, + u: 1499405658849, + B: [ + { + a: 'LTC', + f: '17366.18538083', + l: '0.00000000', + }, + { + a: 'BTC', + f: '10537.85314051', + l: '2.19464093', + }, + { + a: 'ETH', + f: '17902.35190619', + l: '0.00000000', + }, + { + a: 'BNC', + f: '1114503.29769312', + l: '0.00000000', + }, + { + a: 'NEO', + f: '0.00000000', + l: '0.00000000', + }, + ], + } + + userEventHandler(res => { + t.deepEqual(res, { + eventType: 'account', + eventTime: 1499405658849, + makerCommissionRate: 0, + takerCommissionRate: 0, + buyerCommissionRate: 0, + sellerCommissionRate: 0, + canTrade: true, + canWithdraw: true, + canDeposit: true, + lastAccountUpdate: 1499405658849, + balances: { + LTC: { available: '17366.18538083', locked: '0.00000000' }, + BTC: { available: '10537.85314051', locked: '2.19464093' }, + ETH: { available: '17902.35190619', locked: '0.00000000' }, + BNC: { available: '1114503.29769312', locked: '0.00000000' }, + NEO: { available: '0.00000000', locked: '0.00000000' }, + }, + }) + })({ data: JSON.stringify(accountPayload) }) + + const orderPayload = { + e: 'executionReport', + E: 1499405658658, + s: 'ETHBTC', + c: 'mUvoqJxFIILMdfAW5iGSOW', + S: 'BUY', + o: 'LIMIT', + f: 'GTC', + q: '1.00000000', + p: '0.10264410', + P: '0.10285410', + F: '0.00000000', + g: -1, + C: 'null', + x: 'NEW', + X: 'NEW', + r: 'NONE', + i: 4293153, l: '0.00000000', - }, - { - a: 'NEO', - f: '0.00000000', - l: '0.00000000', - }, - ], - } - - userEventHandler(res => { - t.deepEqual(res, { - eventType: 'account', - eventTime: 1499405658849, - makerCommissionRate: 0, - takerCommissionRate: 0, - buyerCommissionRate: 0, - sellerCommissionRate: 0, - canTrade: true, - canWithdraw: true, - canDeposit: true, - lastAccountUpdate: 1499405658849, - balances: { - LTC: { available: '17366.18538083', locked: '0.00000000' }, - BTC: { available: '10537.85314051', locked: '2.19464093' }, - ETH: { available: '17902.35190619', locked: '0.00000000' }, - BNC: { available: '1114503.29769312', locked: '0.00000000' }, - NEO: { available: '0.00000000', locked: '0.00000000' }, - }, - }) - })({ data: JSON.stringify(accountPayload) }) - - const orderPayload = { - e: 'executionReport', - E: 1499405658658, - s: 'ETHBTC', - c: 'mUvoqJxFIILMdfAW5iGSOW', - S: 'BUY', - o: 'LIMIT', - f: 'GTC', - q: '1.00000000', - p: '0.10264410', - P: '0.10285410', - F: '0.00000000', - g: -1, - C: 'null', - x: 'NEW', - X: 'NEW', - r: 'NONE', - i: 4293153, - l: '0.00000000', - z: '0.00000000', - L: '0.00000000', - n: '0', - N: null, - T: 1499405658657, - t: -1, - I: 8641984, - w: true, - m: false, - M: false, - O: 1499405658657, - Q: 0, - Y: 0, - Z: '0.00000000', - } - - userEventHandler(res => { - t.deepEqual(res, { - commission: '0', - commissionAsset: null, - creationTime: 1499405658657, - eventTime: 1499405658658, - eventType: 'executionReport', - executionType: 'NEW', - icebergQuantity: '0.00000000', - isBuyerMaker: false, - isOrderWorking: true, - lastQuoteTransacted: 0, - lastTradeQuantity: '0.00000000', - newClientOrderId: 'mUvoqJxFIILMdfAW5iGSOW', - orderId: 4293153, - orderListId: -1, - orderRejectReason: 'NONE', - orderStatus: 'NEW', - orderTime: 1499405658657, - orderType: 'LIMIT', - originalClientOrderId: 'null', - price: '0.10264410', - priceLastTrade: '0.00000000', - quantity: '1.00000000', - quoteOrderQuantity: 0, - side: 'BUY', - stopPrice: '0.10285410', - symbol: 'ETHBTC', - timeInForce: 'GTC', - totalQuoteTradeQuantity: '0.00000000', - totalTradeQuantity: '0.00000000', - tradeId: -1, - trailingDelta: undefined, - trailingTime: undefined, - }) - })({ data: JSON.stringify(orderPayload) }) - - const tradePayload = { - e: 'executionReport', - E: 1499406026404, - s: 'ETHBTC', - c: '1hRLKJhTRsXy2ilYdSzhkk', - S: 'BUY', - o: 'LIMIT', - f: 'GTC', - q: '22.42906458', - p: '0.10279999', - P: '0.10280001', - F: '0.00000000', - g: -1, - C: 'null', - x: 'TRADE', - X: 'FILLED', - r: 'NONE', - i: 4294220, - l: '17.42906458', - z: '22.42906458', - L: '0.10279999', - n: '0.00000001', - N: 'BNC', - T: 1499406026402, - t: 77517, - I: 8644124, - w: false, - m: false, - M: true, - O: 1499405658657, - Q: 0, - Y: 0, - Z: '2.30570761', - } - - userEventHandler(res => { - t.deepEqual(res, { - eventType: 'executionReport', - eventTime: 1499406026404, - symbol: 'ETHBTC', - newClientOrderId: '1hRLKJhTRsXy2ilYdSzhkk', - originalClientOrderId: 'null', - side: 'BUY', - orderType: 'LIMIT', - timeInForce: 'GTC', - quantity: '22.42906458', - price: '0.10279999', - stopPrice: '0.10280001', - executionType: 'TRADE', - icebergQuantity: '0.00000000', - orderStatus: 'FILLED', - orderRejectReason: 'NONE', - orderId: 4294220, - orderTime: 1499406026402, - lastTradeQuantity: '17.42906458', - totalTradeQuantity: '22.42906458', - priceLastTrade: '0.10279999', - commission: '0.00000001', - commissionAsset: 'BNC', - tradeId: 77517, - isOrderWorking: false, - isBuyerMaker: false, - creationTime: 1499405658657, - totalQuoteTradeQuantity: '2.30570761', - lastQuoteTransacted: 0, - orderListId: -1, - quoteOrderQuantity: 0, - trailingDelta: undefined, - trailingTime: undefined, - }) - })({ data: JSON.stringify(tradePayload) }) - - const listStatusPayload = { - e: 'listStatus', - E: 1661588112531, - s: 'TWTUSDT', - g: 73129826, - c: 'OCO', - l: 'EXEC_STARTED', - L: 'EXECUTING', - r: 'NONE', - C: 'Y3ZgLMRPHZFNqEVSZwoJI7', - T: 1661588112530, - O: [ - { - s: 'TWTUSDT', - i: 209259206, - c: 'electron_f675d1bdea454cd4afeac5664be', - }, - { + z: '0.00000000', + L: '0.00000000', + n: '0', + N: null, + T: 1499405658657, + t: -1, + I: 8641984, + w: true, + m: false, + M: false, + O: 1499405658657, + Q: 0, + Y: 0, + Z: '0.00000000', + } + + userEventHandler(res => { + t.deepEqual(res, { + commission: '0', + commissionAsset: null, + creationTime: 1499405658657, + eventTime: 1499405658658, + eventType: 'executionReport', + executionType: 'NEW', + icebergQuantity: '0.00000000', + isBuyerMaker: false, + isOrderWorking: true, + lastQuoteTransacted: 0, + lastTradeQuantity: '0.00000000', + newClientOrderId: 'mUvoqJxFIILMdfAW5iGSOW', + orderId: 4293153, + orderListId: -1, + orderRejectReason: 'NONE', + orderStatus: 'NEW', + orderTime: 1499405658657, + orderType: 'LIMIT', + originalClientOrderId: 'null', + price: '0.10264410', + priceLastTrade: '0.00000000', + quantity: '1.00000000', + quoteOrderQuantity: 0, + side: 'BUY', + stopPrice: '0.10285410', + symbol: 'ETHBTC', + timeInForce: 'GTC', + totalQuoteTradeQuantity: '0.00000000', + totalTradeQuantity: '0.00000000', + tradeId: -1, + trailingDelta: undefined, + trailingTime: undefined, + }) + })({ data: JSON.stringify(orderPayload) }) + + const tradePayload = { + e: 'executionReport', + E: 1499406026404, + s: 'ETHBTC', + c: '1hRLKJhTRsXy2ilYdSzhkk', + S: 'BUY', + o: 'LIMIT', + f: 'GTC', + q: '22.42906458', + p: '0.10279999', + P: '0.10280001', + F: '0.00000000', + g: -1, + C: 'null', + x: 'TRADE', + X: 'FILLED', + r: 'NONE', + i: 4294220, + l: '17.42906458', + z: '22.42906458', + L: '0.10279999', + n: '0.00000001', + N: 'BNC', + T: 1499406026402, + t: 77517, + I: 8644124, + w: false, + m: false, + M: true, + O: 1499405658657, + Q: 0, + Y: 0, + Z: '2.30570761', + } + + userEventHandler(res => { + t.deepEqual(res, { + eventType: 'executionReport', + eventTime: 1499406026404, + symbol: 'ETHBTC', + newClientOrderId: '1hRLKJhTRsXy2ilYdSzhkk', + originalClientOrderId: 'null', + side: 'BUY', + orderType: 'LIMIT', + timeInForce: 'GTC', + quantity: '22.42906458', + price: '0.10279999', + stopPrice: '0.10280001', + executionType: 'TRADE', + icebergQuantity: '0.00000000', + orderStatus: 'FILLED', + orderRejectReason: 'NONE', + orderId: 4294220, + orderTime: 1499406026402, + lastTradeQuantity: '17.42906458', + totalTradeQuantity: '22.42906458', + priceLastTrade: '0.10279999', + commission: '0.00000001', + commissionAsset: 'BNC', + tradeId: 77517, + isOrderWorking: false, + isBuyerMaker: false, + creationTime: 1499405658657, + totalQuoteTradeQuantity: '2.30570761', + lastQuoteTransacted: 0, + orderListId: -1, + quoteOrderQuantity: 0, + trailingDelta: undefined, + trailingTime: undefined, + }) + })({ data: JSON.stringify(tradePayload) }) + + const listStatusPayload = { + e: 'listStatus', + E: 1661588112531, s: 'TWTUSDT', - i: 209259207, - c: 'electron_38d852a65a89486c98e59879327', - }, - ], - } - - userEventHandler(res => { - t.deepEqual(res, { - eventType: 'listStatus', - eventTime: 1661588112531, - symbol: 'TWTUSDT', - orderListId: 73129826, - contingencyType: 'OCO', - listStatusType: 'EXEC_STARTED', - listOrderStatus: 'EXECUTING', - listRejectReason: 'NONE', - listClientOrderId: 'Y3ZgLMRPHZFNqEVSZwoJI7', - transactionTime: 1661588112530, - orders: [ - { - symbol: 'TWTUSDT', - orderId: 209259206, - clientOrderId: 'electron_f675d1bdea454cd4afeac5664be', - }, - { - symbol: 'TWTUSDT', - orderId: 209259207, - clientOrderId: 'electron_38d852a65a89486c98e59879327', - }, - ], - }) - })({ data: JSON.stringify(listStatusPayload) }) - - const newEvent = { e: 'totallyNewEvent', yolo: 42 } - - userEventHandler(res => { - t.deepEqual(res, { type: 'totallyNewEvent', yolo: 42 }) - })({ data: JSON.stringify(newEvent) }) + g: 73129826, + c: 'OCO', + l: 'EXEC_STARTED', + L: 'EXECUTING', + r: 'NONE', + C: 'Y3ZgLMRPHZFNqEVSZwoJI7', + T: 1661588112530, + O: [ + { + s: 'TWTUSDT', + i: 209259206, + c: 'electron_f675d1bdea454cd4afeac5664be', + }, + { + s: 'TWTUSDT', + i: 209259207, + c: 'electron_38d852a65a89486c98e59879327', + }, + ], + } + + userEventHandler(res => { + t.deepEqual(res, { + eventType: 'listStatus', + eventTime: 1661588112531, + symbol: 'TWTUSDT', + orderListId: 73129826, + contingencyType: 'OCO', + listStatusType: 'EXEC_STARTED', + listOrderStatus: 'EXECUTING', + listRejectReason: 'NONE', + listClientOrderId: 'Y3ZgLMRPHZFNqEVSZwoJI7', + transactionTime: 1661588112530, + orders: [ + { + symbol: 'TWTUSDT', + orderId: 209259206, + clientOrderId: 'electron_f675d1bdea454cd4afeac5664be', + }, + { + symbol: 'TWTUSDT', + orderId: 209259207, + clientOrderId: 'electron_38d852a65a89486c98e59879327', + }, + ], + }) + })({ data: JSON.stringify(listStatusPayload) }) + + const newEvent = { e: 'totallyNewEvent', yolo: 42 } + + userEventHandler(res => { + t.deepEqual(res, { type: 'totallyNewEvent', yolo: 42 }) + })({ data: JSON.stringify(newEvent) }) }) // FUTURES TESTS test('[FUTURES-REST] ping', async t => { - t.truthy(await client.futuresPing(), 'A simple ping should work') + t.truthy(await client.futuresPing(), 'A simple ping should work') }) test('[FUTURES-REST] time', async t => { - const ts = await client.futuresTime() - t.truthy(new Date(ts).getTime() > 0, 'The returned timestamp should be valid') + const ts = await client.futuresTime() + t.truthy(new Date(ts).getTime() > 0, 'The returned timestamp should be valid') }) test('[FUTURES-REST] exchangeInfo', async t => { - const res = await client.futuresExchangeInfo() - checkFields(t, res, ['timezone', 'serverTime', 'rateLimits', 'symbols']) + const res = await client.futuresExchangeInfo() + checkFields(t, res, ['timezone', 'serverTime', 'rateLimits', 'symbols']) }) test('[FUTURES-REST] book', async t => { - try { - await client.futuresBook() - } catch (e) { - t.is(e.message, 'You need to pass a payload object.') - } + try { + await client.futuresBook() + } catch (e) { + t.is(e.message, 'You need to pass a payload object.') + } - try { - await client.futuresBook({}) - } catch (e) { - t.is(e.message, 'Method book requires symbol parameter.') - } + try { + await client.futuresBook({}) + } catch (e) { + t.is(e.message, 'Method book requires symbol parameter.') + } - const book = await client.futuresBook({ symbol: 'BTCUSDT' }) - t.truthy(book.lastUpdateId) - t.truthy(book.asks.length) - t.truthy(book.bids.length) + const book = await client.futuresBook({ symbol: 'BTCUSDT' }) + t.truthy(book.lastUpdateId) + t.truthy(book.asks.length) + t.truthy(book.bids.length) - const [bid] = book.bids - t.truthy(typeof bid.price === 'string') - t.truthy(typeof bid.quantity === 'string') + const [bid] = book.bids + t.truthy(typeof bid.price === 'string') + t.truthy(typeof bid.quantity === 'string') }) test('[FUTURES-REST] markPrice', async t => { - const res = await client.futuresMarkPrice() - t.truthy(Array.isArray(res)) - checkFields(t, res[0], ['symbol', 'markPrice', 'lastFundingRate', 'nextFundingTime', 'time']) + const res = await client.futuresMarkPrice() + t.truthy(Array.isArray(res)) + checkFields(t, res[0], ['symbol', 'markPrice', 'lastFundingRate', 'nextFundingTime', 'time']) }) test.skip('[FUTURES-REST] allForceOrders', async t => { - const res = await client.futuresAllForceOrders() - t.truthy(Array.isArray(res)) - t.truthy(res.length === 100) - checkFields(t, res[0], [ - 'symbol', - 'price', - 'origQty', - 'executedQty', - 'averagePrice', - 'timeInForce', - 'type', - 'side', - 'time', - ]) + const res = await client.futuresAllForceOrders() + t.truthy(Array.isArray(res)) + t.truthy(res.length === 100) + checkFields(t, res[0], [ + 'symbol', + 'price', + 'origQty', + 'executedQty', + 'averagePrice', + 'timeInForce', + 'type', + 'side', + 'time', + ]) }) test('[FUTURES-REST] candles', async t => { - try { - await client.futuresCandles({}) - } catch (e) { - t.is(e.message, 'Method candles requires symbol parameter.') - } + try { + await client.futuresCandles({}) + } catch (e) { + t.is(e.message, 'Method candles requires symbol parameter.') + } - const candles = await client.futuresCandles({ symbol: 'BTCUSDT' }) + const candles = await client.futuresCandles({ symbol: 'BTCUSDT' }) - t.truthy(candles.length) + t.truthy(candles.length) - const [candle] = candles - checkFields(t, candle, candleFields) + const [candle] = candles + checkFields(t, candle, candleFields) }) test('[FUTURES-REST] mark price candles', async t => { - try { - await client.futuresMarkPriceCandles({}) - } catch (e) { - t.is(e.message, 'Method candles requires symbol parameter.') - } + try { + await client.futuresMarkPriceCandles({}) + } catch (e) { + t.is(e.message, 'Method candles requires symbol parameter.') + } - const candles = await client.futuresMarkPriceCandles({ symbol: 'BTCUSDT' }) + const candles = await client.futuresMarkPriceCandles({ symbol: 'BTCUSDT' }) - t.truthy(candles.length) + t.truthy(candles.length) - const [candle] = candles - checkFields(t, candle, candleFields) + const [candle] = candles + checkFields(t, candle, candleFields) }) test('[FUTURES-REST] index price candles', async t => { - try { - await client.futuresIndexPriceCandles({}) - } catch (e) { - t.is(e.message, 'Method candles requires pair parameter.') - } + try { + await client.futuresIndexPriceCandles({}) + } catch (e) { + t.is(e.message, 'Method candles requires pair parameter.') + } - const candles = await client.futuresIndexPriceCandles({ pair: 'BTCUSDT' }) + const candles = await client.futuresIndexPriceCandles({ pair: 'BTCUSDT' }) - t.truthy(candles.length) + t.truthy(candles.length) - const [candle] = candles - checkFields(t, candle, candleFields) + const [candle] = candles + checkFields(t, candle, candleFields) }) test('[FUTURES-REST] trades', async t => { - const trades = await client.futuresTrades({ symbol: 'BTCUSDT', limit: 10 }) - t.is(trades.length, 10) - checkFields(t, trades[0], ['id', 'price', 'qty', 'quoteQty', 'time']) + const trades = await client.futuresTrades({ symbol: 'BTCUSDT', limit: 10 }) + t.is(trades.length, 10) + checkFields(t, trades[0], ['id', 'price', 'qty', 'quoteQty', 'time']) }) test('[FUTURES-REST] dailyStats', async t => { - const res = await client.futuresDailyStats({ symbol: 'BTCUSDT' }) - t.truthy(res) - checkFields(t, res, ['highPrice', 'lowPrice', 'volume', 'priceChange']) + const res = await client.futuresDailyStats({ symbol: 'BTCUSDT' }) + t.truthy(res) + checkFields(t, res, ['highPrice', 'lowPrice', 'volume', 'priceChange']) }) test('[FUTURES-REST] prices', async t => { - const prices = await client.futuresPrices() - t.truthy(prices) - t.truthy(prices.BTCUSDT) + const prices = await client.futuresPrices() + t.truthy(prices) + t.truthy(prices.BTCUSDT) }) test('[FUTURES-REST] allBookTickers', async t => { - const tickers = await client.futuresAllBookTickers() - t.truthy(tickers) - t.truthy(tickers.BTCUSDT) + const tickers = await client.futuresAllBookTickers() + t.truthy(tickers) + t.truthy(tickers.BTCUSDT) }) test('[FUTURES-REST] aggTrades', async t => { - try { - await client.futuresAggTrades({}) - } catch (e) { - t.is(e.message, 'Method aggTrades requires symbol parameter.') - } + try { + await client.futuresAggTrades({}) + } catch (e) { + t.is(e.message, 'Method aggTrades requires symbol parameter.') + } - const trades = await client.futuresAggTrades({ symbol: 'BTCUSDT' }) - t.truthy(trades.length) + const trades = await client.futuresAggTrades({ symbol: 'BTCUSDT' }) + t.truthy(trades.length) - const [trade] = trades - t.truthy(trade.aggId) + const [trade] = trades + t.truthy(trade.aggId) }) test('[FUTURES-REST] fundingRate', async t => { - const fundingRate = await client.futuresFundingRate({ symbol: 'BTCUSDT' }) - checkFields(t, fundingRate[0], ['symbol', 'fundingTime', 'fundingRate']) - t.is(fundingRate.length, 100) + const fundingRate = await client.futuresFundingRate({ symbol: 'BTCUSDT' }) + checkFields(t, fundingRate[0], ['symbol', 'fundingTime', 'fundingRate']) + t.is(fundingRate.length, 100) }) // DELIVERY TESTS test('[DELIVERY-REST] ping', async t => { - t.truthy(await client.deliveryPing(), 'A simple ping should work') + t.truthy(await client.deliveryPing(), 'A simple ping should work') }) test('[DELIVERY-REST] time', async t => { - const ts = await client.deliveryTime() - t.truthy(new Date(ts).getTime() > 0, 'The returned timestamp should be valid') + const ts = await client.deliveryTime() + t.truthy(new Date(ts).getTime() > 0, 'The returned timestamp should be valid') }) test('[DELIVERY-REST] exchangeInfo', async t => { - const res = await client.deliveryExchangeInfo() - checkFields(t, res, ['timezone', 'serverTime', 'rateLimits', 'exchangeFilters', 'symbols']) + const res = await client.deliveryExchangeInfo() + checkFields(t, res, ['timezone', 'serverTime', 'rateLimits', 'exchangeFilters', 'symbols']) }) test('[DELIVERY-REST] book', async t => { - try { - await client.deliveryBook() - } catch (e) { - t.is(e.message, 'You need to pass a payload object.') - } + try { + await client.deliveryBook() + } catch (e) { + t.is(e.message, 'You need to pass a payload object.') + } - try { - await client.deliveryBook({}) - } catch (e) { - t.is(e.message, 'Method book requires symbol parameter.') - } + try { + await client.deliveryBook({}) + } catch (e) { + t.is(e.message, 'Method book requires symbol parameter.') + } - const book = await client.deliveryBook({ symbol: 'TRXUSD_perp' }) - t.truthy(book.lastUpdateId) - t.truthy(book.asks.length) - t.truthy(book.bids.length) + const book = await client.deliveryBook({ symbol: 'TRXUSD_perp' }) + t.truthy(book.lastUpdateId) + t.truthy(book.asks.length) + t.truthy(book.bids.length) - const [bid] = book.bids - t.truthy(typeof bid.price === 'string') - t.truthy(typeof bid.quantity === 'string') + const [bid] = book.bids + t.truthy(typeof bid.price === 'string') + t.truthy(typeof bid.quantity === 'string') }) test('[DELIVERY-REST] markPrice', async t => { - const res = await client.deliveryMarkPrice() - t.truthy(Array.isArray(res)) - checkFields(t, res[0], [ - 'symbol', - 'pair', - 'markPrice', - 'indexPrice', - 'estimatedSettlePrice', - 'time', - ]) + const res = await client.deliveryMarkPrice() + t.truthy(Array.isArray(res)) + checkFields(t, res[0], [ + 'symbol', + 'pair', + 'markPrice', + 'indexPrice', + 'estimatedSettlePrice', + 'time', + ]) }) test('[DELIVERY-REST] candles', async t => { - try { - await client.deliveryCandles({}) - } catch (e) { - t.is(e.message, 'Method candles requires symbol parameter.') - } + try { + await client.deliveryCandles({}) + } catch (e) { + t.is(e.message, 'Method candles requires symbol parameter.') + } - const candles = await client.deliveryCandles({ symbol: 'TRXUSD_perp' }) + const candles = await client.deliveryCandles({ symbol: 'TRXUSD_perp' }) - t.truthy(candles.length) + t.truthy(candles.length) - const [candle] = candles - checkFields(t, candle, deliveryCandleFields) + const [candle] = candles + checkFields(t, candle, deliveryCandleFields) }) test('[DELIVERY-REST] mark price candles', async t => { - try { - await client.deliveryMarkPriceCandles({}) - } catch (e) { - t.is(e.message, 'Method candles requires symbol parameter.') - } + try { + await client.deliveryMarkPriceCandles({}) + } catch (e) { + t.is(e.message, 'Method candles requires symbol parameter.') + } - const candles = await client.deliveryMarkPriceCandles({ symbol: 'TRXUSD_perp' }) + const candles = await client.deliveryMarkPriceCandles({ symbol: 'TRXUSD_perp' }) - t.truthy(candles.length) + t.truthy(candles.length) - const [candle] = candles - checkFields(t, candle, deliveryCandleFields) + const [candle] = candles + checkFields(t, candle, deliveryCandleFields) }) test('[DELIVERY-REST] index price candles', async t => { - try { - await client.deliveryIndexPriceCandles({}) - } catch (e) { - t.is(e.message, 'Method candles requires pair parameter.') - } + try { + await client.deliveryIndexPriceCandles({}) + } catch (e) { + t.is(e.message, 'Method candles requires pair parameter.') + } - const candles = await client.deliveryIndexPriceCandles({ pair: 'TRXUSD' }) + const candles = await client.deliveryIndexPriceCandles({ pair: 'TRXUSD' }) - t.truthy(candles.length) + t.truthy(candles.length) - const [candle] = candles - checkFields(t, candle, deliveryCandleFields) + const [candle] = candles + checkFields(t, candle, deliveryCandleFields) }) test('[DELIVERY-REST] trades', async t => { - const trades = await client.deliveryTrades({ symbol: 'TRXUSD_perp', limit: 10 }) - t.is(trades.length, 10) - checkFields(t, trades[0], ['id', 'price', 'qty', 'baseQty', 'time']) + const trades = await client.deliveryTrades({ symbol: 'TRXUSD_perp', limit: 10 }) + t.is(trades.length, 10) + checkFields(t, trades[0], ['id', 'price', 'qty', 'baseQty', 'time']) }) test('[DELIVERY-REST] dailyStats', async t => { - const res = await client.deliveryDailyStats({ symbol: 'TRXUSD_perp' }) - t.truthy(res) - // Note : delivery API always returns an array hence the res[0] - checkFields(t, res[0], ['pair', 'highPrice', 'lowPrice', 'volume', 'priceChange']) + const res = await client.deliveryDailyStats({ symbol: 'TRXUSD_perp' }) + t.truthy(res) + // Note : delivery API always returns an array hence the res[0] + checkFields(t, res[0], ['pair', 'highPrice', 'lowPrice', 'volume', 'priceChange']) }) test('[DELIVERY-REST] prices', async t => { - const prices = await client.deliveryPrices() - t.truthy(prices) - t.truthy(prices.TRXUSD_PERP) + const prices = await client.deliveryPrices() + t.truthy(prices) + t.truthy(prices.TRXUSD_PERP) }) test('[DELIVERY-REST] allBookTickers', async t => { - const tickers = await client.deliveryAllBookTickers() - t.truthy(tickers) - t.truthy(tickers.TRXUSD_PERP) + const tickers = await client.deliveryAllBookTickers() + t.truthy(tickers) + t.truthy(tickers.TRXUSD_PERP) }) test('[DELIVERY-REST] aggTrades', async t => { - try { - await client.deliveryAggTrades({}) - } catch (e) { - t.is(e.message, 'Method aggTrades requires symbol parameter.') - } + try { + await client.deliveryAggTrades({}) + } catch (e) { + t.is(e.message, 'Method aggTrades requires symbol parameter.') + } - const trades = await client.deliveryAggTrades({ symbol: 'TRXUSD_perp' }) - t.truthy(trades.length) + const trades = await client.deliveryAggTrades({ symbol: 'TRXUSD_perp' }) + t.truthy(trades.length) - const [trade] = trades - t.truthy(trade.aggId) + const [trade] = trades + t.truthy(trade.aggId) }) test('[DELIVERY-REST] fundingRate', async t => { - const fundingRate = await client.deliveryFundingRate({ symbol: 'TRXUSD_perp' }) - checkFields(t, fundingRate[0], ['symbol', 'fundingTime', 'fundingRate']) - t.assert(fundingRate.length >= 100) + const fundingRate = await client.deliveryFundingRate({ symbol: 'TRXUSD_perp' }) + checkFields(t, fundingRate[0], ['symbol', 'fundingTime', 'fundingRate']) + t.assert(fundingRate.length >= 100) }) diff --git a/test/static-tests.js b/test/static-tests.js index 72f458d9..0004ac76 100644 --- a/test/static-tests.js +++ b/test/static-tests.js @@ -1,76 +1,74 @@ import test from 'ava' -import nock from 'nock'; +import nock from 'nock' import Binance, { ErrorCodes } from 'index' // Warning: For now these tests can't run in parallel due to nock interceptors const binance = Binance({ apiKey: 'testkey', - apiSecret: 'test' + apiSecret: 'test', }) const demoBinance = Binance({ testnet: true, -}); +}) function urlToObject(queryString) { - const params = new URLSearchParams(queryString); - const obj = Object.fromEntries(params.entries()); - return obj; + const params = new URLSearchParams(queryString) + const obj = Object.fromEntries(params.entries()) + return obj } - -let interceptedUrl = null; -let interceptedBody = null; +let interceptedUrl = null +let interceptedBody = null test.serial.beforeEach(t => { - interceptedUrl = null; - interceptedBody = null; + interceptedUrl = null + interceptedBody = null nock(/.*/) .get(/.*/) .reply(200, function (uri, requestBody) { - interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; - interceptedBody = requestBody; - return { success: true }; - }); + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}` + interceptedBody = requestBody + return { success: true } + }) nock(/.*/) .post(/.*/) .reply(200, function (uri, requestBody) { - interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; - interceptedBody = requestBody; - return { success: true }; - }); + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}` + interceptedBody = requestBody + return { success: true } + }) nock(/.*/) .delete(/.*/) .reply(200, function (uri, requestBody) { - interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; - interceptedBody = requestBody; - return { success: true }; - }); -}); + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}` + interceptedBody = requestBody + return { success: true } + }) +}) test.serial('[Rest] Spot demo url', async t => { - await demoBinance.time(); + await demoBinance.time() t.is(interceptedUrl, 'https://demo-api.binance.com/api/v3/time') }) test.serial('[Rest] Futures demo url', async t => { - await demoBinance.futuresTime(); + await demoBinance.futuresTime() t.is(interceptedUrl, 'https://demo-fapi.binance.com/fapi/v1/time') }) test.serial('[REST] Prices no symbol', async t => { - await binance.prices(); + await binance.prices() t.is(interceptedUrl, 'https://api.binance.com/api/v3/ticker/price') }) test.serial('[REST] Futures Prices no symbol', async t => { - await binance.futuresPrices(); + await binance.futuresPrices() t.is(interceptedUrl, 'https://fapi.binance.com/fapi/v1/ticker/price') }) - test.serial('[REST] Orderbook', async t => { try { - await binance.book({ symbol: 'BTCUSDT' }); + await binance.book({ symbol: 'BTCUSDT' }) } catch (e) { // it can throw an error because of the mocked response } @@ -79,7 +77,7 @@ test.serial('[REST] Orderbook', async t => { test.serial('[REST] Futures Orderbook', async t => { try { - await binance.futuresBook({ symbol: 'BTCUSDT' }); + await binance.futuresBook({ symbol: 'BTCUSDT' }) } catch (e) { // it can throw an error because of the mocked response } @@ -92,7 +90,11 @@ test.serial('[REST] OHLCVS', async t => { } catch (e) { // it can throw an error because of the mocked response } - t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/klines?interval=5m&symbol=BTCUSDT')) + t.true( + interceptedUrl.startsWith( + 'https://api.binance.com/api/v3/klines?interval=5m&symbol=BTCUSDT', + ), + ) }) test.serial('[REST] Futures OHLCVS', async t => { @@ -141,7 +143,6 @@ test.serial('[REST] PositionRisk V2', async t => { t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v2/positionRisk')) }) - test.serial('[REST] CancelOrder', async t => { await binance.cancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) @@ -158,8 +159,8 @@ test.serial('[REST] Futures CancelOrder', async t => { t.is(obj.orderId, '34234234') }) -const CONTRACT_PREFIX = "x-ftGmvgAN" -const SPOT_PREFIX = "x-B3AUXNYV" +const CONTRACT_PREFIX = 'x-ftGmvgAN' +const SPOT_PREFIX = 'x-B3AUXNYV' test.serial('[REST] MarketBuy', async t => { await binance.order({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) @@ -183,11 +184,16 @@ test.serial('[REST] MarketSell', async t => { t.is(obj.type, 'MARKET') t.is(obj.quantity, '0.5') t.true(obj.newClientOrderId.startsWith(SPOT_PREFIX)) - }) test.serial('[REST] LimitBuy', async t => { - await binance.order({ symbol: 'LTCUSDT', side: 'BUY', type: 'LIMIT', quantity: 0.5, price: 100 }) + await binance.order({ + symbol: 'LTCUSDT', + side: 'BUY', + type: 'LIMIT', + quantity: 0.5, + price: 100, + }) t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') const obj = urlToObject(body) @@ -199,7 +205,13 @@ test.serial('[REST] LimitBuy', async t => { }) test.serial('[REST] LimitSell', async t => { - await binance.order({ symbol: 'LTCUSDT', side: 'SELL', type: 'LIMIT', quantity: 0.5, price: 100 }) + await binance.order({ + symbol: 'LTCUSDT', + side: 'SELL', + type: 'LIMIT', + quantity: 0.5, + price: 100, + }) t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') const obj = urlToObject(body) @@ -210,7 +222,6 @@ test.serial('[REST] LimitSell', async t => { t.true(obj.newClientOrderId.startsWith(SPOT_PREFIX)) }) - test.serial('[REST] Futures MarketBuy', async t => { await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'BUY', type: 'MARKET', quantity: 0.5 }) t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) @@ -234,7 +245,13 @@ test.serial('[REST] Futures MarketSell', async t => { }) test.serial('[REST] Futures LimitBuy', async t => { - await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'BUY', type: 'LIMIT', quantity: 0.5, price: 100 }) + await binance.futuresOrder({ + symbol: 'LTCUSDT', + side: 'BUY', + type: 'LIMIT', + quantity: 0.5, + price: 100, + }) t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) t.is(obj.symbol, 'LTCUSDT') @@ -244,7 +261,13 @@ test.serial('[REST] Futures LimitBuy', async t => { }) test.serial('[REST] Futures LimitSell', async t => { - await binance.futuresOrder({ symbol: 'LTCUSDT', side: 'SELL', type: 'LIMIT', quantity: 0.5, price: 100 }) + await binance.futuresOrder({ + symbol: 'LTCUSDT', + side: 'SELL', + type: 'LIMIT', + quantity: 0.5, + price: 100, + }) t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '')) t.is(obj.symbol, 'LTCUSDT') @@ -254,7 +277,6 @@ test.serial('[REST] Futures LimitSell', async t => { t.true(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) }) - test.serial('[REST] Futures cancel order', async t => { await binance.futuresCancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' }) const url = 'https://fapi.binance.com/fapi/v1/order' @@ -305,7 +327,7 @@ test.serial('[REST] spot order with custom clientorderId', async t => { type: 'LIMIT', quantity: 0.5, price: 100, - newClientOrderId: 'myid' + newClientOrderId: 'myid', }) t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order')) const body = interceptedUrl.replace('https://api.binance.com/api/v3/order', '') @@ -318,7 +340,6 @@ test.serial('[REST] spot order with custom clientorderId', async t => { t.is(obj.newClientOrderId, 'myid') }) - test.serial('[REST] delivery OrderBook', async t => { try { await binance.deliveryBook({ symbol: 'BTCUSD_PERP' }) @@ -334,12 +355,20 @@ test.serial('[REST] futures set leverage', async t => { } catch (e) { // it can throw an error because of the mocked response } - t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/leverage?symbol=BTCUSDT&leverage=5')) + t.true( + interceptedUrl.startsWith( + 'https://fapi.binance.com/fapi/v1/leverage?symbol=BTCUSDT&leverage=5', + ), + ) }) - test.serial('[REST] delivery MarketBuy', async t => { - await binance.deliveryOrder({ symbol: 'BTCUSD_PERP', side: 'BUY', type: 'MARKET', quantity: 0.1 }) + await binance.deliveryOrder({ + symbol: 'BTCUSD_PERP', + side: 'BUY', + type: 'MARKET', + quantity: 0.1, + }) t.true(interceptedUrl.startsWith('https://dapi.binance.com/dapi/v1/order')) const obj = urlToObject(interceptedUrl.replace('https://dapi.binance.com/dapi/v1/order', '')) t.is(obj.symbol, 'BTCUSD_PERP') @@ -347,4 +376,4 @@ test.serial('[REST] delivery MarketBuy', async t => { t.is(obj.type, 'MARKET') t.is(obj.quantity, '0.1') t.true(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) -}) \ No newline at end of file +}) diff --git a/test/types.ts b/test/types.ts index f1951056..044c239e 100644 --- a/test/types.ts +++ b/test/types.ts @@ -4,619 +4,629 @@ import test from 'ava' // This type represents all methods from http-client.js type HttpClientMethods = { - getInfo: () => Promise - ping: () => Promise - time: () => Promise<{ serverTime: number }> - exchangeInfo: (payload?: any) => Promise - book: (payload: { symbol: string }) => Promise - aggTrades: (payload: { symbol: string }) => Promise - candles: (payload: { symbol: string; interval: string }) => Promise - trades: (payload: { symbol: string }) => Promise - tradesHistory: (payload: { symbol: string }) => Promise - dailyStats: (payload: { symbol: string }) => Promise - prices: () => Promise - avgPrice: (payload: { symbol: string }) => Promise - allBookTickers: () => Promise - order: (payload: any) => Promise - orderOco: (payload: any) => Promise - orderTest: (payload: any) => Promise - getOrder: (payload: any) => Promise - getOrderOco: (payload: any) => Promise - cancelOrder: (payload: any) => Promise - cancelOrderOco: (payload: any) => Promise - cancelOpenOrders: (payload: any) => Promise - openOrders: (payload?: any) => Promise - allOrders: (payload: any) => Promise - allOrdersOCO: (payload: any) => Promise - accountInfo: (payload?: any) => Promise - myTrades: (payload: any) => Promise - withdraw: (payload: any) => Promise - withdrawHistory: (payload: any) => Promise - depositHistory: (payload: any) => Promise - depositAddress: (payload: any) => Promise - tradeFee: (payload: any) => Promise - assetDetail: (payload: any) => Promise - accountSnapshot: (payload: any) => Promise - universalTransfer: (payload: any) => Promise - universalTransferHistory: (payload: any) => Promise - dustLog: (payload?: any) => Promise - dustTransfer: (payload: any) => Promise - accountCoins: (payload?: any) => Promise - getBnbBurn: (payload?: any) => Promise - setBnbBurn: (payload: any) => Promise - capitalConfigs: () => Promise + getInfo: () => Promise + ping: () => Promise + time: () => Promise<{ serverTime: number }> + exchangeInfo: (payload?: any) => Promise + book: (payload: { symbol: string }) => Promise + aggTrades: (payload: { symbol: string }) => Promise + candles: (payload: { symbol: string; interval: string }) => Promise + trades: (payload: { symbol: string }) => Promise + tradesHistory: (payload: { symbol: string }) => Promise + dailyStats: (payload: { symbol: string }) => Promise + prices: () => Promise + avgPrice: (payload: { symbol: string }) => Promise + allBookTickers: () => Promise + order: (payload: any) => Promise + orderOco: (payload: any) => Promise + orderTest: (payload: any) => Promise + getOrder: (payload: any) => Promise + getOrderOco: (payload: any) => Promise + cancelOrder: (payload: any) => Promise + cancelOrderOco: (payload: any) => Promise + cancelOpenOrders: (payload: any) => Promise + openOrders: (payload?: any) => Promise + allOrders: (payload: any) => Promise + allOrdersOCO: (payload: any) => Promise + accountInfo: (payload?: any) => Promise + myTrades: (payload: any) => Promise + withdraw: (payload: any) => Promise + withdrawHistory: (payload: any) => Promise + depositHistory: (payload: any) => Promise + depositAddress: (payload: any) => Promise + tradeFee: (payload: any) => Promise + assetDetail: (payload: any) => Promise + accountSnapshot: (payload: any) => Promise + universalTransfer: (payload: any) => Promise + universalTransferHistory: (payload: any) => Promise + dustLog: (payload?: any) => Promise + dustTransfer: (payload: any) => Promise + accountCoins: (payload?: any) => Promise + getBnbBurn: (payload?: any) => Promise + setBnbBurn: (payload: any) => Promise + capitalConfigs: () => Promise - // User Data Stream endpoints - getDataStream: () => Promise - keepDataStream: (payload: any) => Promise - closeDataStream: (payload: any) => Promise - marginGetDataStream: () => Promise - marginKeepDataStream: (payload: any) => Promise - marginCloseDataStream: (payload: any) => Promise - futuresGetDataStream: () => Promise - futuresKeepDataStream: (payload: any) => Promise - futuresCloseDataStream: (payload: any) => Promise - deliveryGetDataStream: () => Promise - deliveryKeepDataStream: (payload: any) => Promise - deliveryCloseDataStream: (payload: any) => Promise + // User Data Stream endpoints + getDataStream: () => Promise + keepDataStream: (payload: any) => Promise + closeDataStream: (payload: any) => Promise + marginGetDataStream: () => Promise + marginKeepDataStream: (payload: any) => Promise + marginCloseDataStream: (payload: any) => Promise + futuresGetDataStream: () => Promise + futuresKeepDataStream: (payload: any) => Promise + futuresCloseDataStream: (payload: any) => Promise + deliveryGetDataStream: () => Promise + deliveryKeepDataStream: (payload: any) => Promise + deliveryCloseDataStream: (payload: any) => Promise - // Futures endpoints - futuresPing: () => Promise - futuresTime: () => Promise<{ serverTime: number }> - futuresExchangeInfo: () => Promise - futuresBook: (payload: any) => Promise - futuresAggTrades: (payload: any) => Promise - futuresMarkPrice: (payload: any) => Promise - futuresAllForceOrders: (payload: any) => Promise - futuresLongShortRatio: (payload: any) => Promise - futuresCandles: (payload: any) => Promise - futuresMarkPriceCandles: (payload: any) => Promise - futuresIndexPriceCandles: (payload: any) => Promise - futuresTrades: (payload: any) => Promise - futuresDailyStats: (payload: any) => Promise - futuresPrices: (payload: any) => Promise - futuresAllBookTickers: () => Promise - futuresFundingRate: (payload: any) => Promise - futuresOrder: (payload: any) => Promise - futuresBatchOrders: (payload: any) => Promise - futuresGetOrder: (payload: any) => Promise - futuresCancelOrder: (payload: any) => Promise - futuresCancelAllOpenOrders: (payload: any) => Promise - futuresCancelBatchOrders: (payload: any) => Promise - futuresOpenOrders: (payload: any) => Promise - futuresAllOrders: (payload: any) => Promise - futuresPositionRisk: (payload: any) => Promise - futuresLeverageBracket: (payload: any) => Promise - futuresAccountBalance: (payload?: any) => Promise - futuresAccountInfo: (payload?: any) => Promise - futuresUserTrades: (payload: any) => Promise - futuresPositionMode: (payload: any) => Promise - futuresPositionModeChange: (payload: any) => Promise - futuresLeverage: (payload: any) => Promise - futuresMarginType: (payload: any) => Promise - futuresPositionMargin: (payload: any) => Promise - futuresMarginHistory: (payload: any) => Promise - futuresIncome: (payload: any) => Promise - getMultiAssetsMargin: (payload: any) => Promise - setMultiAssetsMargin: (payload: any) => Promise + // Futures endpoints + futuresPing: () => Promise + futuresTime: () => Promise<{ serverTime: number }> + futuresExchangeInfo: () => Promise + futuresBook: (payload: any) => Promise + futuresAggTrades: (payload: any) => Promise + futuresMarkPrice: (payload: any) => Promise + futuresAllForceOrders: (payload: any) => Promise + futuresLongShortRatio: (payload: any) => Promise + futuresCandles: (payload: any) => Promise + futuresMarkPriceCandles: (payload: any) => Promise + futuresIndexPriceCandles: (payload: any) => Promise + futuresTrades: (payload: any) => Promise + futuresDailyStats: (payload: any) => Promise + futuresPrices: (payload: any) => Promise + futuresAllBookTickers: () => Promise + futuresFundingRate: (payload: any) => Promise + futuresOrder: (payload: any) => Promise + futuresBatchOrders: (payload: any) => Promise + futuresGetOrder: (payload: any) => Promise + futuresCancelOrder: (payload: any) => Promise + futuresCancelAllOpenOrders: (payload: any) => Promise + futuresCancelBatchOrders: (payload: any) => Promise + futuresOpenOrders: (payload: any) => Promise + futuresAllOrders: (payload: any) => Promise + futuresPositionRisk: (payload: any) => Promise + futuresLeverageBracket: (payload: any) => Promise + futuresAccountBalance: (payload?: any) => Promise + futuresAccountInfo: (payload?: any) => Promise + futuresUserTrades: (payload: any) => Promise + futuresPositionMode: (payload: any) => Promise + futuresPositionModeChange: (payload: any) => Promise + futuresLeverage: (payload: any) => Promise + futuresMarginType: (payload: any) => Promise + futuresPositionMargin: (payload: any) => Promise + futuresMarginHistory: (payload: any) => Promise + futuresIncome: (payload: any) => Promise + getMultiAssetsMargin: (payload: any) => Promise + setMultiAssetsMargin: (payload: any) => Promise - // Delivery endpoints - deliveryPing: () => Promise - deliveryTime: () => Promise<{ serverTime: number }> - deliveryExchangeInfo: () => Promise - deliveryBook: (payload: any) => Promise - deliveryAggTrades: (payload: any) => Promise - deliveryMarkPrice: (payload: any) => Promise - deliveryAllForceOrders: (payload: any) => Promise - deliveryLongShortRatio: (payload: any) => Promise - deliveryCandles: (payload: any) => Promise - deliveryMarkPriceCandles: (payload: any) => Promise - deliveryIndexPriceCandles: (payload: any) => Promise - deliveryTrades: (payload: any) => Promise - deliveryDailyStats: (payload: any) => Promise - deliveryPrices: () => Promise - deliveryAllBookTickers: () => Promise - deliveryFundingRate: (payload: any) => Promise - deliveryOrder: (payload: any) => Promise - deliveryBatchOrders: (payload: any) => Promise - deliveryGetOrder: (payload: any) => Promise - deliveryCancelOrder: (payload: any) => Promise - deliveryCancelAllOpenOrders: (payload: any) => Promise - deliveryCancelBatchOrders: (payload: any) => Promise - deliveryOpenOrders: (payload: any) => Promise - deliveryAllOrders: (payload: any) => Promise - deliveryPositionRisk: (payload: any) => Promise - deliveryLeverageBracket: (payload: any) => Promise - deliveryAccountBalance: (payload?: any) => Promise - deliveryAccountInfo: (payload?: any) => Promise - deliveryUserTrades: (payload: any) => Promise - deliveryPositionMode: (payload: any) => Promise - deliveryPositionModeChange: (payload: any) => Promise - deliveryLeverage: (payload: any) => Promise - deliveryMarginType: (payload: any) => Promise - deliveryPositionMargin: (payload: any) => Promise - deliveryMarginHistory: (payload: any) => Promise - deliveryIncome: (payload: any) => Promise + // Delivery endpoints + deliveryPing: () => Promise + deliveryTime: () => Promise<{ serverTime: number }> + deliveryExchangeInfo: () => Promise + deliveryBook: (payload: any) => Promise + deliveryAggTrades: (payload: any) => Promise + deliveryMarkPrice: (payload: any) => Promise + deliveryAllForceOrders: (payload: any) => Promise + deliveryLongShortRatio: (payload: any) => Promise + deliveryCandles: (payload: any) => Promise + deliveryMarkPriceCandles: (payload: any) => Promise + deliveryIndexPriceCandles: (payload: any) => Promise + deliveryTrades: (payload: any) => Promise + deliveryDailyStats: (payload: any) => Promise + deliveryPrices: () => Promise + deliveryAllBookTickers: () => Promise + deliveryFundingRate: (payload: any) => Promise + deliveryOrder: (payload: any) => Promise + deliveryBatchOrders: (payload: any) => Promise + deliveryGetOrder: (payload: any) => Promise + deliveryCancelOrder: (payload: any) => Promise + deliveryCancelAllOpenOrders: (payload: any) => Promise + deliveryCancelBatchOrders: (payload: any) => Promise + deliveryOpenOrders: (payload: any) => Promise + deliveryAllOrders: (payload: any) => Promise + deliveryPositionRisk: (payload: any) => Promise + deliveryLeverageBracket: (payload: any) => Promise + deliveryAccountBalance: (payload?: any) => Promise + deliveryAccountInfo: (payload?: any) => Promise + deliveryUserTrades: (payload: any) => Promise + deliveryPositionMode: (payload: any) => Promise + deliveryPositionModeChange: (payload: any) => Promise + deliveryLeverage: (payload: any) => Promise + deliveryMarginType: (payload: any) => Promise + deliveryPositionMargin: (payload: any) => Promise + deliveryMarginHistory: (payload: any) => Promise + deliveryIncome: (payload: any) => Promise - // PAPI endpoints - papiPing: () => Promise - papiUmOrder: (payload: any) => Promise - papiUmConditionalOrder: (payload: any) => Promise - papiCmOrder: (payload: any) => Promise - papiCmConditionalOrder: (payload: any) => Promise - papiMarginOrder: (payload: any) => Promise - papiMarginLoan: (payload: any) => Promise - papiRepayLoan: (payload: any) => Promise - papiMarginOrderOco: (payload: any) => Promise - papiUmCancelOrder: (payload: any) => Promise - papiUmCancelAllOpenOrders: (payload: any) => Promise - papiUmCancelConditionalOrder: (payload: any) => Promise - papiUmCancelConditionalAllOpenOrders: (payload: any) => Promise - papiCmCancelOrder: (payload: any) => Promise - papiCmCancelAllOpenOrders: (payload: any) => Promise - papiCmCancelConditionalOrder: (payload: any) => Promise - papiCmCancelConditionalAllOpenOrders: (payload: any) => Promise - papiMarginCancelOrder: (payload: any) => Promise - papiMarginCancelOrderList: (payload: any) => Promise - papiMarginCancelAllOpenOrders: (payload: any) => Promise - papiUmUpdateOrder: (payload: any) => Promise - papiCmUpdateOrder: (payload: any) => Promise - papiUmGetOrder: (payload: any) => Promise - papiUmGetAllOrders: (payload: any) => Promise - papiUmGetOpenOrder: (payload: any) => Promise - papiUmGetOpenOrders: (payload: any) => Promise - papiUmGetConditionalAllOrders: (payload: any) => Promise - papiUmGetConditionalOpenOrders: (payload: any) => Promise - papiUmGetConditionalOpenOrder: (payload: any) => Promise - papiUmGetConditionalOrderHistory: (payload: any) => Promise - papiCmGetOrder: (payload: any) => Promise - papiCmGetAllOrders: (payload: any) => Promise - papiCmGetOpenOrder: (payload: any) => Promise - papiCmGetOpenOrders: (payload: any) => Promise - papiCmGetConditionalOpenOrders: (payload: any) => Promise - papiCmGetConditionalOpenOrder: (payload: any) => Promise - papiCmGetConditionalAllOrders: (payload: any) => Promise - papiCmGetConditionalOrderHistory: (payload: any) => Promise - papiUmGetForceOrders: (payload: any) => Promise - papiCmGetForceOrders: (payload: any) => Promise - papiUmGetOrderAmendment: (payload: any) => Promise - papiCmGetOrderAmendment: (payload: any) => Promise - papiMarginGetForceOrders: (payload: any) => Promise - papiUmGetUserTrades: (payload: any) => Promise - papiCmGetUserTrades: (payload: any) => Promise - papiUmGetAdlQuantile: (payload: any) => Promise - papiCmGetAdlQuantile: (payload: any) => Promise - papiUmFeeBurn: (payload: any) => Promise - papiUmGetFeeBurn: (payload: any) => Promise - papiMarginGetOrder: (payload: any) => Promise - papiMarginGetOpenOrders: (payload: any) => Promise - papiMarginGetAllOrders: (payload: any) => Promise - papiMarginGetOrderList: (payload: any) => Promise - papiMarginGetAllOrderList: (payload: any) => Promise - papiMarginGetOpenOrderList: (payload: any) => Promise - papiMarginGetMyTrades: (payload: any) => Promise - papiMarginRepayDebt: (payload: any) => Promise + // PAPI endpoints + papiPing: () => Promise + papiUmOrder: (payload: any) => Promise + papiUmConditionalOrder: (payload: any) => Promise + papiCmOrder: (payload: any) => Promise + papiCmConditionalOrder: (payload: any) => Promise + papiMarginOrder: (payload: any) => Promise + papiMarginLoan: (payload: any) => Promise + papiRepayLoan: (payload: any) => Promise + papiMarginOrderOco: (payload: any) => Promise + papiUmCancelOrder: (payload: any) => Promise + papiUmCancelAllOpenOrders: (payload: any) => Promise + papiUmCancelConditionalOrder: (payload: any) => Promise + papiUmCancelConditionalAllOpenOrders: (payload: any) => Promise + papiCmCancelOrder: (payload: any) => Promise + papiCmCancelAllOpenOrders: (payload: any) => Promise + papiCmCancelConditionalOrder: (payload: any) => Promise + papiCmCancelConditionalAllOpenOrders: (payload: any) => Promise + papiMarginCancelOrder: (payload: any) => Promise + papiMarginCancelOrderList: (payload: any) => Promise + papiMarginCancelAllOpenOrders: (payload: any) => Promise + papiUmUpdateOrder: (payload: any) => Promise + papiCmUpdateOrder: (payload: any) => Promise + papiUmGetOrder: (payload: any) => Promise + papiUmGetAllOrders: (payload: any) => Promise + papiUmGetOpenOrder: (payload: any) => Promise + papiUmGetOpenOrders: (payload: any) => Promise + papiUmGetConditionalAllOrders: (payload: any) => Promise + papiUmGetConditionalOpenOrders: (payload: any) => Promise + papiUmGetConditionalOpenOrder: (payload: any) => Promise + papiUmGetConditionalOrderHistory: (payload: any) => Promise + papiCmGetOrder: (payload: any) => Promise + papiCmGetAllOrders: (payload: any) => Promise + papiCmGetOpenOrder: (payload: any) => Promise + papiCmGetOpenOrders: (payload: any) => Promise + papiCmGetConditionalOpenOrders: (payload: any) => Promise + papiCmGetConditionalOpenOrder: (payload: any) => Promise + papiCmGetConditionalAllOrders: (payload: any) => Promise + papiCmGetConditionalOrderHistory: (payload: any) => Promise + papiUmGetForceOrders: (payload: any) => Promise + papiCmGetForceOrders: (payload: any) => Promise + papiUmGetOrderAmendment: (payload: any) => Promise + papiCmGetOrderAmendment: (payload: any) => Promise + papiMarginGetForceOrders: (payload: any) => Promise + papiUmGetUserTrades: (payload: any) => Promise + papiCmGetUserTrades: (payload: any) => Promise + papiUmGetAdlQuantile: (payload: any) => Promise + papiCmGetAdlQuantile: (payload: any) => Promise + papiUmFeeBurn: (payload: any) => Promise + papiUmGetFeeBurn: (payload: any) => Promise + papiMarginGetOrder: (payload: any) => Promise + papiMarginGetOpenOrders: (payload: any) => Promise + papiMarginGetAllOrders: (payload: any) => Promise + papiMarginGetOrderList: (payload: any) => Promise + papiMarginGetAllOrderList: (payload: any) => Promise + papiMarginGetOpenOrderList: (payload: any) => Promise + papiMarginGetMyTrades: (payload: any) => Promise + papiMarginRepayDebt: (payload: any) => Promise - // Margin endpoints - marginOrder: (payload: any) => Promise - marginOrderOco: (payload: any) => Promise - marginGetOrder: (payload: any) => Promise - marginGetOrderOco: (payload: any) => Promise - marginCancelOrder: (payload: any) => Promise - marginOpenOrders: (payload: any) => Promise - marginCancelOpenOrders: (payload: any) => Promise - marginAccountInfo: (payload: any) => Promise - marginRepay: (payload: any) => Promise - marginLoan: (payload: any) => Promise - marginIsolatedAccount: (payload: any) => Promise - marginMaxBorrow: (payload: any) => Promise - marginCreateIsolated: (payload: any) => Promise - marginIsolatedTransfer: (payload: any) => Promise - marginIsolatedTransferHistory: (payload: any) => Promise - disableMarginAccount: (payload: any) => Promise - enableMarginAccount: (payload: any) => Promise - marginAccount: () => Promise + // Margin endpoints + marginOrder: (payload: any) => Promise + marginOrderOco: (payload: any) => Promise + marginGetOrder: (payload: any) => Promise + marginGetOrderOco: (payload: any) => Promise + marginCancelOrder: (payload: any) => Promise + marginOpenOrders: (payload: any) => Promise + marginCancelOpenOrders: (payload: any) => Promise + marginAccountInfo: (payload: any) => Promise + marginRepay: (payload: any) => Promise + marginLoan: (payload: any) => Promise + marginIsolatedAccount: (payload: any) => Promise + marginMaxBorrow: (payload: any) => Promise + marginCreateIsolated: (payload: any) => Promise + marginIsolatedTransfer: (payload: any) => Promise + marginIsolatedTransferHistory: (payload: any) => Promise + disableMarginAccount: (payload: any) => Promise + enableMarginAccount: (payload: any) => Promise + marginAccount: () => Promise - // Portfolio margin endpoints - portfolioMarginAccountInfo: () => Promise - portfolioMarginCollateralRate: () => Promise - portfolioMarginLoan: (payload: any) => Promise - portfolioMarginLoanRepay: (payload: any) => Promise - portfolioMarginInterestHistory: (payload: any) => Promise + // Portfolio margin endpoints + portfolioMarginAccountInfo: () => Promise + portfolioMarginCollateralRate: () => Promise + portfolioMarginLoan: (payload: any) => Promise + portfolioMarginLoanRepay: (payload: any) => Promise + portfolioMarginInterestHistory: (payload: any) => Promise - // Savings endpoints - savingsProducts: (payload: any) => Promise - savingsPurchase: (payload: any) => Promise - savingsRedeem: (payload: any) => Promise - savingsRedemptionQuota: (payload: any) => Promise - fundingWallet: (payload: any) => Promise - convertTradeFlow: (payload: any) => Promise - rebateTaxQuery: () => Promise - payTradeHistory: (payload: any) => Promise - apiRestrictions: (payload: any) => Promise - savingsAccount: () => Promise + // Savings endpoints + savingsProducts: (payload: any) => Promise + savingsPurchase: (payload: any) => Promise + savingsRedeem: (payload: any) => Promise + savingsRedemptionQuota: (payload: any) => Promise + fundingWallet: (payload: any) => Promise + convertTradeFlow: (payload: any) => Promise + rebateTaxQuery: () => Promise + payTradeHistory: (payload: any) => Promise + apiRestrictions: (payload: any) => Promise + savingsAccount: () => Promise - // Mining endpoints - miningHashrateResaleRequest: (payload: any) => Promise - miningHashrateResaleCancel: (payload: any) => Promise - miningStatistics: (payload: any) => Promise + // Mining endpoints + miningHashrateResaleRequest: (payload: any) => Promise + miningHashrateResaleCancel: (payload: any) => Promise + miningStatistics: (payload: any) => Promise - // Utility endpoints - privateRequest: (method: string, url: string, payload: any) => Promise - publicRequest: (method: string, url: string, payload: any) => Promise + // Utility endpoints + privateRequest: (method: string, url: string, payload: any) => Promise + publicRequest: (method: string, url: string, payload: any) => Promise } // This test will fail at compile time if the types are incorrect test('types should compile correctly', async t => { - // Create a typed instance - const Binance = (await import('..')).default - const client: BinanceRest = Binance({ - apiKey: 'dummy', - apiSecret: 'dummy', - }) + // Create a typed instance + const Binance = (await import('..')).default + const client: BinanceRest = Binance({ + apiKey: 'dummy', + apiSecret: 'dummy', + }) - // Test base/generic endpoints - const info = client.getInfo() - const ping = client.ping() - const time = client.time() - const exchangeInfo = client.exchangeInfo() + // Test base/generic endpoints + const info = client.getInfo() + const ping = client.ping() + const time = client.time() + const exchangeInfo = client.exchangeInfo() - // Test market data endpoints - const book = client.book({ symbol: 'BTCUSDT' }) - const aggTrades = client.aggTrades({ symbol: 'BTCUSDT' }) - const candles = client.candles({ symbol: 'BTCUSDT', interval: '1m' }) - const trades = client.trades({ symbol: 'BTCUSDT' }) - const tradesHistory = client.tradesHistory({ symbol: 'BTCUSDT' }) - const dailyStats = client.dailyStats({ symbol: 'BTCUSDT' }) - const prices = client.prices() - const avgPrice = client.avgPrice({ symbol: 'BTCUSDT' }) - const allBookTickers = client.allBookTickers() + // Test market data endpoints + const book = client.book({ symbol: 'BTCUSDT' }) + const aggTrades = client.aggTrades({ symbol: 'BTCUSDT' }) + const candles = client.candles({ symbol: 'BTCUSDT', interval: '1m' }) + const trades = client.trades({ symbol: 'BTCUSDT' }) + const tradesHistory = client.tradesHistory({ symbol: 'BTCUSDT' }) + const dailyStats = client.dailyStats({ symbol: 'BTCUSDT' }) + const prices = client.prices() + const avgPrice = client.avgPrice({ symbol: 'BTCUSDT' }) + const allBookTickers = client.allBookTickers() - // Test order endpoints - const order = client.order({ - symbol: 'BTCUSDT', - side: OrderSide.BUY, - type: OrderType.LIMIT, - quantity: '1', - price: '50000', - timeInForce: TimeInForce.GTC, - }) - const orderOco = client.orderOco({ - symbol: 'BTCUSDT', - side: OrderSide.BUY, - quantity: '1', - price: '50000', - stopPrice: '51000', - }) - const orderTest = client.orderTest({ - symbol: 'BTCUSDT', - side: OrderSide.BUY, - type: OrderType.LIMIT, - quantity: '1', - price: '50000', - }) - const getOrder = client.getOrder({ symbol: 'BTCUSDT', orderId: 12345 }) - const getOrderOco = client.getOrderOco({ orderListId: 12345 }) - const cancelOrder = client.cancelOrder({ symbol: 'BTCUSDT', orderId: 12345 }) - const cancelOrderOco = client.cancelOrderOco({ symbol: 'BTCUSDT', orderListId: 12345 }) - const cancelOpenOrders = client.cancelOpenOrders({ symbol: 'BTCUSDT' }) - const openOrders = client.openOrders({ symbol: 'BTCUSDT' }) - const allOrders = client.allOrders({ symbol: 'BTCUSDT' }) - const allOrdersOCO = client.allOrdersOCO({ fromId: 12345 }) + // Test order endpoints + const order = client.order({ + symbol: 'BTCUSDT', + side: OrderSide.BUY, + type: OrderType.LIMIT, + quantity: '1', + price: '50000', + timeInForce: TimeInForce.GTC, + }) + const orderOco = client.orderOco({ + symbol: 'BTCUSDT', + side: OrderSide.BUY, + quantity: '1', + price: '50000', + stopPrice: '51000', + }) + const orderTest = client.orderTest({ + symbol: 'BTCUSDT', + side: OrderSide.BUY, + type: OrderType.LIMIT, + quantity: '1', + price: '50000', + }) + const getOrder = client.getOrder({ symbol: 'BTCUSDT', orderId: 12345 }) + const getOrderOco = client.getOrderOco({ orderListId: 12345 }) + const cancelOrder = client.cancelOrder({ symbol: 'BTCUSDT', orderId: 12345 }) + const cancelOrderOco = client.cancelOrderOco({ symbol: 'BTCUSDT', orderListId: 12345 }) + const cancelOpenOrders = client.cancelOpenOrders({ symbol: 'BTCUSDT' }) + const openOrders = client.openOrders({ symbol: 'BTCUSDT' }) + const allOrders = client.allOrders({ symbol: 'BTCUSDT' }) + const allOrdersOCO = client.allOrdersOCO({ fromId: 12345 }) - // Test account endpoints - const accountInfo = client.accountInfo() - const myTrades = client.myTrades({ symbol: 'BTCUSDT' }) - const withdraw = client.withdraw({ coin: 'BTC', address: 'address', amount: '1' }) - const withdrawHistory = client.withdrawHistory({ coin: 'BTC' }) - const depositHistory = client.depositHistory({ coin: 'BTC' }) - const depositAddress = client.depositAddress({ coin: 'BTC' }) - const tradeFee = client.tradeFee({ symbol: 'BTCUSDT' }) - const assetDetail = client.assetDetail({ asset: 'BTC' }) - const accountSnapshot = client.accountSnapshot({ type: 'SPOT' }) - const universalTransfer = client.universalTransfer({ - type: 'MAIN_UMFUTURE', - asset: 'BTC', - amount: '1', - }) - const universalTransferHistory = client.universalTransferHistory({ type: 'MAIN_UMFUTURE' }) - const dustLog = client.dustLog() - const dustTransfer = client.dustTransfer({ asset: ['BTC', 'ETH'] }) - const accountCoins = client.accountCoins() - const getBnbBurn = client.getBnbBurn() - const setBnbBurn = client.setBnbBurn({ spotBNBBurn: true }) + // Test account endpoints + const accountInfo = client.accountInfo() + const myTrades = client.myTrades({ symbol: 'BTCUSDT' }) + const withdraw = client.withdraw({ coin: 'BTC', address: 'address', amount: '1' }) + const withdrawHistory = client.withdrawHistory({ coin: 'BTC' }) + const depositHistory = client.depositHistory({ coin: 'BTC' }) + const depositAddress = client.depositAddress({ coin: 'BTC' }) + const tradeFee = client.tradeFee({ symbol: 'BTCUSDT' }) + const assetDetail = client.assetDetail({ asset: 'BTC' }) + const accountSnapshot = client.accountSnapshot({ type: 'SPOT' }) + const universalTransfer = client.universalTransfer({ + type: 'MAIN_UMFUTURE', + asset: 'BTC', + amount: '1', + }) + const universalTransferHistory = client.universalTransferHistory({ type: 'MAIN_UMFUTURE' }) + const dustLog = client.dustLog() + const dustTransfer = client.dustTransfer({ asset: ['BTC', 'ETH'] }) + const accountCoins = client.accountCoins() + const getBnbBurn = client.getBnbBurn() + const setBnbBurn = client.setBnbBurn({ spotBNBBurn: true }) - // Test user data stream endpoints - const getDataStream = client.getDataStream() - const keepDataStream = client.keepDataStream({ listenKey: 'key' }) - const closeDataStream = client.closeDataStream({ listenKey: 'key' }) - const marginGetDataStream = client.marginGetDataStream() - const marginKeepDataStream = client.marginKeepDataStream({ listenKey: 'key' }) - const marginCloseDataStream = client.marginCloseDataStream({ listenKey: 'key' }) - const futuresGetDataStream = client.futuresGetDataStream() - const futuresKeepDataStream = client.futuresKeepDataStream({ listenKey: 'key' }) - const futuresCloseDataStream = client.futuresCloseDataStream({ listenKey: 'key' }) - const deliveryGetDataStream = client.deliveryGetDataStream() - const deliveryKeepDataStream = client.deliveryKeepDataStream({ listenKey: 'key' }) - const deliveryCloseDataStream = client.deliveryCloseDataStream({ listenKey: 'key' }) + // Test user data stream endpoints + const getDataStream = client.getDataStream() + const keepDataStream = client.keepDataStream({ listenKey: 'key' }) + const closeDataStream = client.closeDataStream({ listenKey: 'key' }) + const marginGetDataStream = client.marginGetDataStream() + const marginKeepDataStream = client.marginKeepDataStream({ listenKey: 'key' }) + const marginCloseDataStream = client.marginCloseDataStream({ listenKey: 'key' }) + const futuresGetDataStream = client.futuresGetDataStream() + const futuresKeepDataStream = client.futuresKeepDataStream({ listenKey: 'key' }) + const futuresCloseDataStream = client.futuresCloseDataStream({ listenKey: 'key' }) + const deliveryGetDataStream = client.deliveryGetDataStream() + const deliveryKeepDataStream = client.deliveryKeepDataStream({ listenKey: 'key' }) + const deliveryCloseDataStream = client.deliveryCloseDataStream({ listenKey: 'key' }) - // Test futures endpoints - const futuresPing = client.futuresPing() - const futuresTime = client.futuresTime() - const futuresExchangeInfo = client.futuresExchangeInfo() - const futuresBook = client.futuresBook({ symbol: 'BTCUSDT' }) - const futuresAggTrades = client.futuresAggTrades({ symbol: 'BTCUSDT' }) - const futuresMarkPrice = client.futuresMarkPrice({ symbol: 'BTCUSDT' }) - const futuresAllForceOrders = client.futuresAllForceOrders({ symbol: 'BTCUSDT' }) - const futuresLongShortRatio = client.futuresLongShortRatio({ symbol: 'BTCUSDT' }) - const futuresCandles = client.futuresCandles({ symbol: 'BTCUSDT', interval: '1m' }) - const futuresMarkPriceCandles = client.futuresMarkPriceCandles({ - symbol: 'BTCUSDT', - interval: '1m', - }) - const futuresIndexPriceCandles = client.futuresIndexPriceCandles({ - pair: 'BTCUSDT', - interval: '1m', - }) - const futuresTrades = client.futuresTrades({ symbol: 'BTCUSDT' }) - const futuresDailyStats = client.futuresDailyStats({ symbol: 'BTCUSDT' }) - const futuresPrices = client.futuresPrices({ symbol: 'BTCUSDT' }) - const futuresAllBookTickers = client.futuresAllBookTickers() - const futuresFundingRate = client.futuresFundingRate({ symbol: 'BTCUSDT' }) - const futuresOrder = client.futuresOrder({ - symbol: 'BTCUSDT', - side: OrderSide.BUY, - type: OrderType.LIMIT, - quantity: '1', - price: '50000', - }) - const futuresBatchOrders = client.futuresBatchOrders({ - batchOrders: [ - { + // Test futures endpoints + const futuresPing = client.futuresPing() + const futuresTime = client.futuresTime() + const futuresExchangeInfo = client.futuresExchangeInfo() + const futuresBook = client.futuresBook({ symbol: 'BTCUSDT' }) + const futuresAggTrades = client.futuresAggTrades({ symbol: 'BTCUSDT' }) + const futuresMarkPrice = client.futuresMarkPrice({ symbol: 'BTCUSDT' }) + const futuresAllForceOrders = client.futuresAllForceOrders({ symbol: 'BTCUSDT' }) + const futuresLongShortRatio = client.futuresLongShortRatio({ symbol: 'BTCUSDT' }) + const futuresCandles = client.futuresCandles({ symbol: 'BTCUSDT', interval: '1m' }) + const futuresMarkPriceCandles = client.futuresMarkPriceCandles({ + symbol: 'BTCUSDT', + interval: '1m', + }) + const futuresIndexPriceCandles = client.futuresIndexPriceCandles({ + pair: 'BTCUSDT', + interval: '1m', + }) + const futuresTrades = client.futuresTrades({ symbol: 'BTCUSDT' }) + const futuresDailyStats = client.futuresDailyStats({ symbol: 'BTCUSDT' }) + const futuresPrices = client.futuresPrices({ symbol: 'BTCUSDT' }) + const futuresAllBookTickers = client.futuresAllBookTickers() + const futuresFundingRate = client.futuresFundingRate({ symbol: 'BTCUSDT' }) + const futuresOrder = client.futuresOrder({ symbol: 'BTCUSDT', side: OrderSide.BUY, type: OrderType.LIMIT, quantity: '1', price: '50000', - }, - ], - }) - const futuresGetOrder = client.futuresGetOrder({ symbol: 'BTCUSDT', orderId: 12345 }) - const futuresCancelOrder = client.futuresCancelOrder({ symbol: 'BTCUSDT', orderId: 12345 }) - const futuresCancelAllOpenOrders = client.futuresCancelAllOpenOrders({ symbol: 'BTCUSDT' }) - const futuresCancelBatchOrders = client.futuresCancelBatchOrders({ - symbol: 'BTCUSDT', - orderIdList: [12345], - }) - const futuresOpenOrders = client.futuresOpenOrders({ symbol: 'BTCUSDT' }) - const futuresAllOrders = client.futuresAllOrders({ symbol: 'BTCUSDT' }) - const futuresPositionRisk = client.futuresPositionRisk({ symbol: 'BTCUSDT' }) - const futuresLeverageBracket = client.futuresLeverageBracket({ symbol: 'BTCUSDT' }) - const futuresAccountBalance = client.futuresAccountBalance() - const futuresAccountInfo = client.futuresAccountInfo() - const futuresUserTrades = client.futuresUserTrades({ symbol: 'BTCUSDT' }) - const futuresPositionMode = client.futuresPositionMode({ dualSidePosition: true }) - const futuresPositionModeChange = client.futuresPositionModeChange({ dualSidePosition: true }) - const futuresLeverage = client.futuresLeverage({ symbol: 'BTCUSDT', leverage: 10 }) - const futuresMarginType = client.futuresMarginType({ symbol: 'BTCUSDT', marginType: 'ISOLATED' }) - const futuresPositionMargin = client.futuresPositionMargin({ - symbol: 'BTCUSDT', - positionSide: 'BOTH', - amount: '1', - type: 1, - }) - const futuresMarginHistory = client.futuresMarginHistory({ symbol: 'BTCUSDT' }) - const futuresIncome = client.futuresIncome({ symbol: 'BTCUSDT' }) - const getMultiAssetsMargin = client.getMultiAssetsMargin({ multiAssetsMargin: true }) - const setMultiAssetsMargin = client.setMultiAssetsMargin({ multiAssetsMargin: true }) + }) + const futuresBatchOrders = client.futuresBatchOrders({ + batchOrders: [ + { + symbol: 'BTCUSDT', + side: OrderSide.BUY, + type: OrderType.LIMIT, + quantity: '1', + price: '50000', + }, + ], + }) + const futuresGetOrder = client.futuresGetOrder({ symbol: 'BTCUSDT', orderId: 12345 }) + const futuresCancelOrder = client.futuresCancelOrder({ symbol: 'BTCUSDT', orderId: 12345 }) + const futuresCancelAllOpenOrders = client.futuresCancelAllOpenOrders({ symbol: 'BTCUSDT' }) + const futuresCancelBatchOrders = client.futuresCancelBatchOrders({ + symbol: 'BTCUSDT', + orderIdList: [12345], + }) + const futuresOpenOrders = client.futuresOpenOrders({ symbol: 'BTCUSDT' }) + const futuresAllOrders = client.futuresAllOrders({ symbol: 'BTCUSDT' }) + const futuresPositionRisk = client.futuresPositionRisk({ symbol: 'BTCUSDT' }) + const futuresLeverageBracket = client.futuresLeverageBracket({ symbol: 'BTCUSDT' }) + const futuresAccountBalance = client.futuresAccountBalance() + const futuresAccountInfo = client.futuresAccountInfo() + const futuresUserTrades = client.futuresUserTrades({ symbol: 'BTCUSDT' }) + const futuresPositionMode = client.futuresPositionMode({ dualSidePosition: true }) + const futuresPositionModeChange = client.futuresPositionModeChange({ dualSidePosition: true }) + const futuresLeverage = client.futuresLeverage({ symbol: 'BTCUSDT', leverage: 10 }) + const futuresMarginType = client.futuresMarginType({ + symbol: 'BTCUSDT', + marginType: 'ISOLATED', + }) + const futuresPositionMargin = client.futuresPositionMargin({ + symbol: 'BTCUSDT', + positionSide: 'BOTH', + amount: '1', + type: 1, + }) + const futuresMarginHistory = client.futuresMarginHistory({ symbol: 'BTCUSDT' }) + const futuresIncome = client.futuresIncome({ symbol: 'BTCUSDT' }) + const getMultiAssetsMargin = client.getMultiAssetsMargin({ multiAssetsMargin: true }) + const setMultiAssetsMargin = client.setMultiAssetsMargin({ multiAssetsMargin: true }) - // Test delivery endpoints - const deliveryPing = client.deliveryPing() - const deliveryTime = client.deliveryTime() - const deliveryExchangeInfo = client.deliveryExchangeInfo() - const deliveryBook = client.deliveryBook({ symbol: 'BTCUSD_PERP' }) - const deliveryAggTrades = client.deliveryAggTrades({ symbol: 'BTCUSD_PERP' }) - const deliveryMarkPrice = client.deliveryMarkPrice({ symbol: 'BTCUSD_PERP' }) - const deliveryAllForceOrders = client.deliveryAllForceOrders({ symbol: 'BTCUSD_PERP' }) - const deliveryLongShortRatio = client.deliveryLongShortRatio({ symbol: 'BTCUSD_PERP' }) - const deliveryCandles = client.deliveryCandles({ symbol: 'BTCUSD_PERP', interval: '1m' }) - const deliveryMarkPriceCandles = client.deliveryMarkPriceCandles({ - symbol: 'BTCUSD_PERP', - interval: '1m', - }) - const deliveryIndexPriceCandles = client.deliveryIndexPriceCandles({ - pair: 'BTCUSD', - interval: '1m', - }) - const deliveryTrades = client.deliveryTrades({ symbol: 'BTCUSD_PERP' }) - const deliveryDailyStats = client.deliveryDailyStats({ symbol: 'BTCUSD_PERP' }) - const deliveryPrices = client.deliveryPrices() - const deliveryAllBookTickers = client.deliveryAllBookTickers() - const deliveryFundingRate = client.deliveryFundingRate({ symbol: 'BTCUSD_PERP' }) - const deliveryOrder = client.deliveryOrder({ - symbol: 'BTCUSD_PERP', - side: OrderSide.BUY, - type: OrderType.LIMIT, - quantity: '1', - price: '50000', - }) - const deliveryBatchOrders = client.deliveryBatchOrders({ - batchOrders: [ - { + // Test delivery endpoints + const deliveryPing = client.deliveryPing() + const deliveryTime = client.deliveryTime() + const deliveryExchangeInfo = client.deliveryExchangeInfo() + const deliveryBook = client.deliveryBook({ symbol: 'BTCUSD_PERP' }) + const deliveryAggTrades = client.deliveryAggTrades({ symbol: 'BTCUSD_PERP' }) + const deliveryMarkPrice = client.deliveryMarkPrice({ symbol: 'BTCUSD_PERP' }) + const deliveryAllForceOrders = client.deliveryAllForceOrders({ symbol: 'BTCUSD_PERP' }) + const deliveryLongShortRatio = client.deliveryLongShortRatio({ symbol: 'BTCUSD_PERP' }) + const deliveryCandles = client.deliveryCandles({ symbol: 'BTCUSD_PERP', interval: '1m' }) + const deliveryMarkPriceCandles = client.deliveryMarkPriceCandles({ + symbol: 'BTCUSD_PERP', + interval: '1m', + }) + const deliveryIndexPriceCandles = client.deliveryIndexPriceCandles({ + pair: 'BTCUSD', + interval: '1m', + }) + const deliveryTrades = client.deliveryTrades({ symbol: 'BTCUSD_PERP' }) + const deliveryDailyStats = client.deliveryDailyStats({ symbol: 'BTCUSD_PERP' }) + const deliveryPrices = client.deliveryPrices() + const deliveryAllBookTickers = client.deliveryAllBookTickers() + const deliveryFundingRate = client.deliveryFundingRate({ symbol: 'BTCUSD_PERP' }) + const deliveryOrder = client.deliveryOrder({ symbol: 'BTCUSD_PERP', side: OrderSide.BUY, type: OrderType.LIMIT, quantity: '1', price: '50000', - }, - ], - }) - const deliveryGetOrder = client.deliveryGetOrder({ symbol: 'BTCUSD_PERP', orderId: 12345 }) - const deliveryCancelOrder = client.deliveryCancelOrder({ symbol: 'BTCUSD_PERP', orderId: 12345 }) - const deliveryCancelAllOpenOrders = client.deliveryCancelAllOpenOrders({ symbol: 'BTCUSD_PERP' }) - const deliveryCancelBatchOrders = client.deliveryCancelBatchOrders({ - symbol: 'BTCUSD_PERP', - orderIdList: [12345], - }) - const deliveryOpenOrders = client.deliveryOpenOrders({ symbol: 'BTCUSD_PERP' }) - const deliveryAllOrders = client.deliveryAllOrders({ symbol: 'BTCUSD_PERP' }) - const deliveryPositionRisk = client.deliveryPositionRisk({ symbol: 'BTCUSD_PERP' }) - const deliveryLeverageBracket = client.deliveryLeverageBracket({ symbol: 'BTCUSD_PERP' }) - const deliveryAccountBalance = client.deliveryAccountBalance() - const deliveryAccountInfo = client.deliveryAccountInfo() - const deliveryUserTrades = client.deliveryUserTrades({ symbol: 'BTCUSD_PERP' }) - const deliveryPositionMode = client.deliveryPositionMode({ dualSidePosition: true }) - const deliveryPositionModeChange = client.deliveryPositionModeChange({ dualSidePosition: true }) - const deliveryLeverage = client.deliveryLeverage({ symbol: 'BTCUSD_PERP', leverage: 10 }) - const deliveryMarginType = client.deliveryMarginType({ - symbol: 'BTCUSD_PERP', - marginType: 'ISOLATED', - }) - const deliveryPositionMargin = client.deliveryPositionMargin({ - symbol: 'BTCUSD_PERP', - positionSide: 'BOTH', - amount: '1', - type: 1, - }) - const deliveryMarginHistory = client.deliveryMarginHistory({ symbol: 'BTCUSD_PERP' }) - const deliveryIncome = client.deliveryIncome({ symbol: 'BTCUSD_PERP' }) + }) + const deliveryBatchOrders = client.deliveryBatchOrders({ + batchOrders: [ + { + symbol: 'BTCUSD_PERP', + side: OrderSide.BUY, + type: OrderType.LIMIT, + quantity: '1', + price: '50000', + }, + ], + }) + const deliveryGetOrder = client.deliveryGetOrder({ symbol: 'BTCUSD_PERP', orderId: 12345 }) + const deliveryCancelOrder = client.deliveryCancelOrder({ + symbol: 'BTCUSD_PERP', + orderId: 12345, + }) + const deliveryCancelAllOpenOrders = client.deliveryCancelAllOpenOrders({ + symbol: 'BTCUSD_PERP', + }) + const deliveryCancelBatchOrders = client.deliveryCancelBatchOrders({ + symbol: 'BTCUSD_PERP', + orderIdList: [12345], + }) + const deliveryOpenOrders = client.deliveryOpenOrders({ symbol: 'BTCUSD_PERP' }) + const deliveryAllOrders = client.deliveryAllOrders({ symbol: 'BTCUSD_PERP' }) + const deliveryPositionRisk = client.deliveryPositionRisk({ symbol: 'BTCUSD_PERP' }) + const deliveryLeverageBracket = client.deliveryLeverageBracket({ symbol: 'BTCUSD_PERP' }) + const deliveryAccountBalance = client.deliveryAccountBalance() + const deliveryAccountInfo = client.deliveryAccountInfo() + const deliveryUserTrades = client.deliveryUserTrades({ symbol: 'BTCUSD_PERP' }) + const deliveryPositionMode = client.deliveryPositionMode({ dualSidePosition: true }) + const deliveryPositionModeChange = client.deliveryPositionModeChange({ dualSidePosition: true }) + const deliveryLeverage = client.deliveryLeverage({ symbol: 'BTCUSD_PERP', leverage: 10 }) + const deliveryMarginType = client.deliveryMarginType({ + symbol: 'BTCUSD_PERP', + marginType: 'ISOLATED', + }) + const deliveryPositionMargin = client.deliveryPositionMargin({ + symbol: 'BTCUSD_PERP', + positionSide: 'BOTH', + amount: '1', + type: 1, + }) + const deliveryMarginHistory = client.deliveryMarginHistory({ symbol: 'BTCUSD_PERP' }) + const deliveryIncome = client.deliveryIncome({ symbol: 'BTCUSD_PERP' }) - // Test PAPI endpoints - const papiPing = client.papiPing() - const papiUmOrder = client.papiUmOrder({ - symbol: 'BTCUSDT', - side: OrderSide.BUY, - type: OrderType.LIMIT, - quantity: '1', - price: '50000', - }) - const papiUmConditionalOrder = client.papiUmConditionalOrder({ - symbol: 'BTCUSDT', - side: OrderSide.BUY, - type: OrderType.LIMIT, - quantity: '1', - price: '50000', - stopPrice: '51000', - }) - const papiCmOrder = client.papiCmOrder({ - symbol: 'BTCUSD_PERP', - side: OrderSide.BUY, - type: OrderType.LIMIT, - quantity: '1', - price: '50000', - }) - const papiCmConditionalOrder = client.papiCmConditionalOrder({ - symbol: 'BTCUSD_PERP', - side: OrderSide.BUY, - type: OrderType.LIMIT, - quantity: '1', - price: '50000', - stopPrice: '51000', - }) - const papiMarginOrder = client.papiMarginOrder({ - symbol: 'BTCUSDT', - side: OrderSide.BUY, - type: OrderType.LIMIT, - quantity: '1', - price: '50000', - }) - const papiMarginLoan = client.papiMarginLoan({ asset: 'BTC', amount: '1' }) - const papiRepayLoan = client.papiRepayLoan({ asset: 'BTC', amount: '1' }) - const papiMarginOrderOco = client.papiMarginOrderOco({ - symbol: 'BTCUSDT', - side: OrderSide.BUY, - quantity: '1', - price: '50000', - stopPrice: '51000', - }) - const papiUmCancelOrder = client.papiUmCancelOrder({ symbol: 'BTCUSDT', orderId: 12345 }) - const papiUmCancelAllOpenOrders = client.papiUmCancelAllOpenOrders({ symbol: 'BTCUSDT' }) - const papiUmCancelConditionalOrder = client.papiUmCancelConditionalOrder({ - symbol: 'BTCUSDT', - orderId: 12345, - }) - const papiUmCancelConditionalAllOpenOrders = client.papiUmCancelConditionalAllOpenOrders({ - symbol: 'BTCUSDT', - }) - const papiCmCancelOrder = client.papiCmCancelOrder({ symbol: 'BTCUSD_PERP', orderId: 12345 }) - const papiCmCancelAllOpenOrders = client.papiCmCancelAllOpenOrders({ symbol: 'BTCUSD_PERP' }) - const papiCmCancelConditionalOrder = client.papiCmCancelConditionalOrder({ - symbol: 'BTCUSD_PERP', - orderId: 12345, - }) - const papiCmCancelConditionalAllOpenOrders = client.papiCmCancelConditionalAllOpenOrders({ - symbol: 'BTCUSD_PERP', - }) + // Test PAPI endpoints + const papiPing = client.papiPing() + const papiUmOrder = client.papiUmOrder({ + symbol: 'BTCUSDT', + side: OrderSide.BUY, + type: OrderType.LIMIT, + quantity: '1', + price: '50000', + }) + const papiUmConditionalOrder = client.papiUmConditionalOrder({ + symbol: 'BTCUSDT', + side: OrderSide.BUY, + type: OrderType.LIMIT, + quantity: '1', + price: '50000', + stopPrice: '51000', + }) + const papiCmOrder = client.papiCmOrder({ + symbol: 'BTCUSD_PERP', + side: OrderSide.BUY, + type: OrderType.LIMIT, + quantity: '1', + price: '50000', + }) + const papiCmConditionalOrder = client.papiCmConditionalOrder({ + symbol: 'BTCUSD_PERP', + side: OrderSide.BUY, + type: OrderType.LIMIT, + quantity: '1', + price: '50000', + stopPrice: '51000', + }) + const papiMarginOrder = client.papiMarginOrder({ + symbol: 'BTCUSDT', + side: OrderSide.BUY, + type: OrderType.LIMIT, + quantity: '1', + price: '50000', + }) + const papiMarginLoan = client.papiMarginLoan({ asset: 'BTC', amount: '1' }) + const papiRepayLoan = client.papiRepayLoan({ asset: 'BTC', amount: '1' }) + const papiMarginOrderOco = client.papiMarginOrderOco({ + symbol: 'BTCUSDT', + side: OrderSide.BUY, + quantity: '1', + price: '50000', + stopPrice: '51000', + }) + const papiUmCancelOrder = client.papiUmCancelOrder({ symbol: 'BTCUSDT', orderId: 12345 }) + const papiUmCancelAllOpenOrders = client.papiUmCancelAllOpenOrders({ symbol: 'BTCUSDT' }) + const papiUmCancelConditionalOrder = client.papiUmCancelConditionalOrder({ + symbol: 'BTCUSDT', + orderId: 12345, + }) + const papiUmCancelConditionalAllOpenOrders = client.papiUmCancelConditionalAllOpenOrders({ + symbol: 'BTCUSDT', + }) + const papiCmCancelOrder = client.papiCmCancelOrder({ symbol: 'BTCUSD_PERP', orderId: 12345 }) + const papiCmCancelAllOpenOrders = client.papiCmCancelAllOpenOrders({ symbol: 'BTCUSD_PERP' }) + const papiCmCancelConditionalOrder = client.papiCmCancelConditionalOrder({ + symbol: 'BTCUSD_PERP', + orderId: 12345, + }) + const papiCmCancelConditionalAllOpenOrders = client.papiCmCancelConditionalAllOpenOrders({ + symbol: 'BTCUSD_PERP', + }) - // Test margin endpoints - const marginOrder = client.marginOrder({ - symbol: 'BTCUSDT', - side: OrderSide.BUY, - type: OrderType.LIMIT, - quantity: '1', - price: '50000', - }) - const marginOrderOco = client.marginOrderOco({ - symbol: 'BTCUSDT', - side: OrderSide.BUY, - quantity: '1', - price: '50000', - stopPrice: '51000', - }) - const marginGetOrder = client.marginGetOrder({ symbol: 'BTCUSDT', orderId: '12345' }) - const marginGetOrderOco = client.marginGetOrderOco({ symbol: 'BTCUSDT', orderListId: 12345 }) - const marginCancelOrder = client.marginCancelOrder({ symbol: 'BTCUSDT', orderId: 12345 }) - const marginOpenOrders = client.marginOpenOrders({ symbol: 'BTCUSDT' }) - const marginCancelOpenOrders = client.marginCancelOpenOrders({ symbol: 'BTCUSDT' }) - const marginAccountInfo = client.marginAccountInfo() - const marginRepay = client.marginRepay({ asset: 'BTC', amount: '1' }) - const marginLoan = client.marginLoan({ asset: 'BTC', amount: '1' }) - const marginIsolatedAccount = client.marginIsolatedAccount({ symbols: 'BTCUSDT' }) - const marginMaxBorrow = client.marginMaxBorrow({ asset: 'BTC' }) - const marginCreateIsolated = client.marginCreateIsolated({ base: 'BTC', quote: 'USDT' }) - const marginIsolatedTransfer = client.marginIsolatedTransfer({ - asset: 'BTC', - symbol: 'BTCUSDT', - transFrom: 'SPOT', - transTo: 'ISOLATED_MARGIN', - amount: '1', - }) - const marginIsolatedTransferHistory = client.marginIsolatedTransferHistory({ symbol: 'BTCUSDT' }) - const disableMarginAccount = client.disableMarginAccount({ symbol: 'BTCUSDT' }) - const enableMarginAccount = client.enableMarginAccount({ symbol: 'BTCUSDT' }) - const marginAccount = client.marginAccount() + // Test margin endpoints + const marginOrder = client.marginOrder({ + symbol: 'BTCUSDT', + side: OrderSide.BUY, + type: OrderType.LIMIT, + quantity: '1', + price: '50000', + }) + const marginOrderOco = client.marginOrderOco({ + symbol: 'BTCUSDT', + side: OrderSide.BUY, + quantity: '1', + price: '50000', + stopPrice: '51000', + }) + const marginGetOrder = client.marginGetOrder({ symbol: 'BTCUSDT', orderId: '12345' }) + const marginGetOrderOco = client.marginGetOrderOco({ symbol: 'BTCUSDT', orderListId: 12345 }) + const marginCancelOrder = client.marginCancelOrder({ symbol: 'BTCUSDT', orderId: 12345 }) + const marginOpenOrders = client.marginOpenOrders({ symbol: 'BTCUSDT' }) + const marginCancelOpenOrders = client.marginCancelOpenOrders({ symbol: 'BTCUSDT' }) + const marginAccountInfo = client.marginAccountInfo() + const marginRepay = client.marginRepay({ asset: 'BTC', amount: '1' }) + const marginLoan = client.marginLoan({ asset: 'BTC', amount: '1' }) + const marginIsolatedAccount = client.marginIsolatedAccount({ symbols: 'BTCUSDT' }) + const marginMaxBorrow = client.marginMaxBorrow({ asset: 'BTC' }) + const marginCreateIsolated = client.marginCreateIsolated({ base: 'BTC', quote: 'USDT' }) + const marginIsolatedTransfer = client.marginIsolatedTransfer({ + asset: 'BTC', + symbol: 'BTCUSDT', + transFrom: 'SPOT', + transTo: 'ISOLATED_MARGIN', + amount: '1', + }) + const marginIsolatedTransferHistory = client.marginIsolatedTransferHistory({ + symbol: 'BTCUSDT', + }) + const disableMarginAccount = client.disableMarginAccount({ symbol: 'BTCUSDT' }) + const enableMarginAccount = client.enableMarginAccount({ symbol: 'BTCUSDT' }) + const marginAccount = client.marginAccount() - // Test portfolio margin endpoints - const portfolioMarginAccountInfo = client.portfolioMarginAccountInfo() - const portfolioMarginCollateralRate = client.portfolioMarginCollateralRate() - const portfolioMarginLoan = client.portfolioMarginLoan({ asset: 'BTC', amount: '1' }) - const portfolioMarginLoanRepay = client.portfolioMarginLoanRepay({ asset: 'BTC', amount: '1' }) - const portfolioMarginInterestHistory = client.portfolioMarginInterestHistory({ asset: 'BTC' }) + // Test portfolio margin endpoints + const portfolioMarginAccountInfo = client.portfolioMarginAccountInfo() + const portfolioMarginCollateralRate = client.portfolioMarginCollateralRate() + const portfolioMarginLoan = client.portfolioMarginLoan({ asset: 'BTC', amount: '1' }) + const portfolioMarginLoanRepay = client.portfolioMarginLoanRepay({ asset: 'BTC', amount: '1' }) + const portfolioMarginInterestHistory = client.portfolioMarginInterestHistory({ asset: 'BTC' }) - // Test savings endpoints - const savingsProducts = client.savingsProducts({ type: 'ACTIVITY' }) - const savingsPurchase = client.savingsPurchase({ productId: '123', amount: '1' }) - const savingsRedeem = client.savingsRedeem({ productId: '123', amount: '1', type: 'FAST' }) - const savingsRedemptionQuota = client.savingsRedemptionQuota({ productId: '123', type: 'FAST' }) - const savingsAccount = client.savingsAccount() + // Test savings endpoints + const savingsProducts = client.savingsProducts({ type: 'ACTIVITY' }) + const savingsPurchase = client.savingsPurchase({ productId: '123', amount: '1' }) + const savingsRedeem = client.savingsRedeem({ productId: '123', amount: '1', type: 'FAST' }) + const savingsRedemptionQuota = client.savingsRedemptionQuota({ productId: '123', type: 'FAST' }) + const savingsAccount = client.savingsAccount() - // Test mining endpoints - const miningHashrateResaleRequest = client.miningHashrateResaleRequest({ - userName: 'user', - coinName: 'BTC', - algo: 'sha256', - startDate: 1234567890, - endDate: 1234567890, - pageIndex: 1, - pageSize: 10, - }) - const miningHashrateResaleCancel = client.miningHashrateResaleCancel({ - configId: 123, - userName: 'user', - }) - const miningStatistics = client.miningStatistics({ userName: 'user', algo: 'sha256' }) + // Test mining endpoints + const miningHashrateResaleRequest = client.miningHashrateResaleRequest({ + userName: 'user', + coinName: 'BTC', + algo: 'sha256', + startDate: 1234567890, + endDate: 1234567890, + pageIndex: 1, + pageSize: 10, + }) + const miningHashrateResaleCancel = client.miningHashrateResaleCancel({ + configId: 123, + userName: 'user', + }) + const miningStatistics = client.miningStatistics({ userName: 'user', algo: 'sha256' }) - // Test utility endpoints - const privateRequest = client.privateRequest('GET', '/api/v3/account', {}) - const publicRequest = client.publicRequest('GET', '/api/v3/ticker/price', {}) + // Test utility endpoints + const privateRequest = client.privateRequest('GET', '/api/v3/account', {}) + const publicRequest = client.publicRequest('GET', '/api/v3/ticker/price', {}) }) diff --git a/test/utils.js b/test/utils.js index 5ea04e0c..1443366c 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,24 +1,26 @@ import http from 'http' export const checkFields = (t, object, fields) => { - fields.forEach(field => { - t.truthy(object[field]) - }) + fields.forEach(field => { + t.truthy(object[field]) + }) } const generatePort = (() => { - let portNum = 9000 - return () => portNum++ + let portNum = 9000 + return () => portNum++ })() export const createHttpServer = requestHandler => { - const server = http.createServer(requestHandler) - const port = generatePort() - return { - url: `http://127.0.0.1:${port}`, - start: () => - new Promise((resolve, reject) => server.listen(port, err => (err ? reject(err) : resolve()))), - stop: () => - new Promise((resolve, reject) => server.close(err => (err ? reject(err) : resolve()))), - } + const server = http.createServer(requestHandler) + const port = generatePort() + return { + url: `http://127.0.0.1:${port}`, + start: () => + new Promise((resolve, reject) => + server.listen(port, err => (err ? reject(err) : resolve())), + ), + stop: () => + new Promise((resolve, reject) => server.close(err => (err ? reject(err) : resolve()))), + } } diff --git a/test/websockets/bookTicker.js b/test/websockets/bookTicker.js index b6e96c15..bb7d8ff7 100644 --- a/test/websockets/bookTicker.js +++ b/test/websockets/bookTicker.js @@ -7,84 +7,106 @@ import { checkFields } from '../utils' const client = Binance() test('[WS] bookTicker - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.bookTicker('ETHBTC', ticker => { - checkFields(t, ticker, ['updateId', 'symbol', 'bestBid', 'bestBidQnt', 'bestAsk', 'bestAskQnt']) - t.is(ticker.symbol, 'ETHBTC') - t.truthy(typeof ticker.bestBid === 'string') - t.truthy(typeof ticker.bestAsk === 'string') - t.truthy(typeof ticker.bestBidQnt === 'string') - t.truthy(typeof ticker.bestAskQnt === 'string') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.bookTicker('ETHBTC', ticker => { + checkFields(t, ticker, [ + 'updateId', + 'symbol', + 'bestBid', + 'bestBidQnt', + 'bestAsk', + 'bestAskQnt', + ]) + t.is(ticker.symbol, 'ETHBTC') + t.truthy(typeof ticker.bestBid === 'string') + t.truthy(typeof ticker.bestAsk === 'string') + t.truthy(typeof ticker.bestBidQnt === 'string') + t.truthy(typeof ticker.bestAskQnt === 'string') + clean() + resolve() + }) }) - }) }) test('[WS] bookTicker - multiple symbols', t => { - return new Promise(resolve => { - const symbols = ['ETHBTC', 'BTCUSDT', 'BNBBTC'] + return new Promise(resolve => { + const symbols = ['ETHBTC', 'BTCUSDT', 'BNBBTC'] - const clean = client.ws.bookTicker(symbols, ticker => { - checkFields(t, ticker, ['updateId', 'symbol', 'bestBid', 'bestBidQnt', 'bestAsk', 'bestAskQnt']) - t.truthy(symbols.includes(ticker.symbol)) - clean() - resolve() + const clean = client.ws.bookTicker(symbols, ticker => { + checkFields(t, ticker, [ + 'updateId', + 'symbol', + 'bestBid', + 'bestBidQnt', + 'bestAsk', + 'bestAskQnt', + ]) + t.truthy(symbols.includes(ticker.symbol)) + clean() + resolve() + }) }) - }) }) test('[WS] bookTicker - raw data without transform', t => { - return new Promise(resolve => { - const clean = client.ws.bookTicker('ETHBTC', ticker => { - // Raw data should have lowercase field names - t.truthy(ticker.u) - t.truthy(ticker.s) - t.truthy(ticker.b) - t.truthy(ticker.B) - t.truthy(ticker.a) - t.truthy(ticker.A) - clean() - resolve() - }, false) - }) + return new Promise(resolve => { + const clean = client.ws.bookTicker( + 'ETHBTC', + ticker => { + // Raw data should have lowercase field names + t.truthy(ticker.u) + t.truthy(ticker.s) + t.truthy(ticker.b) + t.truthy(ticker.B) + t.truthy(ticker.a) + t.truthy(ticker.A) + clean() + resolve() + }, + false, + ) + }) }) test('[WS] bookTicker - transformed data', t => { - return new Promise(resolve => { - const clean = client.ws.bookTicker('ETHBTC', ticker => { - // Transformed data should have camelCase field names - t.truthy(ticker.updateId) - t.truthy(ticker.symbol) - t.truthy(ticker.bestBid) - t.truthy(ticker.bestBidQnt) - t.truthy(ticker.bestAsk) - t.truthy(ticker.bestAskQnt) - // Should NOT have raw field names - t.falsy(ticker.u) - t.falsy(ticker.s) - clean() - resolve() - }, true) - }) + return new Promise(resolve => { + const clean = client.ws.bookTicker( + 'ETHBTC', + ticker => { + // Transformed data should have camelCase field names + t.truthy(ticker.updateId) + t.truthy(ticker.symbol) + t.truthy(ticker.bestBid) + t.truthy(ticker.bestBidQnt) + t.truthy(ticker.bestAsk) + t.truthy(ticker.bestAskQnt) + // Should NOT have raw field names + t.falsy(ticker.u) + t.falsy(ticker.s) + clean() + resolve() + }, + true, + ) + }) }) test('[WS] bookTicker - cleanup function', t => { - const clean = client.ws.bookTicker('ETHBTC', () => { - // Callback implementation - }) + const clean = client.ws.bookTicker('ETHBTC', () => { + // Callback implementation + }) - // Verify clean is a function - t.is(typeof clean, 'function') + // Verify clean is a function + t.is(typeof clean, 'function') - // Clean up immediately - clean() + // Clean up immediately + clean() - // Give it a moment and verify cleanup executed properly - return new Promise(resolve => { - setTimeout(() => { - t.pass('Cleanup function executed without errors') - resolve() - }, 100) - }) + // Give it a moment and verify cleanup executed properly + return new Promise(resolve => { + setTimeout(() => { + t.pass('Cleanup function executed without errors') + resolve() + }, 100) + }) }) diff --git a/test/websockets/candles.js b/test/websockets/candles.js index aa19d0b4..135c22d1 100644 --- a/test/websockets/candles.js +++ b/test/websockets/candles.js @@ -7,93 +7,135 @@ import { checkFields } from '../utils' const client = Binance() test('[WS] candles - missing parameters', t => { - try { - client.ws.candles('ETHBTC', d => d) - t.fail('Should have thrown an error') - } catch (e) { - t.is(e.message, 'Please pass a symbol, interval and callback.') - } + try { + client.ws.candles('ETHBTC', d => d) + t.fail('Should have thrown an error') + } catch (e) { + t.is(e.message, 'Please pass a symbol, interval and callback.') + } }) test('[WS] candles - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.candles('ETHBTC', '5m', candle => { - checkFields(t, candle, ['open', 'high', 'low', 'close', 'volume', 'trades', 'quoteVolume']) - t.is(candle.symbol, 'ETHBTC') - t.is(candle.interval, '5m') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.candles('ETHBTC', '5m', candle => { + checkFields(t, candle, [ + 'open', + 'high', + 'low', + 'close', + 'volume', + 'trades', + 'quoteVolume', + ]) + t.is(candle.symbol, 'ETHBTC') + t.is(candle.interval, '5m') + clean() + resolve() + }) }) - }) }) test('[WS] candles - multiple symbols', t => { - return new Promise(resolve => { - const symbols = ['ETHBTC', 'BNBBTC', 'BNTBTC'] + return new Promise(resolve => { + const symbols = ['ETHBTC', 'BNBBTC', 'BNTBTC'] - const clean = client.ws.candles(symbols, '5m', candle => { - checkFields(t, candle, ['open', 'high', 'low', 'close', 'volume', 'trades', 'quoteVolume']) - t.truthy(symbols.includes(candle.symbol)) - clean() - resolve() + const clean = client.ws.candles(symbols, '5m', candle => { + checkFields(t, candle, [ + 'open', + 'high', + 'low', + 'close', + 'volume', + 'trades', + 'quoteVolume', + ]) + t.truthy(symbols.includes(candle.symbol)) + clean() + resolve() + }) }) - }) }) test('[WS] candles - raw data without transform', t => { - return new Promise(resolve => { - const clean = client.ws.candles('ETHBTC', '1m', candle => { - // Raw data should have the structure with 'k' key - t.truthy(candle.e) - t.truthy(candle.E) - t.truthy(candle.s) - t.truthy(candle.k) - clean() - resolve() - }, false) - }) + return new Promise(resolve => { + const clean = client.ws.candles( + 'ETHBTC', + '1m', + candle => { + // Raw data should have the structure with 'k' key + t.truthy(candle.e) + t.truthy(candle.E) + t.truthy(candle.s) + t.truthy(candle.k) + clean() + resolve() + }, + false, + ) + }) }) test('[WS] candles - transformed data', t => { - return new Promise(resolve => { - const clean = client.ws.candles('ETHBTC', '1m', candle => { - // Transformed data should have camelCase field names - t.truthy(candle.eventType) - t.truthy(candle.eventTime) - t.truthy(candle.symbol) - t.truthy(candle.open) - t.truthy(candle.close) - // Should NOT have raw field names - t.falsy(candle.k) - clean() - resolve() - }, true) - }) + return new Promise(resolve => { + const clean = client.ws.candles( + 'ETHBTC', + '1m', + candle => { + // Transformed data should have camelCase field names + t.truthy(candle.eventType) + t.truthy(candle.eventTime) + t.truthy(candle.symbol) + t.truthy(candle.open) + t.truthy(candle.close) + // Should NOT have raw field names + t.falsy(candle.k) + clean() + resolve() + }, + true, + ) + }) }) test('[WS] futuresCandles - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.futuresCandles('BTCUSDT', '5m', candle => { - checkFields(t, candle, ['open', 'high', 'low', 'close', 'volume', 'trades', 'quoteVolume']) - t.is(candle.symbol, 'BTCUSDT') - t.is(candle.interval, '5m') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresCandles('BTCUSDT', '5m', candle => { + checkFields(t, candle, [ + 'open', + 'high', + 'low', + 'close', + 'volume', + 'trades', + 'quoteVolume', + ]) + t.is(candle.symbol, 'BTCUSDT') + t.is(candle.interval, '5m') + clean() + resolve() + }) }) - }) }) test.skip('[WS] deliveryCandles - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.deliveryCandles('TRXUSD_PERP', '5m', candle => { - checkFields(t, candle, ['open', 'high', 'low', 'close', 'volume', 'trades', 'baseVolume']) - t.is(candle.symbol, 'TRXUSD_PERP') - t.is(candle.interval, '5m') - // Delivery candles have baseVolume instead of quoteVolume - t.truthy(candle.baseVolume) - t.falsy(candle.quoteVolume) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.deliveryCandles('TRXUSD_PERP', '5m', candle => { + checkFields(t, candle, [ + 'open', + 'high', + 'low', + 'close', + 'volume', + 'trades', + 'baseVolume', + ]) + t.is(candle.symbol, 'TRXUSD_PERP') + t.is(candle.interval, '5m') + // Delivery candles have baseVolume instead of quoteVolume + t.truthy(candle.baseVolume) + t.falsy(candle.quoteVolume) + clean() + resolve() + }) }) - }) }) diff --git a/test/websockets/customSubStream.js b/test/websockets/customSubStream.js index 32748291..33203ca3 100644 --- a/test/websockets/customSubStream.js +++ b/test/websockets/customSubStream.js @@ -5,130 +5,130 @@ import Binance from 'index' const client = Binance() test('[WS] customSubStream - single stream', t => { - return new Promise(resolve => { - const clean = client.ws.customSubStream('ethbtc@ticker', data => { - t.truthy(data) - t.truthy(data.e || data.stream) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.customSubStream('ethbtc@ticker', data => { + t.truthy(data) + t.truthy(data.e || data.stream) + clean() + resolve() + }) }) - }) }) test('[WS] customSubStream - multiple streams', t => { - return new Promise(resolve => { - const streams = ['ethbtc@ticker', 'btcusdt@ticker'] - - const clean = client.ws.customSubStream(streams, data => { - t.truthy(data) - clean() - resolve() + return new Promise(resolve => { + const streams = ['ethbtc@ticker', 'btcusdt@ticker'] + + const clean = client.ws.customSubStream(streams, data => { + t.truthy(data) + clean() + resolve() + }) }) - }) }) test('[WS] customSubStream - depth stream', t => { - return new Promise(resolve => { - const clean = client.ws.customSubStream('ethbtc@depth', data => { - t.truthy(data) - t.truthy(data.e || data.lastUpdateId) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.customSubStream('ethbtc@depth', data => { + t.truthy(data) + t.truthy(data.e || data.lastUpdateId) + clean() + resolve() + }) }) - }) }) test('[WS] customSubStream - aggTrade stream', t => { - return new Promise(resolve => { - const clean = client.ws.customSubStream('ethbtc@aggTrade', data => { - t.truthy(data) - t.truthy(data.e || data.a) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.customSubStream('ethbtc@aggTrade', data => { + t.truthy(data) + t.truthy(data.e || data.a) + clean() + resolve() + }) }) - }) }) test('[WS] customSubStream - kline stream', t => { - return new Promise(resolve => { - const clean = client.ws.customSubStream('ethbtc@kline_1m', data => { - t.truthy(data) - t.truthy(data.e || data.k) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.customSubStream('ethbtc@kline_1m', data => { + t.truthy(data) + t.truthy(data.e || data.k) + clean() + resolve() + }) }) - }) }) test('[WS] customSubStream - cleanup function', t => { - const clean = client.ws.customSubStream('ethbtc@ticker', () => { - // Callback implementation - }) - - // Verify clean is a function - t.is(typeof clean, 'function') - - // Clean up immediately - clean() - - // Give it a moment and verify cleanup executed properly - return new Promise(resolve => { - setTimeout(() => { - t.pass('Cleanup function executed without errors') - resolve() - }, 100) - }) + const clean = client.ws.customSubStream('ethbtc@ticker', () => { + // Callback implementation + }) + + // Verify clean is a function + t.is(typeof clean, 'function') + + // Clean up immediately + clean() + + // Give it a moment and verify cleanup executed properly + return new Promise(resolve => { + setTimeout(() => { + t.pass('Cleanup function executed without errors') + resolve() + }, 100) + }) }) test('[WS] futuresCustomSubStream - single stream', t => { - return new Promise(resolve => { - const clean = client.ws.futuresCustomSubStream('btcusdt@ticker', data => { - t.truthy(data) - t.truthy(data.e || data.stream) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresCustomSubStream('btcusdt@ticker', data => { + t.truthy(data) + t.truthy(data.e || data.stream) + clean() + resolve() + }) }) - }) }) test('[WS] futuresCustomSubStream - aggTrade stream', t => { - return new Promise(resolve => { - const clean = client.ws.futuresCustomSubStream('btcusdt@aggTrade', data => { - t.truthy(data) - t.truthy(data.e || data.a) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresCustomSubStream('btcusdt@aggTrade', data => { + t.truthy(data) + t.truthy(data.e || data.a) + clean() + resolve() + }) }) - }) }) test.skip('[WS] deliveryCustomSubStream - single stream', t => { - return new Promise(resolve => { - const clean = client.ws.deliveryCustomSubStream('trxusd_perp@ticker', data => { - t.truthy(data) - t.truthy(data.e || data.stream) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.deliveryCustomSubStream('trxusd_perp@ticker', data => { + t.truthy(data) + t.truthy(data.e || data.stream) + clean() + resolve() + }) }) - }) }) test('[WS] futuresCustomSubStream - cleanup function', t => { - const clean = client.ws.futuresCustomSubStream('btcusdt@ticker', () => { - // Callback implementation - }) - - // Verify clean is a function - t.is(typeof clean, 'function') - - // Clean up immediately - clean() - - // Give it a moment and verify cleanup executed properly - return new Promise(resolve => { - setTimeout(() => { - t.pass('Cleanup function executed without errors') - resolve() - }, 100) - }) + const clean = client.ws.futuresCustomSubStream('btcusdt@ticker', () => { + // Callback implementation + }) + + // Verify clean is a function + t.is(typeof clean, 'function') + + // Clean up immediately + clean() + + // Give it a moment and verify cleanup executed properly + return new Promise(resolve => { + setTimeout(() => { + t.pass('Cleanup function executed without errors') + resolve() + }, 100) + }) }) diff --git a/test/websockets/depth.js b/test/websockets/depth.js index dabdc547..9ca234f3 100644 --- a/test/websockets/depth.js +++ b/test/websockets/depth.js @@ -7,161 +7,161 @@ import { checkFields } from '../utils' const client = Binance() test('[WS] depth - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.depth('ETHBTC', depth => { - checkFields(t, depth, [ - 'eventType', - 'eventTime', - 'firstUpdateId', - 'finalUpdateId', - 'symbol', - 'bidDepth', - 'askDepth', - ]) - t.is(depth.symbol, 'ETHBTC') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.depth('ETHBTC', depth => { + checkFields(t, depth, [ + 'eventType', + 'eventTime', + 'firstUpdateId', + 'finalUpdateId', + 'symbol', + 'bidDepth', + 'askDepth', + ]) + t.is(depth.symbol, 'ETHBTC') + clean() + resolve() + }) }) - }) }) test('[WS] depth - with update speed', t => { - return new Promise(resolve => { - const clean = client.ws.depth('ETHBTC@100ms', depth => { - checkFields(t, depth, [ - 'eventType', - 'eventTime', - 'firstUpdateId', - 'finalUpdateId', - 'symbol', - 'bidDepth', - 'askDepth', - ]) - t.is(depth.symbol, 'ETHBTC') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.depth('ETHBTC@100ms', depth => { + checkFields(t, depth, [ + 'eventType', + 'eventTime', + 'firstUpdateId', + 'finalUpdateId', + 'symbol', + 'bidDepth', + 'askDepth', + ]) + t.is(depth.symbol, 'ETHBTC') + clean() + resolve() + }) }) - }) }) test('[WS] partialDepth - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.partialDepth({ symbol: 'ETHBTC', level: 10 }, depth => { - checkFields(t, depth, ['lastUpdateId', 'bids', 'asks', 'symbol', 'level']) - t.is(depth.symbol, 'ETHBTC') - t.is(depth.level, 10) - t.truthy(Array.isArray(depth.bids)) - t.truthy(Array.isArray(depth.asks)) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.partialDepth({ symbol: 'ETHBTC', level: 10 }, depth => { + checkFields(t, depth, ['lastUpdateId', 'bids', 'asks', 'symbol', 'level']) + t.is(depth.symbol, 'ETHBTC') + t.is(depth.level, 10) + t.truthy(Array.isArray(depth.bids)) + t.truthy(Array.isArray(depth.asks)) + clean() + resolve() + }) }) - }) }) test('[WS] partialDepth - with update speed', t => { - return new Promise(resolve => { - const clean = client.ws.partialDepth({ symbol: 'ETHBTC@100ms', level: 10 }, depth => { - checkFields(t, depth, ['lastUpdateId', 'bids', 'asks']) - t.truthy(Array.isArray(depth.bids)) - t.truthy(Array.isArray(depth.asks)) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.partialDepth({ symbol: 'ETHBTC@100ms', level: 10 }, depth => { + checkFields(t, depth, ['lastUpdateId', 'bids', 'asks']) + t.truthy(Array.isArray(depth.bids)) + t.truthy(Array.isArray(depth.asks)) + clean() + resolve() + }) }) - }) }) test('[WS] futuresDepth - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.futuresDepth('BTCUSDT', depth => { - checkFields(t, depth, [ - 'eventType', - 'eventTime', - 'transactionTime', - 'symbol', - 'firstUpdateId', - 'finalUpdateId', - 'prevFinalUpdateId', - 'bidDepth', - 'askDepth', - ]) - t.is(depth.symbol, 'BTCUSDT') - t.truthy(depth.prevFinalUpdateId !== undefined) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresDepth('BTCUSDT', depth => { + checkFields(t, depth, [ + 'eventType', + 'eventTime', + 'transactionTime', + 'symbol', + 'firstUpdateId', + 'finalUpdateId', + 'prevFinalUpdateId', + 'bidDepth', + 'askDepth', + ]) + t.is(depth.symbol, 'BTCUSDT') + t.truthy(depth.prevFinalUpdateId !== undefined) + clean() + resolve() + }) }) - }) }) test('[WS] futuresPartialDepth - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.futuresPartialDepth({ symbol: 'BTCUSDT', level: 10 }, depth => { - checkFields(t, depth, [ - 'level', - 'eventType', - 'eventTime', - 'transactionTime', - 'symbol', - 'firstUpdateId', - 'finalUpdateId', - 'prevFinalUpdateId', - 'bidDepth', - 'askDepth', - ]) - t.is(depth.symbol, 'BTCUSDT') - t.is(depth.level, 10) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresPartialDepth({ symbol: 'BTCUSDT', level: 10 }, depth => { + checkFields(t, depth, [ + 'level', + 'eventType', + 'eventTime', + 'transactionTime', + 'symbol', + 'firstUpdateId', + 'finalUpdateId', + 'prevFinalUpdateId', + 'bidDepth', + 'askDepth', + ]) + t.is(depth.symbol, 'BTCUSDT') + t.is(depth.level, 10) + clean() + resolve() + }) }) - }) }) test.skip('[WS] deliveryDepth - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.deliveryDepth('TRXUSD_PERP', depth => { - checkFields(t, depth, [ - 'eventType', - 'eventTime', - 'transactionTime', - 'symbol', - 'pair', - 'firstUpdateId', - 'finalUpdateId', - 'prevFinalUpdateId', - 'bidDepth', - 'askDepth', - ]) - t.is(depth.symbol, 'TRXUSD_PERP') - t.truthy(depth.pair) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.deliveryDepth('TRXUSD_PERP', depth => { + checkFields(t, depth, [ + 'eventType', + 'eventTime', + 'transactionTime', + 'symbol', + 'pair', + 'firstUpdateId', + 'finalUpdateId', + 'prevFinalUpdateId', + 'bidDepth', + 'askDepth', + ]) + t.is(depth.symbol, 'TRXUSD_PERP') + t.truthy(depth.pair) + clean() + resolve() + }) }) - }) }) test.skip('[WS] deliveryPartialDepth - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.deliveryPartialDepth( - { symbol: 'TRXUSD_PERP', level: 10 }, - depth => { - checkFields(t, depth, [ - 'level', - 'eventType', - 'eventTime', - 'transactionTime', - 'symbol', - 'pair', - 'firstUpdateId', - 'finalUpdateId', - 'prevFinalUpdateId', - 'bidDepth', - 'askDepth', - ]) - t.is(depth.symbol, 'TRXUSD_PERP') - t.is(depth.level, 10) - t.truthy(depth.pair) - clean() - resolve() - }, - ) - }) + return new Promise(resolve => { + const clean = client.ws.deliveryPartialDepth( + { symbol: 'TRXUSD_PERP', level: 10 }, + depth => { + checkFields(t, depth, [ + 'level', + 'eventType', + 'eventTime', + 'transactionTime', + 'symbol', + 'pair', + 'firstUpdateId', + 'finalUpdateId', + 'prevFinalUpdateId', + 'bidDepth', + 'askDepth', + ]) + t.is(depth.symbol, 'TRXUSD_PERP') + t.is(depth.level, 10) + t.truthy(depth.pair) + clean() + resolve() + }, + ) + }) }) diff --git a/test/websockets/liquidations.js b/test/websockets/liquidations.js index 1815299d..82657aac 100644 --- a/test/websockets/liquidations.js +++ b/test/websockets/liquidations.js @@ -9,121 +9,125 @@ const client = Binance() // Note: Liquidation streams are skipped as they may not always have active liquidations to test test.skip('[WS] futuresLiquidations - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.futuresLiquidations('BTCUSDT', liquidation => { - checkFields(t, liquidation, [ - 'symbol', - 'price', - 'origQty', - 'lastFilledQty', - 'accumulatedQty', - 'averagePrice', - 'status', - 'timeInForce', - 'type', - 'side', - 'time', - ]) - t.is(liquidation.symbol, 'BTCUSDT') - t.truthy(typeof liquidation.price === 'string') - t.truthy(typeof liquidation.origQty === 'string') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresLiquidations('BTCUSDT', liquidation => { + checkFields(t, liquidation, [ + 'symbol', + 'price', + 'origQty', + 'lastFilledQty', + 'accumulatedQty', + 'averagePrice', + 'status', + 'timeInForce', + 'type', + 'side', + 'time', + ]) + t.is(liquidation.symbol, 'BTCUSDT') + t.truthy(typeof liquidation.price === 'string') + t.truthy(typeof liquidation.origQty === 'string') + clean() + resolve() + }) }) - }) }) test.skip('[WS] futuresLiquidations - multiple symbols', t => { - return new Promise(resolve => { - const symbols = ['BTCUSDT', 'ETHUSDT'] + return new Promise(resolve => { + const symbols = ['BTCUSDT', 'ETHUSDT'] - const clean = client.ws.futuresLiquidations(symbols, liquidation => { - checkFields(t, liquidation, [ - 'symbol', - 'price', - 'origQty', - 'lastFilledQty', - 'accumulatedQty', - 'averagePrice', - 'status', - 'timeInForce', - 'type', - 'side', - 'time', - ]) - t.truthy(symbols.includes(liquidation.symbol)) - clean() - resolve() + const clean = client.ws.futuresLiquidations(symbols, liquidation => { + checkFields(t, liquidation, [ + 'symbol', + 'price', + 'origQty', + 'lastFilledQty', + 'accumulatedQty', + 'averagePrice', + 'status', + 'timeInForce', + 'type', + 'side', + 'time', + ]) + t.truthy(symbols.includes(liquidation.symbol)) + clean() + resolve() + }) }) - }) }) test.skip('[WS] futuresLiquidations - raw data', t => { - return new Promise(resolve => { - const clean = client.ws.futuresLiquidations('BTCUSDT', liquidation => { - // Raw data should contain the 'o' object wrapper - t.truthy(liquidation.o) - t.truthy(liquidation.o.s) - t.truthy(liquidation.o.p) - t.truthy(liquidation.o.q) - clean() - resolve() - }, false) - }) + return new Promise(resolve => { + const clean = client.ws.futuresLiquidations( + 'BTCUSDT', + liquidation => { + // Raw data should contain the 'o' object wrapper + t.truthy(liquidation.o) + t.truthy(liquidation.o.s) + t.truthy(liquidation.o.p) + t.truthy(liquidation.o.q) + clean() + resolve() + }, + false, + ) + }) }) test.skip('[WS] futuresAllLiquidations', t => { - return new Promise(resolve => { - const clean = client.ws.futuresAllLiquidations(liquidation => { - checkFields(t, liquidation, [ - 'symbol', - 'price', - 'origQty', - 'lastFilledQty', - 'accumulatedQty', - 'averagePrice', - 'status', - 'timeInForce', - 'type', - 'side', - 'time', - ]) - t.truthy(liquidation.symbol) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresAllLiquidations(liquidation => { + checkFields(t, liquidation, [ + 'symbol', + 'price', + 'origQty', + 'lastFilledQty', + 'accumulatedQty', + 'averagePrice', + 'status', + 'timeInForce', + 'type', + 'side', + 'time', + ]) + t.truthy(liquidation.symbol) + clean() + resolve() + }) }) - }) }) test.skip('[WS] futuresAllLiquidations - raw data', t => { - return new Promise(resolve => { - const clean = client.ws.futuresAllLiquidations(liquidation => { - // Raw data should contain the 'o' object wrapper - t.truthy(liquidation.o) - t.truthy(liquidation.o.s) - t.truthy(liquidation.o.p) - clean() - resolve() - }, false) - }) + return new Promise(resolve => { + const clean = client.ws.futuresAllLiquidations(liquidation => { + // Raw data should contain the 'o' object wrapper + t.truthy(liquidation.o) + t.truthy(liquidation.o.s) + t.truthy(liquidation.o.p) + clean() + resolve() + }, false) + }) }) test.skip('[WS] futuresAllLiquidations - cleanup function', t => { - const clean = client.ws.futuresAllLiquidations(() => { - // Callback implementation - }) + const clean = client.ws.futuresAllLiquidations(() => { + // Callback implementation + }) - // Verify clean is a function - t.is(typeof clean, 'function') + // Verify clean is a function + t.is(typeof clean, 'function') - // Clean up immediately - clean() + // Clean up immediately + clean() - // Give it a moment and verify cleanup executed properly - return new Promise(resolve => { - setTimeout(() => { - t.pass('Cleanup function executed without errors') - resolve() - }, 100) - }) + // Give it a moment and verify cleanup executed properly + return new Promise(resolve => { + setTimeout(() => { + t.pass('Cleanup function executed without errors') + resolve() + }, 100) + }) }) diff --git a/test/websockets/markPrices.js b/test/websockets/markPrices.js index 68487732..36fbd6bb 100644 --- a/test/websockets/markPrices.js +++ b/test/websockets/markPrices.js @@ -7,130 +7,138 @@ import { checkFields } from '../utils' const client = Binance() test('[WS] futuresAllMarkPrices - default speed', t => { - return new Promise(resolve => { - const clean = client.ws.futuresAllMarkPrices({}, markPrices => { - t.truthy(Array.isArray(markPrices)) - t.truthy(markPrices.length > 0) - - const [firstPrice] = markPrices - checkFields(t, firstPrice, [ - 'eventType', - 'eventTime', - 'symbol', - 'markPrice', - 'indexPrice', - 'settlePrice', - 'fundingRate', - 'nextFundingRate', - ]) - - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresAllMarkPrices({}, markPrices => { + t.truthy(Array.isArray(markPrices)) + t.truthy(markPrices.length > 0) + + const [firstPrice] = markPrices + checkFields(t, firstPrice, [ + 'eventType', + 'eventTime', + 'symbol', + 'markPrice', + 'indexPrice', + 'settlePrice', + 'fundingRate', + 'nextFundingRate', + ]) + + clean() + resolve() + }) }) - }) }) test('[WS] futuresAllMarkPrices - 1s update speed', t => { - return new Promise(resolve => { - const clean = client.ws.futuresAllMarkPrices({ updateSpeed: '1s' }, markPrices => { - t.truthy(Array.isArray(markPrices)) - t.truthy(markPrices.length > 0) - - const [firstPrice] = markPrices - checkFields(t, firstPrice, [ - 'eventType', - 'eventTime', - 'symbol', - 'markPrice', - 'indexPrice', - 'settlePrice', - 'fundingRate', - 'nextFundingRate', - ]) - - t.is(firstPrice.eventType, 'markPriceUpdate') - - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresAllMarkPrices({ updateSpeed: '1s' }, markPrices => { + t.truthy(Array.isArray(markPrices)) + t.truthy(markPrices.length > 0) + + const [firstPrice] = markPrices + checkFields(t, firstPrice, [ + 'eventType', + 'eventTime', + 'symbol', + 'markPrice', + 'indexPrice', + 'settlePrice', + 'fundingRate', + 'nextFundingRate', + ]) + + t.is(firstPrice.eventType, 'markPriceUpdate') + + clean() + resolve() + }) }) - }) }) test.skip('[WS] futuresAllMarkPrices - raw data without transform', t => { - return new Promise(resolve => { - const clean = client.ws.futuresAllMarkPrices({}, markPrices => { - t.truthy(Array.isArray(markPrices)) - t.truthy(markPrices.length > 0) - - // Note: Raw data test - implementation needs verification - clean() - resolve() - }, false) - }) + return new Promise(resolve => { + const clean = client.ws.futuresAllMarkPrices( + {}, + markPrices => { + t.truthy(Array.isArray(markPrices)) + t.truthy(markPrices.length > 0) + + // Note: Raw data test - implementation needs verification + clean() + resolve() + }, + false, + ) + }) }) test('[WS] futuresAllMarkPrices - transformed data', t => { - return new Promise(resolve => { - const clean = client.ws.futuresAllMarkPrices({}, markPrices => { - t.truthy(Array.isArray(markPrices)) - t.truthy(markPrices.length > 0) - - const [firstPrice] = markPrices - // Transformed data should have camelCase field names - t.truthy(firstPrice.eventType) - t.truthy(firstPrice.eventTime) - t.truthy(firstPrice.symbol) - t.truthy(firstPrice.markPrice) - t.truthy(firstPrice.indexPrice) - t.truthy(firstPrice.settlePrice) - t.truthy(firstPrice.fundingRate) - t.truthy(firstPrice.nextFundingRate) - - // Should NOT have raw field names - t.falsy(firstPrice.e) - t.falsy(firstPrice.E) - - clean() - resolve() - }, true) - }) + return new Promise(resolve => { + const clean = client.ws.futuresAllMarkPrices( + {}, + markPrices => { + t.truthy(Array.isArray(markPrices)) + t.truthy(markPrices.length > 0) + + const [firstPrice] = markPrices + // Transformed data should have camelCase field names + t.truthy(firstPrice.eventType) + t.truthy(firstPrice.eventTime) + t.truthy(firstPrice.symbol) + t.truthy(firstPrice.markPrice) + t.truthy(firstPrice.indexPrice) + t.truthy(firstPrice.settlePrice) + t.truthy(firstPrice.fundingRate) + t.truthy(firstPrice.nextFundingRate) + + // Should NOT have raw field names + t.falsy(firstPrice.e) + t.falsy(firstPrice.E) + + clean() + resolve() + }, + true, + ) + }) }) test('[WS] futuresAllMarkPrices - verify data types', t => { - return new Promise(resolve => { - const clean = client.ws.futuresAllMarkPrices({}, markPrices => { - t.truthy(Array.isArray(markPrices)) - t.truthy(markPrices.length > 0) - - const [firstPrice] = markPrices - t.truthy(typeof firstPrice.eventTime === 'number') - t.truthy(typeof firstPrice.symbol === 'string') - t.truthy(typeof firstPrice.markPrice === 'string') - t.truthy(typeof firstPrice.indexPrice === 'string') - t.truthy(typeof firstPrice.fundingRate === 'string') - - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresAllMarkPrices({}, markPrices => { + t.truthy(Array.isArray(markPrices)) + t.truthy(markPrices.length > 0) + + const [firstPrice] = markPrices + t.truthy(typeof firstPrice.eventTime === 'number') + t.truthy(typeof firstPrice.symbol === 'string') + t.truthy(typeof firstPrice.markPrice === 'string') + t.truthy(typeof firstPrice.indexPrice === 'string') + t.truthy(typeof firstPrice.fundingRate === 'string') + + clean() + resolve() + }) }) - }) }) test('[WS] futuresAllMarkPrices - cleanup function', t => { - const clean = client.ws.futuresAllMarkPrices({}, () => { - // Callback implementation - }) - - // Verify clean is a function - t.is(typeof clean, 'function') - - // Clean up immediately - clean() - - // Give it a moment and verify cleanup executed properly - return new Promise(resolve => { - setTimeout(() => { - t.pass('Cleanup function executed without errors') - resolve() - }, 100) - }) + const clean = client.ws.futuresAllMarkPrices({}, () => { + // Callback implementation + }) + + // Verify clean is a function + t.is(typeof clean, 'function') + + // Clean up immediately + clean() + + // Give it a moment and verify cleanup executed properly + return new Promise(resolve => { + setTimeout(() => { + t.pass('Cleanup function executed without errors') + resolve() + }, 100) + }) }) diff --git a/test/websockets/ticker.js b/test/websockets/ticker.js index 631c3854..a4bef32c 100644 --- a/test/websockets/ticker.js +++ b/test/websockets/ticker.js @@ -7,231 +7,246 @@ import { checkFields } from '../utils' const client = Binance() test('[WS] ticker - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.ticker('ETHBTC', ticker => { - checkFields(t, ticker, [ - 'eventType', - 'eventTime', - 'symbol', - 'priceChange', - 'priceChangePercent', - 'weightedAvg', - 'prevDayClose', - 'curDayClose', - 'closeTradeQuantity', - 'bestBid', - 'bestBidQnt', - 'bestAsk', - 'bestAskQnt', - 'open', - 'high', - 'low', - 'volume', - 'volumeQuote', - 'openTime', - 'closeTime', - 'firstTradeId', - 'lastTradeId', - 'totalTrades', - ]) - t.is(ticker.symbol, 'ETHBTC') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.ticker('ETHBTC', ticker => { + checkFields(t, ticker, [ + 'eventType', + 'eventTime', + 'symbol', + 'priceChange', + 'priceChangePercent', + 'weightedAvg', + 'prevDayClose', + 'curDayClose', + 'closeTradeQuantity', + 'bestBid', + 'bestBidQnt', + 'bestAsk', + 'bestAskQnt', + 'open', + 'high', + 'low', + 'volume', + 'volumeQuote', + 'openTime', + 'closeTime', + 'firstTradeId', + 'lastTradeId', + 'totalTrades', + ]) + t.is(ticker.symbol, 'ETHBTC') + clean() + resolve() + }) }) - }) }) test('[WS] ticker - multiple symbols', t => { - return new Promise(resolve => { - let count = 0 - const symbols = ['ETHBTC', 'BTCUSDT', 'BNBBTC'] - const receivedSymbols = {} - - const clean = client.ws.ticker(symbols, ticker => { - checkFields(t, ticker, ['symbol', 'eventType', 'eventTime', 'priceChange', 'open', 'high']) - t.truthy(symbols.includes(ticker.symbol)) - receivedSymbols[ticker.symbol] = true - count++ - - // Once we've received at least one message for each symbol, we're done - if (Object.keys(receivedSymbols).length === symbols.length || count >= 10) { - clean() - resolve() - } + return new Promise(resolve => { + let count = 0 + const symbols = ['ETHBTC', 'BTCUSDT', 'BNBBTC'] + const receivedSymbols = {} + + const clean = client.ws.ticker(symbols, ticker => { + checkFields(t, ticker, [ + 'symbol', + 'eventType', + 'eventTime', + 'priceChange', + 'open', + 'high', + ]) + t.truthy(symbols.includes(ticker.symbol)) + receivedSymbols[ticker.symbol] = true + count++ + + // Once we've received at least one message for each symbol, we're done + if (Object.keys(receivedSymbols).length === symbols.length || count >= 10) { + clean() + resolve() + } + }) }) - }) }) test('[WS] ticker - raw data without transform', t => { - return new Promise(resolve => { - const clean = client.ws.ticker('ETHBTC', ticker => { - // Raw data should have lowercase field names (e, E, s, p, etc.) - t.truthy(ticker.e) - t.truthy(ticker.E) - t.truthy(ticker.s) - t.is(ticker.s, 'ETHBTC') - clean() - resolve() - }, false) - }) + return new Promise(resolve => { + const clean = client.ws.ticker( + 'ETHBTC', + ticker => { + // Raw data should have lowercase field names (e, E, s, p, etc.) + t.truthy(ticker.e) + t.truthy(ticker.E) + t.truthy(ticker.s) + t.is(ticker.s, 'ETHBTC') + clean() + resolve() + }, + false, + ) + }) }) test('[WS] ticker - transformed data', t => { - return new Promise(resolve => { - const clean = client.ws.ticker('ETHBTC', ticker => { - // Transformed data should have camelCase field names - t.truthy(ticker.eventType) - t.truthy(ticker.eventTime) - t.truthy(ticker.symbol) - t.is(ticker.eventType, '24hrTicker') - t.is(ticker.symbol, 'ETHBTC') - // Should NOT have raw field names - t.falsy(ticker.e) - t.falsy(ticker.E) - t.falsy(ticker.s) - clean() - resolve() - }, true) - }) + return new Promise(resolve => { + const clean = client.ws.ticker( + 'ETHBTC', + ticker => { + // Transformed data should have camelCase field names + t.truthy(ticker.eventType) + t.truthy(ticker.eventTime) + t.truthy(ticker.symbol) + t.is(ticker.eventType, '24hrTicker') + t.is(ticker.symbol, 'ETHBTC') + // Should NOT have raw field names + t.falsy(ticker.e) + t.falsy(ticker.E) + t.falsy(ticker.s) + clean() + resolve() + }, + true, + ) + }) }) test('[WS] ticker - cleanup function', t => { - const clean = client.ws.ticker('ETHBTC', () => { - // Callback implementation - }) - - // Verify clean is a function - t.is(typeof clean, 'function') - - // Clean up immediately - clean() - - // Give it a moment and verify cleanup executed properly - return new Promise(resolve => { - setTimeout(() => { - t.pass('Cleanup function executed without errors') - resolve() - }, 100) - }) + const clean = client.ws.ticker('ETHBTC', () => { + // Callback implementation + }) + + // Verify clean is a function + t.is(typeof clean, 'function') + + // Clean up immediately + clean() + + // Give it a moment and verify cleanup executed properly + return new Promise(resolve => { + setTimeout(() => { + t.pass('Cleanup function executed without errors') + resolve() + }, 100) + }) }) test('[WS] allTickers', t => { - return new Promise(resolve => { - const clean = client.ws.allTickers(tickers => { - t.truthy(Array.isArray(tickers)) - t.is(tickers[0].eventType, '24hrTicker') - checkFields(t, tickers[0], ['symbol', 'priceChange', 'priceChangePercent']) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.allTickers(tickers => { + t.truthy(Array.isArray(tickers)) + t.is(tickers[0].eventType, '24hrTicker') + checkFields(t, tickers[0], ['symbol', 'priceChange', 'priceChangePercent']) + clean() + resolve() + }) }) - }) }) test('[WS] miniTicker - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.miniTicker('ETHBTC', ticker => { - checkFields(t, ticker, [ - 'open', - 'high', - 'low', - 'curDayClose', - 'eventTime', - 'symbol', - 'volume', - 'volumeQuote', - ]) - t.is(ticker.symbol, 'ETHBTC') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.miniTicker('ETHBTC', ticker => { + checkFields(t, ticker, [ + 'open', + 'high', + 'low', + 'curDayClose', + 'eventTime', + 'symbol', + 'volume', + 'volumeQuote', + ]) + t.is(ticker.symbol, 'ETHBTC') + clean() + resolve() + }) }) - }) }) test('[WS] allMiniTickers', t => { - return new Promise(resolve => { - const clean = client.ws.allMiniTickers(tickers => { - t.truthy(Array.isArray(tickers)) - t.is(tickers[0].eventType, '24hrMiniTicker') - checkFields(t, tickers[0], [ - 'open', - 'high', - 'low', - 'curDayClose', - 'eventTime', - 'symbol', - 'volume', - 'volumeQuote', - ]) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.allMiniTickers(tickers => { + t.truthy(Array.isArray(tickers)) + t.is(tickers[0].eventType, '24hrMiniTicker') + checkFields(t, tickers[0], [ + 'open', + 'high', + 'low', + 'curDayClose', + 'eventTime', + 'symbol', + 'volume', + 'volumeQuote', + ]) + clean() + resolve() + }) }) - }) }) test('[WS] futuresTicker - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.futuresTicker('BTCUSDT', ticker => { - checkFields(t, ticker, [ - 'eventType', - 'eventTime', - 'symbol', - 'priceChange', - 'priceChangePercent', - 'weightedAvg', - 'curDayClose', - 'closeTradeQuantity', - 'open', - 'high', - 'low', - 'volume', - 'volumeQuote', - 'openTime', - 'closeTime', - 'firstTradeId', - 'lastTradeId', - 'totalTrades', - ]) - t.is(ticker.symbol, 'BTCUSDT') - // Futures ticker should NOT have prevDayClose, bestBid, bestBidQnt, bestAsk, bestAskQnt - t.falsy(ticker.prevDayClose) - t.falsy(ticker.bestBid) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresTicker('BTCUSDT', ticker => { + checkFields(t, ticker, [ + 'eventType', + 'eventTime', + 'symbol', + 'priceChange', + 'priceChangePercent', + 'weightedAvg', + 'curDayClose', + 'closeTradeQuantity', + 'open', + 'high', + 'low', + 'volume', + 'volumeQuote', + 'openTime', + 'closeTime', + 'firstTradeId', + 'lastTradeId', + 'totalTrades', + ]) + t.is(ticker.symbol, 'BTCUSDT') + // Futures ticker should NOT have prevDayClose, bestBid, bestBidQnt, bestAsk, bestAskQnt + t.falsy(ticker.prevDayClose) + t.falsy(ticker.bestBid) + clean() + resolve() + }) }) - }) }) test.skip('[WS] deliveryTicker - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.deliveryTicker('TRXUSD_PERP', ticker => { - checkFields(t, ticker, [ - 'eventType', - 'eventTime', - 'symbol', - 'pair', - 'priceChange', - 'priceChangePercent', - 'weightedAvg', - 'curDayClose', - 'closeTradeQuantity', - 'open', - 'high', - 'low', - 'volume', - 'volumeBase', - 'openTime', - 'closeTime', - 'firstTradeId', - 'lastTradeId', - 'totalTrades', - ]) - t.is(ticker.symbol, 'TRXUSD_PERP') - t.truthy(ticker.pair) - // Delivery ticker has volumeBase instead of volumeQuote - t.truthy(ticker.volumeBase) - t.falsy(ticker.volumeQuote) - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.deliveryTicker('TRXUSD_PERP', ticker => { + checkFields(t, ticker, [ + 'eventType', + 'eventTime', + 'symbol', + 'pair', + 'priceChange', + 'priceChangePercent', + 'weightedAvg', + 'curDayClose', + 'closeTradeQuantity', + 'open', + 'high', + 'low', + 'volume', + 'volumeBase', + 'openTime', + 'closeTime', + 'firstTradeId', + 'lastTradeId', + 'totalTrades', + ]) + t.is(ticker.symbol, 'TRXUSD_PERP') + t.truthy(ticker.pair) + // Delivery ticker has volumeBase instead of volumeQuote + t.truthy(ticker.volumeBase) + t.falsy(ticker.volumeQuote) + clean() + resolve() + }) }) - }) }) diff --git a/test/websockets/trades.js b/test/websockets/trades.js index acf1e6f5..42b5e1a8 100644 --- a/test/websockets/trades.js +++ b/test/websockets/trades.js @@ -7,156 +7,164 @@ import { checkFields } from '../utils' const client = Binance() test('[WS] trades - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.trades('ETHBTC', trade => { - checkFields(t, trade, [ - 'eventType', - 'tradeId', - 'tradeTime', - 'quantity', - 'price', - 'symbol', - ]) - t.is(trade.symbol, 'ETHBTC') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.trades('ETHBTC', trade => { + checkFields(t, trade, [ + 'eventType', + 'tradeId', + 'tradeTime', + 'quantity', + 'price', + 'symbol', + ]) + t.is(trade.symbol, 'ETHBTC') + clean() + resolve() + }) }) - }) }) test('[WS] trades - multiple symbols', t => { - return new Promise(resolve => { - const symbols = ['BNBBTC', 'ETHBTC', 'BNTBTC'] + return new Promise(resolve => { + const symbols = ['BNBBTC', 'ETHBTC', 'BNTBTC'] - const clean = client.ws.trades(symbols, trade => { - checkFields(t, trade, [ - 'eventType', - 'tradeId', - 'tradeTime', - 'quantity', - 'price', - 'symbol', - ]) - t.truthy(symbols.includes(trade.symbol)) - clean() - resolve() + const clean = client.ws.trades(symbols, trade => { + checkFields(t, trade, [ + 'eventType', + 'tradeId', + 'tradeTime', + 'quantity', + 'price', + 'symbol', + ]) + t.truthy(symbols.includes(trade.symbol)) + clean() + resolve() + }) }) - }) }) test('[WS] trades - raw data without transform', t => { - return new Promise(resolve => { - const clean = client.ws.trades('ETHBTC', trade => { - // Raw data should have lowercase field names - t.truthy(trade.e) - t.truthy(trade.E) - t.truthy(trade.s) - t.truthy(trade.t) - t.truthy(trade.p) - t.truthy(trade.q) - clean() - resolve() - }, false) - }) + return new Promise(resolve => { + const clean = client.ws.trades( + 'ETHBTC', + trade => { + // Raw data should have lowercase field names + t.truthy(trade.e) + t.truthy(trade.E) + t.truthy(trade.s) + t.truthy(trade.t) + t.truthy(trade.p) + t.truthy(trade.q) + clean() + resolve() + }, + false, + ) + }) }) test('[WS] aggTrades - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.aggTrades(['ETHBTC'], trade => { - checkFields(t, trade, [ - 'eventType', - 'aggId', - 'timestamp', - 'quantity', - 'price', - 'symbol', - 'firstId', - 'lastId', - ]) - t.is(trade.symbol, 'ETHBTC') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.aggTrades(['ETHBTC'], trade => { + checkFields(t, trade, [ + 'eventType', + 'aggId', + 'timestamp', + 'quantity', + 'price', + 'symbol', + 'firstId', + 'lastId', + ]) + t.is(trade.symbol, 'ETHBTC') + clean() + resolve() + }) }) - }) }) test('[WS] aggTrades - multiple symbols', t => { - return new Promise(resolve => { - const symbols = ['BNBBTC', 'ETHBTC', 'BNTBTC'] + return new Promise(resolve => { + const symbols = ['BNBBTC', 'ETHBTC', 'BNTBTC'] - const clean = client.ws.aggTrades(symbols, trade => { - checkFields(t, trade, [ - 'eventType', - 'aggId', - 'timestamp', - 'quantity', - 'price', - 'symbol', - 'firstId', - 'lastId', - ]) - t.truthy(symbols.includes(trade.symbol)) - clean() - resolve() + const clean = client.ws.aggTrades(symbols, trade => { + checkFields(t, trade, [ + 'eventType', + 'aggId', + 'timestamp', + 'quantity', + 'price', + 'symbol', + 'firstId', + 'lastId', + ]) + t.truthy(symbols.includes(trade.symbol)) + clean() + resolve() + }) }) - }) }) test('[WS] aggTrades - raw data without transform', t => { - return new Promise(resolve => { - const clean = client.ws.aggTrades('ETHBTC', trade => { - // Raw data should have lowercase field names - t.truthy(trade.e) - t.truthy(trade.E) - t.truthy(trade.s) - t.truthy(trade.a) - t.truthy(trade.p) - t.truthy(trade.q) - clean() - resolve() - }, false) - }) + return new Promise(resolve => { + const clean = client.ws.aggTrades( + 'ETHBTC', + trade => { + // Raw data should have lowercase field names + t.truthy(trade.e) + t.truthy(trade.E) + t.truthy(trade.s) + t.truthy(trade.a) + t.truthy(trade.p) + t.truthy(trade.q) + clean() + resolve() + }, + false, + ) + }) }) test.skip('[WS] futuresAggTrades - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.futuresAggTrades(['BTCUSDT'], trade => { - checkFields(t, trade, [ - 'eventType', - 'symbol', - 'aggId', - 'price', - 'quantity', - 'firstId', - 'lastId', - 'timestamp', - 'isBuyerMaker', - ]) - t.is(trade.symbol, 'BTCUSDT') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.futuresAggTrades(['BTCUSDT'], trade => { + checkFields(t, trade, [ + 'eventType', + 'symbol', + 'aggId', + 'price', + 'quantity', + 'firstId', + 'lastId', + 'timestamp', + 'isBuyerMaker', + ]) + t.is(trade.symbol, 'BTCUSDT') + clean() + resolve() + }) }) - }) }) test.skip('[WS] deliveryAggTrades - single symbol', t => { - return new Promise(resolve => { - const clean = client.ws.deliveryAggTrades('TRXUSD_PERP', trade => { - checkFields(t, trade, [ - 'eventType', - 'eventTime', - 'symbol', - 'aggId', - 'price', - 'quantity', - 'firstId', - 'lastId', - 'timestamp', - 'isBuyerMaker', - ]) - t.is(trade.symbol, 'TRXUSD_PERP') - clean() - resolve() + return new Promise(resolve => { + const clean = client.ws.deliveryAggTrades('TRXUSD_PERP', trade => { + checkFields(t, trade, [ + 'eventType', + 'eventTime', + 'symbol', + 'aggId', + 'price', + 'quantity', + 'firstId', + 'lastId', + 'timestamp', + 'isBuyerMaker', + ]) + t.is(trade.symbol, 'TRXUSD_PERP') + clean() + resolve() + }) }) - }) }) diff --git a/test/websockets/user.js b/test/websockets/user.js index 2c823406..41235491 100644 --- a/test/websockets/user.js +++ b/test/websockets/user.js @@ -7,325 +7,325 @@ import { userEventHandler } from 'websocket' // These tests use userEventHandler to test event transformations without needing live connections test('[WS] userEvents - outboundAccountInfo', t => { - const accountPayload = { - e: 'outboundAccountInfo', - E: 1499405658849, - m: 0, - t: 0, - b: 0, - s: 0, - T: true, - W: true, - D: true, - u: 1499405658849, - B: [ - { - a: 'LTC', - f: '17366.18538083', - l: '0.00000000', - }, - { - a: 'BTC', - f: '10537.85314051', - l: '2.19464093', - }, - { - a: 'ETH', - f: '17902.35190619', - l: '0.00000000', - }, - { - a: 'BNC', - f: '1114503.29769312', - l: '0.00000000', - }, - { - a: 'NEO', - f: '0.00000000', - l: '0.00000000', - }, - ], - } + const accountPayload = { + e: 'outboundAccountInfo', + E: 1499405658849, + m: 0, + t: 0, + b: 0, + s: 0, + T: true, + W: true, + D: true, + u: 1499405658849, + B: [ + { + a: 'LTC', + f: '17366.18538083', + l: '0.00000000', + }, + { + a: 'BTC', + f: '10537.85314051', + l: '2.19464093', + }, + { + a: 'ETH', + f: '17902.35190619', + l: '0.00000000', + }, + { + a: 'BNC', + f: '1114503.29769312', + l: '0.00000000', + }, + { + a: 'NEO', + f: '0.00000000', + l: '0.00000000', + }, + ], + } - userEventHandler(res => { - t.deepEqual(res, { - eventType: 'account', - eventTime: 1499405658849, - makerCommissionRate: 0, - takerCommissionRate: 0, - buyerCommissionRate: 0, - sellerCommissionRate: 0, - canTrade: true, - canWithdraw: true, - canDeposit: true, - lastAccountUpdate: 1499405658849, - balances: { - LTC: { available: '17366.18538083', locked: '0.00000000' }, - BTC: { available: '10537.85314051', locked: '2.19464093' }, - ETH: { available: '17902.35190619', locked: '0.00000000' }, - BNC: { available: '1114503.29769312', locked: '0.00000000' }, - NEO: { available: '0.00000000', locked: '0.00000000' }, - }, - }) - })({ data: JSON.stringify(accountPayload) }) + userEventHandler(res => { + t.deepEqual(res, { + eventType: 'account', + eventTime: 1499405658849, + makerCommissionRate: 0, + takerCommissionRate: 0, + buyerCommissionRate: 0, + sellerCommissionRate: 0, + canTrade: true, + canWithdraw: true, + canDeposit: true, + lastAccountUpdate: 1499405658849, + balances: { + LTC: { available: '17366.18538083', locked: '0.00000000' }, + BTC: { available: '10537.85314051', locked: '2.19464093' }, + ETH: { available: '17902.35190619', locked: '0.00000000' }, + BNC: { available: '1114503.29769312', locked: '0.00000000' }, + NEO: { available: '0.00000000', locked: '0.00000000' }, + }, + }) + })({ data: JSON.stringify(accountPayload) }) }) test('[WS] userEvents - executionReport NEW', t => { - const orderPayload = { - e: 'executionReport', - E: 1499405658658, - s: 'ETHBTC', - c: 'mUvoqJxFIILMdfAW5iGSOW', - S: 'BUY', - o: 'LIMIT', - f: 'GTC', - q: '1.00000000', - p: '0.10264410', - P: '0.10285410', - F: '0.00000000', - g: -1, - C: 'null', - x: 'NEW', - X: 'NEW', - r: 'NONE', - i: 4293153, - l: '0.00000000', - z: '0.00000000', - L: '0.00000000', - n: '0', - N: null, - T: 1499405658657, - t: -1, - I: 8641984, - w: true, - m: false, - M: false, - O: 1499405658657, - Q: 0, - Y: 0, - Z: '0.00000000', - } + const orderPayload = { + e: 'executionReport', + E: 1499405658658, + s: 'ETHBTC', + c: 'mUvoqJxFIILMdfAW5iGSOW', + S: 'BUY', + o: 'LIMIT', + f: 'GTC', + q: '1.00000000', + p: '0.10264410', + P: '0.10285410', + F: '0.00000000', + g: -1, + C: 'null', + x: 'NEW', + X: 'NEW', + r: 'NONE', + i: 4293153, + l: '0.00000000', + z: '0.00000000', + L: '0.00000000', + n: '0', + N: null, + T: 1499405658657, + t: -1, + I: 8641984, + w: true, + m: false, + M: false, + O: 1499405658657, + Q: 0, + Y: 0, + Z: '0.00000000', + } - userEventHandler(res => { - t.deepEqual(res, { - commission: '0', - commissionAsset: null, - creationTime: 1499405658657, - eventTime: 1499405658658, - eventType: 'executionReport', - executionType: 'NEW', - icebergQuantity: '0.00000000', - isBuyerMaker: false, - isOrderWorking: true, - lastQuoteTransacted: 0, - lastTradeQuantity: '0.00000000', - newClientOrderId: 'mUvoqJxFIILMdfAW5iGSOW', - orderId: 4293153, - orderListId: -1, - orderRejectReason: 'NONE', - orderStatus: 'NEW', - orderTime: 1499405658657, - orderType: 'LIMIT', - originalClientOrderId: 'null', - price: '0.10264410', - priceLastTrade: '0.00000000', - quantity: '1.00000000', - quoteOrderQuantity: 0, - side: 'BUY', - stopPrice: '0.10285410', - symbol: 'ETHBTC', - timeInForce: 'GTC', - totalQuoteTradeQuantity: '0.00000000', - totalTradeQuantity: '0.00000000', - tradeId: -1, - trailingDelta: undefined, - trailingTime: undefined, - }) - })({ data: JSON.stringify(orderPayload) }) + userEventHandler(res => { + t.deepEqual(res, { + commission: '0', + commissionAsset: null, + creationTime: 1499405658657, + eventTime: 1499405658658, + eventType: 'executionReport', + executionType: 'NEW', + icebergQuantity: '0.00000000', + isBuyerMaker: false, + isOrderWorking: true, + lastQuoteTransacted: 0, + lastTradeQuantity: '0.00000000', + newClientOrderId: 'mUvoqJxFIILMdfAW5iGSOW', + orderId: 4293153, + orderListId: -1, + orderRejectReason: 'NONE', + orderStatus: 'NEW', + orderTime: 1499405658657, + orderType: 'LIMIT', + originalClientOrderId: 'null', + price: '0.10264410', + priceLastTrade: '0.00000000', + quantity: '1.00000000', + quoteOrderQuantity: 0, + side: 'BUY', + stopPrice: '0.10285410', + symbol: 'ETHBTC', + timeInForce: 'GTC', + totalQuoteTradeQuantity: '0.00000000', + totalTradeQuantity: '0.00000000', + tradeId: -1, + trailingDelta: undefined, + trailingTime: undefined, + }) + })({ data: JSON.stringify(orderPayload) }) }) test('[WS] userEvents - executionReport TRADE', t => { - const tradePayload = { - e: 'executionReport', - E: 1499406026404, - s: 'ETHBTC', - c: '1hRLKJhTRsXy2ilYdSzhkk', - S: 'BUY', - o: 'LIMIT', - f: 'GTC', - q: '22.42906458', - p: '0.10279999', - P: '0.10280001', - F: '0.00000000', - g: -1, - C: 'null', - x: 'TRADE', - X: 'FILLED', - r: 'NONE', - i: 4294220, - l: '17.42906458', - z: '22.42906458', - L: '0.10279999', - n: '0.00000001', - N: 'BNC', - T: 1499406026402, - t: 77517, - I: 8644124, - w: false, - m: false, - M: true, - O: 1499405658657, - Q: 0, - Y: 0, - Z: '2.30570761', - } + const tradePayload = { + e: 'executionReport', + E: 1499406026404, + s: 'ETHBTC', + c: '1hRLKJhTRsXy2ilYdSzhkk', + S: 'BUY', + o: 'LIMIT', + f: 'GTC', + q: '22.42906458', + p: '0.10279999', + P: '0.10280001', + F: '0.00000000', + g: -1, + C: 'null', + x: 'TRADE', + X: 'FILLED', + r: 'NONE', + i: 4294220, + l: '17.42906458', + z: '22.42906458', + L: '0.10279999', + n: '0.00000001', + N: 'BNC', + T: 1499406026402, + t: 77517, + I: 8644124, + w: false, + m: false, + M: true, + O: 1499405658657, + Q: 0, + Y: 0, + Z: '2.30570761', + } - userEventHandler(res => { - t.deepEqual(res, { - eventType: 'executionReport', - eventTime: 1499406026404, - symbol: 'ETHBTC', - newClientOrderId: '1hRLKJhTRsXy2ilYdSzhkk', - originalClientOrderId: 'null', - side: 'BUY', - orderType: 'LIMIT', - timeInForce: 'GTC', - quantity: '22.42906458', - price: '0.10279999', - stopPrice: '0.10280001', - executionType: 'TRADE', - icebergQuantity: '0.00000000', - orderStatus: 'FILLED', - orderRejectReason: 'NONE', - orderId: 4294220, - orderTime: 1499406026402, - lastTradeQuantity: '17.42906458', - totalTradeQuantity: '22.42906458', - priceLastTrade: '0.10279999', - commission: '0.00000001', - commissionAsset: 'BNC', - tradeId: 77517, - isOrderWorking: false, - isBuyerMaker: false, - creationTime: 1499405658657, - totalQuoteTradeQuantity: '2.30570761', - lastQuoteTransacted: 0, - orderListId: -1, - quoteOrderQuantity: 0, - trailingDelta: undefined, - trailingTime: undefined, - }) - })({ data: JSON.stringify(tradePayload) }) + userEventHandler(res => { + t.deepEqual(res, { + eventType: 'executionReport', + eventTime: 1499406026404, + symbol: 'ETHBTC', + newClientOrderId: '1hRLKJhTRsXy2ilYdSzhkk', + originalClientOrderId: 'null', + side: 'BUY', + orderType: 'LIMIT', + timeInForce: 'GTC', + quantity: '22.42906458', + price: '0.10279999', + stopPrice: '0.10280001', + executionType: 'TRADE', + icebergQuantity: '0.00000000', + orderStatus: 'FILLED', + orderRejectReason: 'NONE', + orderId: 4294220, + orderTime: 1499406026402, + lastTradeQuantity: '17.42906458', + totalTradeQuantity: '22.42906458', + priceLastTrade: '0.10279999', + commission: '0.00000001', + commissionAsset: 'BNC', + tradeId: 77517, + isOrderWorking: false, + isBuyerMaker: false, + creationTime: 1499405658657, + totalQuoteTradeQuantity: '2.30570761', + lastQuoteTransacted: 0, + orderListId: -1, + quoteOrderQuantity: 0, + trailingDelta: undefined, + trailingTime: undefined, + }) + })({ data: JSON.stringify(tradePayload) }) }) test('[WS] userEvents - listStatus', t => { - const listStatusPayload = { - e: 'listStatus', - E: 1661588112531, - s: 'TWTUSDT', - g: 73129826, - c: 'OCO', - l: 'EXEC_STARTED', - L: 'EXECUTING', - r: 'NONE', - C: 'Y3ZgLMRPHZFNqEVSZwoJI7', - T: 1661588112530, - O: [ - { + const listStatusPayload = { + e: 'listStatus', + E: 1661588112531, s: 'TWTUSDT', - i: 209259206, - c: 'electron_f675d1bdea454cd4afeac5664be', - }, - { - s: 'TWTUSDT', - i: 209259207, - c: 'electron_38d852a65a89486c98e59879327', - }, - ], - } + g: 73129826, + c: 'OCO', + l: 'EXEC_STARTED', + L: 'EXECUTING', + r: 'NONE', + C: 'Y3ZgLMRPHZFNqEVSZwoJI7', + T: 1661588112530, + O: [ + { + s: 'TWTUSDT', + i: 209259206, + c: 'electron_f675d1bdea454cd4afeac5664be', + }, + { + s: 'TWTUSDT', + i: 209259207, + c: 'electron_38d852a65a89486c98e59879327', + }, + ], + } - userEventHandler(res => { - t.deepEqual(res, { - eventType: 'listStatus', - eventTime: 1661588112531, - symbol: 'TWTUSDT', - orderListId: 73129826, - contingencyType: 'OCO', - listStatusType: 'EXEC_STARTED', - listOrderStatus: 'EXECUTING', - listRejectReason: 'NONE', - listClientOrderId: 'Y3ZgLMRPHZFNqEVSZwoJI7', - transactionTime: 1661588112530, - orders: [ - { - symbol: 'TWTUSDT', - orderId: 209259206, - clientOrderId: 'electron_f675d1bdea454cd4afeac5664be', - }, - { - symbol: 'TWTUSDT', - orderId: 209259207, - clientOrderId: 'electron_38d852a65a89486c98e59879327', - }, - ], - }) - })({ data: JSON.stringify(listStatusPayload) }) + userEventHandler(res => { + t.deepEqual(res, { + eventType: 'listStatus', + eventTime: 1661588112531, + symbol: 'TWTUSDT', + orderListId: 73129826, + contingencyType: 'OCO', + listStatusType: 'EXEC_STARTED', + listOrderStatus: 'EXECUTING', + listRejectReason: 'NONE', + listClientOrderId: 'Y3ZgLMRPHZFNqEVSZwoJI7', + transactionTime: 1661588112530, + orders: [ + { + symbol: 'TWTUSDT', + orderId: 209259206, + clientOrderId: 'electron_f675d1bdea454cd4afeac5664be', + }, + { + symbol: 'TWTUSDT', + orderId: 209259207, + clientOrderId: 'electron_38d852a65a89486c98e59879327', + }, + ], + }) + })({ data: JSON.stringify(listStatusPayload) }) }) test('[WS] userEvents - unknown event type', t => { - const newEvent = { e: 'totallyNewEvent', yolo: 42 } + const newEvent = { e: 'totallyNewEvent', yolo: 42 } - userEventHandler(res => { - t.deepEqual(res, { type: 'totallyNewEvent', yolo: 42 }) - })({ data: JSON.stringify(newEvent) }) + userEventHandler(res => { + t.deepEqual(res, { type: 'totallyNewEvent', yolo: 42 }) + })({ data: JSON.stringify(newEvent) }) }) test('[WS] userEvents - balanceUpdate', t => { - const balancePayload = { - e: 'balanceUpdate', - E: 1573200697110, - a: 'BTC', - d: '100.00000000', - T: 1573200697068, - } + const balancePayload = { + e: 'balanceUpdate', + E: 1573200697110, + a: 'BTC', + d: '100.00000000', + T: 1573200697068, + } - userEventHandler(res => { - t.deepEqual(res, { - eventType: 'balanceUpdate', - eventTime: 1573200697110, - asset: 'BTC', - balanceDelta: '100.00000000', - clearTime: 1573200697068, - }) - })({ data: JSON.stringify(balancePayload) }) + userEventHandler(res => { + t.deepEqual(res, { + eventType: 'balanceUpdate', + eventTime: 1573200697110, + asset: 'BTC', + balanceDelta: '100.00000000', + clearTime: 1573200697068, + }) + })({ data: JSON.stringify(balancePayload) }) }) test('[WS] userEvents - outboundAccountPosition', t => { - const positionPayload = { - e: 'outboundAccountPosition', - E: 1564034571105, - u: 1564034571073, - B: [ - { - a: 'ETH', - f: '10000.000000', - l: '0.000000', - }, - ], - } + const positionPayload = { + e: 'outboundAccountPosition', + E: 1564034571105, + u: 1564034571073, + B: [ + { + a: 'ETH', + f: '10000.000000', + l: '0.000000', + }, + ], + } - userEventHandler(res => { - t.deepEqual(res, { - eventType: 'outboundAccountPosition', - eventTime: 1564034571105, - lastAccountUpdate: 1564034571073, - balances: [ - { - asset: 'ETH', - free: '10000.000000', - locked: '0.000000', - }, - ], - }) - })({ data: JSON.stringify(positionPayload) }) + userEventHandler(res => { + t.deepEqual(res, { + eventType: 'outboundAccountPosition', + eventTime: 1564034571105, + lastAccountUpdate: 1564034571073, + balances: [ + { + asset: 'ETH', + free: '10000.000000', + locked: '0.000000', + }, + ], + }) + })({ data: JSON.stringify(positionPayload) }) }) From 2ab08b2170178871d13d45db42d772ac5f23162b Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:09:21 +0100 Subject: [PATCH 12/13] try with proxy --- test/index.js | 2 +- test/websockets/bookTicker.js | 2 +- test/websockets/candles.js | 2 +- test/websockets/customSubStream.js | 2 +- test/websockets/depth.js | 2 +- test/websockets/liquidations.js | 2 +- test/websockets/markPrices.js | 2 +- test/websockets/ticker.js | 2 +- test/websockets/trades.js | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/index.js b/test/index.js index eea45135..23c3fe45 100644 --- a/test/index.js +++ b/test/index.js @@ -6,7 +6,7 @@ import { userEventHandler } from 'websocket' import { checkFields, createHttpServer } from './utils' -const client = Binance() +const client = Binance({proxy: "http://188.245.226.105:8911"}) test('[MISC] Some error codes are defined', t => { t.truthy(ErrorCodes, 'The map is there') diff --git a/test/websockets/bookTicker.js b/test/websockets/bookTicker.js index bb7d8ff7..7b9a85d9 100644 --- a/test/websockets/bookTicker.js +++ b/test/websockets/bookTicker.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance() +const client = Binance({proxy: "http://188.245.226.105:8911"}) test('[WS] bookTicker - single symbol', t => { return new Promise(resolve => { diff --git a/test/websockets/candles.js b/test/websockets/candles.js index 135c22d1..9a99f180 100644 --- a/test/websockets/candles.js +++ b/test/websockets/candles.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance() +const client = Binance({proxy: "http://188.245.226.105:8911"}) test('[WS] candles - missing parameters', t => { try { diff --git a/test/websockets/customSubStream.js b/test/websockets/customSubStream.js index 33203ca3..b9a201c1 100644 --- a/test/websockets/customSubStream.js +++ b/test/websockets/customSubStream.js @@ -2,7 +2,7 @@ import test from 'ava' import Binance from 'index' -const client = Binance() +const client = Binance({proxy: "http://188.245.226.105:8911"}) test('[WS] customSubStream - single stream', t => { return new Promise(resolve => { diff --git a/test/websockets/depth.js b/test/websockets/depth.js index 9ca234f3..d2673ee9 100644 --- a/test/websockets/depth.js +++ b/test/websockets/depth.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance() +const client = Binance({proxy: "http://188.245.226.105:8911"}) test('[WS] depth - single symbol', t => { return new Promise(resolve => { diff --git a/test/websockets/liquidations.js b/test/websockets/liquidations.js index 82657aac..4929cc0b 100644 --- a/test/websockets/liquidations.js +++ b/test/websockets/liquidations.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance() +const client = Binance({proxy: "http://188.245.226.105:8911"}) // Note: Liquidation streams are skipped as they may not always have active liquidations to test diff --git a/test/websockets/markPrices.js b/test/websockets/markPrices.js index 36fbd6bb..bdebb4a6 100644 --- a/test/websockets/markPrices.js +++ b/test/websockets/markPrices.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance() +const client = Binance({proxy: "http://188.245.226.105:8911"}) test('[WS] futuresAllMarkPrices - default speed', t => { return new Promise(resolve => { diff --git a/test/websockets/ticker.js b/test/websockets/ticker.js index a4bef32c..849f5aad 100644 --- a/test/websockets/ticker.js +++ b/test/websockets/ticker.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance() +const client = Binance({proxy: "http://188.245.226.105:8911"}) test('[WS] ticker - single symbol', t => { return new Promise(resolve => { diff --git a/test/websockets/trades.js b/test/websockets/trades.js index 42b5e1a8..5579b0de 100644 --- a/test/websockets/trades.js +++ b/test/websockets/trades.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance() +const client = Binance({proxy: "http://188.245.226.105:8911"}) test('[WS] trades - single symbol', t => { return new Promise(resolve => { From df601c45abc4ac803c9b4bb9552cca1770abe760 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:11:50 +0100 Subject: [PATCH 13/13] prettier fix --- test/index.js | 2 +- test/websockets/bookTicker.js | 2 +- test/websockets/candles.js | 2 +- test/websockets/customSubStream.js | 2 +- test/websockets/depth.js | 2 +- test/websockets/liquidations.js | 2 +- test/websockets/markPrices.js | 2 +- test/websockets/ticker.js | 2 +- test/websockets/trades.js | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/index.js b/test/index.js index 23c3fe45..f821208c 100644 --- a/test/index.js +++ b/test/index.js @@ -6,7 +6,7 @@ import { userEventHandler } from 'websocket' import { checkFields, createHttpServer } from './utils' -const client = Binance({proxy: "http://188.245.226.105:8911"}) +const client = Binance({ proxy: 'http://188.245.226.105:8911' }) test('[MISC] Some error codes are defined', t => { t.truthy(ErrorCodes, 'The map is there') diff --git a/test/websockets/bookTicker.js b/test/websockets/bookTicker.js index 7b9a85d9..16e0c7e4 100644 --- a/test/websockets/bookTicker.js +++ b/test/websockets/bookTicker.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance({proxy: "http://188.245.226.105:8911"}) +const client = Binance({ proxy: 'http://188.245.226.105:8911' }) test('[WS] bookTicker - single symbol', t => { return new Promise(resolve => { diff --git a/test/websockets/candles.js b/test/websockets/candles.js index 9a99f180..170175c0 100644 --- a/test/websockets/candles.js +++ b/test/websockets/candles.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance({proxy: "http://188.245.226.105:8911"}) +const client = Binance({ proxy: 'http://188.245.226.105:8911' }) test('[WS] candles - missing parameters', t => { try { diff --git a/test/websockets/customSubStream.js b/test/websockets/customSubStream.js index b9a201c1..569c5f65 100644 --- a/test/websockets/customSubStream.js +++ b/test/websockets/customSubStream.js @@ -2,7 +2,7 @@ import test from 'ava' import Binance from 'index' -const client = Binance({proxy: "http://188.245.226.105:8911"}) +const client = Binance({ proxy: 'http://188.245.226.105:8911' }) test('[WS] customSubStream - single stream', t => { return new Promise(resolve => { diff --git a/test/websockets/depth.js b/test/websockets/depth.js index d2673ee9..7883b7d3 100644 --- a/test/websockets/depth.js +++ b/test/websockets/depth.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance({proxy: "http://188.245.226.105:8911"}) +const client = Binance({ proxy: 'http://188.245.226.105:8911' }) test('[WS] depth - single symbol', t => { return new Promise(resolve => { diff --git a/test/websockets/liquidations.js b/test/websockets/liquidations.js index 4929cc0b..2a7150bb 100644 --- a/test/websockets/liquidations.js +++ b/test/websockets/liquidations.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance({proxy: "http://188.245.226.105:8911"}) +const client = Binance({ proxy: 'http://188.245.226.105:8911' }) // Note: Liquidation streams are skipped as they may not always have active liquidations to test diff --git a/test/websockets/markPrices.js b/test/websockets/markPrices.js index bdebb4a6..5e97d45f 100644 --- a/test/websockets/markPrices.js +++ b/test/websockets/markPrices.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance({proxy: "http://188.245.226.105:8911"}) +const client = Binance({ proxy: 'http://188.245.226.105:8911' }) test('[WS] futuresAllMarkPrices - default speed', t => { return new Promise(resolve => { diff --git a/test/websockets/ticker.js b/test/websockets/ticker.js index 849f5aad..b669c478 100644 --- a/test/websockets/ticker.js +++ b/test/websockets/ticker.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance({proxy: "http://188.245.226.105:8911"}) +const client = Binance({ proxy: 'http://188.245.226.105:8911' }) test('[WS] ticker - single symbol', t => { return new Promise(resolve => { diff --git a/test/websockets/trades.js b/test/websockets/trades.js index 5579b0de..e0b96a61 100644 --- a/test/websockets/trades.js +++ b/test/websockets/trades.js @@ -4,7 +4,7 @@ import Binance from 'index' import { checkFields } from '../utils' -const client = Binance({proxy: "http://188.245.226.105:8911"}) +const client = Binance({ proxy: 'http://188.245.226.105:8911' }) test('[WS] trades - single symbol', t => { return new Promise(resolve => {