From 07c02f718ee20a8e891e7c2d9ab4794fddcb2e68 Mon Sep 17 00:00:00 2001 From: Aloysius Pendergast Date: Mon, 18 Sep 2017 12:55:04 +0200 Subject: [PATCH] New route to retrieve last trades on Binance (ie: historical data) --- app/exchanges/binance/exchange.js | 91 ++++++++++++++++++++++ app/exchanges/binance/routes.js | 44 ++++++++++- doc/exchanges/binance/index.adoc | 2 + doc/exchanges/binance/trades.adoc | 124 ++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 doc/exchanges/binance/trades.adoc diff --git a/app/exchanges/binance/exchange.js b/app/exchanges/binance/exchange.js index 70c37b94..aa763a7d 100644 --- a/app/exchanges/binance/exchange.js +++ b/app/exchanges/binance/exchange.js @@ -390,6 +390,97 @@ pairs(opt) }); } +/** + * Returns last trades + * + * If opt.outputFormat is 'exchange', the result returned by remote exchange will be returned untouched + * + * [ + * { + * "a":1132434, + * "p":"0.07252000", + * "q":"0.50000000", + * "f":1199586, + * "l":1199586, + * "T":1505725537806, + * "m":true, + * "M":true + * }, + * { + * "a":1132435, + * "p":"0.07252000", + * "q":"0.50000000", + * "f":1199587, + * "l":1199587, + * "T":1505725538108, + * "m":true, + * "M":true + * } + * ] + * + * If opt.outputFormat is 'custom', the result will be as below + * + * [ + * { + * "id":1132933, + * "quantity":0.95, + * "rate":0.072699, + * "price":0.06906405, + * "timestamp":1505731777 + * }, + * { + * "id":1132932, + * "quantity":1, + * "rate":0.072602, + * "price":0.072602, + * "timestamp":1505731693 + * } + * ] + * + * @param {string} opt.outputFormat (custom|exchange) if value is 'exchange' result returned by remote exchange will be returned untouched + * @param {integer} opt.afterTradeId only retrieve trade with an ID > opt.afterTradeId (optional, will be ignored if opt.outputFormat is set to 'exchange') + * @param {string} opt.pair pair to retrieve order book for (X-Y) + * @return {Promise} format depends on parameter opt.outputFormat + */ + trades(opt) { + let self = this; + return this._limiterGlobal.schedule(function(){ + let exchangePair = self._toExchangePair(opt.pair); + let params = {symbol:exchangePair}; + if (undefined !== opt.afterTradeId) + { + // fromId is inclusive, we want all trades with an ID > afterTradeId + params.fromId = opt.afterTradeId + 1; + } + let p = self._restClient.aggTrades(params); + // return raw results + if ('exchange' == opt.outputFormat) + { + return p; + } + return new Promise((resolve, reject) => { + p.then(function(data){ + let list = []; + _.forEach(data, function(entry){ + let quantity = parseFloat(entry.q); + let rate = parseFloat(entry.p); + let price = quantity * rate; + list.unshift({ + id:entry.a, + quantity:quantity, + rate:rate, + price:price, + timestamp:parseInt(entry.T / 1000.0) + }) + }); + resolve(list); + }).catch(function(err){ + reject(err.msg); + }); + }); + }); +} + /** * @param {string} orderNumber identifier of the order * @param {string} pair pair of the order (X-Y) diff --git a/app/exchanges/binance/routes.js b/app/exchanges/binance/routes.js index cce05ca6..e366289d 100644 --- a/app/exchanges/binance/routes.js +++ b/app/exchanges/binance/routes.js @@ -141,7 +141,7 @@ app.get('/exchanges/binance/pairs', (req, res) => { * * @param {string} outputFormat (custom|exchange) if value is 'exchange' result returned by remote exchange will be returned untouched (optional, default = 'custom') * @param {string} pair pair to retrieve order book for - * @param {integer} limit how many entries to retrieve (optional, default = 100, max = 100) (must be a ) + * @param {integer} limit how many entries to retrieve (optional, default = 100, max = 100) */ app.get('/exchanges/binance/orderBooks/:pair', (req, res) => { let opt = {outputFormat:'custom', limit:100}; @@ -182,6 +182,48 @@ app.get('/exchanges/binance/orderBooks/:pair', (req, res) => { }); }); +/** + * Returns last trades for a given pair (Binance only allows to retrieve last 500) + * + * @param {string} outputFormat (custom|exchange) if value is 'exchange' result returned by remote exchange will be returned untouched (optional, default = 'custom') + * @param {integer} afterTradeId only retrieve trade with an ID > afterTradeId (optional, will be ignored if outputFormat is set to 'exchange') + * @param {string} pair pair to retrieve last trades for + */ +app.get('/exchanges/binance/trades/:pair', (req, res) => { + let opt = {outputFormat:'custom'}; + if (undefined === req.params.pair || '' == req.params.pair) + { + res.status(400).send({origin:"gateway",error:"Missing url parameter 'pair'"}); + return; + } + opt.pair = req.params.pair; + if ('exchange' == req.query.outputFormat) + { + opt.outputFormat = 'exchange'; + } + if ('custom' == opt.outputFormat) + { + if (undefined !== req.query.afterTradeId) + { + let afterTradeId = parseInt(req.query.afterTradeId); + if (isNaN(afterTradeId) || afterTradeId <= 0) + { + res.status(400).send({origin:"gateway",error:util.format("Parameter 'afterTradeId' should be an integer > 0 : value = '%s'", req.query.afterTradeId)}); + return; + } + opt.afterTradeId = afterTradeId; + } + } + exchange.trades(opt) + .then(function(data) { + res.send(data); + }) + .catch(function(err) + { + res.status(503).send({origin:"remote",error:err}); + }); +}); + //-- below routes require valid key/secret if ('' === config.exchanges.binance.key || '' === config.exchanges.binance.secret) { diff --git a/doc/exchanges/binance/index.adoc b/doc/exchanges/binance/index.adoc index 375e74d8..03471c5c 100644 --- a/doc/exchanges/binance/index.adoc +++ b/doc/exchanges/binance/index.adoc @@ -8,6 +8,8 @@ include::tickers.adoc[leveloffset=1] include::orderBooks.adoc[leveloffset=1] +include::trades.adoc[leveloffset=1] + include::openOrders.adoc[leveloffset=1] include::closedOrders.adoc[leveloffset=1] diff --git a/doc/exchanges/binance/trades.adoc b/doc/exchanges/binance/trades.adoc new file mode 100644 index 00000000..d8c258c1 --- /dev/null +++ b/doc/exchanges/binance/trades.adoc @@ -0,0 +1,124 @@ += Trades + +== Retrieves last trades for a given pair + +*GET* _/exchanges/binance/trades/{pair}_ + +Uses Binance _aggTrades_ API + +=== Url parameters + +[cols="1,1a,3a", options="header"] +|=== + +|Name +|Type +|Description + +|{pair} +|string +|Pair to retrieve last trades for (_X-Y_) + +|=== + +=== Optional query parameters + +[cols="1,1a,1a,3a", options="header"] +|=== + +|Name +|Type +|Default +|Description + +|outputFormat +|string (_custom_,_exchange_) +|custom +|If value is _exchange_ result returned by remote exchange will be returned untouched + +|afterTradeId +|integer +| +|Only retrieve trades with an _id_ > _afterTradeId_ (will be ignored if _outputFormat_ is set to _exchange_) + +|=== + +[NOTE] +==== +Binance only allows to retrieve a maximum of 500 trades at once +==== + +=== Result + +[NOTE] +==== +This only applies when _outputFormat_ is set to _custom_ +==== + +Result will be an array of trade object (*the first one being the newest*) + +[cols="1,1a,3a", options="header"] +|=== +|Name +|Type +|Description + +|id +|integer +|Unique identifier of the trade + +|quantity +|float +|Quantity bougth/sold during the trade + +|rate +|float +|Per-unit price + +|price +|float +|Total price (_quantity_ * _rate_) + +|timestamp +|integer (unix timestamp) +|Unix timestamp when trade was executed + +|=== + +.Examples + +Example for *GET* _/exchanges/binance/trades/BTC-ETH_ + +[source,json] +---- +[ + { + "id":1132933, + "quantity":0.95, + "rate":0.072699, + "price":0.06906405, + "timestamp":1505731777 + }, + { + "id":1132932, + "quantity":1, + "rate":0.072602, + "price":0.072602, + "timestamp":1505731693 + }, + { + "id":1132931, + "quantity":20, + "rate":0.072684, + "price":1.4536799999999999, + "timestamp":1505731638 + }, + { + "id":1132930, + "quantity":0.951, + "rate":0.072615, + "price":0.069056865, + "timestamp":1505731450 + } +] +----