diff --git a/README.md b/README.md index 03e5834..5f0f8f7 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Currently, Intrinio offers realtime data from the following providers: * IEX - [Homepage](https://iextrading.com/) * QUODD - [Homepage](http://home.quodd.com/) +* Cryptoquote - [Homepage](https://www.cryptoquote.io/) Each has distinct price channels and quote formats, but a very similar API. @@ -169,6 +170,85 @@ NOTE: Messages from QUOOD reflect _changes_ in market data. Not all fields will * **size** - the size of the `last` trade, or total volume of orders at the top-of-book `bid` or `ask` price * **price** - the price in USD +### Cryptoquote + +#### Level 1 - Price Update + +NOTE: Null values for some fields denote no change from previous value. + +```javascript +{ last_updated: "2018-10-29 23:08:02.277Z", + pair_name: "BTCUSD", + pair_code: "btcusd", + exchange_name: "Binance", + exchange_code: "binance", + bid: 6326, + bid_size: 6.51933000, + ask: 6326.97, + ask_size: 6.12643000, + change: -151.6899999999996, + change_percent: -2.340895061728389, + volume: 13777.232772, + open: 6480, + high: 6505.01, + low: 6315, + last_trade_time: "2018-10-29 23:08:01.834Z", + last_trade_side: null, + last_trade_price: 6326.97000000, + last_trade_size: 0.00001200, + type: "level_1" } +``` + +* **last_updated** - a UTC timestamp of when the data was last updated +* **pair_name** - the name of the currency pair +* **pair_code** - the code of the currency pair +* **exchange_name** - the name of the exchange +* **exchange_code** - the code of the exchange +* **ask** - the ask for the currency pair on the exchange +* **ask_size** - the size of the ask for the currency pair on the exchange +* **bid** - the bid for the currency pair on the exchange +* **bid_size** - the size of the bid for the currency pair on the exchange +* **change** - the notional change in price since the last ticker +* **change_percent** - the percent change in price since the last ticker +* **volume** - the volume of the currency pair on the exchange +* **open** - the opening price of the currency pair on the exchange +* **high** - the highest price of the currency pair on the exchange +* **low** - the lowest price of the currency pair on the exchange +* **last_trade_time** - a UTC timestamp of the last trade for the currency pair on the exchange +* **last_trade_side** - the side of the last trade + * **`buy`** - this is an update to the buy side of the book + * **`sell`** - this is an update to the sell side of the book +* **last_trade_price** - the price of the last trade for the currency pair on the exchange +* **last_trade_size** - the size of the last trade for the currency pair on the exchange +* **type** - the type of message this is + * **`level_1`** - a messages that denotes a change to the last traded price or top-of-the-book bid or ask + * **`level_2`** - a message that denotes a change to an order book + +#### Level 2 - Book Update +```javascript +{ pair_name: "BTCUSD", + pair_code: "btcusd", + exchange_name: "Gemini", + exchange_code: "gemini", + side: "buy", + price: 6337.4, + size: 0.3, + type: "level_2" } +``` + +* **pair_name** - the name of the currency pair +* **pair_code** - the code of the currency pair +* **exchange_name** - the name of the exchange +* **exchange_code** - the code of the exchange +* **side** - the side of the book this update is for + * **`buy`** - this is an update to the buy side of the book + * **`sell`** - this is an update to the sell side of the book +* **price** - the price of this book entry +* **size** - the size of this book entry +* **type** - the type of message this is + * **`level_1`** - a messages that denotes a change to the last traded price or top-of-the-book bid or ask + * **`level_2`** - a message that denotes a change to an order book + ## Channels ### QUODD @@ -185,6 +265,20 @@ To receive price quotes from the Intrinio Real-Time API, you need to instruct th Special access is required for both lobby channels. [Contact us](mailto:sales@intrinio.com) for more information. +### Cryptoquote + +To receive price quotes from Cryptoquote, you need to instruct the client to "join" a channel. A channel can be + +* `crypto:market_level_1:{pair_code}` - the Level 1 Market channel where all Level 1 price updates for the provided currency pair in all exchanges are posted (i.e. `crypto:pair:market_level_1:btcusd`) +* `crypto:exchange_level_1:{exchange_code}:{pair_code}` - the Level 1 Market channel where all Level 1 price updates for the provided currency pair and exchange are posted +* `crypto:exchange_level_2:{exchange_code}:{pair_code}` - the Level 2 Market channel where all Level 2 book updates for the provided currency pair and exchange are posted +* `crypto:firehose` - the Firehose channel where all message types for all currency pairs are posted (special access required) + +The Intrinio REST API provides a listing of pairs, exchanges, and their corresponding codes: + +* [Crypto Currency Pairs](https://intrinio.com/documentation/download#crypto_currency_pairs) +* [Crypto Exchanges](https://intrinio.com/documentation/download#crypto_exchanges) + ## API Keys You will receive your Intrinio API Key after [creating an account](https://intrinio.com/signup). You will need a subscription to the [IEX Real-Time Stock Prices](https://intrinio.com/data/realtime-stock-prices) data feed as well. @@ -193,7 +287,7 @@ You will receive your Intrinio API Key after [creating an account](https://intri ### Methods `constructor(options)` - Creates a new instance of the IntrinioRealtime client. -* **Parameter** `options`: An object with properties `api_key` and `provider`, corresponding to your Intrinio API Key and a provider code ("iex" or "quodd"). +* **Parameter** `options`: An object with properties `api_key` and `provider`, corresponding to your Intrinio API Key and a provider code ("iex", "quodd", or "cryptoquote"). ```javascript var ir = new IntrinioRealtime({ api_key: "INTRINIO_API_KEY", diff --git a/index.js b/index.js index 92dd93c..c743343 100644 --- a/index.js +++ b/index.js @@ -49,8 +49,8 @@ class IntrinioRealtime extends EventEmitter { } } - if (!options.provider || (options.provider != "iex" && options.provider != "quodd")) { - this._throw("Need a valid provider: iex or quodd") + if (!options.provider || (options.provider != "iex" && options.provider != "quodd" && options.provider != "cryptoquote")) { + this._throw("Need a valid provider: iex, quodd, or cryptoquote") } // Establish connection @@ -110,7 +110,7 @@ class IntrinioRealtime extends EventEmitter { this.ready = true this.emit('connect') this._stopSelfHeal() - if (this.options.provider == "iex") { + if (this.options.provider == "iex" || this.options.provider == "cryptoquote") { this._refreshChannels() } }, @@ -140,6 +140,12 @@ class IntrinioRealtime extends EventEmitter { path: "/token?type=QUODD" } } + else if (this.options.provider == "cryptoquote") { + auth_url = { + host: "crypto.intrinio.com", + path: "/auth" + } + } if (this.options.api_key) { auth_url = this._makeAPIAuthUrl(auth_url) @@ -228,6 +234,9 @@ class IntrinioRealtime extends EventEmitter { else if (this.options.provider == "quodd") { return 'wss://www5.quodd.com/websocket/webStreamer/intrinio/' + encodeURIComponent(this.token) } + else if (this.options.provider == "cryptoquote") { + return 'wss://crypto.intrinio.com/socket/websocket?vsn=1.0.0&token=' + encodeURIComponent(this.token) + } } _refreshWebsocket() { @@ -284,6 +293,11 @@ class IntrinioRealtime extends EventEmitter { quote = message.data } } + else if (this.options.provider == "cryptoquote") { + if (message.event === 'book_update' || message.event === 'ticker' || message.event === 'trade') { + quote = message.payload + } + } if (quote) { if (typeof this.quote_callback === 'function') { @@ -357,6 +371,9 @@ class IntrinioRealtime extends EventEmitter { else if (this.options.provider == "quodd") { return {event: 'heartbeat', data: {action: 'heartbeat', ticker: Date.now()}} } + else if (this.options.provider == "cryptoquote") { + return {topic: 'phoenix', event: 'heartbeat', payload: {}, ref: null} + } } _heartbeat() { @@ -390,7 +407,7 @@ class IntrinioRealtime extends EventEmitter { }) channels.forEach(channel => { - if (channel.length == 0 || channel.length > 20) { + if (channel.length == 0) { this._throw("Invalid channel provided") } }) @@ -416,6 +433,14 @@ class IntrinioRealtime extends EventEmitter { } } } + else if (this.options.provider == "cryptoquote") { + return { + topic: channel, + event: 'phx_join', + payload: {}, + ref: null + } + } } _makeLeaveMessage(channel) { @@ -436,6 +461,14 @@ class IntrinioRealtime extends EventEmitter { } } } + else if (this.options.provider == "cryptoquote") { + return { + topic: channel, + event: 'phx_leave', + payload: {}, + ref: null + } + } } _parseIexTopic(channel) {