In this tutorial, you will use the CBPTT's common API framework to build a simple ticker tape console script

Introduction

One of the core aims of the CBPTT is to make building trading bots easy. One of the ways we do this is by hiding away all the specifics of the various crypto-exchanges’ APIs and make everything accessible via a common, unified framework.

Whether you want a ticker price, to place an order, or get your account balances, you’ll use the same CBPTT command regardless of which exchange you’re targeting.

In this tutorial, we will build a simple ticker-tape console program using the Coinbase Pro Trading Toolkit’s common API framework.

Getting started

Every exchange supported by the CBPTT has its REST API abstracted behind two interfaces: PublicExchangeAPI and AuthenticatedExchangeAPI.

The first interface provides a common way of polling for publicly accessible information on the exchanges, such as the ticker price, and order books. AuthenticatedExchangeAPI provides access to calls requiring API keys, such as wallet balances or placing trades.

So let’s try something. Assume you have two exchange classes configured (we’ll get to that in a moment) as follows:

let exchanges = [ coinbaseProExchangeAPI, bitfinexExchangeAPI ];

You can get the trading pairs supported by each exchange as follows:

exchanges.forEach((exchange: PublicExchangeAPI) => {
    exchange.loadProducts().then(products => {
        logger.log('info', 'Products for ' + exchange.owner, products.map(p => p.id).join(' '));

The output of this looks like

2017-08-16T18:57:03.800Z - info: Products for Coinbase Pro LTC-EUR LTC-BTC BTC-GBP BTC-EUR ETH-EUR ETH-BTC LTC-USD BTC-USD ETH-USD
2017-08-16T18:57:04.394Z - info: Products for Bitfinex BTC-USD LTC-USD LTC-BTC ETH-USD ETH-BTC etcbtc etcusd rrtusd rrtbtc zecusd zecbtc xmrusd xmrbtc dshusd dshbtc bccbtc bcubtc bccusd bcuusd xrpusd xrpbtc iotusd iotbtc ioteth eosusd eosbtc eoseth sanusd sanbtc saneth omgusd omgbtc omgeth bchusd bchbtc bcheth

Configuration

For convenience, we’ll provide a common Logger object to all our API objects. A simple logger – one that simply writes messages to the console – can be created using the ConsoleLoggerFactory factory method (though it’s easy to create your own to write to file, RDBMS, Kibana etc., just make sure your implementation implements the Logger interface).

const logger = CBPTT.utils.ConsoleLoggerFactory({ level: 'info' });

To set up the exchange objects, we need to provide a config object for each exchange. The exact configuration can vary from exchange to exchange, but typically, you need to give authorization credentials and a product ID. Note that the CBPTT always accepts a Coinbase Pro-standard product name (e.g. BTC-USD) and will map to the target exchange name behind the scenes).

Since we’re configuring the Bitfinex and Coinbase Pro API interfaces, we’ll supply a BitfinexConfig and CoinbaseProConfig object to the corresponding public API interface implementation.

const bitfinexConfig: BitfinexConfig = {
    coinbaseProProduct: 'BTC-USD',
    logger: logger,
    auth: {
        key: process.env.BITFINEX_KEY,
        secret: process.env.BITFINEX_SECRET
    }
};

const coinbaseProConfig: CoinbaseProConfig = {
    apiURL: process.env.COINBASE_PRO_API_URL || 'https://api.pro.coinbase.com',
    product: 'BTC-USD',
    auth: {
        key: process.env.COINBASE_PRO_KEY,
        secret: process.env.COINBASE_PRO_SECRET,
        passphrase: process.env.COINBASE_PRO_PASSPHRASE
    }
};

const bitfinex = new BitfinexExchangeAPI(bitfinexConfig);
const coinbasePro = new CoinbaseProExchangeAPI(coinbaseProConfig);

Strictly speaking, you can leave the auth fields out, since we’re not making any authenticated calls in this tutorial, but it’s included in the config object as a demonstration of how one would keep your API credentials from leaking to anyone seeing your code.

A ticker tape parade in your console

Since we’re going to make calls against the PublicExchangeAPI interface, we can forget about the different implementations of each exchange and just make some calls.

The essential strategy in our ticker app is, starting with an array of PublicExchangeAPI instances, make a call to loadTicker, collect the results, and print them out.

All the real work is done in this two-line function:

function getTickers(exchanges: PublicExchangeAPI[], product: string): Promise<Ticker[]> {
    const promises = exchanges.map((ex: PublicExchangeAPI) => ex.loadTicker(product));
    return Promise.all(promises);
}

Basically, we use the Array.map method to call loadTicker for each exchange. Each call returns a promise for a ticker which will resolve to the actual Ticker object at some point in the future (once the REST API call returns with its data, in fact). So we use the Promise.all function to wait for all the results, collect them up and return them as an array of Ticker objects.

The rest is just formatting:

function getAndPrintTickers(exchanges: PublicExchangeAPI[], product: string) {
    return getTickers(publicExchanges, product).then((tickers: Ticker[]) => {
        const quoteCurrency = tickers[0].productId.split('-')[1];
        console.log(`${new Date().toTimeString()}\t| Price ${quoteCurrency}  |   Best Bid |   Best Ask`);
        for (let i = 0; i < exchanges.length; i++) {
            printTicker(exchanges[i], tickers[i])
        }
        console.log();
        return Promise.resolve();
    });
}

Run getAndPrintTickers inside an interval timer and away we go:

setInterval(() => {
    getAndPrintTickers(publicExchanges, 'BTC-USD').then(() => {
        return getAndPrintTickers(publicExchanges, 'ETH-USD');
    }).catch((err) => {
        logger.log('error', err.message, err);
    });
}, 5000);

There are a few formatting details left out of the code snippets above, but these are fairly simple and left as an exercise to the reader. The full working example that produced the output below is available as tutorials/t003_tickertape.ts.

Pulling this all together and running it produces something like:

22:21:25 GMT+0200 (SAST)        | Price USD  |   Best Bid |   Best Ask
BTC-USD (Coinbase Pro)                  |    4310.02 |    4310.01 |    4310.02
BTC-USD (Bitfinex)              |    4324.10 |    4324.10 |    4324.20

22:21:26 GMT+0200 (SAST)        | Price USD  |   Best Bid |   Best Ask
ETH-USD (Coinbase Pro)                  |     297.99 |     297.98 |     297.99
ETH-USD (Bitfinex)              |     299.03 |     298.64 |     299.00

22:21:30 GMT+0200 (SAST)        | Price USD  |   Best Bid |   Best Ask
BTC-USD (Coinbase Pro)                  |    4310.02 |    4310.01 |    4310.02
BTC-USD (Bitfinex)              |    4324.10 |    4324.10 |    4324.20

22:21:31 GMT+0200 (SAST)        | Price USD  |   Best Bid |   Best Ask
ETH-USD (Coinbase Pro)                  |     297.99 |     297.98 |     297.99
ETH-USD (Bitfinex)              |     299.03 |     298.64 |     299.00

A final comment

This tutorial produced crypto prices by polling the REST API endpoint of a few exchanges. If you’ve done the tutorial on live order books, you may be thinking that there must be a better way than having your information being up to 5 seconds out of date, and you’d be right!

A much better version of this little app would be to respond to the ticker event from an array of message feeds. That would make your ticker data realtime, but the implementation is left as an exercise :)