UNPKG

tlab-trading-toolkit

Version:

A trading toolkit for building advanced trading bots on the GDAX platform

208 lines (157 loc) 9.12 kB
--- title: Introduction to the Feed streams keywords: GTT, feed data, exchange, GDAX last_updated: August 2, 2017 tags: [getting_started, tutorials] summary: "Connecting to a data feed source" sidebar: gtt_sidebar permalink: gtt_tutorials_feed.html folder: gtt --- # Introduction In the GTT, all feed data is consolidated and standardized and streamed as a set of [Messages](apiref/modules/_src_core_messages_.html). Regardless of whether the underlying exchange offers a Websocket feed, REST API, or both, the data is collected and reformulated into a consistent data set and behaviour. In this chapter we're simply going to connect to the GDAX data feed and tally up the types of messages that are received. # GTT Messages All messages passed from one component to another in the GTT are derived from `StreamMessage`. It is defined as export interface StreamMessage { type: string, time: Date, } ## LiveOrderbook messages There are several message types derived from this, including `OrderbookMessage` which is the base class for messages that represent realtime data: export interface OrderbookMessage extends StreamMessage { sequence: number; sourceSequence?: number; productId: string; side: string; } Then there messages that are used for maintaining a live orderbook state. There are two broad classes of order messages depending on whether we're dealing with fine-grained (Level 3, or order-level) messaging or aggregated, or level 2-based messages. Where possible, the GTT will use level 2 messages for live orderbooks, but level 3 messages are also supported for exchanges that don't offer the former. export interface BaseOrderMessage extends OrderbookMessage { orderId: string; price: string; } export interface LevelMessage extends OrderbookMessage { price: string; size: string; count: number; } Notice that the `BaseOrderMessage` carries an `orderId`, while the `LevelMessage` does not. Each message has several sub-types representing new orders, cancelled orders, and trades. See the [reference documentation](apiref/modules/_src_core_messages_.html) for specifics. # Diving In The data feed is the starting point for nearly everything you will do in the GTT. Once a handle to a feed stream is obtained, you can treat it as you would any other node.js stream, including pipe it to other streams, attach and listeners to it. In this section we'll show you how easy it is to connect to a feed source and process the messages it emits. ## The basics Most GTT components take a logger object in their configuration, so let's create a default logger that writes to the console. There's a convenience `ConsoleLoggerFactory` method that will create one for you, so let's use that: import * as GTT from 'gdax-trading-toolkit'; const logger = GTT.utils.ConsoleLoggerFactory(); The easiest way to grab hold of a feed stream is to use the utility factory function `FeedFactory`. There will be one of these functions for each exchange supported on the GTT. For GDAX feeds, all we need is a logger instance and an array of products to subscribe to. Under the hood, a connection to the REST API and Websocket feeds will be made and the data will be coerced into GTT messages. `FeedFactory` returns a Promise, so we must wait for it to be resolved before we can start working with data. The GTT convention is that once a product has been subscribed to, the first message received is a `SnapshotMessage` giving the current state of the order book. Subsequent messages reflect changes to the orderbook. Other messages that you might expect include `TickerMessage` and `TradeMessage`. If you supply authentication credentials to the feed factory, you might also receive `MyOrderPlacedMessage` emitted when _my_ order has been placed, `TradeExecutedMessage`, emitted when one of _my_ orders is matched, `TradeFinalizedMessage`, for when my order is completely filled, or cancelled. const products: string[] = ['BTC-USD', 'ETH-USD', 'LTC-USD']; GTT.Factories.GDAX.FeedFactory(logger, products).then((feed: GDAXFeed) => { // Do stuff with the feed }); {% include tip.html content="For brevity, `import` statements will be omitted from tutorial source snippets. If you're using a TypeScript-friendly IDE, type definition imports should be added automatically. If you're using standard Javascript, the GTT is usually the only import you'll need." %} ## Doing something (not so) useful Let's do something with the data. How about we tally up each message, sorting by type, and separating by product, and then periodically print the tallies? This is pretty straightforward. After setting up the tally variables, we subscribe to the `data` event on the feed (it's just a standard node.js stream and EventEmitter) and process the messages as they arrive. Node streams emit objects of type `any`, but we know these are GTT messages, so we will typecast messages as an [`OrderbookMessage`](apiref/interfaces/_src_core_messages_.orderbookmessage.html) which carries a `productId` field. Most messages will be, but we check for the presence of that field first and log it as an 'other' message if not. const logger = GTT.utils.ConsoleLoggerFactory(); const products: string[] = ['BTC-USD', 'ETH-USD', 'LTC-USD']; const tallies: any = {}; products.forEach((product: string) => { tallies[product] = {}; }); let count = 0; GTT.Factories.GDAX.FeedFactory(logger, products).then((feed: GDAXFeed) => { feed.on('data', (msg: OrderbookMessage) => { count++; if (!(msg as any).productId) { tallies.other += 1; } else { const tally = tallies[msg.productId]; if (!tally[msg.type]) { tally[msg.type] = 0; } tally[msg.type] += 1; } if (count % 1000 === 0) { printTallies(); } }); }).catch((err: Error) => { logger.log('error', err.message); process.exit(1); }); The `printTallies` function is just a bit of string-fu: function printTallies() { console.log(`${count} messages received`); for (let p in tallies) { let types: string[] = Object.keys(tallies[p]).sort(); let tally: string = types.map(t => `${t}: ${tallies[p][t]}`).join('\t'); console.log(`${p}: ${tally}`); } } ## Run the script If you've skipped ahead, or want to see the finished product, the full script resides at `/tutorials/t001_feeds.ts`. Execute it with $ ts-node tutorials/t001_feeds.ts {% include tip.html content="`ts-node` is a useful utility that compiles and runs Typescript code directly. Install it with `npm -g ts-node typescript`" %} Typical output for the script will look like: 2017-08-02T14:31:58.648Z - info: Creating new GDAX Websocket connection to wss://ws-feed.gdax.com 2017-08-02T14:32:00.203Z - debug: Connection to wss://ws-feed.gdax.com has been established. 1000 messages received BTC-USD: newOrder: 111 orderDone: 112 snapshot: 1 trade: 1 unknown: 111 ETH-USD: newOrder: 168 orderDone: 174 snapshot: 1 trade: 7 unknown: 168 LTC-USD: newOrder: 42 orderDone: 56 snapshot: 1 trade: 3 unknown: 44 2000 messages received BTC-USD: newOrder: 250 orderDone: 254 snapshot: 1 trade: 1 unknown: 250 ETH-USD: newOrder: 302 orderDone: 303 snapshot: 1 trade: 8 unknown: 303 LTC-USD: newOrder: 101 orderDone: 114 snapshot: 1 trade: 8 unknown: 103 If you're familiar with the GDAX WS API, you'll see that the message types have changed. `done` and `open` messages have been converted to `orderDone` and `newOrder` respectively. Other messages, such as `received` don't have a use in the GTT and are passed on as `unknown` messages. # Things to try 1. Do a similar tally using stats from Bitfinex. All you need to do is swap out the corresponding `FeedFactory` function. You can still use GDAX product names in the factory method call (they automatically get mapped to Bitfinex names if they are different). 1. Dig a bit deeper and see what other data is provided in the messages. 1. Explore the underlying `getFeed` and `getSubscribedFeeds` functions that offer greater flexibility in how the feed streams are configured. 1. Provide your authentication details in a [GDAXAuthConfig](apiref/interfaces/_src_exchanges_gdax_gdaxexchangeapi_.gdaxauthconfig.html) object and check for additional messages in the authenticated feed if you place orders or rtades on the exchange. # See also * [GDAX Exchange API](apiref/classes/_src_exchanges_gdax_gdaxexchangeapi_.gdaxexchangeapi.html) * [GDAX Feed](apiref/modules/_src_exchanges_gdax_gdaxfeed_.html) * [GDAX Feed factories](apiref/modules/_src_factories_gdaxfactories_.html) * [Bitfinex Feed factories](apiref/modules/_src_factories_bitfinexfactories_.html)