How To Get Real Time Crypto Market Data

Using the Kucoin Exchange API #HowToBUIDL (7/n)

Dan Emmons
Coinmonks
Published in
12 min readJul 12, 2018

--

In the previous article, we focused on obtaining approximate price data for just about any cryptocurrency or ERC20 token listed on any exchange. Some people in crypto don’t necessarily need the most up-to-date data, so the article on using the CoinMarketCap API might suffice. It provides “good enough” data to give people some sort of idea on the overall market. For those that are trying to actually build businesses that exchange tokens for goods and services, run stores that sell items for crypto, or active traders, CMC is simply not good enough.

The longer you stay in crypto, at some point you’re going to need more accurate data. To get this information, you need a market data service provider, or even more directly, an exchange itself.

For this set of code examples, we’ll look at #BUIDLing a command line tool or custom web app for requesting current trade data from the Kucoin API. You might even be so bold as to create a custom AirSwap Team maker, so that you can integrate with the #decentralized market tool that allows you to securely participate in atomic swaps. “AirSwap is not just a marketplace, but a secure consumer token trading network that can only exist with a truly decentralized peer-to-peer protocol. When you buy and sell on AirSwap, you’re doing so with others around the world, wallet-to-wallet, between a buyer, a seller, and a smart contract. No friction, no custody, no fees.

Discover and review best Crypto api providers

Market makers provide liquidity by suggesting exchange rates for one quantity of token versus another, and takers accept the bids or offers, creating a sort of atomic token swap that would not really be possible without the current centralized options of performing two different trades versus BTC. Now, if you wanted to create your own Maker for AirSwap, you better have some information about current market prices, so we’ll look into how you might go about getting that info with Kucoin.

AirSwap was built from the ground up with market makers in mind. We’ve published Swap Protocol clients for both JavaScript and Python, with quick start guides coming soon. — Don Mosites

Kucoin is a Chinese digital currency exchange based in Hong Kong. I’m choosing it for this example because it is often easier or more affordable to list a coin here than something like Binance, and as a result, contains some of the coins I’ll be using for my examples. In addition, it seems to be one of the more reliable exchanges, hasn’t suffered any security breaches to my knowledge. As an added bonus, Kucoin offers profit-sharing of fees with their KCS token holders, so there are user incentives to hold tokens as passive dividend income. That’s a bit of a rarity among centralized crypto exchanges. Let’s get started.

Let’s install the open source javascript RESTful API wrapper for Kucoin.
I’m using a forked version of the satoshinaire/kucoin-api.
The full API documentation can be read here.
Special thanks to Satoshinaire for his API wrapper!

npm install kucoin-api

Now that it’s available in our project, obtain a reference to the library, and instantiate an instance of the API object.

Kucoin = require("kucoin-api");
kucoin = new Kucoin(); // read only, not passing in API (key,secret)

Exploring the API

Let’s obtain a list of all available coins with the async getCoins function.

kucoin.getCoins();
coins = _;

Now let’s inspect it. We’ll see the same 177 coins in the list as on the site.

coins.data.length; //177

locicoin = coins.data.filter(c=> c.coin==’LOCI’)[0];

{ withdrawMinFee: 4, coinType: 'ERC20', withdrawMinAmount: 10, withdrawRemark: null, orgAddress: null, txUrl: 'https://etherscan.io/tx/{txId}', userAddressName: null, withdrawFeeRate: 0.001, confirmationCount: 12, infoUrl: null, enable: true, name: 'LOCIcoin', tradePrecision: 4, depositRemark: null, enableWithdraw: true, enableDeposit: true, coin: 'LOCI' }

That JSON representation of LOCI tells us a few interesting things. We can determine it is an ERC20 coinType, you must have a minimum of 10 in order to withdraw, there is a 4 coin withrawMinFee for any transfers out of the exchange, and any transfers into the exchange must wait 12 confirmationCount in order for any balances to be counted.

kucoin.getLanguages();{ success: true, code: 'OK', msg: 'Operation succeeded.', timestamp: 1531253711223, data: [ [ 'en_US', 'English', true ], [ 'ru_RU', 'русский', true ], [ 'ko_KR', '한국어', true ], [ 'pt_PT', 'Portugues', true ], [ 'zh_CN', '中文简体', true ], [ 'nl_NL', 'Nederlands', true ], [ 'zh_HK', '中文繁体', true ], [ 'de_DE', 'Deutsch', true ], [ 'fr_FR', 'Français', true ], [ 'es_ES', 'Español', true ], [ 'vi_VN', 'Tiếng Việt', true ], [ 'tr_TR', 'Türkçe', true ] ] }languages = _;

