Update CoinMarketCap graph data endpoint

pull/94/head
Miguel Mota 3 years ago
parent 0576b043d4
commit 295f163ae2

@ -1008,6 +1008,12 @@ Frequently asked questions:
- A: Run the command `cointop reset` to delete the config files and cache. Cointop will generate a new config when starting up. You can run `cointop --reset` to reset before running cointop.
- Q: Why aren't <kbd>Home</kbd> or <kbd>End</kbd> keys working for me?
- A: Make sure to not manually set `TERM` in your `~/.bashrc`, `~/.zshrc`, or any where else. The `TERM` environment variable should be automatically set by your terminal, otherwise this may cause the terminal emulator to send escape codes when these keys are pressed. See [this Arch wiki](https://wiki.archlinux.org/index.php/Home_and_End_keys_not_working) for more info.
- A: Use the `cointop price` command. Here are some examples:
- Q: What is the size of the binary?
- A: The Go build size is ~8MB but packed with UPX it's only a ~3MB executable binary.
@ -1040,8 +1046,7 @@ Frequently asked questions:
- Q: Does cointop do mining?
- A: Cointop does not do any kind of mining.
- A: Cointop does not do any kind of cryptocurrency mining.
- Q: How can I run the cointop SSH server on port 22?

@ -2,6 +2,7 @@ package cointop
import (
"fmt"
"sort"
"strings"
"sync"
"time"
@ -142,7 +143,7 @@ func (ct *Cointop) ChartPoints(symbol string, name string) error {
}
for i := range graphData.MarketCapByAvailableSupply {
price := graphData.MarketCapByAvailableSupply[i][1]
data = append(data, price/1e9)
data = append(data, price)
}
} else {
convert := ct.State.currencyConversion
@ -150,9 +151,12 @@ func (ct *Cointop) ChartPoints(symbol string, name string) error {
if err != nil {
return nil
}
for i := range graphData.Price {
price := graphData.Price[i][1]
sorted := graphData.Price
sort.Slice(sorted[:], func(i, j int) bool {
return sorted[i][0] < sorted[j][0]
})
for i := range sorted {
price := sorted[i][1]
data = append(data, price)
}
}
@ -230,8 +234,12 @@ func (ct *Cointop) PortfolioChart() error {
if err != nil {
return err
}
for i := range apiGraphData.Price {
price := apiGraphData.Price[i][1]
sorted := apiGraphData.Price
sort.Slice(sorted[:], func(i, j int) bool {
return sorted[i][0] < sorted[j][0]
})
for i := range sorted {
price := sorted[i][1]
graphData = append(graphData, price)
}
}
@ -358,6 +366,7 @@ func (ct *Cointop) ToggleCoinChart() error {
ct.UpdateChart()
}()
// TODO: not do this (SoC)
go ct.UpdateMarketbar()
return nil

@ -225,7 +225,7 @@ func NewCointop(config *Config) (*Cointop, error) {
marketBarHeight: 1,
onlyTable: config.OnlyTable,
refreshRate: 60 * time.Second,
selectedChartRange: "7D",
selectedChartRange: "1Y",
shortcutKeys: DefaultShortcuts(),
sortBy: "rank",
page: 0,
@ -340,10 +340,6 @@ func NewCointop(config *Config) (*Cointop, error) {
}
}
if ct.apiChoice == CoinGecko {
ct.State.selectedChartRange = "1Y"
}
if ct.apiChoice == CoinMarketCap {
ct.api = api.NewCMC(ct.apiKeys.cmc)
} else if ct.apiChoice == CoinGecko {

@ -11,7 +11,7 @@ require (
github.com/maruel/panicparse v1.5.0
github.com/mattn/go-colorable v0.1.7 // indirect
github.com/mattn/go-runewidth v0.0.9
github.com/miguelmota/go-coinmarketcap v0.1.6
github.com/miguelmota/go-coinmarketcap v0.1.7
github.com/miguelmota/gocui v0.4.2
github.com/miguelmota/termbox-go v0.0.0-20191229070316-58d4fcbce2a7
github.com/mitchellh/go-wordwrap v1.0.0

@ -93,8 +93,8 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miguelmota/go-coinmarketcap v0.1.6 h1:YIe+VdFhEgyGESfmkL7BHRDIdf6CUOAjJisml01AFqs=
github.com/miguelmota/go-coinmarketcap v0.1.6/go.mod h1:Jdv/kqtKclIElmoNAZMMJn0DSQv+j7p/H1te/GGnxhA=
github.com/miguelmota/go-coinmarketcap v0.1.7 h1:9kTFWMom73IuGXqacD/LYPiUeX1qLpuLH8BhceHXYt0=
github.com/miguelmota/go-coinmarketcap v0.1.7/go.mod h1:hBjej1IiB5+pfj+0cZhnxRkAc2bgky8qWLhCJTQ3zjw=
github.com/miguelmota/gocui v0.4.2 h1:nMYnYn3RjV7FlWFcidQa9eAkX3kT7XMI6yJMxEkAz6s=
github.com/miguelmota/gocui v0.4.2/go.mod h1:wVtmhuLR+VAS9VRBIJZBNJS9IgH+9QOZ/m/MvRarOZ4=
github.com/miguelmota/termbox-go v0.0.0-20191229070316-58d4fcbce2a7 h1:sZmjSV25xMXIGAaATVuOtC9VtGHMydXpd9OejNaTxQE=

@ -1,9 +1,13 @@
package coinmarketcap
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"sort"
"strings"
"time"
@ -19,6 +23,9 @@ var ErrQuoteNotFound = errors.New("quote not found")
// ErrPingFailed is the error for when pinging the API fails
var ErrPingFailed = errors.New("failed to ping")
// ErrFetchGraphData is the error for when fetching graph data fails
var ErrFetchGraphData = errors.New("graph data fetch error")
// Service service
type Service struct {
client *cmc.Client
@ -149,35 +156,138 @@ func (s *Service) GetCoinDataBatch(names []string, convert string) ([]apitypes.C
// 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,
symbol = strings.ToUpper(symbol)
info, err := s.client.Cryptocurrency.Info(&cmc.InfoOptions{
Slug: name,
})
if err != nil {
return ret, err
}
ret.MarketCapByAvailableSupply = graphData.MarketCapByAvailableSupply
ret.PriceBTC = graphData.PriceBTC
ret.Price = graphData.PriceUSD
ret.Volume = graphData.VolumeUSD
var coinID string
if len(info) == 0 {
return ret, ErrFetchGraphData
}
for k := range info {
coinID = fmt.Sprintf("%v", info[k].ID)
}
if convert == "" {
convert = "usd"
}
convert = strings.ToUpper(convert)
interval := getChartInterval(start, end)
params := []string{
fmt.Sprintf("convert=%s,%s", convert, symbol),
"format=chart_crypto_details",
fmt.Sprintf("id=%s", coinID),
fmt.Sprintf("interval=%s", interval),
fmt.Sprintf("time_start=%v", start),
fmt.Sprintf("time_end=%v", end),
}
baseURL := "https://web-api.coinmarketcap.com/v1.1"
url := fmt.Sprintf("%s/cryptocurrency/quotes/historical?%s", baseURL, strings.Join(params, "&"))
resp, err := makeReq(url)
if err != nil {
return ret, err
}
var result map[string]interface{}
err = json.Unmarshal(resp, &result)
if err != nil {
return ret, err
}
data, ok := result["data"]
if !ok {
return ret, ErrFetchGraphData
}
ifcs, ok := data.(map[string]interface{})
if !ok {
return ret, ErrFetchGraphData
}
var prices [][]float64
for datetime, item := range ifcs {
ifc, ok := item.(map[string]interface{})
if !ok {
return ret, ErrFetchGraphData
}
for key, obj := range ifc {
if key != convert {
continue
}
arrIfc, ok := obj.([]interface{})
if !ok {
return ret, ErrFetchGraphData
}
if len(arrIfc) == 0 {
return ret, ErrFetchGraphData
}
val := arrIfc[0].(float64)
t, err := time.Parse(time.RFC3339, datetime)
if err != nil {
return ret, err
}
prices = append(prices, []float64{float64(t.Unix()), val})
}
}
sort.Slice(prices[:], func(i, j int) bool {
return prices[i][0] < prices[j][0]
})
ret.Price = prices
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 convert == "" {
convert = "usd"
}
convert = strings.ToUpper(convert)
interval := getChartInterval(start, end)
params := []string{
fmt.Sprintf("convert=%s", convert),
"format=chart",
fmt.Sprintf("interval=%s", interval),
fmt.Sprintf("time_start=%v", start),
fmt.Sprintf("time_end=%v", end),
}
baseURL := "https://web-api.coinmarketcap.com/v1.1"
url := fmt.Sprintf("%s/global-metrics/quotes/historical?%s", baseURL, strings.Join(params, "&"))
resp, err := makeReq(url)
if err != nil {
return ret, err
}
ret.MarketCapByAvailableSupply = graphData.MarketCapByAvailableSupply
ret.VolumeUSD = graphData.VolumeUSD
var result map[string]interface{}
err = json.Unmarshal(resp, &result)
if err != nil {
return ret, err
}
data, ok := result["data"]
if !ok {
return ret, ErrFetchGraphData
}
mapIfc, ok := data.(map[string]interface{})
if !ok {
return ret, ErrFetchGraphData
}
var marketCap [][]float64
for datetime, item := range mapIfc {
arrIfc, ok := item.([]interface{})
if !ok {
return ret, ErrFetchGraphData
}
if len(arrIfc) == 0 {
return ret, ErrFetchGraphData
}
val := arrIfc[0].(float64)
t, err := time.Parse(time.RFC3339, datetime)
if err != nil {
return ret, err
}
marketCap = append(marketCap, []float64{float64(t.Unix()), val})
}
sort.Slice(marketCap[:], func(i, j int) bool {
return marketCap[i][0] < marketCap[j][0]
})
ret.MarketCapByAvailableSupply = marketCap
return ret, nil
}
@ -229,7 +339,6 @@ func (s *Service) CoinLink(name string) string {
// SupportedCurrencies returns a list of supported currencies
func (s *Service) SupportedCurrencies() []string {
// keep these in alphabetical order
return []string{
"AUD",
@ -269,3 +378,55 @@ func (s *Service) SupportedCurrencies() []string {
"ZAR",
}
}
// doReq does HTTP request with client
func doReq(req *http.Request) ([]byte, error) {
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("%s", body)
}
return body, nil
}
// makeReq is an HTTP GET request helper
func makeReq(url string) ([]byte, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
resp, err := doReq(req)
if err != nil {
return nil, err
}
return resp, err
}
// getChartInterval returns the interval to use for given time range
func getChartInterval(start, end int64) string {
interval := "15m"
delta := end - start
if delta >= 604800 {
interval = "1h"
}
if delta >= 2629746 {
interval = "1d"
}
if delta >= 604800 {
interval = "1h"
}
if delta >= 2592000 {
interval = "1d"
}
return interval
}

@ -198,7 +198,16 @@ func (lc *LineChart) calcLabelX() {
}
func shortenFloatVal(x float64) string {
s := fmt.Sprintf("%.2f", x)
if x > 1e12 {
return fmt.Sprintf("%.2fT", x/1e12)
}
if x > 1e9 {
return fmt.Sprintf("%.2fB", x/1e9)
}
if x > 1e6 {
return fmt.Sprintf("%.2fB", x/1e6)
}
//if len(s)-3 > 3 {
//s = fmt.Sprintf("%.2e", x)
//}
@ -206,7 +215,7 @@ func shortenFloatVal(x float64) string {
//if x < 0 {
//s = fmt.Sprintf("%.2f", x)
//}
return s
return fmt.Sprintf("%.2f", x)
}
func (lc *LineChart) calcLabelY() {
@ -217,7 +226,8 @@ func (lc *LineChart) calcLabelY() {
lc.labelY = make([][]rune, n)
maxLen := 0
for i := 0; i < n; i++ {
s := str2runes(shortenFloatVal(lc.bottomValue + float64(i)*span/float64(n)))
val := lc.bottomValue + float64(i)*span/float64(n)
s := str2runes(shortenFloatVal(val))
if len(s) > maxLen {
maxLen = len(s)
}

Loading…
Cancel
Save