Witness Price Feeds: A problem and a solution

in Witness Activities2 years ago (edited)

Presenting a flaw in current price feed calculation, and a solution.

prices.jpg

Background

I believe @justyy's price feed script is the most popular way for witnesses to publish price feeds.

It's a fork of @yabapmatt's original and now unmaintained script, which no longer works in its fallback mode because the old V1 CoinMarketCap API is retired.

It looks like @justyy forked it after waiting 2 years for his pull request to be merged.

However, there's a logic flaw in @justyy's fork.

The Flaw

Looking at the original logic, you can see that spot prices from up to 3 exchanges were taken, and averaged. This is fairly reasonable, but not perfect: The prices from each exchange should ideally be volume-weighted so that exchanges with low volume do not have as much impact on the price as more popular exchanges.

CoinMarketCap was only ever used as a fallback datasource in the case that no exchanges were enabled in the config, though using CMC is arguably better, since its price is based on far more exchanges, and is volume weighted.

Compare this to the new logic. The broken CoinMarketCap API is removed, and two new aggregators are added as datasources: CoinGecko and CryptoCompare.

The problem is that in the default config, the price is obtained by averaging the spot prices from the 3 exchanges and the volume-weighted aggregate prices from the 2 aggregators.

Lumping spot prices in with aggregated prices and taking an average makes no sense. The 3 exchanges used (Bittrex, Poloniex and Binance) are all really low volume for Steem compared to the most popular exchanges, and as such are almost guaranteed to skew the final average incorrectly.

Note that I'm ignoring the "cloudflare" datasource, as I'm not sure where its prices are coming from.

Quantifying The Error

To see how bad the skew is, I produced a quick script which would monitor the % error when running with the default config (though again excluding the unknown cloudflare endpoint), assuming the aggregator price to be the "correct" price:

error2.PNG

Above it's running on a 1 minute interval. In default config, the error is currently around +/- 0.3%. If running with exchanges only and no aggregators, which you're doing if running @yabapmatt's ancient version, it rises to +/- 0.6%.

Are these huge errors? No. But we can still do better!

Another Small Bug

When writing the error checker, I discovered a further issue with the current retry code, which is supposed to make a second attempt at a given datasource after a delay.

Instead of retrying, a failure on Poloniex will attempt to get a price from Bittrex -- further skewing Bittrex's price influence. Meanwhile, retries on the "cloudflare" endpoint try to retry with Binance!

By default, retries are set to zero, so this particular bug shouldn't come up in practice unless you've edited feed.js.

Potential Fixes

Option A

The simplest solution is to ensure you are not including those exchanges in your calculation, by editing your config.json so that the "exchanges" param is as follows:

"exchanges": ["coingecko", "cryptocompare"],

In my opinion, using 2 independent aggregators gives enough decentralisation and is certainly better than the other option of dropping the aggregators and just using the 3 spot prices from low-volume exchanges.

Option B

The arguably best solution from a decentralisation point of view is to drop the aggregators, and calculate our own volume-weighted average price from the individual markets. However, that involves 30+ pairs across 18 exchanges, and that's an awful lot of APIs to implement (and, potentially, API keys for the user to have to generate).

Option C

A decent compromise solution would be to drop the individual exchanges, as in Option A, but also integrate the current CoinMarketCap API so that we have an average from 3 aggregators.

Let's do it!

Our Solution

I've implemented Option C above.

In this fork, all individual exchanges have been removed, CoinMarketCap has been added, and the retry bug has been fixed. You can see the changes in this commit.

The default config is "exchanges": ["coingecko", "cryptocompare", "coinmarketcap"].

To use CoinMarketCap, you need to sign up for a free API key, and paste it into config.json (or set it as an environment variable, in common with the other config values in the script).

@justyy's "cloudflare" datasource is still available, but it's not enabled by default purely because I don't currently understand how the prices there are calculated.

To replicate my error test from above, just run npm test.

Disclaimer

I'm not trying to cast aspersions on @justyy or his great work. When coding, we all leave ego at the door :) If Issues were enabled on his repo, I would have initiated this discussion there instead.

@justyy, if you would like me to raise a PR, just let me know. If we can merge I'll close the fork.

steemwow-midcrop-2x.png

Vote for SteemWOW!

We'd be very grateful for your support in the form of a witness vote for our @steemwow project, which you can cast by visiting the Steemit Wallet Witness list, scrolling to the bottom, and filling in the form:

witpic

Sort:  

UPDATE: This code is now rolled into @justyy's main price feed repo :)

I am wondering if the choice of Bittrex, Poloniex and Binance is a historical legacy?

Maybe in the past they saw much more STEEM transaction volume.

Nowadays one would think the exchanges that see most volume like Huobi Global and Upbit would have been used.

Indeed, and although I haven't checked whether Huobi and Upbit offer APIs they almost certain do.

"Option B" in the post doesn't have to be completely exhaustive; integrating APIs from Steem's top 10 exchanges by volume would probably cover what, 99% of volume? (Again, I haven't checked.)

However I'd rather add more aggregator endpoints I think, if anyone has any more to suggest.

This post has been featured in the latest edition of Witness Weekly...

Brilliant! I'll do a PR tomorrow :)

Just to confirm that I've now submitted a pull request. Thanks!

This post has been featured in the latest edition of Steem News...

Your post is manually rewarded by the
World of Xpilar Community Curation Trail

STEEM AUTO OPERATED AND MAINTAINED BY XPILAR TEAM
https://steemit.com/~witnesses vote xpilar.witness

Coin Marketplace

STEEM 0.30
TRX 0.11
JST 0.033
BTC 64223.84
ETH 3158.34
USDT 1.00
SBD 4.29