diff --git a/README.md b/README.md
index 44591dee..d6d2bf62 100644
--- a/README.md
+++ b/README.md
@@ -353,6 +353,21 @@ console.log(await client.getOrder({
Output
```js
+{
+ symbol: 'ENGETH',
+ orderId: 191938,
+ clientOrderId: '1XZTVBTGS4K1e',
+ price: '0.00138000',
+ origQty: '1.00000000',
+ executedQty: '1.00000000',
+ status: 'FILLED',
+ timeInForce: 'GTC',
+ type: 'LIMIT',
+ side: 'SELL',
+ stopPrice: '0.00000000',
+ icebergQty: '0.00000000',
+ time: 1508611114735
+}
```
@@ -428,6 +443,21 @@ console.log(await client.allOrders({
Output
```js
+[{
+ symbol: 'ENGETH',
+ orderId: 191938,
+ clientOrderId: '1XZTVBTGS4K1e',
+ price: '0.00138000',
+ origQty: '1.00000000',
+ executedQty: '1.00000000',
+ status: 'FILLED',
+ timeInForce: 'GTC',
+ type: 'LIMIT',
+ side: 'SELL',
+ stopPrice: '0.00000000',
+ icebergQty: '0.00000000',
+ time: 1508611114735
+}]
```
@@ -437,7 +467,7 @@ console.log(await client.allOrders({
Get current account information.
```js
-console.log(await client.openOrders())
+console.log(await client.accountInfo())
```
|Param|Type|Required|
@@ -448,6 +478,19 @@ console.log(await client.openOrders())
Output
```js
+{
+ makerCommission: 10,
+ takerCommission: 10,
+ buyerCommission: 0,
+ sellerCommission: 0,
+ canTrade: true,
+ canWithdraw: true,
+ canDeposit: true,
+ balances: [
+ { asset: 'BTC', free: '0.00000000', locked: '0.00000000' },
+ { asset: 'LTC', free: '0.00000000', locked: '0.00000000' },
+ ]
+}
```
@@ -473,6 +516,18 @@ console.log(await client.myTrades({
Output
```js
+[{
+ id: 9960,
+ orderId: 191939,
+ price: '0.00138000',
+ qty: '10.00000000',
+ commission: '0.00001380',
+ commissionAsset: 'ETH',
+ time: 1508611114735,
+ isBuyer: false,
+ isMaker: false,
+ isBestMatch: true
+}]
```
@@ -481,7 +536,8 @@ console.log(await client.myTrades({
#### depth
-Live depth market data feed for a given symbol.
+Live depth market data feed. The first parameter can either
+be a single symbol string or an array of symbols.
```js
client.ws.depth('ETHBTC', depth => {
@@ -489,9 +545,32 @@ client.ws.depth('ETHBTC', depth => {
})
```
+
+Output
+
+```js
+{
+ eventType: 'depthUpdate',
+ eventTime: 1508612956950,
+ symbol: 'ETHBTC',
+ updateId: 18331140,
+ bidDepth: [
+ { price: '0.04896500', quantity: '0.00000000' },
+ { price: '0.04891100', quantity: '15.00000000' },
+ { price: '0.04891000', quantity: '0.00000000' } ],
+ askDepth: [
+ { price: '0.04910600', quantity: '0.00000000' },
+ { price: '0.04910700', quantity: '11.24900000' }
+ ]
+}
+```
+
+
+
#### candles
-Live candle data feed for a given symbol and interval.
+Live candle data feed for a given interval. You can pass either a symbol string
+or a symbol array.
```js
client.ws.candles('ETHBTC', '1m', candle => {
@@ -499,16 +578,57 @@ client.ws.candles('ETHBTC', '1m', candle => {
})
```
+
+Output
+
+```js
+{
+ eventType: 'kline',
+ eventTime: 1508613366276,
+ symbol: 'ETHBTC',
+ open: '0.04898000',
+ high: '0.04902700',
+ low: '0.04898000',
+ close: '0.04901900',
+ volume: '37.89600000',
+ trades: 30,
+ interval: '5m',
+ isFinal: false,
+ quoteVolume: '1.85728874',
+ buyVolume: '21.79900000',
+ quoteBuyVolume: '1.06838790'
+}
+```
+
+
+
#### trades
-Live trade data feed for a given symbol.
+Live trade data feed. Pass either a single symbol string or an array of symbols.
```js
-client.ws.trades('ETHBTC', trade => {
+client.ws.trades(['ETHBTC', 'BNBBTC'], trade => {
console.log(trade)
})
```
+
+Output
+
+```js
+{
+ eventType: 'aggTrade',
+ eventTime: 1508614495052,
+ symbol: 'ETHBTC',
+ price: '0.04923600',
+ quantity: '3.43500000',
+ maker: false,
+ tradeId: 2148226
+}
+```
+
+
+
#### user
Live user messages data feed.
@@ -523,3 +643,20 @@ const clean = await client.ws.user(msg => {
Note that this method returns a promise returning a `clean` callback, that will clear
the keep-alive interval and close the data stream.
+
+
+Output
+
+```js
+{
+ eventType: 'account',
+ eventTime: 1508614885818,
+ balances: {
+ '123': { available: '0.00000000', locked: '0.00000000' },
+ '456': { available: '0.00000000', locked: '0.00000000' },
+ BTC: { available: '0.00000000', locked: '0.00000000' },
+ ]
+}
+```
+
+
diff --git a/src/websocket.js b/src/websocket.js
index 0bd1104a..e5a6cb49 100644
--- a/src/websocket.js
+++ b/src/websocket.js
@@ -1,86 +1,102 @@
import WebSocket from 'ws'
+import zip from 'lodash.zipobject'
import httpMethods from 'http'
const BASE = 'wss://stream.binance.com:9443/ws'
-const depth = (symbol, cb) => {
- const w = new WebSocket(`${BASE}/${symbol.toLowerCase()}@depth`)
- w.on('message', msg => {
- const {
- e: eventType,
- E: eventTime,
- s: symbol,
- u: updateId,
- b: bidDepth,
- a: askDepth,
- } = JSON.parse(msg)
+const depth = (payload, cb) =>
+ (Array.isArray(payload) ? payload : [payload]).forEach(symbol => {
+ const w = new WebSocket(`${BASE}/${symbol.toLowerCase()}@depth`)
+ w.on('message', msg => {
+ const {
+ e: eventType,
+ E: eventTime,
+ s: symbol,
+ u: updateId,
+ b: bidDepth,
+ a: askDepth,
+ } = JSON.parse(msg)
- cb({ eventType, eventTime, symbol, updateId, bidDepth, askDepth })
+ cb({
+ eventType,
+ eventTime,
+ symbol,
+ updateId,
+ bidDepth: bidDepth.map(b => zip(['price', 'quantity'], b)),
+ askDepth: askDepth.map(a => zip(['price', 'quantity'], a)),
+ })
+ })
})
-}
-const candles = (symbol, interval, cb) => {
- const w = new WebSocket(`${BASE}/${symbol.toLowerCase()}@kline_${interval}`)
- w.on('message', msg => {
- const { e: eventType, E: eventTime, s: symbol, k: tick } = JSON.parse(msg)
- const {
- o: open,
- h: high,
- l: low,
- c: close,
- v: volume,
- n: trades,
- i: interval,
- x: isFinal,
- q: quoteVolume,
- V: buyVolume,
- Q: quoteBuyVolume,
- } = tick
+const candles = (payload, interval, cb) => {
+ if (!interval || !cb) {
+ throw new Error('Please pass a symbol, interval and callback.')
+ }
- cb({
- eventType,
- eventTime,
- symbol,
- open,
- high,
- low,
- close,
- volume,
- trades,
- interval,
- isFinal,
- quoteVolume,
- buyVolume,
- quoteBuyVolume,
+ ;(Array.isArray(payload) ? payload : [payload]).forEach(symbol => {
+ const w = new WebSocket(`${BASE}/${symbol.toLowerCase()}@kline_${interval}`)
+ w.on('message', msg => {
+ const { e: eventType, E: eventTime, s: symbol, k: tick } = JSON.parse(msg)
+ const {
+ o: open,
+ h: high,
+ l: low,
+ c: close,
+ v: volume,
+ n: trades,
+ i: interval,
+ x: isFinal,
+ q: quoteVolume,
+ V: buyVolume,
+ Q: quoteBuyVolume,
+ } = tick
+
+ cb({
+ eventType,
+ eventTime,
+ symbol,
+ open,
+ high,
+ low,
+ close,
+ volume,
+ trades,
+ interval,
+ isFinal,
+ quoteVolume,
+ buyVolume,
+ quoteBuyVolume,
+ })
})
})
}
-const trades = (symbol, cb) => {
- const w = new WebSocket(`${BASE}/${symbol.toLowerCase()}@aggTrades`)
- w.on('message', msg => {
- const {
- e: eventType,
- E: eventTime,
- s: symbol,
- p: price,
- q: quantity,
- m: maker,
- a: tradeId,
- } = JSON.parse(msg)
+const trades = (payload, cb) =>
+ (Array.isArray(payload) ? payload : [payload]).forEach(symbol => {
+ const w = new WebSocket(`${BASE}/${symbol.toLowerCase()}@aggTrade`)
+ w.on('message', msg => {
+ const {
+ e: eventType,
+ E: eventTime,
+ s: symbol,
+ p: price,
+ q: quantity,
+ m: maker,
+ a: tradeId,
+ } = JSON.parse(msg)
- cb({
- eventType,
- eventTime,
- symbol,
- price,
- quantity,
- maker,
- tradeId,
+ cb({
+ eventType,
+ eventTime,
+ symbol,
+ price,
+ quantity,
+ maker,
+ tradeId,
+ })
})
})
-}
const userTransforms = {
outboundAccountInfo: m => ({
diff --git a/test/authenticated.js b/test/authenticated.js
index 8fe8ad3c..2f13a481 100644
--- a/test/authenticated.js
+++ b/test/authenticated.js
@@ -2,6 +2,8 @@ import test from 'ava'
import Binance from 'index'
+import { checkFields } from './utils'
+
const client = Binance({
apiKey: process.env.API_KEY,
apiSecret: process.env.API_SECRET,
@@ -24,3 +26,77 @@ test.serial('[REST] order', async t => {
t.pass()
})
+
+test.serial('[REST] allOrders / getOrder', async t => {
+ try {
+ await client.getOrder({ symbol: 'ETHBTC' })
+ } catch (e) {
+ t.is(
+ e.message,
+ "Param 'origClientOrderId' or 'orderId' must be sent, but both were empty/null!",
+ )
+ }
+
+ try {
+ await client.getOrder({ symbol: 'ETHBTC', orderId: 1 })
+ } catch (e) {
+ t.is(e.message, 'Order does not exist.')
+ }
+
+ // Note that this test will fail if you don't have any ENG order in your account ;)
+ const orders = await client.allOrders({
+ symbol: 'ENGETH',
+ })
+
+ 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: 'ENGETH',
+ orderId: order.orderId,
+ })
+
+ t.truthy(res)
+ checkFields(t, res, ['orderId', 'symbol', 'price', 'type', 'side'])
+})
+
+test.serial('[REST] openOrders', async t => {
+ const orders = await client.openOrders({
+ symbol: 'ETHBTC',
+ })
+
+ t.true(Array.isArray(orders))
+})
+
+test.serial('[REST] cancelOrder', async t => {
+ try {
+ await client.cancelOrder({ symbol: 'ETHBTC', orderId: 1 })
+ } catch (e) {
+ t.is(e.message, 'UNKNOWN_ORDER')
+ }
+})
+
+test.serial('[REST] accountInfo', async t => {
+ const account = await client.accountInfo()
+ t.truthy(account)
+ checkFields(t, account, ['makerCommission', 'takerCommission', 'balances'])
+ t.truthy(account.balances.length)
+})
+
+test.serial('[REST] myTrades', async t => {
+ const trades = await client.myTrades({ symbol: 'ENGETH' })
+ t.true(Array.isArray(trades))
+ const [trade] = trades
+ checkFields(t, trade, ['id', 'orderId', 'qty', 'commission', 'time'])
+})
+
+test.serial('[WS] user', async t => {
+ const clean = await client.ws.user()
+ t.truthy(clean)
+ t.true(typeof clean === 'function')
+ clean()
+})
diff --git a/test/index.js b/test/index.js
index ba3e9ee4..0321e23a 100644
--- a/test/index.js
+++ b/test/index.js
@@ -4,16 +4,12 @@ import dotenv from 'dotenv'
import Binance from 'index'
import { candleFields } from 'http'
+import { checkFields } from './utils'
+
dotenv.load()
const client = Binance()
-const checkFields = (t, object, fields) => {
- fields.forEach(field => {
- t.truthy(object[field])
- })
-}
-
test.serial('[REST] ping', async t => {
t.truthy(await client.ping(), 'A simple ping should work')
})
@@ -63,7 +59,7 @@ test.serial('[REST] candles', async t => {
test.serial('[REST] aggTrades', async t => {
try {
- client.aggTrades({})
+ await client.aggTrades({})
} catch (e) {
t.is(e.message, 'Method aggTrades requires symbol parameter.')
}
@@ -77,7 +73,7 @@ test.serial('[REST] aggTrades', async t => {
test.serial('[REST] dailyStats', async t => {
try {
- client.dailyStats({})
+ await client.dailyStats({})
} catch (e) {
t.is(e.message, 'Method dailyStats requires symbol parameter.')
}
@@ -99,16 +95,55 @@ test.serial('[REST] allBookTickers', async t => {
t.truthy(tickers.ETHBTC)
})
-test.serial('[REST] Signed call without creds', t => {
- const client = Binance()
-
+test.serial('[REST] Signed call without creds', async t => {
try {
- client.order({ symbol: 'ETHBTC', side: 'BUY', quantity: 1 })
+ await client.order({ symbol: 'ETHBTC', side: 'BUY', quantity: 1 })
} catch (e) {
t.is(e.message, 'You need to pass an API key and secret to make authenticated calls.')
}
})
+test.serial('[WS] depth', t => {
+ return new Promise(resolve => {
+ client.ws.depth('ETHBTC', depth => {
+ t.is(depth, depth, 'ETHBTC')
+ checkFields(t, depth, [
+ 'eventType',
+ 'eventTime',
+ 'updateId',
+ 'symbol',
+ 'bidDepth',
+ 'askDepth',
+ ])
+ resolve()
+ })
+ })
+})
+
+test.serial('[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', '5m', candle => {
+ checkFields(t, candle, ['open', 'high', 'low', 'close', 'volume', 'trades', 'quoteVolume'])
+ resolve()
+ })
+ })
+})
+
+test.serial('[WS] trades', t => {
+ return new Promise(resolve => {
+ client.ws.trades(['BNBBTC', 'ETHBTC', 'BNTBTC'], trade => {
+ checkFields(t, trade, ['eventType', 'tradeId', 'maker', 'quantity', 'price', 'symbol'])
+ resolve()
+ })
+ })
+})
+
if (process.env.API_KEY) {
require('./authenticated')
}
diff --git a/test/utils.js b/test/utils.js
new file mode 100644
index 00000000..9bbe3291
--- /dev/null
+++ b/test/utils.js
@@ -0,0 +1,5 @@
+export const checkFields = (t, object, fields) => {
+ fields.forEach(field => {
+ t.truthy(object[field])
+ })
+}