Skip to content

Instantly share code, notes, and snippets.

@donpdonp
Last active December 31, 2023 08:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save donpdonp/fb21db62bf6bd95644cc9129b8fc0a1d to your computer and use it in GitHub Desktop.
Save donpdonp/fb21db62bf6bd95644cc9129b8fc0a1d to your computer and use it in GitHub Desktop.
gluon coinmarketcap coin price
(function() {
setup()
return {
name: "coin"
}
})
var etherscan_apikey
var coingecko_apikey
var cache_date = new Date(0)
var cache = {data:[]}
var ok_channels = ['#pdxcoins', '#zrobo', '#pdxbots', '#portlandor']
var gecko_list = {}
function setup() {
gecko_list = JSON.parse(http.get("https://api.coingecko.com/api/v3/coins/list"))
bot.say(bot.admin_channel, 'coingecko list cached '+gecko_list.length+' coins')
db.get('coin:etherscan_apikey', function(key){
if(key) {
etherscan_apikey = key
bot.say(bot.admin_channel, 'coin: etherscan apikey loaded')
} else {
bot.say(bot.admin_channel, 'coin: etherscan apikey missing')
}
})
db.get('coin:coingecko_apikey', function(key){
if(key) {
coingecko_apikey = key
bot.say(bot.admin_channel, 'coin: coingecko apikey loaded')
} else {
bot.say(bot.admin_channel, 'coin: coingecko apikey missing')
}
})
}
function go(msg) {
if(msg.method == "irc.privmsg") {
var cmd = /^\!coin(\s+(.+))?$/
var match = cmd.exec(msg.params.message)
if(match){
var service = 'gecko'
var ticker = match[2]
//report(msg.params, ticker)
quote(msg.params.channel, service, ticker)
}
var parts = msg.params.message.split(' ')
for(var i=0; i < parts.length; i++) {
var word = parts[i]
var coincmd = /\$((\w+):)?([0-9A-Za-z]+\S*)+/
var coinmatch = coincmd.exec(word)
if (coinmatch) {
//bot.say(bot.admin_channel, "coin: "+word+" 1:"+coinmatch[1]+" 2:"+coinmatch[2]+" 3:"+coinmatch[3])
if (ok_channels.indexOf(msg.params.channel) >= 0) {
var service = coinmatch[2]
var ticker = coinmatch[3]
if (!service) { service = "gecko" }
quote(msg.params.channel, service, ticker)
}
} else {
return //early abort
}
}
}
}
function quote(channel, service, ticker) {
switch(service) {
case 'coinmarketcap':
// obsolete
var quote = coinmarketcap(ticker)
if(quote) {
var parts = coinmarketcapreport(quote)
bot.say(channel, parts.join(' ')+" coinmarketcap.com")
}
break
case 'coincap':
var quote = coincap(ticker)
if(quote) {
var parts = coincapreport(quote)
bot.say(channel, parts.join(' ')+" coincap.io")
}
break
case 'gecko':
var quotes = gecko(ticker)
quotes.forEach(function(quote){
var parts = geckoreport(quote)
bot.say(channel, parts.join(' ')+" coingecko.com")
})
break
case 'yahoo':
var quote = yahoo(ticker)
if(quote.status == 200) {
var parts = yahooreport(JSON.parse(quote.body))
bot.say(channel, parts+" yahoo.com")
} else {
bot.say(channel, "yahoo err http " + quote.status + " " + quote.body.substr(0,90) )
}
break
case 'compound':
var data = compound_data()
var quote = compound_find(data, ticker.toUpperCase())
if (quote) {
var eth_price = coincap('ETH').priceUsd
var csay = compoundreport(quote, eth_price)
bot.say(channel, ticker+" "+csay)
} else {
var token_list = data.map(function(c){return c.underlying_symbol}).join(", ")
bot.say(channel, "compound cTokens are: "+token_list)
}
break
case 'dai':
bot.say(channel, daicdpfee(ticker))
break
case 'fed':
fedstlouis(ticker, function(quote){
if(quote){
bot.say(channel, fedstlouisreport(quote))
}
})
break
case 'eth':
bot.say(channel, "ethgasstation fast: "+eth('gas')+" gwei")
break
case 'allbridge':
bot.say(channel, "allbridge "+ticker+": "+allbridge(ticker).toFixed(1)+"%")
break
}
}
function eth(cmd) {
switch(cmd) {
case 'gas':
var url = 'https://api.etherscan.io/api?module=gastracker&action=gasoracle&apikey='+etherscan_apikey
var json = http.get(url)
var data = JSON.parse(json)
return data.result.FastGasPrice
}
}
function alphavantage(ticker) {
db.get('coin:alphavantage', function(key){ })
//https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=MSFT&apikey=
}
function worldtradingdata() {
db.get('coin:worldtradingdata', function(key){ })
//https://api.worldtradingdata.com/api/v1/stock?symbol=SNAP,TWTR,VOD.L&api_token=
}
function fedstlouis(series, cb) {
var unit = ''
if(!series || series == "rate") { series = 'EFFR'; unit = '%' }
if(series == "m3") { series = 'MABMM301USM189S' }
if(series == "m2") { series = 'M2SL' }
if(series == "m1") { series = 'M1SL' }
db.get('coin:fedstlouis', function(key){
var meta_url = 'https://api.stlouisfed.org/fred/series?series_id='+series+'&api_key='+key+'&file_type=json'
var meta_json = http.get(meta_url)
var meta = JSON.parse(meta_json)
var title = meta['seriess'][0]['title']
var data_url = 'https://api.stlouisfed.org/fred/series/observations?series_id='+series+'&api_key='+key+'&file_type=json'
bot.say(bot.admin_channel, data_url)
var json = http.get(data_url)
var data = JSON.parse(json)
var ocount = data.observations.length
var last_report = data.observations[ocount-1]
last_report.title = title
last_report.unit = unit
cb(last_report)
})
}
function fedstlouisreport(quote) {
return quote.title+": "+quote.value+quote.unit+" stlouisfed.org"
}
function coincapreport(quote) {
var parts = [quote.id]
parts.push("$"+(quote.priceUsd/1).toFixed(4))
parts.push("#"+quote.rank)
if(quote.vwap24Hr) {
var h24chg = quote.priceUsd/quote.vwap24Hr
var sign
if (h24chg > 1) {
sign = "+"
h24chg -= 1
} else {
sign = "-"
h24chg = 1-h24chg
}
parts.push("24hr chg "+sign+(h24chg*100).toFixed(1)+"%")
}
return parts
}
function allbridge(ticker) {
var url = "https://core.api.allbridgecoreapi.net/token-info"
//bot.say(bot.admin_channel, url)
try {
var json = http.get(url)
var data = JSON.parse(json)
var tokens = data['ETH']['tokens'].filter(function(c){return c.symbol.toLowerCase() == ticker.toLowerCase() })
if (tokens.length > 0) {
return parseFloat(tokens[0]['apr']) * 100
} else {
return ticker+" not found"
}
} catch(e) {
bot.say(bot.admin_channel, "allbridge api query failed "+e+" "+url)
}
}
function coinmarketcap(ticker) {
var url = "https://api.coinmarketcap.com/v2/ticker/" // limit=100 (maximum and default)
bot.say(bot.admin_channel, url)
try {
var json = http.get(url)
var quote = JSON.parse(json)
var winner
Object.keys(quote.data).forEach(function(k) {
var q = quote.data[k]
if (q.symbol == ticker.toUpperCase()) {
winner = q
}
})
return winner
} catch(e) {
bot.say(bot.admin_channel, "coinmarketcap api query failed "+e+" "+url)
}
}
function coinmarketcapreport(report) {
//{"circulating_supply":17927750,"id":1,"last_updated":1568143291,"max_supply":21000000,"name":"Bitcoin",
//"quotes":{"USD":{"market_cap":183306673898,"percent_change_1h":-0.19,"percent_change_24h":-0.69,
// "percent_change_7d":-4.01,"price":10224.7450962,"volume_24h":14149978597.9706}},
//"rank":1,"symbol":"BTC","total_supply":17927750,"website_slug":"bitcoin"}
return [report.symbol, report.quotes.USD.price.toFixed(4), "24 hr chg", report.quotes.USD.percent_change_24h+"%"]
}
function gecko_find(ticker) {
return gecko_list.reduce(function(m, record){
var ticker_lower = ticker.toLowerCase()
if (ticker_lower == record.symbol.toLowerCase() ||
ticker_lower == record.id.toLowerCase()) {
m.push(record)
}
return m
}, [])
}
function gecko(ticker) {
var coinlist = gecko_find(ticker)
if (coinlist.length == 0) {
bot.say(bot.admin_channel, "coingecko list does not contain '"+ticker+"'")
}
var close_coins = coinlist.reduce(function(m,coin){
var url = "https://api.coingecko.com/api/v3/coins/"+coin.id+"?x_cg_demo_api_key="+coingecko_apikey
//bot.say(bot.admin_channel, url)
try {
var json = http.get(url)
var quote = JSON.parse(json)
if (quote.market_data) {
m.push(quote)
} else {
bot.say(bot.admin_channel, "coingecko "+coin.id+" missing market_data: "+json)
}
} catch(e) {
bot.say(bot.admin_channel, "coingecko api query failed "+e+" "+url)
} finally {
return m
}
}, [])
var sorted_close_coins = close_coins.sort(function(a,b){return a.market_data.total_volume.usd < b.market_data.total_volume.usd})
if (sorted_close_coins.length > 1) {
var a = sorted_close_coins[0]
var b = sorted_close_coins[1]
if (a.market_data.total_volume.usd > 10*b.market_data.total_volume.usd) { // clear winner
return [a]
}
}
return sorted_close_coins
}
function geckoreport(report) {
var parts = [report['name']+'('+report['symbol']+')',
'$'+report['market_data']['current_price']['usd']]
var rank = report['market_cap_rank']
if (rank) { parts.push('#'+rank) }
var pcp24 = report['market_data']['price_change_percentage_24h']
var sign = pcp24 > 0 ? "+" : ""
if (pcp24) {
parts.push(sign+pcp24.toFixed(1)+'%')
} else {
parts.push('no price')
}
var vol = report['market_data']['total_volume']['usd']
if (vol) {
parts.push('$'+units(vol)+"vol")
} else {
parts.push('no volume')
}
var ath_date_str = report['market_data']['ath_date']['usd']
if (ath_date_str) {
var ath_date = new Date(ath_date_str)
var ath_time_diff = new Date() - ath_date
if (ath_time_diff < 1000 * 60 * 60 * 24) {
var ath = report['market_data']['ath']['usd']
parts.push(" All time high was today: $" + ath)
}
}
return parts
}
function yahoo(ticker) {
return http.get({url:'https://query1.finance.yahoo.com/v8/finance/chart/'+ticker})
}
function yahooreport(report) {
if (report.chart.error) {
return report.chart.error.description
} else {
var meta = report.chart.result[0].meta
return meta.symbol + '('+meta.instrumentType+') $' + meta.regularMarketPrice + meta.currency+ ' '+
'(previous close $' + meta.previousClose+')'
}
}
function coincap(ticker) {
/* */
var url = "https://api.coincap.io/v2/assets?limit=1&search="+encodeURIComponent(ticker)
try {
var json = http.get(url)
var quote = JSON.parse(json)
if (quote.data) {
return quote.data[0]
}
} catch(e) {
}
}
function compound_data() {
var url = 'https://api.compound.finance/api/v2/ctoken'
var json = http.get(url)
var data = JSON.parse(json)
return data.cToken
}
function compound_find(tokens, ticker) {
for(var i=0; i < tokens.length; i++) {
var token = tokens[i]
if(token.underlying_symbol == ticker) {
return token
}
}
}
function compoundreport(quote, eth_price) {
var supply_usd = quote.total_supply.value*quote.underlying_price.value*eth_price
var borrow_usd = quote.total_borrows.value*quote.underlying_price.value*eth_price
return "supply "+((quote.total_supply.value)/1000000).toFixed(3)+"M"+
"($"+(supply_usd/1000000).toFixed(3)+"M) pays "+
(quote.supply_rate.value*100).toFixed(1)+"%/yr. "+
"borrow "+((quote.total_borrows.value)/1000000).toFixed(3)+"M"+
"($"+(borrow_usd/1000000).toFixed(3)+"M) costs "+
(quote.borrow_rate.value*100).toFixed(1)+"%/yr. "+
"compound.finance v2"
/*
{
"borrow_rate": {
"value": "0.030601136172511351"
},
"cash": {
"value": "82.55526888"
},
"collateral_factor": {
"value": "0"
},
"exchange_rate": {
"value": "0.020001332991763432"
},
"interest_rate_model_address": "0xbae04cbf96391086dc643e842b517734e214d698",
"name": "Compound Wrapped BTC",
"number_of_borrowers": 31,
"number_of_suppliers": 75,
"reserves": {
"value": "0.0006450620000000000000000"
},
"supply_rate": {
"value": "0.000973227770582953"
},
"symbol": "cWBTC",
"token_address": "0xc11b1268c1a384e55c48c2391d8d480264a3a7f4",
"total_borrows": {
"value": "3.02412952"
},
"total_supply": {
"value": "4278.65249647"
},
"underlying_address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"underlying_name": "Wrapped BTC",
"underlying_price": {
"value": "53.1117068891602500000000000"
},
"underlying_symbol": "WBTC"
}
*/
}
function report(params, ticker) {
coinmarketcap_freshen(params.channel)
var matches = cache_find(ticker)
if (matches.length == 0) {
bot.say(params.channel, "no coins match "+ticker+" ("+Object.keys(cache.data).length+" coins in cache)")
}
if (matches.length == 1) {
var coin = matches[0]
coin.last_updated = new Date(parseInt(coin.last_updated)*1000)
var delay = (new Date() - coin.last_updated)
//{"circulating_supply":17180737,"id":1,"last_updated":"2018-07-30T23:23:28.000Z","max_supply"
// :21000000,"name":"Bitcoin","quotes":{"USD":{"market_cap":140363700920,"percent_change_1h":0.06,"per
// cent_change_24h":-0.24,"percent_change_7d":5.48,"price":8169.83002069,"volume_24h":5602012567.1652}
// },"rank":1,"symbol":"BTC","total_supply":17180737,"website_slug":"bitcoin"}
var msg = coin.symbol.toLowerCase() + " $" + usd_digits(coin.quotes.USD.price) +
" rank: #"+coin.rank+" 24hr: "+parseInt(coin.quotes.USD.percent_change_24h)+"% "+
"[coinmarketcap.com "+duration_seconds(new Date(), coin.last_updated)+"]"
if (delay > 1000 * 60 * 10) {
msg = msg + " warning: report is more than 10 min old."
}
bot.say(params.channel, msg)
}
if (matches.length > 1) {
bot.say(params.channel, ""+matches.length+" coins match "+ticker)
}
}
function daicdpfee(symbol) {
if (symbol == "fee") {
try {
var url = "https://defiexplore.com/api/stats/globalInfo"
bot.say(bot.admin_channel, url)
var json = http.get(url)
var data = JSON.parse(json).tokenData
if (data) {
var fee_desc = function(coin, coin_data){return coin + " " + coin_data.stabilityFee +"%/yr"}
return "DAI loan fee from "+ Object.keys(data).map(function (coin) {return fee_desc(coin, data[coin])}).join(", ")
} else {
return symbol + " data missing? " + Object.keys(data)
}
} catch(e) {
return e + ". bad json? " + json.substr(0,20) + ".." + json.substr(json.length-20, json.length)
}
} else {
return "unknown "+symbol+" available commands: $dai:fee"
}
}
function siafee(ticker) {
if(ticker == 'STORAGE') {
return JSON.parse(http.get("https://siastats.info/dbs/storagepricesdb.json"))[0]
}
}
function cache_find(ticker) {
return Object.keys(cache.data).filter(function(coin_id){
var coin=cache.data[coin_id];
return coin.symbol.toLowerCase() == ticker.toLowerCase()
}).map(function(coin_id){return cache.data[coin_id]})
}
function coinmarketcap_freshen(channel) {
var limit = new Date(new Date().getTime() - (5*60*1000))
if (cache_date < limit) {
var start = new Date()
var url = "https://api.coinmarketcap.com/v2/ticker/" // limit=100 (maximum and default)
try {
var json = http.get(url)
cache = JSON.parse(json)
cache_date = new Date()
bot.say(channel, "coinmarketcap "+Object.keys(cache.data).length+" of "+cache.metadata.num_cryptocurrencies+
" coins in "+ duration_seconds(cache_date, start))
} catch(e) {
bot.say(channel, "coinmarketcap api query failed "+e+" "+url)
}
} else {
var duration = (new Date()) - cache_date
//bot.say(params.channel, "coinmarketcap cache good. "+duration+" seconds")
}
}
/*
{
"data": {
"1": {
"id": 1,
"name": "Bitcoin",
"symbol": "BTC",
"website_slug": "bitcoin",
"rank": 1,
"circulating_supply": 17180662.0,
"total_supply": 17180662.0,
"max_supply": 21000000.0,
"quotes": {
"USD": {
"price": 8167.57617335,
"volume_24h": 5603038684.12216,
"market_cap": 140324365594.0,
"percent_change_1h": 0.3,
"percent_change_24h": -0.35,
"percent_change_7d": 5.5
}
},
"last_updated": 1532991927
}, */
function duration_seconds(a, b) {
var count = (a - b) / 1000
var unit = "seconds"
if (count >= 60) {
count = count / 60
unit = "minutes"
}
return count.toFixed(1) + " " + unit
}
function usd_digits(price_str) {
price = parseFloat(price_str)
if (price >= 0.1) {
return price.toFixed(2)
} else {
return price.toFixed(4)
}
}
function units(num) {
int = num
units = ""
if (num >= 1000) {
int = num/1000
units="K"
}
if (num >= 1000000) {
int = num/1000000
units="M"
}
if (num >= 1000000000) {
int = num/1000000000
units="B"
}
return int.toFixed(1)+units
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment