Added market sync options: average and coingecko

-The previous market price calculation setting was hardcoded to only display market and USD prices for a single exchange and trading pair which was not very accurate for coins listed on multiple exchanges or with multiple trading pairs. The new default is to average the market prices for all supported exchanges and trading pairs
-The coingecko market price option was added to allow fetching the market price directly from the coingecko api instead of calculating it via supported exchanges known to the explorer
-Added a new root setting option for default_coingecko_ids which allows presetting symbols to their associated internal coingecko id to help prevent matching to the wrong currency with same symbol via coingecko api calls
-Fixed an issue where the explorer would fail to start with an enabled exchange that had no defined trading pairs
This commit is contained in:
Joe Uhren
2024-01-05 00:47:22 -07:00
parent ed8d7a5964
commit 65c48ea829
10 changed files with 443 additions and 195 deletions
+226 -29
View File
@@ -1013,28 +1013,206 @@ function check_show_sync_message(blocks_to_sync) {
return retVal;
}
function get_last_usd_price() {
console.log('Calculating market price.. Please wait..');
function get_market_price(market_array) {
// check how the market price should be updated
if (settings.markets_page.market_price == 'COINGECKO') {
// find the coingecko id
find_coingecko_id(settings.coin.symbol, function(coingecko_id) {
// check if the coingecko_id was found
if (coingecko_id != null && coingecko_id != '') {
const coingecko = require('../lib/apis/coingecko');
const currency = lib.get_market_currency_code();
// get the last usd price for coinstats
db.get_last_usd_price(function(err) {
// check for errors
if (err == null) {
// update markets_last_updated value
db.update_last_updated_stats(settings.coin.name, { markets_last_updated: Math.floor(new Date() / 1000) }, function(cb) {
// check if the script stopped prematurely
if (stopSync) {
console.log('Market sync was stopped prematurely');
exit(1);
} else {
console.log('Market sync complete');
exit(0);
console.log('Calculating market price.. Please wait..');
// get the market price from coingecko api
coingecko.get_market_prices(coingecko_id, currency, function (err, last_price, last_usd_price) {
// check for errors
if (err == null) {
// get current stats
Stats.findOne({coin: settings.coin.name}).then((stats) => {
// update market stat prices
Stats.updateOne({coin: settings.coin.name}, {
last_price: (last_price == null ? 0 : last_price),
last_usd_price: (last_usd_price == null ? 0 : last_usd_price)
}).then(() => {
// market prices updated successfully
finish_market_sync();
}).catch((err) => {
// error saving stats
console.log(err);
exit(1);
});
}).catch((err) => {
// error getting stats
console.log(err);
exit(1);
});
} else {
// coingecko api returned an error
console.log(err);
exit(1);
}
});
} else {
// coingecko_id is not set which should have already thrown an error, so just exit
exit(1);
}
});
} else {
console.log('Calculating market price.. Please wait..');
// get the list of coins from coingecko
coingecko_coin_list_api(market_array, function (coin_err, coin_list) {
// check for errors
if (coin_err == null) {
let api_ids = '';
// loop through all unique currencies in the market_array
for (let m = 0; m < market_array.length; m++) {
const index = coin_list.findIndex(p => p.symbol.toLowerCase() == market_array[m].currency.toLowerCase());
// check if the market currency is found in the coin list
if (index > -1) {
// add to the list of api_ids
api_ids += (api_ids == '' ? '' : ',') + coin_list[index].id;
// add the coingecko id back to the market_array
market_array[m].coingecko_id = coin_list[index].id;
} else {
// coin symbol not found in the api
console.log('Error: Cannot find symbol "' + market_array[m].currency + '" in the coingecko api');
}
}
// check if any api_ids were found
if (api_ids != '') {
const coingecko = require('../lib/apis/coingecko');
const currency = lib.get_market_currency_code();
// get the market price from coingecko api
coingecko.get_avg_market_prices(api_ids, currency, market_array, function (mkt_err, last_price, last_usd) {
// check for errors
if (mkt_err == null) {
// update the last usd price
Stats.updateOne({coin: settings.coin.name}, {
last_price: last_price,
last_usd_price: last_usd
}).then(() => {
// market price updated successfully
finish_market_sync();
}).catch((err) => {
// error saving stat data
console.log(err);
exit(1);
});
} else {
// coingecko api returned an error
console.log(mkt_err);
exit(1);
}
});
} else {
// no api_ids found so cannot continue to getting the usd price and error msgs were already thrown, so just exit
exit(1);
}
} else {
// coingecko api returned an error
console.log(coin_err);
exit(1);
}
});
}
}
function finish_market_sync() {
// update markets_last_updated value
db.update_last_updated_stats(settings.coin.name, { markets_last_updated: Math.floor(new Date() / 1000) }, function() {
// check if the script stopped prematurely
if (stopSync) {
console.log('Market sync was stopped prematurely');
exit(1);
} else {
console.log('Market sync complete');
exit(0);
}
});
}
function coingecko_coin_list_api(market_symbols, cb) {
let coin_array = [];
let call_coin_list_api = false;
// check if market_symbols is an array
if (!Array.isArray(market_symbols)) {
// add this symbol to an array
market_symbols = [{currency: market_symbols}];
}
// loop through all symbols
for (var symbol of market_symbols) {
// check if this symbol has a default coingecko id in the settings
const index = settings.default_coingecko_ids.findIndex(p => p.symbol.toLowerCase() == symbol.currency.toLowerCase());
// check if the coin symbol is found in settings
if (index > -1) {
// add this symbol and id to a new array
coin_array.push({
id: settings.default_coingecko_ids[index].id.toLowerCase(),
symbol: symbol.currency.toLowerCase()
});
} else {
// display error msg
console.log('Error: %s', err);
exit(1);
// missing at least 1 symbol, so the coingecko api must be called
call_coin_list_api = true;
break;
}
}
// check if the coin list api needs to be called
if (call_coin_list_api) {
const coingecko = require('../lib/apis/coingecko');
// get the list of coins from coingecko
coingecko.get_coin_data(function (err, coin_list) {
// check if there was an error
if (err == null) {
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
const rateLimitLib = require('../lib/ratelimit');
const rateLimit = new rateLimitLib.RateLimit(1, 2000, false);
// automatically pause for 2 seconds in between requests
rateLimit.schedule(function() {
return cb(err, coin_list);
});
} else {
return cb(err, coin_list);
}
});
} else {
// return the custom array of known symbols and ids
return cb(null, coin_array);
}
}
function find_coingecko_id(symbol, cb) {
coingecko_coin_list_api(symbol, function (err, coin_list) {
// check for errors
if (err == null) {
// find the index of the first coin symbol match
const index = coin_list.findIndex(p => p.symbol.toLowerCase() == symbol.toLowerCase());
// check if the coin symbol is found in the api coin list
if (index > -1)
return cb(coin_list[index].id);
else {
// coin symbol not found in the api
console.log('Error: Cannot find symbol "' + symbol + '" in the coingecko api');
return cb('');
}
} else {
// failed to get the coingecko api list
console.log(err);
return cb('');
}
});
}
@@ -1514,15 +1692,16 @@ if (lib.is_locked([database]) == false) {
}
});
} else {
// check if market feature is enabled
if (settings.markets_page.enabled == true) {
// start market sync
// check if market feature is enabled or the market_price option is set to COINGECKO
if (settings.markets_page.enabled == true || settings.markets_page.market_price == 'COINGECKO') {
var total_pairs = 0;
var exchanges = Object.keys(settings.markets_page.exchanges);
// loop through all exchanges to determine how many trading pairs must be updated
exchanges.forEach(function(key, index, map) {
// check if market is enabled via settings
if (settings.markets_page.exchanges[key].enabled == true) {
if (settings.markets_page.enabled == true && settings.markets_page.exchanges[key].enabled == true) {
// check if market is installed/supported
if (db.fs.existsSync('./lib/markets/' + key + '.js')) {
// add trading pairs to total
@@ -1539,6 +1718,8 @@ if (lib.is_locked([database]) == false) {
// check if there are any trading pairs to update
if (total_pairs > 0) {
let market_array = [];
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
var rateLimitLib = require('../lib/ratelimit');
var rateLimit = new rateLimitLib.RateLimit(1, 2000, false);
@@ -1563,16 +1744,30 @@ if (lib.is_locked([database]) == false) {
// automatically pause for 2 seconds in between requests
rateLimit.schedule(function() {
// update market data
db.update_markets_db(key, split_pair[0], split_pair[1], function(err) {
if (!err)
db.update_markets_db(key, split_pair[0], split_pair[1], function(err, last_price) {
if (!err) {
console.log('%s[%s]: Market data updated successfully', key, pair_key);
else
// only add to the market_array if market data is being averaged
if (settings.markets_page.market_price == 'AVERAGE') {
// check if the currency already exists in the market array
const index = market_array.findIndex(item => item.currency.toUpperCase() == split_pair[1].toUpperCase());
if (index != -1) {
// update the last_price
market_array[index].last_price = (market_array[index].last_price + last_price) / 2;
} else {
// add new object to the array
market_array.push({currency: split_pair[1], last_price: last_price});
}
}
} else
console.log('%s[%s] Error: %s', key, pair_key, err);
complete++;
if (complete == total_pairs || stopSync)
get_last_usd_price();
get_market_price(market_array);
});
});
} else {
@@ -1580,7 +1775,7 @@ if (lib.is_locked([database]) == false) {
complete++;
if (complete == total_pairs || stopSync)
get_last_usd_price();
get_market_price(market_array);
}
});
} else {
@@ -1589,7 +1784,7 @@ if (lib.is_locked([database]) == false) {
complete++;
if (complete == total_pairs || stopSync)
get_last_usd_price();
get_market_price(market_array);
}
});
} else {
@@ -1598,11 +1793,13 @@ if (lib.is_locked([database]) == false) {
complete++;
if (complete == total_pairs || stopSync)
get_last_usd_price();
get_market_price(market_array);
}
}
});
} else {
} else if (settings.markets_page.market_price == 'COINGECKO')
get_market_price([]);
else {
// no market trading pairs are enabled
console.log('Error: No market trading pairs are enabled in settings');
exit(1);