coinbase-exchange
Advanced tools
Comparing version 0.1.0 to 0.2.0
12
index.js
@@ -1,9 +0,13 @@ | ||
var PublicClient = require('./lib/clients/public.js'); | ||
var PublicClient = require('./lib/clients/public.js'); | ||
var WebsocketClient = require('./lib/clients/websocket.js'); | ||
var AuthenticatedClient = require('./lib/clients/authenticated.js'); | ||
var OrderBook = require('./lib/orderbook.js'); | ||
var Orderbook = require('./lib/orderbook.js'); | ||
var OrderbookSync = require('./lib/orderbook_sync.js'); | ||
module.exports = exports = { | ||
'PublicClient': PublicClient, | ||
'PublicClient' : PublicClient, | ||
'WebsocketClient' : WebsocketClient, | ||
'AuthenticatedClient': AuthenticatedClient, | ||
'OrderBook': OrderBook, | ||
'Orderbook' : Orderbook, | ||
'OrderbookSync' : OrderbookSync, | ||
}; |
var util = require('util'); | ||
var crypto = require('crypto'); | ||
var querystring = require('querystring'); | ||
@@ -16,3 +17,4 @@ var _ = { | ||
var self = this; | ||
PublicClient.call(self, apiURI); | ||
PublicClient.call(self, '', apiURI); | ||
self.key = key; | ||
@@ -24,2 +26,3 @@ self.b64secret = b64secret; | ||
util.inherits(AuthenticatedClient, PublicClient); | ||
_.assign(AuthenticatedClient.prototype, new function() { | ||
@@ -30,4 +33,4 @@ var prototype = this; | ||
var self = this; | ||
opts = opts || {}; | ||
method = method.toUpperCase(); | ||
if (!callback && (opts instanceof Function)) { | ||
@@ -37,19 +40,35 @@ callback = opts; | ||
} | ||
if (!callback) { | ||
throw "Must supply a callback" | ||
} | ||
var relativeURI = self.makeRelativeURI(uriParts); | ||
method = method.toUpperCase(); | ||
_.assign(opts, { | ||
'method': method, | ||
'uri': self.makeAbsoluteURI(relativeURI), | ||
'uri': self.makeAbsoluteURI(relativeURI) | ||
}); | ||
if (opts.body && (typeof opts.body !== 'string')) { | ||
opts.body = JSON.stringify(opts.body); | ||
self.addHeaders(opts, self._getSignature(method, relativeURI, opts)); | ||
request(opts, self.makeRequestCallback(callback)); | ||
}; | ||
prototype._getSignature = function(method, relativeURI, opts) { | ||
var self = this; | ||
var body = ''; | ||
if (opts.body) { | ||
body = JSON.stringify(opts.body); | ||
opts.body = body; | ||
} else if (opts.qs && Object.keys(opts.qs).length !== 0) { | ||
body = '?' + querystring.stringify(opts.qs); | ||
} | ||
var timestamp = Date.now() / 1000; | ||
var what = timestamp + method + relativeURI + (opts.body || ''); | ||
var what = timestamp + method + relativeURI + body; | ||
var key = Buffer(self.b64secret, 'base64'); | ||
var hmac = crypto.createHmac('sha256', key); | ||
var signature = hmac.update(what).digest('base64'); | ||
self.addHeaders(opts, { | ||
return { | ||
'CB-ACCESS-KEY': self.key, | ||
@@ -59,4 +78,3 @@ 'CB-ACCESS-SIGN': signature, | ||
'CB-ACCESS-PASSPHRASE': self.passphrase, | ||
}); | ||
request(opts, self.makeRequestCallback(callback)); | ||
}; | ||
}; | ||
@@ -78,10 +96,26 @@ | ||
prototype.getAccountHistory = function(accountID, callback) { | ||
prototype.getAccountHistory = function(accountID, args, callback) { | ||
var self = this; | ||
return prototype.get.call(self, ['accounts', accountID, 'ledger'], callback); | ||
args = args || {} | ||
if (!callback && (args instanceof Function)) { | ||
callback = args; | ||
args = {}; | ||
} | ||
var opts = { 'qs': args }; | ||
return prototype.get.call(self, ['accounts', accountID, 'ledger'], opts, callback); | ||
}; | ||
prototype.getAccountHolds = function(accountID, callback) { | ||
prototype.getAccountHolds = function(accountID, args, callback) { | ||
var self = this; | ||
return prototype.get.call(self, ['accounts', accountID, 'holds'], callback); | ||
args = args || {} | ||
if (!callback && (args instanceof Function)) { | ||
callback = args; | ||
args = {}; | ||
} | ||
var opts = { 'qs': args }; | ||
return prototype.get.call(self, ['accounts', accountID, 'holds'], opts, callback); | ||
}; | ||
@@ -96,3 +130,3 @@ | ||
}); | ||
opts = {'body': params}; | ||
var opts = { 'body': params }; | ||
return prototype.post.call(self, ['orders'], opts, callback); | ||
@@ -118,5 +152,13 @@ }; | ||
prototype.getOrders = function(callback) { | ||
prototype.getOrders = function(args, callback) { | ||
var self = this; | ||
return prototype.get.call(self, ['orders'], callback); | ||
args = args || {} | ||
if (!callback && (args instanceof Function)) { | ||
callback = args; | ||
args = {}; | ||
} | ||
var opts = { 'qs': args }; | ||
return prototype.get.call(self, ['orders'], opts, callback); | ||
}; | ||
@@ -129,5 +171,13 @@ | ||
prototype.getFills = function(callback) { | ||
prototype.getFills = function(args, callback) { | ||
var self = this; | ||
return prototype.get.call(self, ['fills'], callback); | ||
args = args || {} | ||
if (!callback && (args instanceof Function)) { | ||
callback = args; | ||
args = {}; | ||
} | ||
var opts = { 'qs': args }; | ||
return prototype.get.call(self, ['fills'], opts, callback); | ||
}; | ||
@@ -154,3 +204,3 @@ | ||
}); | ||
opts = {'body': params}; | ||
var opts = { 'body': params }; | ||
return prototype.post.call(self, ['transfers'], opts, callback); | ||
@@ -157,0 +207,0 @@ }; |
@@ -9,9 +9,11 @@ var request = require('request'); | ||
var PublicClient = function(apiURI) { | ||
var PublicClient = function(productID, apiURI) { | ||
var self = this; | ||
self.productID = productID || 'BTC-USD'; | ||
self.apiURI = apiURI || 'https://api.exchange.coinbase.com'; | ||
}; | ||
PublicClient.prototype = new function() { | ||
_.assign(PublicClient.prototype, new function() { | ||
var prototype = this; | ||
prototype.addHeaders = function(obj, additional) { | ||
@@ -31,4 +33,3 @@ obj.headers = obj.headers || {}; | ||
prototype.makeAbsoluteURI = function(relativeURI) { | ||
var self = this; | ||
return self.apiURI + relativeURI; | ||
return this.apiURI + relativeURI; | ||
}; | ||
@@ -60,3 +61,2 @@ | ||
'uri': self.makeAbsoluteURI(self.makeRelativeURI(uriParts)), | ||
'json': true, | ||
}); | ||
@@ -76,31 +76,43 @@ self.addHeaders(opts); | ||
prototype.getProductOrderBook = function(productID, level, callback) { | ||
prototype.getProductOrderBook = function(args, callback) { | ||
var self = this; | ||
if (!callback && (level instanceof Function)) { | ||
callback = level; | ||
level = null; | ||
args = args || {} | ||
if (!callback && (args instanceof Function)) { | ||
callback = args; | ||
args = {}; | ||
} | ||
var opts = level && {'qs': {'level': level}}; | ||
var opts = { 'qs': args }; | ||
return prototype.get.call( | ||
self, ['products', productID, 'book'], opts, callback); | ||
self, ['products', self.productID, 'book'], opts, callback); | ||
}; | ||
prototype.getProductTicker = function(productID, callback) { | ||
prototype.getProductTicker = function(callback) { | ||
var self = this; | ||
return prototype.get.call(self, ['products', productID, 'ticker'], callback); | ||
return prototype.get.call(self, ['products', self.productID, 'ticker'], callback); | ||
}; | ||
prototype.getProductTrades = function(productID, callback) { | ||
prototype.getProductTrades = function(args, callback) { | ||
var self = this; | ||
return prototype.get.call(self, ['products', productID, 'trades'], callback); | ||
var opts = {'qs': args}; | ||
return prototype.get.call(self, ['products', self.productID, 'trades'], opts, callback); | ||
}; | ||
prototype.getProductHistoricRates = function(productID, callback) { | ||
prototype.getProductHistoricRates = function(args, callback) { | ||
var self = this; | ||
return prototype.get.call(self, ['products', productID, 'candles'], callback); | ||
args = args || {} | ||
if (!callback && (args instanceof Function)) { | ||
callback = args; | ||
args = {}; | ||
} | ||
var opts = {'qs': args}; | ||
return prototype.get.call(self, ['products', self.productID, 'candles'], opts, callback); | ||
}; | ||
prototype.getProduct24HrStats = function(productID, callback) { | ||
prototype.getProduct24HrStats = function(callback) { | ||
var self = this; | ||
return prototype.get.call(self, ['products', productID, 'stats'], callback); | ||
return prototype.get.call(self, ['products', self.productID, 'stats'], callback); | ||
}; | ||
@@ -117,4 +129,4 @@ | ||
}; | ||
}; | ||
}); | ||
module.exports = exports = PublicClient; |
@@ -1,199 +0,168 @@ | ||
var EventEmitter = require('events').EventEmitter; | ||
var util = require('util'); | ||
var RBTree = require('bintrees').RBTree; | ||
var num = require('num'); | ||
var assert = require('assert'); | ||
var _ = {assign: require('lodash.assign')} | ||
var WebSocket = require('ws'); | ||
var _ = { | ||
'forEach': require('lodash.foreach'), | ||
'assign': require('lodash.assign'), | ||
}; | ||
var request = require('request'); | ||
var Orderbook = function() { | ||
var self = this; | ||
// Orders hashed by ID | ||
self._ordersByID = {}; | ||
var OrderBook = function(productID, websocketURI) { | ||
var self = this; | ||
EventEmitter.call(self); | ||
self._bids = new RBTree(function(a, b) { | ||
return a.price.cmp(b.price); | ||
}); | ||
self.productID = productID || 'BTC-USD'; | ||
self.websocketURI = websocketURI || 'wss://ws-feed.exchange.coinbase.com'; | ||
self.state = self.STATES.closed; | ||
self.queue = []; | ||
self.book = { | ||
'sequence': -1, | ||
'bids': {}, | ||
'asks': {}, | ||
}; | ||
self.connect(); | ||
self._asks = new RBTree(function(a, b) { | ||
return a.price.cmp(b.price); | ||
}); | ||
}; | ||
util.inherits(OrderBook, EventEmitter); | ||
_.assign(OrderBook.prototype, new function() { | ||
_.assign(Orderbook.prototype, new function() { | ||
var prototype = this; | ||
prototype.STATES = { | ||
'closed': 'closed', | ||
'open': 'open', | ||
'syncing': 'syncing', | ||
'processing': 'processing', | ||
'error': 'error', | ||
prototype._getTree = function(side) { | ||
return side == 'buy' ? this._bids : this._asks; | ||
}; | ||
prototype.connect = function() { | ||
prototype.state = function(book) { | ||
var self = this; | ||
if (self.socket) { | ||
self.socket.close(); | ||
} | ||
self.socket = new WebSocket(self.websocketURI); | ||
self.socket.on('message', self.onMessage.bind(self)); | ||
self.socket.on('open', self.onOpen.bind(self)); | ||
self.socket.on('close', self.onClose.bind(self)); | ||
}; | ||
prototype.disconnect = function() { | ||
var self = this; | ||
if (!self.socket) { | ||
throw "Could not disconnect (not connected)" | ||
} | ||
self.socket.close(); | ||
self.onClose(); | ||
}; | ||
if (book) { | ||
prototype.changeState = function(stateName) { | ||
var self = this; | ||
var newState = self.STATES[stateName]; | ||
if (newState === undefined) { | ||
throw "Unrecognized state: " + stateName; | ||
} | ||
var oldState = self.state; | ||
self.state = newState; | ||
if (self.state === self.STATES.error) { | ||
self.socket.close(); | ||
}; | ||
self.emit('statechange', {'old': oldState, 'new': newState}); | ||
}; | ||
book.bids.forEach(function(order) { | ||
order = { | ||
id: order[2], | ||
side: 'buy', | ||
price: num(order[0]), | ||
size: num(order[1]) | ||
} | ||
self.add(order); | ||
}); | ||
prototype.onOpen = function() { | ||
var self = this; | ||
self.changeState(self.STATES.open); | ||
self.sync(); | ||
}; | ||
book.asks.forEach(function(order) { | ||
order = { | ||
id: order[2], | ||
side: 'sell', | ||
price: num(order[0]), | ||
size: num(order[1]) | ||
} | ||
self.add(order); | ||
}); | ||
prototype.onClose = function() { | ||
var self = this; | ||
self.changeState(self.STATES.closed); | ||
}; | ||
} else { | ||
prototype.onMessage = function(datastr) { | ||
var self = this; | ||
var data = JSON.parse(datastr); | ||
if (self.state !== self.STATES.processing) { | ||
self.queue.push(data); | ||
} else { | ||
self.processMessage(data); | ||
book = { | ||
asks: [], | ||
bids: [] | ||
}; | ||
self._bids.reach(function(bid) { | ||
bid.orders.forEach(function(order) { | ||
book.bids.push(order); | ||
}); | ||
}); | ||
self._asks.each(function(ask) { | ||
ask.orders.forEach(function(order) { | ||
book.asks.push(order); | ||
}); | ||
}); | ||
return book; | ||
} | ||
}; | ||
prototype.sync = function() { | ||
var self = this; | ||
self.changeState(self.STATES.syncing); | ||
var subscribeMessage = { | ||
'type': 'subscribe', | ||
'product_id': self.productID, | ||
}; | ||
self.socket.send(JSON.stringify(subscribeMessage)); | ||
self.loadSnapshot(); | ||
prototype.get = function(orderId) { | ||
return this._ordersByID[orderId] | ||
}; | ||
prototype.loadSnapshot = function(snapshotData) { | ||
prototype.add = function(order) { | ||
var self = this; | ||
var load = function(data) { | ||
var i; | ||
var convertSnapshotArray = function(array) { | ||
return {'price': array[0], 'size': array[1], 'id': array[2]} | ||
}; | ||
for (i = 0; data.bids && i < data.bids.length; i++) { | ||
bid = convertSnapshotArray(data.bids[i]); | ||
self.book.bids[bid.id] = bid; | ||
}; | ||
for (i = 0; data.asks && i < data.asks.length; i++) { | ||
ask = convertSnapshotArray(data.asks[i]); | ||
self.book.asks[ask.id] = ask; | ||
}; | ||
self.book.sequence = data.sequence | ||
_.forEach(self.queue, self.processMessage.bind(self)); | ||
self.queue = []; | ||
self.changeState(self.STATES.processing); | ||
order = { | ||
id: order.order_id || order.id, | ||
side: order.side, | ||
price: num(order.price), | ||
size: num(order.size || order.remaining_size), | ||
}; | ||
if (snapshotData) { | ||
load(data); | ||
} else { | ||
request({ | ||
'url': 'https://api.exchange.coinbase.com/products/BTC-USD/book?level=3', | ||
'headers': {'User-Agent': 'coinbase-node-client'}, | ||
}, function(err, response, body) { | ||
if (err) { | ||
self.changeState(self.STATES.error); | ||
throw "Failed to load snapshot: " + err; | ||
} | ||
if (response.statusCode !== 200) { | ||
self.changeState(self.STATES.error); | ||
throw "Failed to load snapshot: " + response.statusCode; | ||
} | ||
load(JSON.parse(body)); | ||
}); | ||
var tree = self._getTree(order.side); | ||
var node = tree.find({price: order.price}); | ||
if (!node) { | ||
node = { | ||
price: order.price, | ||
orders: [] | ||
} | ||
tree.insert(node); | ||
} | ||
node.orders.push(order); | ||
self._ordersByID[order.id] = order; | ||
}; | ||
prototype.processMessage = function(message) { | ||
prototype.remove = function(orderId) { | ||
var self = this; | ||
if (message.sequence <= self.book.sequence) { | ||
self.emit('ignored', message); | ||
var order = self.get(orderId); | ||
if (!order) { | ||
return; | ||
} | ||
if (message.sequence != self.book.sequence + 1) { | ||
self.changeState(self.STATES.error); | ||
throw "Received message out of order: " + message.sequence; | ||
var tree = self._getTree(order.side); | ||
var node = tree.find({price: order.price}); | ||
assert(node); | ||
var orders = node.orders; | ||
orders.splice(orders.indexOf(order), 1); | ||
if (orders.length === 0) { | ||
tree.remove(node); | ||
} | ||
self.book.sequence = message.sequence; | ||
if (message.type === 'open' || | ||
message.type === 'received' || | ||
message.type === 'change') { | ||
var dataset = message.side === 'buy' ? self.book.bids : self.book.asks; | ||
var newData = { | ||
'size': message.size || message.remaining_size || message.new_size, | ||
'price': message.price, | ||
'id': message.order_id, | ||
}; | ||
dataset[newData.id] = newData; | ||
} else if (message.type === 'match') { | ||
var makerDataset = message.side === 'buy' ? self.book.bids : self.book.asks; | ||
var takerDataset = message.side === 'buy' ? self.book.asks : self.book.bids; | ||
var makerSize = makerDataset[message.maker_order_id].size; | ||
var takerSize = takerDataset[message.taker_order_id].size; | ||
makerDataset[message.maker_order_id].size = '' + (makerSize - message.size); | ||
takerDataset[message.taker_order_id].size = '' + (takerSize - message.size); | ||
delete self._ordersByID[order.id]; | ||
}; | ||
} else if (message.type === 'done') { | ||
var dataset = message.side === 'buy' ? self.book.bids : self.book.asks; | ||
var id = message.order_id; | ||
delete dataset[id]; | ||
prototype.match = function(match) { | ||
var self = this; | ||
} else if (message.type === 'error') { | ||
self.changeState(self.STATES.error); | ||
self.emit(message.type, message); | ||
throw "Received error: " + message.message | ||
var size = num(match.size); | ||
var price = num(match.price); | ||
var tree = self._getTree(match.side); | ||
var node = tree.find({price: price}); | ||
assert(node); | ||
} else { | ||
self.changeState(self.STATES.error); | ||
self.emit('unknown', data); | ||
throw "Received unknown message type: " + message.type; | ||
var order = node.orders[0]; | ||
assert.equal(order.id, match.maker_order_id); | ||
order.size = order.size.sub(size); | ||
self._ordersByID[order.id] = order; | ||
assert(order.size >= 0); | ||
if (order.size.eq(0)) { | ||
self.remove(order.id); | ||
} | ||
}; | ||
self.emit(message.type, message); | ||
prototype.change = function(change) { | ||
var self = this; | ||
var size = num(change.new_size); | ||
var price = num(change.price); | ||
var order = self.get(change.order_id) | ||
var tree = self._getTree(change.side); | ||
var node = tree.find({price: price}); | ||
assert(node); | ||
var nodeOrder = node.orders[node.orders.indexOf(order)]; | ||
assert.equal(order.size, change.old_size); | ||
nodeOrder.size = size; | ||
self._ordersByID[nodeOrder.id] = nodeOrder; | ||
}; | ||
}); | ||
module.exports = exports = OrderBook; | ||
module.exports = exports = Orderbook; |
{ | ||
"name": "coinbase-exchange", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"author": "Coinbase", | ||
@@ -9,3 +9,6 @@ "bugs": "https://github.com/coinbase/coinbase-exchange-node/issues", | ||
"name": "Peter Downs", | ||
"url": "http://peterdowns.com" } | ||
"url": "http://peterdowns.com" }, | ||
{ "email": "maksimus16@gmail.com", | ||
"name": "Maksim Stepanenko", | ||
"url": "http://maksim.ms" } | ||
], | ||
@@ -17,6 +20,10 @@ "dependencies": { | ||
"request": "2.51.0", | ||
"ws": "0.7.0" | ||
"ws": "0.7.0", | ||
"num": "0.2.1", | ||
"bintrees": "1.0.0" | ||
}, | ||
"description": "Client for the Coinbase Exchange API", | ||
"devDependencies": {}, | ||
"devDependencies": { | ||
"mocha": "1.20.1" | ||
}, | ||
"directories": { | ||
@@ -41,3 +48,5 @@ "lib": "./lib" | ||
}, | ||
"scripts": {} | ||
"scripts": { | ||
"test": "mocha --ui qunit --bail --reporter list tests/*.js" | ||
} | ||
} |
116
README.md
@@ -70,5 +70,5 @@ # Coinbase Exchange | ||
// Get the order book at the default level of detail. | ||
publicClient.getProductOrderBook('BTC-USD', callback); | ||
publicClient.getProductOrderBook(callback); | ||
// Get the order book at a specific level of detail. | ||
publicClient.getProductOrderBook('BTC-USD', 3, callback); | ||
publicClient.getProductOrderBook({'level': 3}, callback); | ||
``` | ||
@@ -78,3 +78,3 @@ | ||
```javascript | ||
publicClient.getProductTicker('BTC-USD', callback); | ||
publicClient.getProductTicker(callback); | ||
``` | ||
@@ -84,3 +84,5 @@ | ||
```javascript | ||
publicClient.getProductTrades('BTC-USD', callback); | ||
publicClient.getProductTrades(callback); | ||
// To make paginated requests, include page parameters | ||
publicClient.getProductTrades({'after': 1000}, callback); | ||
``` | ||
@@ -90,3 +92,5 @@ | ||
```javascript | ||
publicClient.getProductHistoricRates('BTC-USD', callback); | ||
publicClient.getProductHistoricRates(callback); | ||
// To include extra parameters: | ||
publicClient.getProductHistoricRates({'granularity': 3000}, callback); | ||
``` | ||
@@ -96,3 +100,3 @@ | ||
```javascript | ||
publicClient.getProduct24HrStats('BTC-USD', callback); | ||
publicClient.getProduct24HrStats(callback); | ||
``` | ||
@@ -152,2 +156,4 @@ | ||
authedClient.getAccountHistory(accountID, callback); | ||
// For pagination, you can include extra page arguments | ||
authedClient.getAccountHistory(accountID, {'before': 3000}, callback); | ||
``` | ||
@@ -159,2 +165,4 @@ | ||
authedClient.getAccountHolds(accountID, callback); | ||
// For pagination, you can include extra page arguments | ||
authedClient.getAccountHolds(accountID, {'before': 3000}, callback); | ||
``` | ||
@@ -190,2 +198,4 @@ | ||
authedClient.getOrders(callback); | ||
// For pagination, you can include extra page arguments | ||
authedClient.getOrders({'after': 3000}, callback); | ||
``` | ||
@@ -202,2 +212,4 @@ | ||
authedClient.getFills(callback); | ||
// For pagination, you can include extra page arguments | ||
authedClient.getFills({'before': 3000}, callback); | ||
``` | ||
@@ -234,77 +246,37 @@ | ||
### The Order Book | ||
The `OrderBook` is a local mirror of the Coinbase Exchange's order book, synced | ||
via WebSockets. | ||
##### Setup | ||
### Websocket client | ||
The `WebsocketClient` allows you to connect and listen to the | ||
[exchange websocket messages](https://docs.exchange.coinbase.com/#messages). | ||
```javascript | ||
var CoinbaseExchange = require('coinbase-exchange'); | ||
var orderBook = new CoinbaseExchange.OrderBook(); | ||
var websocket = new CoinbaseExchange.WebsocketClient(); | ||
websocket.on('message', function(data) { console.log(data); }); | ||
``` | ||
The following events can be emitted from the `WebsocketClient`: | ||
* `open` | ||
* `message` | ||
* `close` | ||
##### Listening to Events | ||
The order book is a type of | ||
[`EventEmitter`](http://nodejs.org/api/events.html#events_events). For the | ||
following events, the data emitted is always in the same form as the messages | ||
received over WebSocket – you can [learn more about those message types | ||
here](https://docs.exchange.coinbase.com/#messages). | ||
* [`"received"`](https://docs.exchange.coinbase.com/#received) | ||
* [`"open"`](https://docs.exchange.coinbase.com/#open) | ||
* [`"done"`](https://docs.exchange.coinbase.com/#done) | ||
* [`"match"`](https://docs.exchange.coinbase.com/#match) | ||
* [`"error"`](https://docs.exchange.coinbase.com/#match) | ||
These events are emitted immediately after the OrderBook has been updated to | ||
include the message's contents. So by the time your code is notified, the book | ||
will already reflect the changes described by the message. | ||
*Example: listening to order matches:* | ||
### Orderbook | ||
`Orderbook` is a data structure that can be used to store a local copy of the orderbook. | ||
```javascript | ||
orderBook.on('match', function(message) { | ||
console.log("Order", | ||
message.maker_order_id, | ||
"matched with order", | ||
message.taker_order_id); | ||
console.log(message.size, "BTC @", message.price, "USD"); | ||
}); | ||
var CoinbaseExchange = require('coinbase-exchange'); | ||
var orderbook = new CoinbaseExchange.Orderbook(); | ||
``` | ||
The orderbook has the following methods: | ||
* `state(book)` | ||
* `get(orderId)` | ||
* `add(order)` | ||
* `remove(orderId)` | ||
* `match(match)` | ||
* `change(change)` | ||
There are other events to which you can listen: | ||
### Orderbook Sync | ||
`OrderbookSync` creates a local mirror of the orderbook on Coinbase Exchange using | ||
`Orderbook` and `WebsocketClient` as described [here](https://docs.exchange.coinbase.com/#real-time-order-book). | ||
* `"ignored"`: Emitted as part of the order book syncing process, once for | ||
every out-of-date message that is ignored. The data is the original message | ||
sent over the websocket, one of the types listed above. | ||
* `"unknown"`: Emitted when a message is received with a type that the | ||
OrderBook doesn't know how to handle. The data is the original message sent | ||
over the websocket. | ||
* `"statechange"`: Emitted any time the order book instance changes state. A | ||
hash with two keys, `"old"` mapping to the previous state, `"new"` mapping to | ||
the new, current state of the order book. | ||
*Example: listening for all errors* | ||
```javascript | ||
orderBook.on('statechange', function(state) { | ||
if (state.new === orderBook.STATES.error) { | ||
console.log("Was", state.old, "now in state", state.new); | ||
// clean up things here | ||
} | ||
}); | ||
var CoinbaseExchange = require('coinbase-exchange'); | ||
var orderbookSync = new CoinbaseExchange.OrderbookSync(); | ||
console.log(orderbookSync.book.state()); | ||
``` | ||
#### States of the order book | ||
An instance of the order book can be in the following states: | ||
* `"closed"`: the WebSocket connection has been closed and no new messages are | ||
being processed. | ||
* `"open"`: the WebSocket connection is open, but no new messages are being | ||
processed. | ||
* `"syncing"`: the WebSocket connection is open, new messages are being queued, | ||
and the order book snapshot is being loaded. | ||
* `"processing"`: the WebSocket connection is open, the order book is in sync, | ||
and new messages are being processed as they're received. | ||
* `"error"`: an error has occurred and an exception has been thrown. The | ||
WebSocket connection is closed and no new messages are being received or | ||
processed. | ||
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
32095
11
718
7
1
273
2
+ Addedbintrees@1.0.0
+ Addednum@0.2.1
+ Addedbintrees@1.0.0(transitive)
+ Addedint@0.1.1(transitive)
+ Addednum@0.2.1(transitive)