Hmm. Ok. Kucoin supports (12)languages.length on the site. Great for internationalization. There must be a way to change the user’s preferred language through the API. I took some Spanish es_ESback in my younger years, but for now I’ll leave that alone. US English en_USwill do.

While all that is interesting and all, it wasn’t what we came here for. Sometimes simply exploring an API and playing around with it in the code is necessary just to gain your bearings and determine what you can and cannot do with a chunk of code. Junior developers often don’t understand that “software development” isn’t just banging out precise code all day. It involves exploration, planning, navigating code, certainly writing, but a lot more reading of code you didn’t write, and often those that did are no longer with the company. That’s all part of the experience you build up by checking out other projects and learning how to navigate new-to-you codebases.

Exchange Rates for Papa Bitcoin

There’s a function called getExchangeRates. That sounds like something we might really be interested in.

kucoin.getExchangeRates();
xrates = _;

Now xrates.data.currencies contains an array list of [name,symbol] pairs.

[[ 'USD', '$' ], [ 'EUR', '€' ], [ 'AUD', '$' ], ... ]

and xrates.data.rates contains a property for BTC that is a mapping of those currencies to the value in that currency:

BTC : { /*...*/ EUR: 5442.33, DKK: 40570.1, USD: 6380.3, /*...*/ }

Now, these exchange rates are moving around constantly, so I can’t really grab an exact screenshot from when I pulled the rates, but if I look at what Bitstamp is reporting for the BTC/USD rate, it’s ~6380 there as well. Since Kucoin doesn’t really have USD fiat trading pairs, the closest you’d be able to get is comparing BTC/USDT, which is the pair with the “stable coin” called USDT or Tether.

bitcoinwisdom.com/markets/bitstamp/btcusd

So, if you had a ticker that was priced in BTC, like LOCI/BTC, you could take the current price times the xrates.data.rates.BTC.USD and obtain the US Dollar equivalent price.

Trading Pairs and Price Data

So how would we go about finding the trading symbol pairs that are available through the Kucoin Exchange API? Well, there’s a getTradingSymbols function. This goes one step further than just getCoins. Not every single coin has all BTC, ETH, NEO, USDT, and KCS pairs. Most have just BTC & ETH.

kucoin.getTradingSymbols();
symbols = _;
symbols.data.length; // 353

You’d see this graphically on the Kucoin website on the Markets screen.

https://www.kucoin.com/#/markets

So we can see there are more tradingSymbols pairs than coins, and we’d expect that. From our earlier getCoins example, locicoin is a coin, whereas

locipairs = symbols.data.filter( s=> s.coinType === locicoin.coin );

is a set of all pairs for locicoin.coin (coin property is 'LOCI').

[ 
{ coinType: 'LOCI', trading: true, symbol: 'LOCI-BTC', lastDealPrice: 0.00000517, buy: 0.00000457, sell: 0.00000514, change: -7e-8, coinTypePair: 'BTC', sort: 100, feeRate: 0.001, volValue: 0.09000668, high: 0.00000552, datetime: 1531255308000, vol: 18075.6256, low: 0.00000452, changeRate: -0.0134 },
{ coinType: 'LOCI', trading: true, symbol: 'LOCI-ETH', lastDealPrice: 0.00008, buy: 0.0000675, sell: 0.000081, change: 0.000007, coinTypePair: 'ETH', sort: 0, feeRate: 0.001, volValue: 1.55628934, high: 0.00008, datetime: 1531255308000, vol: 20357.2562, low: 0.0000652, changeRate: 0.0959 }

]

So, we can see that sure enough it trades as LOCI-BTC and LOCI-ETH pairs when we go to the Markets screen and use the filter/search widget for loci.

Note that the price of LOCI-BTC is listed as 0.00000517(BTC) on the user interface. That matches the real time data we retrieved in locibtcpair below.

locibtcpair = locipairs.filter( p=> p.coinTypePair === 'BTC' )[0];
locibtc = locibtcpair.lastDealPrice; // 0.00000517
lociusd = locibtc * xrates.data.rates.BTC.USD; // 6380.30 is BTCUSD

Now we can convert that locibtc to lociusd = $0.032986151 or ~$0.03.
To be sure, if we open the graphical trading interface on Kucoin, we see $0.03.

https://www.kucoin.com/#/trade.pro/LOCI-BTC

