mirror of
https://github.com/miguelmota/cointop
synced 2024-11-14 18:12:57 +00:00
272 lines
6.2 KiB
Go
272 lines
6.2 KiB
Go
package coinmarketcap
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
apitypes "github.com/miguelmota/cointop/pkg/api/types"
|
|
util "github.com/miguelmota/cointop/pkg/api/util"
|
|
cmc "github.com/miguelmota/go-coinmarketcap/pro/v1"
|
|
cmcv2 "github.com/miguelmota/go-coinmarketcap/v2"
|
|
)
|
|
|
|
// ErrQuoteNotFound is the error for when a quote is not found
|
|
var ErrQuoteNotFound = errors.New("Quote not found")
|
|
|
|
// ErrPingFailed is the error for when pinging the API fails
|
|
var ErrPingFailed = errors.New("Failed to ping")
|
|
|
|
// Service service
|
|
type Service struct {
|
|
client *cmc.Client
|
|
}
|
|
|
|
// NewCMC new service
|
|
func NewCMC(apiKey string) *Service {
|
|
if apiKey == "" {
|
|
apiKey = os.Getenv("CMC_PRO_API_KEY")
|
|
}
|
|
client := cmc.NewClient(&cmc.Config{
|
|
ProAPIKey: apiKey,
|
|
})
|
|
return &Service{
|
|
client: client,
|
|
}
|
|
}
|
|
|
|
// Ping ping API
|
|
func (s *Service) Ping() error {
|
|
info, err := s.client.Cryptocurrency.Info(&cmc.InfoOptions{
|
|
Symbol: "BTC",
|
|
})
|
|
if err != nil {
|
|
return ErrPingFailed
|
|
}
|
|
if info == nil {
|
|
return ErrPingFailed
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) getPaginatedCoinData(convert string, offset int) ([]apitypes.Coin, error) {
|
|
var ret []apitypes.Coin
|
|
max := 100
|
|
|
|
listings, err := s.client.Cryptocurrency.LatestListings(&cmc.ListingOptions{
|
|
Limit: max,
|
|
Convert: convert,
|
|
Start: max * offset,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, v := range listings {
|
|
quote, ok := v.Quote[convert]
|
|
if !ok {
|
|
return nil, ErrQuoteNotFound
|
|
}
|
|
|
|
ret = append(ret, apitypes.Coin{
|
|
ID: util.FormatID(v.Name),
|
|
Name: util.FormatName(v.Name),
|
|
Symbol: util.FormatSymbol(v.Symbol),
|
|
Rank: util.FormatRank(v.CMCRank),
|
|
AvailableSupply: util.FormatSupply(v.CirculatingSupply),
|
|
TotalSupply: util.FormatSupply(v.TotalSupply),
|
|
MarketCap: util.FormatMarketCap(quote.MarketCap),
|
|
Price: util.FormatPrice(v.Quote[convert].Price, convert),
|
|
PercentChange1H: util.FormatPercentChange(quote.PercentChange1H),
|
|
PercentChange24H: util.FormatPercentChange(quote.PercentChange24H),
|
|
PercentChange7D: util.FormatPercentChange(quote.PercentChange7D),
|
|
Volume24H: util.FormatVolume(v.Quote[convert].Volume24H),
|
|
LastUpdated: util.FormatLastUpdated(v.LastUpdated),
|
|
})
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
// GetAllCoinData gets all coin data. Need to paginate through all pages
|
|
func (s *Service) GetAllCoinData(convert string, ch chan []apitypes.Coin) error {
|
|
go func() {
|
|
maxPages := 10
|
|
defer close(ch)
|
|
for i := 0; i < maxPages; i++ {
|
|
if i > 0 {
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
|
|
coins, err := s.getPaginatedCoinData(convert, i)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ch <- coins
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
// GetCoinData gets all data of a coin.
|
|
func (s *Service) GetCoinData(name string, convert string) (apitypes.Coin, error) {
|
|
ret := apitypes.Coin{}
|
|
coins, err := s.getPaginatedCoinData(convert, 0)
|
|
if err != nil {
|
|
return ret, err
|
|
}
|
|
|
|
for _, coin := range coins {
|
|
if coin.Name == name {
|
|
return coin, nil
|
|
}
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
// GetCoinDataBatch gets all data of specified coins.
|
|
func (s *Service) GetCoinDataBatch(names []string, convert string) ([]apitypes.Coin, error) {
|
|
ret := []apitypes.Coin{}
|
|
coins, err := s.getPaginatedCoinData(convert, 0)
|
|
if err != nil {
|
|
return ret, err
|
|
}
|
|
|
|
for _, coin := range coins {
|
|
for _, name := range names {
|
|
if coin.Name == name {
|
|
ret = append(ret, coin)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
// GetCoinGraphData gets coin graph data
|
|
func (s *Service) GetCoinGraphData(convert, symbol string, name string, start int64, end int64) (apitypes.CoinGraph, error) {
|
|
ret := apitypes.CoinGraph{}
|
|
graphData, err := cmcv2.TickerGraph(&cmcv2.TickerGraphOptions{
|
|
Symbol: symbol,
|
|
Start: start,
|
|
End: end,
|
|
})
|
|
if err != nil {
|
|
return ret, err
|
|
}
|
|
|
|
ret.MarketCapByAvailableSupply = graphData.MarketCapByAvailableSupply
|
|
ret.PriceBTC = graphData.PriceBTC
|
|
ret.Price = graphData.PriceUSD
|
|
ret.Volume = graphData.VolumeUSD
|
|
return ret, nil
|
|
}
|
|
|
|
// GetGlobalMarketGraphData gets global market graph data
|
|
func (s *Service) GetGlobalMarketGraphData(convert string, start int64, end int64) (apitypes.MarketGraph, error) {
|
|
ret := apitypes.MarketGraph{}
|
|
graphData, err := cmcv2.GlobalMarketGraph(&cmcv2.GlobalMarketGraphOptions{
|
|
Start: start,
|
|
End: end,
|
|
})
|
|
if err != nil {
|
|
return ret, err
|
|
}
|
|
|
|
ret.MarketCapByAvailableSupply = graphData.MarketCapByAvailableSupply
|
|
ret.VolumeUSD = graphData.VolumeUSD
|
|
return ret, nil
|
|
}
|
|
|
|
// GetGlobalMarketData gets global market data
|
|
func (s *Service) GetGlobalMarketData(convert string) (apitypes.GlobalMarketData, error) {
|
|
ret := apitypes.GlobalMarketData{}
|
|
market, err := s.client.GlobalMetrics.LatestQuotes(&cmc.QuoteOptions{
|
|
Convert: convert,
|
|
})
|
|
|
|
if err != nil {
|
|
return ret, err
|
|
}
|
|
ret = apitypes.GlobalMarketData{
|
|
TotalMarketCapUSD: market.Quote[convert].TotalMarketCap,
|
|
Total24HVolumeUSD: market.Quote[convert].TotalVolume24H,
|
|
BitcoinPercentageOfMarketCap: market.BTCDominance,
|
|
ActiveCurrencies: int(market.ActiveCryptocurrencies),
|
|
ActiveAssets: 0,
|
|
ActiveMarkets: int(market.ActiveMarketPairs),
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
// Price returns the current price of the coin
|
|
func (s *Service) Price(name string, convert string) (float64, error) {
|
|
convert = strings.ToUpper(convert)
|
|
symbol, err := cmcv2.CoinSymbol(util.NameToSlug(name))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
price, err := cmcv2.Price(&cmcv2.PriceOptions{
|
|
Symbol: symbol,
|
|
Convert: convert,
|
|
})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return util.FormatPrice(price, convert), nil
|
|
}
|
|
|
|
// CoinLink returns the URL link for the coin
|
|
func (s *Service) CoinLink(name string) string {
|
|
slug := util.NameToSlug(name)
|
|
return fmt.Sprintf("https://coinmarketcap.com/currencies/%s", slug)
|
|
}
|
|
|
|
// SupportedCurrencies returns a list of supported currencies
|
|
func (s *Service) SupportedCurrencies() []string {
|
|
|
|
// keep these in alphabetical order
|
|
return []string{
|
|
"AUD",
|
|
"BRL",
|
|
"BTC",
|
|
"CAD",
|
|
"CFH",
|
|
"CLP",
|
|
"CNY",
|
|
"CZK",
|
|
"DKK",
|
|
"ETH",
|
|
"EUR",
|
|
"GBP",
|
|
"HKD",
|
|
"HUF",
|
|
"IDR",
|
|
"ILS",
|
|
"INR",
|
|
"JPY",
|
|
"KRW",
|
|
"MXN",
|
|
"MYR",
|
|
"NOK",
|
|
"NZD",
|
|
"PHP",
|
|
"PKR",
|
|
"PLN",
|
|
"RUB",
|
|
"SEK",
|
|
"SGD",
|
|
"THB",
|
|
"TRY",
|
|
"TWD",
|
|
"USD",
|
|
"VND",
|
|
"ZAR",
|
|
}
|
|
}
|