forked from syscoin/blockbook
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcoingecko.go
136 lines (122 loc) · 3.67 KB
/
coingecko.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package fiat
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"strconv"
"time"
"github.com/golang/glog"
"github.com/syscoin/blockbook/db"
)
// Coingecko is a structure that implements RatesDownloaderInterface
type Coingecko struct {
url string
coin string
httpTimeoutSeconds time.Duration
timeFormat string
}
// NewCoinGeckoDownloader creates a coingecko structure that implements the RatesDownloaderInterface
func NewCoinGeckoDownloader(url string, coin string, timeFormat string) RatesDownloaderInterface {
return &Coingecko{
url: url,
coin: coin,
httpTimeoutSeconds: 15 * time.Second,
timeFormat: timeFormat,
}
}
// makeRequest retrieves the response from Coingecko API at the specified date.
// If timestamp is nil, it fetches the latest market data available.
func (cg *Coingecko) makeRequest(timestamp *time.Time) ([]byte, error) {
requestURL := cg.url + "/coins/" + cg.coin
if timestamp != nil {
requestURL += "/history"
}
req, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
glog.Errorf("Error creating a new request for %v: %v", requestURL, err)
return nil, err
}
req.Close = true
req.Header.Set("Content-Type", "application/json")
// Add query parameters
q := req.URL.Query()
// Add a unix timestamp to query parameters to get uncached responses
currentTimestamp := strconv.FormatInt(time.Now().UTC().UnixNano(), 10)
q.Add("current_timestamp", currentTimestamp)
if timestamp == nil {
q.Add("market_data", "true")
q.Add("localization", "false")
q.Add("tickers", "false")
q.Add("community_data", "false")
q.Add("developer_data", "false")
} else {
timestampFormatted := timestamp.Format(cg.timeFormat)
q.Add("date", timestampFormatted)
}
req.URL.RawQuery = q.Encode()
client := &http.Client{
Timeout: cg.httpTimeoutSeconds,
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.New("Invalid response status: " + string(resp.Status))
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return bodyBytes, nil
}
// GetData gets fiat rates from API at the specified date and returns a CurrencyRatesTicker
// If timestamp is nil, it will download the current fiat rates.
func (cg *Coingecko) getTicker(timestamp *time.Time) (*db.CurrencyRatesTicker, error) {
dataTimestamp := timestamp
if timestamp == nil {
timeNow := time.Now()
dataTimestamp = &timeNow
}
dataTimestampUTC := dataTimestamp.UTC()
ticker := &db.CurrencyRatesTicker{Timestamp: &dataTimestampUTC}
bodyBytes, err := cg.makeRequest(timestamp)
if err != nil {
return nil, err
}
type FiatRatesResponse struct {
MarketData struct {
Prices map[string]float64 `json:"current_price"`
} `json:"market_data"`
}
var data FiatRatesResponse
err = json.Unmarshal(bodyBytes, &data)
if err != nil {
glog.Errorf("Error parsing FiatRates response: %v", err)
return nil, err
}
ticker.Rates = data.MarketData.Prices
return ticker, nil
}
// MarketDataExists checks if there's data available for the specific timestamp.
func (cg *Coingecko) marketDataExists(timestamp *time.Time) (bool, error) {
resp, err := cg.makeRequest(timestamp)
if err != nil {
glog.Error("Error getting market data: ", err)
return false, err
}
type FiatRatesResponse struct {
MarketData struct {
Prices map[string]interface{} `json:"current_price"`
} `json:"market_data"`
}
var data FiatRatesResponse
err = json.Unmarshal(resp, &data)
if err != nil {
glog.Errorf("Error parsing Coingecko response: %v", err)
return false, err
}
return len(data.MarketData.Prices) != 0, nil
}