Before we proceed further, let’s take a look at a few other properties of locibtcpair and see if there was anything else useful. Sure enough, there was a buy and sell property, which matched the highest bid and ask price from the order book.

locipairs[0].buy is the highest bid, locipairs[0].sell is the lowest offer

The buy indicated a highest bid of 0.00000457 BTC per LOCI and the sell indicated the lowest offer of 0.00000514 BTC per LOCI. We might also call this 457 satoshis (or sats for short) on the bid, and 514 sats on the offer. The difference between the bid and ask is the spread. In lower volume traded coins, this spread can be wide because there’s not enough activity in the books at each price level. This can throw off your pricing if you’re trying to determine how many of a coin you could buy in the market or sell to the market at this particular point in time.

In the future, we can get at the information above little more quickly by using the handy getTicker function, which takes a pair property parameter.

kucoin.getTicker({pair:locibtcpair.symbol}); // {pair:'LOCI-BTC'}

It’s just a slightly more efficient way of getting at specific tickers rather than pulling in all of them at once as in kucoin.getTradingSymbols().

Remember in our CoinMarketCap example, we wanted to determine how many WAX tokens a single XMR token could buy in USD equivalent. Now let’s try that same type of thing with another pair. How about a custom pair like LOCI/WTC? I’d like to know how many LOCI are equivalent to a Waltonchain token.

First some setup to make our lives easier:

const async = require('asyncawait/async');
const await = require('asyncawait/await');
async function get_kucoin_ticker_price_in_btc(pair) {
return new Promise(function(res, rej) {
kucoin.getTicker({pair:pair}).then( function(ticker) {
res(ticker.data.lastDealPrice);
});
});
}

Now we can do some quick calls:

let locibtc = await get_kucoin_ticker_price_in_btc('LOCI-BTC');
let wtcbtc = await get_kucoin_ticker_price_in_btc('WTC-BTC');
// locibtc = 0.00000517 BTC
// wtcbtc = 0.00100298 BTC
let loci_per_wtc = wtcbtc / locibtc; // ~194 LOCI per WTC

What can we do with this information? We can approximate how many dollars equivalent a given WTC is versus LOCI without even converting to USD. That’s very clever. That means we could come up with our own base pairs that you wouldn’t find on just about any sites but your own. Or, you could build a service that takes different sorts of tokens under certain conditions as “compatible tokens” as equivalent methods for swapping payment. You could also build a screener that shows how your set of tokens in your portfolio are faring against other tokens you have on a relative-strength basis, or give you a quick way of instantly seeing how many more of X token you could accumulate versus coin Y, using non-stale data, but instead exchange data from Kucoin.

But wait! We’re not finished.

Remember the end goal is to come up with a custom base pair for a set of tokens that do not commonly get listed against one another, like LOCI-BTC but using the most accurate information we can with a live exchange data source. In the prior example, we took information from the lastDealPrice and came up with an estimate for the exchange rate based off of it. It seems reasonable, right? Well, just because it traded there in the past, it’s still a stale quote, and just because someone else traded a number of tokens for LOCI or WTC versus BTC doesn’t mean that exact trade price will be available again.

We don’t get to trade the markets we wish existed, nor can we trade the past. We can only trade the markets that actually exist in the present. If you wanted to exchange some of your WTC for an equivalent amount of LOCI, you’d actually have to perform two trades in real life: (1) Sell your WTC at the highest available bid and (2) Buy your LOCI for the lowest available offer. You’ll give up a little on the spread on each side in real life, so our custom base pair LOCI-WTCprice estimate should account for that.

Inspect the Order Books

Retrieve all orders currently on the books for the LOCI-BTC trading pair:

kucoin.getOrderBooks({pair:'LOCI-BTC'});
books = _;
{ success: true, code: 'OK', msg: 'Operation succeeded.', timestamp: 1531259489343, data: { SELL: [ [Array], [Array], [Array], [Array], [Array], [Array] ], BUY: [ [Array], [Array], [Array], [Array], [Array], [Array] ], timestamp: 1531259319987 } }

Note that the books.data contains both SELL (offer) and BUY (bid) sides. We can actually specify a type so that we only get data for BUY or SELL, and we can include a limit so as to bring in more rows of data from the response.

kucoin.getOrderBooks({pair:'LOCI-BTC',limit:10,type:'SELL'});
loci_offers = _;
kucoin.getOrderBooks({pair:'WTC-BTC',limit:10,type:'BUY'});
wtc_bids = _;

