mirror of https://github.com/miguelmota/cointop
currencyconversion var
parent
9bc7d61f02
commit
40f0b3a3b3
@ -0,0 +1,356 @@
|
||||
// Package coinmarketcap Coin Market Cap API client for Go
|
||||
package coinmarketcap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/anaskhan96/soup"
|
||||
"github.com/coincircle/go-coinmarketcap/types"
|
||||
)
|
||||
|
||||
var (
|
||||
siteURL = "https://coinmarketcap.com"
|
||||
baseURL = "https://api.coinmarketcap.com/v2"
|
||||
coinGraphURL = "https://graphs2.coinmarketcap.com/currencies"
|
||||
globalMarketGraphURL = "https://graphs2.coinmarketcap.com/global/marketcap-total"
|
||||
altcoinMarketGraphURL = "https://graphs2.coinmarketcap.com/global/marketcap-altcoin"
|
||||
)
|
||||
|
||||
// Interface interface
|
||||
type Interface interface {
|
||||
Listings() ([]*types.Listing, error)
|
||||
Tickers(options *TickersOptions) (map[string]*types.Ticker, error)
|
||||
Ticker(options *TickerOptions) (*types.Ticker, error)
|
||||
TickerGraph(options *TickerGraphOptions) (*types.TickerGraph, error)
|
||||
GlobalMarket(options *GlobalMarketOptions) (*types.GlobalMarket, error)
|
||||
GlobalMarketGraph(options *GlobalMarketGraphOptions) (*types.MarketGraph, error)
|
||||
GlobalAltcoinMarketGraph(options *GlobalAltcoinMarketGraphOptions) (*types.MarketGraph, error)
|
||||
Markets(options *MarketsOptions) ([]*types.Market, error)
|
||||
Price(options *PriceOptions) (float64, error)
|
||||
CoinID(symbol string) (int, error)
|
||||
CoinSlug(symbol string) (string, error)
|
||||
}
|
||||
|
||||
// listingsMedia listings response media
|
||||
type listingsMedia struct {
|
||||
Data []*types.Listing `json:"data"`
|
||||
}
|
||||
|
||||
// Listings gets all coin listings
|
||||
func Listings() ([]*types.Listing, error) {
|
||||
url := fmt.Sprintf("%s/listings", baseURL)
|
||||
resp, err := makeReq(url)
|
||||
var body listingsMedia
|
||||
err = json.Unmarshal(resp, &body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body.Data, nil
|
||||
}
|
||||
|
||||
// TickersOptions options for tickers method
|
||||
type TickersOptions struct {
|
||||
Start int
|
||||
Limit int
|
||||
Convert string
|
||||
}
|
||||
|
||||
// tickerMedia tickers response media
|
||||
type tickersMedia struct {
|
||||
Data map[string]*types.Ticker `json:"data"`
|
||||
}
|
||||
|
||||
// Tickers gets ticker information on coins
|
||||
func Tickers(options *TickersOptions) (map[string]*types.Ticker, error) {
|
||||
var params []string
|
||||
if options.Start >= 0 {
|
||||
params = append(params, fmt.Sprintf("start=%v", options.Start))
|
||||
}
|
||||
if options.Limit >= 0 {
|
||||
params = append(params, fmt.Sprintf("limit=%v", options.Limit))
|
||||
}
|
||||
if options.Convert != "" {
|
||||
params = append(params, fmt.Sprintf("convert=%v", options.Convert))
|
||||
}
|
||||
url := fmt.Sprintf("%s/ticker?%s", baseURL, strings.Join(params, "&"))
|
||||
resp, err := makeReq(url)
|
||||
var body tickersMedia
|
||||
err = json.Unmarshal(resp, &body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tickers := make(map[string]*types.Ticker)
|
||||
data := body.Data
|
||||
for _, v := range data {
|
||||
tickers[strings.ToUpper(string(v.Symbol))] = v
|
||||
}
|
||||
return tickers, nil
|
||||
}
|
||||
|
||||
// TickerOptions options for ticker method
|
||||
type TickerOptions struct {
|
||||
Symbol string
|
||||
Convert string
|
||||
}
|
||||
|
||||
type tickerMedia struct {
|
||||
Data *types.Ticker `json:"data"`
|
||||
}
|
||||
|
||||
// Ticker gets ticker information about a cryptocurrency
|
||||
func Ticker(options *TickerOptions) (*types.Ticker, error) {
|
||||
var params []string
|
||||
if options.Convert != "" {
|
||||
params = append(params, fmt.Sprintf("convert=%v", options.Convert))
|
||||
}
|
||||
id, err := CoinID(options.Symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url := fmt.Sprintf("%s/ticker/%v?%s", baseURL, id, strings.Join(params, "&"))
|
||||
resp, err := makeReq(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var body tickerMedia
|
||||
err = json.Unmarshal(resp, &body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body.Data, nil
|
||||
}
|
||||
|
||||
// TickerGraphOptions options for ticker graph
|
||||
type TickerGraphOptions struct {
|
||||
Symbol string
|
||||
Start int64
|
||||
End int64
|
||||
}
|
||||
|
||||
// TickerGraph gets graph data points for a cryptocurrency
|
||||
func TickerGraph(options *TickerGraphOptions) (*types.TickerGraph, error) {
|
||||
slug, err := CoinSlug(options.Symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url := fmt.Sprintf("%s/%s/%d/%d", coinGraphURL, slug, options.Start*1000, options.End*1000)
|
||||
resp, err := makeReq(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data *types.TickerGraph
|
||||
err = json.Unmarshal(resp, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GlobalMarketOptions options for global data method
|
||||
type GlobalMarketOptions struct {
|
||||
Convert string
|
||||
}
|
||||
|
||||
// globalMedia global data response media
|
||||
type globalMarketMedia struct {
|
||||
Data *types.GlobalMarket `json:"data"`
|
||||
}
|
||||
|
||||
// GlobalMarket gets information about the global market of the cryptocurrencies
|
||||
func GlobalMarket(options *GlobalMarketOptions) (*types.GlobalMarket, error) {
|
||||
var params []string
|
||||
if options.Convert != "" {
|
||||
params = append(params, fmt.Sprintf("convert=%v", options.Convert))
|
||||
}
|
||||
url := fmt.Sprintf("%s/global?%s", baseURL, strings.Join(params, "&"))
|
||||
resp, err := makeReq(url)
|
||||
var body globalMarketMedia
|
||||
err = json.Unmarshal(resp, &body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body.Data, nil
|
||||
}
|
||||
|
||||
// GlobalMarketGraphOptions options for global market graph method
|
||||
type GlobalMarketGraphOptions struct {
|
||||
Start int64
|
||||
End int64
|
||||
}
|
||||
|
||||
// GlobalMarketGraph get graph data points of global market
|
||||
func GlobalMarketGraph(options *GlobalMarketGraphOptions) (*types.MarketGraph, error) {
|
||||
url := fmt.Sprintf("%s/%d/%d", globalMarketGraphURL, options.Start*1000, options.End*1000)
|
||||
resp, err := makeReq(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data *types.MarketGraph
|
||||
err = json.Unmarshal(resp, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GlobalAltcoinMarketGraphOptions options for global altcoin market graph method
|
||||
type GlobalAltcoinMarketGraphOptions struct {
|
||||
Start int64
|
||||
End int64
|
||||
}
|
||||
|
||||
// GlobalAltcoinMarketGraph gets graph data points of altcoin market
|
||||
func GlobalAltcoinMarketGraph(options *GlobalAltcoinMarketGraphOptions) (*types.MarketGraph, error) {
|
||||
url := fmt.Sprintf("%s/%d/%d", altcoinMarketGraphURL, options.Start*1000, options.End*1000)
|
||||
resp, err := makeReq(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data *types.MarketGraph
|
||||
err = json.Unmarshal(resp, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// MarketsOptions options for markets method
|
||||
type MarketsOptions struct {
|
||||
Symbol string
|
||||
}
|
||||
|
||||
// Markets get market data for a cryptocurrency
|
||||
func Markets(options *MarketsOptions) ([]*types.Market, error) {
|
||||
slug, err := CoinSlug(options.Symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url := fmt.Sprintf("%s/currencies/%s/#markets", siteURL, slug)
|
||||
var markets []*types.Market
|
||||
response, err := soup.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows := soup.HTMLParse(response).Find("table", "id", "markets-table").Find("tbody").FindAll("tr")
|
||||
for _, row := range rows {
|
||||
var data []string
|
||||
for _, column := range row.FindAll("td") {
|
||||
attrs := column.Attrs()
|
||||
if attrs["data-sort"] != "" {
|
||||
data = append(data, attrs["data-sort"])
|
||||
} else {
|
||||
data = append(data, column.Text())
|
||||
}
|
||||
}
|
||||
markets = append(markets, &types.Market{
|
||||
Rank: toInt(data[0]),
|
||||
Exchange: data[1],
|
||||
Pair: data[2],
|
||||
VolumeUSD: toFloat(data[3]),
|
||||
Price: toFloat(data[4]),
|
||||
VolumePercent: toFloat(data[5]),
|
||||
Updated: data[6],
|
||||
})
|
||||
}
|
||||
return markets, nil
|
||||
}
|
||||
|
||||
// PriceOptions options for price method
|
||||
type PriceOptions struct {
|
||||
Symbol string
|
||||
Convert string
|
||||
}
|
||||
|
||||
// Price gets price of a cryptocurrency
|
||||
func Price(options *PriceOptions) (float64, error) {
|
||||
coins, err := Tickers(&TickersOptions{
|
||||
Convert: options.Convert,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
coin := coins[options.Symbol]
|
||||
if coin == nil {
|
||||
return 0, errors.New("coin not found")
|
||||
}
|
||||
return coin.Quotes[options.Convert].Price, nil
|
||||
}
|
||||
|
||||
// CoinID gets the ID for the cryptocurrency
|
||||
func CoinID(symbol string) (int, error) {
|
||||
symbol = strings.ToUpper(strings.TrimSpace(symbol))
|
||||
coins, err := Tickers(&TickersOptions{})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
coin := coins[symbol]
|
||||
if coin == nil {
|
||||
return 0, errors.New("coin not found")
|
||||
}
|
||||
return coin.ID, nil
|
||||
}
|
||||
|
||||
// CoinSlug gets the slug for the cryptocurrency
|
||||
func CoinSlug(symbol string) (string, error) {
|
||||
symbol = strings.ToUpper(strings.TrimSpace(symbol))
|
||||
coins, err := Tickers(&TickersOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
coin := coins[symbol]
|
||||
if coin == nil {
|
||||
return "", errors.New("coin not found")
|
||||
}
|
||||
return coin.Slug, nil
|
||||
}
|
||||
|
||||
// toInt helper for parsing strings to int
|
||||
func toInt(rawInt string) int {
|
||||
parsed, _ := strconv.Atoi(strings.Replace(strings.Replace(rawInt, "$", "", -1), ",", "", -1))
|
||||
return parsed
|
||||
}
|
||||
|
||||
// toFloat helper for parsing strings to float
|
||||
func toFloat(rawFloat string) float64 {
|
||||
parsed, _ := strconv.ParseFloat(strings.Replace(strings.Replace(strings.Replace(rawFloat, "$", "", -1), ",", "", -1), "%", "", -1), 64)
|
||||
return parsed
|
||||
}
|
||||
|
||||
// doReq HTTP 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 200 != resp.StatusCode {
|
||||
return nil, fmt.Errorf("%s", body)
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// makeReq HTTP 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
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package types
|
||||
|
||||
// Listing struct
|
||||
type Listing struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Slug string `json:"website_slug"`
|
||||
}
|
||||
|
||||
// Ticker struct
|
||||
type Ticker struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Slug string `json:"website_slug"`
|
||||
Rank int `json:"rank"`
|
||||
CirculatingSupply float64 `json:"circulating_supply"`
|
||||
TotalSupply float64 `json:"total_supply"`
|
||||
MaxSupply float64 `json:"max_supply"`
|
||||
Quotes map[string]*TickerQuote `json:"quotes"`
|
||||
LastUpdated int `json:"last_updated"`
|
||||
}
|
||||
|
||||
// TickerQuote struct
|
||||
type TickerQuote struct {
|
||||
Price float64 `json:"price"`
|
||||
Volume24H float64 `json:"volume_24h"`
|
||||
MarketCap float64 `json:"market_cap"`
|
||||
PercentChange1H float64 `json:"percent_change_1h"`
|
||||
PercentChange24H float64 `json:"percent_change_24h"`
|
||||
PercentChange7D float64 `json:"percent_change_7d"`
|
||||
}
|
||||
|
||||
// GlobalMarket struct
|
||||
type GlobalMarket struct {
|
||||
ActiveCurrencies int `json:"active_cryptocurrencies"`
|
||||
ActiveMarkets int `json:"active_markets"`
|
||||
BitcoinPercentageOfMarketCap float64 `json:"bitcoin_percentage_of_market_cap"`
|
||||
LastUpdated int `json:"last_updated"`
|
||||
Quotes map[string]*GlobalMarketQuote `json:"quotes"`
|
||||
}
|
||||
|
||||
// GlobalMarketQuote struct
|
||||
type GlobalMarketQuote struct {
|
||||
TotalMarketCap float64 `json:"total_market_cap"`
|
||||
TotalVolume24H float64 `json:"total_volume_24h"`
|
||||
}
|
||||
|
||||
// TickerGraph struct
|
||||
type TickerGraph struct {
|
||||
MarketCapByAvailableSupply [][]float64 `json:"market_cap_by_available_supply"`
|
||||
PriceBTC [][]float64 `json:"price_btc"`
|
||||
PriceUSD [][]float64 `json:"price_usd"`
|
||||
VolumeUSD [][]float64 `json:"volume_usd"`
|
||||
}
|
||||
|
||||
// Market struct
|
||||
type Market struct {
|
||||
Rank int
|
||||
Exchange string
|
||||
Pair string
|
||||
VolumeUSD float64
|
||||
Price float64
|
||||
VolumePercent float64
|
||||
Updated string
|
||||
}
|
||||
|
||||
// MarketGraph struct
|
||||
type MarketGraph struct {
|
||||
MarketCapByAvailableSupply [][]float64 `json:"market_cap_by_available_supply"`
|
||||
VolumeUSD [][]float64 `json:"volume_usd"`
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
.DS_Store
|
@ -1,9 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.9.x"
|
||||
- "1.10.x"
|
||||
- "master"
|
||||
|
||||
script:
|
||||
- make test
|
@ -1,24 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/anaskhan96/soup"
|
||||
packages = ["."]
|
||||
revision = "7a8d31f81bad1a5abeed0d0219c35e5e295a5a76"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"html",
|
||||
"html/atom"
|
||||
]
|
||||
revision = "dc948dff8834a7fe1ca525f8d04e261c2b56e70d"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "ef92c4a55490132c4b677cba28dd5f0ee0bb397d0b3581ea46df6c3f4f169646"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
@ -1,34 +0,0 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/anaskhan96/soup"
|
||||
version = "1.0.1"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
@ -1,21 +0,0 @@
|
||||
MIT license
|
||||
|
||||
Copyright (C) 2015 Miguel Mota
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -1,5 +0,0 @@
|
||||
all:
|
||||
@echo "no default"
|
||||
|
||||
test:
|
||||
go test -v
|
@ -1,47 +0,0 @@
|
||||
# go-coinmarketcap
|
||||
|
||||
> The Unofficial [CoinMarketCap](https://coinmarketcap.com/) API client for Go.
|
||||
|
||||
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/miguelmota/go-coinmarketcap/master/LICENSE.md) [![Build Status](https://travis-ci.org/miguelmota/go-coinmarketcap.svg?branch=master)](https://travis-ci.org/miguelmota/go-coinmarketcap) [![Go Report Card](https://goreportcard.com/badge/github.com/miguelmota/go-coinmarketcap?)](https://goreportcard.com/report/github.com/miguelmota/go-coinmarketcap) [![GoDoc](https://godoc.org/github.com/miguelmota/go-coinmarketcap?status.svg)](https://godoc.org/github.com/miguelmota/go-coinmarketcap)
|
||||
|
||||
## Documentation
|
||||
|
||||
[https://godoc.org/github.com/miguelmota/go-coinmarketcap](https://godoc.org/github.com/miguelmota/go-coinmarketcap)
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get -u github.com/miguelmota/go-coinmarketcap
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
cmc "github.com/miguelmota/go-coinmarketcap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
coins, err := cmc.GetAllCoinData(0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, coin := range coins {
|
||||
fmt.Println(coin.Symbol, coin.PriceUSD)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Check out the [`./example`](./example) directory and documentation.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
@ -1,210 +0,0 @@
|
||||
// Package coinmarketcap Coin Market Cap API client for Go
|
||||
package coinmarketcap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/anaskhan96/soup"
|
||||
)
|
||||
|
||||
var (
|
||||
baseURL = "https://api.coinmarketcap.com/v1"
|
||||
coinGraphURL = "https://graphs2.coinmarketcap.com/currencies"
|
||||
globalMarketGraphURL = "https://graphs2.coinmarketcap.com/global/marketcap-total"
|
||||
altcoinMarketGraphURL = "https://graphs2.coinmarketcap.com/global/marketcap-altcoin"
|
||||
)
|
||||
|
||||
// GetGlobalMarketData get information about the global market data of the cryptocurrencies
|
||||
func GetGlobalMarketData() (GlobalMarketData, error) {
|
||||
url := fmt.Sprintf(baseURL + "/global/")
|
||||
|
||||
resp, err := makeReq(url)
|
||||
|
||||
var data GlobalMarketData
|
||||
err = json.Unmarshal(resp, &data)
|
||||
if err != nil {
|
||||
return GlobalMarketData{}, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetGlobalMarketGraphData get graph data points of global market
|
||||
func GetGlobalMarketGraphData(start int64, end int64) (MarketGraph, error) {
|
||||
url := fmt.Sprintf("%s/%d/%d", globalMarketGraphURL, start*1000, end*1000)
|
||||
resp, err := makeReq(url)
|
||||
if err != nil {
|
||||
return MarketGraph{}, err
|
||||
}
|
||||
var data MarketGraph
|
||||
err = json.Unmarshal(resp, &data)
|
||||
if err != nil {
|
||||
return MarketGraph{}, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetAltcoinMarketGraphData get graph data points of altcoin market
|
||||
func GetAltcoinMarketGraphData(start int64, end int64) (MarketGraph, error) {
|
||||
url := fmt.Sprintf("%s/%d/%d", altcoinMarketGraphURL, start*1000, end*1000)
|
||||
resp, err := makeReq(url)
|
||||
if err != nil {
|
||||
return MarketGraph{}, err
|
||||
}
|
||||
var data MarketGraph
|
||||
err = json.Unmarshal(resp, &data)
|
||||
if err != nil {
|
||||
return MarketGraph{}, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetCoinData get information about a crypto currency
|
||||
func GetCoinData(coin string) (Coin, error) {
|
||||
coin = strings.ToLower(coin)
|
||||
url := fmt.Sprintf("%s/ticker/%s", baseURL, coin)
|
||||
resp, err := makeReq(url)
|
||||
if err != nil {
|
||||
return Coin{}, err
|
||||
}
|
||||
var data []Coin
|
||||
err = json.Unmarshal(resp, &data)
|
||||
if err != nil {
|
||||
return Coin{}, err
|
||||
}
|
||||
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
// GetAllCoinData get information about all coins listed in Coin Market Cap
|
||||
func GetAllCoinData(limit int) (map[string]Coin, error) {
|
||||
var l string
|
||||
if limit >= 0 {
|
||||
l = fmt.Sprintf("?limit=%v", limit)
|
||||
}
|
||||
url := fmt.Sprintf("%s/ticker/%s", baseURL, l)
|
||||
|
||||
resp, err := makeReq(url)
|
||||
|
||||
var data []Coin
|
||||
err = json.Unmarshal(resp, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// creating map from the array
|
||||
allCoins := make(map[string]Coin)
|
||||
for i := 0; i < len(data); i++ {
|
||||
allCoins[data[i].ID] = data[i]
|
||||
}
|
||||
|
||||
return allCoins, nil
|
||||
}
|
||||
|
||||
// GetCoinGraphData get graph data points for a crypto currency
|
||||
func GetCoinGraphData(coin string, start int64, end int64) (CoinGraph, error) {
|
||||
url := fmt.Sprintf("%s/%s/%d/%d", coinGraphURL, strings.ToLower(coin), start*1000, end*1000)
|
||||
resp, err := makeReq(url)
|
||||
if err != nil {
|
||||
return CoinGraph{}, err
|
||||
}
|
||||
var data CoinGraph
|
||||
err = json.Unmarshal(resp, &data)
|
||||
if err != nil {
|
||||
return CoinGraph{}, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetCoinPriceUSD get USD price of crypto currency
|
||||
func GetCoinPriceUSD(coin string) (float64, error) {
|
||||
data, err := GetCoinData(strings.ToLower(coin))
|
||||
if err != nil {
|
||||
return float64(0), nil
|
||||
}
|
||||
return data.PriceUSD, nil
|
||||
}
|
||||
|
||||
// GetCoinMarkets get market data for a coin name
|
||||
func GetCoinMarkets(coin string) ([]Market, error) {
|
||||
url := fmt.Sprintf("https://coinmarketcap.com/currencies/%s/#markets", strings.ToLower(coin))
|
||||
var markets []Market
|
||||
response, err := soup.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows := soup.HTMLParse(response).Find("table", "id", "markets-table").Find("tbody").FindAll("tr")
|
||||
for _, row := range rows {
|
||||
var data []string
|
||||
for _, column := range row.FindAll("td") {
|
||||
attrs := column.Attrs()
|
||||
if attrs["data-sort"] != "" {
|
||||
data = append(data, attrs["data-sort"])
|
||||
} else {
|
||||
data = append(data, column.Text())
|
||||
}
|
||||
}
|
||||
markets = append(markets, Market{
|
||||
Rank: toInt(data[0]),
|
||||
Exchange: data[1],
|
||||
Pair: data[2],
|
||||
VolumeUSD: toFloat(data[3]),
|
||||
Price: toFloat(data[4]),
|
||||
VolumePercent: toFloat(data[5]),
|
||||
Updated: data[6],
|
||||
})
|
||||
}
|
||||
return markets, nil
|
||||
}
|
||||
|
||||
// doReq HTTP 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 200 != resp.StatusCode {
|
||||
return nil, fmt.Errorf("%s", body)
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// makeReq HTTP 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
|
||||
}
|
||||
|
||||
// toInt helper for parsing strings to int
|
||||
func toInt(rawInt string) int {
|
||||
parsed, _ := strconv.Atoi(strings.Replace(strings.Replace(rawInt, "$", "", -1), ",", "", -1))
|
||||
return parsed
|
||||
}
|
||||
|
||||
// toFloat helper for parsing strings to float
|
||||
func toFloat(rawFloat string) float64 {
|
||||
parsed, _ := strconv.ParseFloat(strings.Replace(strings.Replace(strings.Replace(rawFloat, "$", "", -1), ",", "", -1), "%", "", -1), 64)
|
||||
return parsed
|
||||
}
|
@ -1,219 +0,0 @@
|
||||
package coinmarketcap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetGlobalMarketData(t *testing.T) {
|
||||
market, err := GetGlobalMarketData()
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if market.ActiveAssets == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.ActiveCurrencies == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.ActiveMarkets == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.BitcoinPercentageOfMarketCap == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.Total24HVolumeUSD == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.TotalMarketCapUSD == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGlobalMarketGraphData(t *testing.T) {
|
||||
var threeMonths int64 = (60 * 60 * 24 * 90)
|
||||
end := time.Now().Unix()
|
||||
start := end - threeMonths
|
||||
|
||||
graph, err := GetGlobalMarketGraphData(start, end)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if graph.MarketCapByAvailableSupply[0][0] == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if graph.VolumeUSD[0][0] == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAltcoinMarketGraphData(t *testing.T) {
|
||||
var threeMonths int64 = (60 * 60 * 24 * 90)
|
||||
end := time.Now().Unix()
|
||||
start := end - threeMonths
|
||||
|
||||
graph, err := GetAltcoinMarketGraphData(start, end)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if graph.MarketCapByAvailableSupply[0][0] == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if graph.VolumeUSD[0][0] == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCoinData(t *testing.T) {
|
||||
coin, err := GetCoinData("ethereum")
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if coin.AvailableSupply == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.ID == "" {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.LastUpdated == "" {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.MarketCapUSD == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.Name == "" {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.PercentChange1H == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.PercentChange24H == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.PercentChange7D == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.PriceBTC == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.PriceUSD == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.Rank == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.Symbol == "" {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.TotalSupply == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if coin.USD24HVolume == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllCoinData(t *testing.T) {
|
||||
coins, err := GetAllCoinData(10)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if len(coins) != 10 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCoinGraphData(t *testing.T) {
|
||||
var threeMonths int64 = (60 * 60 * 24 * 90)
|
||||
end := time.Now().Unix()
|
||||
start := end - threeMonths
|
||||
|
||||
graph, err := GetCoinGraphData("ethereum", start, end)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if graph.MarketCapByAvailableSupply[0][0] == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if graph.PriceBTC[0][0] == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if graph.PriceUSD[0][0] == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if graph.VolumeUSD[0][0] == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCoinPriceUSD(t *testing.T) {
|
||||
price, err := GetCoinPriceUSD("ethereum")
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
if price <= 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCoinMarkets(t *testing.T) {
|
||||
markets, err := GetCoinMarkets("ethereum")
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
if len(markets) == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
market := markets[0]
|
||||
if market.Rank == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.Exchange == "" {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.Pair == "" {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.VolumeUSD == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.Price == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.VolumePercent == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if market.Updated == "" {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoReq(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestMakeReq(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestToInt(t *testing.T) {
|
||||
v := toInt("5")
|
||||
if v != 5 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestToFloat(t *testing.T) {
|
||||
v := toFloat("5.2")
|
||||
if v != 5.2 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
cmc "github.com/miguelmota/go-coinmarketcap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// get data for all coins
|
||||
coins, err := cmc.GetAllCoinData(0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, coin := range coins {
|
||||
fmt.Println(coin.Symbol, coin.PriceUSD)
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
cmc "github.com/miguelmota/go-coinmarketcap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Get info about coin
|
||||
coinInfo, err := cmc.GetCoinData("ethereum")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(coinInfo)
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
cmc "github.com/miguelmota/go-coinmarketcap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
threeMonths := int64(60 * 60 * 24 * 90)
|
||||
now := time.Now()
|
||||
secs := now.Unix()
|
||||
start := secs - threeMonths
|
||||
end := secs
|
||||
|
||||
// Get graph data for coin
|
||||
coinGraphData, err := cmc.GetCoinGraphData("ethereum", start, end)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(coinGraphData)
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
cmc "github.com/miguelmota/go-coinmarketcap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Get global market data
|
||||
marketInfo, err := cmc.GetGlobalMarketData()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(marketInfo)
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package coinmarketcap
|
||||
|
||||
// Interface interface
|
||||
type Interface interface {
|
||||
GetGlobalMarketData() (GlobalMarketData, error)
|
||||
GetGlobalMarketGraphData(start int64, end int64) (MarketGraph, error)
|
||||
GetAltcoinMarketGraphData(start int64, end int64) (MarketGraph, error)
|
||||
GetCoinData(coin string) (Coin, error)
|
||||
GetAllCoinData(limit int) (map[string]Coin, error)
|
||||
GetCoinGraphData(coin string, start int64, end int64) (CoinGraph, error)
|
||||
GetCoinPriceUSD(coin string) (float64, error)
|
||||
GetCoinMarkets(coin string) ([]Market, error)
|
||||
}
|
||||
|
||||
// Coin struct
|
||||
type Coin struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Rank int `json:"rank,string"`
|
||||
PriceUSD float64 `json:"price_usd,string"`
|
||||
PriceBTC float64 `json:"price_btc,string"`
|
||||
USD24HVolume float64 `json:"24h_volume_usd,string"`
|
||||
MarketCapUSD float64 `json:"market_cap_usd,string"`
|
||||
AvailableSupply float64 `json:"available_supply,string"`
|
||||
TotalSupply float64 `json:"total_supply,string"`
|
||||
PercentChange1H float64 `json:"percent_change_1h,string"`
|
||||
PercentChange24H float64 `json:"percent_change_24h,string"`
|
||||
PercentChange7D float64 `json:"percent_change_7d,string"`
|
||||
LastUpdated string `json:"last_updated"`
|
||||
}
|
||||
|
||||
// GlobalMarketData struct
|
||||
type GlobalMarketData struct {
|
||||
TotalMarketCapUSD float64 `json:"total_market_cap_usd"`
|
||||
Total24HVolumeUSD float64 `json:"total_24h_volume_usd"`
|
||||
BitcoinPercentageOfMarketCap float64 `json:"bitcoin_percentage_of_market_cap"`
|
||||
ActiveCurrencies int `json:"active_currencies"`
|
||||
ActiveAssets int `json:"active_assets"`
|
||||
ActiveMarkets int `json:"active_markets"`
|
||||
}
|
||||
|
||||
// CoinGraph struct
|
||||
type CoinGraph struct {
|
||||
MarketCapByAvailableSupply [][]float64 `json:"market_cap_by_available_supply"`
|
||||
PriceBTC [][]float64 `json:"price_btc"`
|
||||
PriceUSD [][]float64 `json:"price_usd"`
|
||||
VolumeUSD [][]float64 `json:"volume_usd"`
|
||||
}
|
||||
|
||||
// Market struct
|
||||
type Market struct {
|
||||
Rank int
|
||||
Exchange string
|
||||
Pair string
|
||||
VolumeUSD float64
|
||||
Price float64
|
||||
VolumePercent float64
|
||||
Updated string
|
||||
}
|
||||
|
||||
// MarketGraph struct
|
||||
type MarketGraph struct {
|
||||
MarketCapByAvailableSupply [][]float64 `json:"market_cap_by_available_supply"`
|
||||
VolumeUSD [][]float64 `json:"volume_usd"`
|
||||
}
|
Loading…
Reference in New Issue