The wtc_bids.data contains rows of arrays. The elements in each row represent the same data that comes back on the user interface:
[ priceBTC, amountOfCoin, amountOfCoinInBTC ]

The loci_offers.data also contains rows of arrays. The elements in each row represent the same data that comes back on the user interface:
[ priceBTC, amountOfCoin, amountOfCoinInBTC ]

The first element in both loci_offers.data and wtc_bids.data are the same that would have come back from the getTickers({pair:'LOCI-BTC'}).then(t=>t.data.sell) or getTickers({pair:'WTC-BTC'}).then(t=>t.data.buy). If we are only concerned with the highest bid and the lowest offer price we could just use the current WTC bid and LOCI offer and call it a day as a close enough real-time approximation. That would look something like this:

async function get_kucoin_price_in_btc(pair,side) {
return new Promise(function(res, rej) {
kc.getOrderBooks({pair:pair}).then( function(books) {
res(books.data[side][0][0]);
});
});
}
let allowance = 5000000000000000000; // 5 WTC in wei (18 decimals)

let wtcprice = await get_kucoin_price_in_btc('WTC-BTC','BUY'); let lociprice = await get_kucoin_price_in_btc('LOCI-BTC','SELL');
let wtcloci = wtcprice / lociprice; // .00101807 / .00000514 = ~198
let exchanged = wtcloci * allowance; // About 5*198 = ~990.34 LOCI

In this example, using bids and asks gets us a little closer to the real-time exchange rate for LOCI-WTC, which is roughly 1 WTC =~198 LOCI.

One More Thing…

If we’re exchanging illiquid coins, or large amounts of coins, we better take into account the volume that is available from the getOrderBooks data.

We can do better than simply assume we can buy or sell our entire quantity at the closest BUY or SELL prices. One method we can try is computing the VWAP or Volume Weighted Average Price.

We can compute a rough estimation for VWAP by doing the following:
1. We reducethe data rows and sum together each priceBTC * amountOfCoin, and record the result as total_price_times_volume.
2. Wereduce the datarows and sum up the volume into a total_volume.
3. Divide total_price_times_volume by total_volume.

Here’s how the above algorithm could be coded in practice

const sum_price_times_volume = function(total,row) { 
return parseFloat(total) +
(parseFloat(row[0])*parseFloat(row[1]));
}
const sum_volume = function(total,row) {
return parseFloat(total) + parseFloat(row[1]);
}
async function get_kucoin_vwap_in_btc(pair,side) { return new Promise(function(res, rej) {
kucoin.getOrderBooks({pair:pair}).then( function(books) {
let total_price_times_volume =
books.data[side].reduce(sum_price_times_volume); // #1
let total_volume = books.data[side].reduce(sum_volume); // #2
res(total_price_times_volume / total_volume); // #3
});
});
}
let allowance = 5000000000000000000; // 5 WTC in wei (18 decimals)

let wtcprice = await get_kucoin_vwap_in_btc('WTC-BTC','BUY');
let lociprice = await get_kucoin_vwap_in_btc('LOCI-BTC','SELL');
let wtcloci = wtcprice / lociprice;
let exchanged = wtcloci * allowance;

We’re dealing with real-time data so the prices have changed in the books.
For demonstration purposes, I coded up a truffle test to see the results in action. In the results, we’ll see the slight differences between using the stale coinmarketcap-api data, the slightly naive bid and ask prices, and the final result of using the get_kucoin_vwap_in_btc method described above.

Notice how the loci-per-wtc rate varies depending on the pricing datasource provided, and it might not seem like much of a difference for a single use-case. However, it could mean a world of difference for repeated transfers, or relatively larger volumes being exchanged.

Key Takeaways

  • Favor real-time data over stale data for accurate pricing data.
  • Get data from the source if possible, like kucoin-api vs coinmarketcap-api
  • Consider the realities of the bid / ask spread.
  • Make use of the current order book data to account for real volumes.
  • Consolidate the order book data into a VWAP for even better results.

If you’ve got even more ideas on how to make use of the API, please feel free to leave some comments below!

Dan Emmons is a Blockchain Developer, owner of Emmonspired LLC, a Certified Bitcoin Professional, Certified Ethereum Developer, Full Stack Developer and Advisor on Cryptocurrency projects. He is also the creator of a Youtube Channel and iTunes Podcast called #ByteSizeBlockchain.

--

--

Dan Emmons
Coinmonks

A leader who strives to make issues that seem complex, overwhelming, or insurmountable more manageable for the Team & provide exceptional service to my clients.