package coindesk import ( "encoding/json" "strconv" "strings" "time" ) // Asset is a cryptocurrency, like Bitcoin, Ethereum, etc. type Asset string // Response represents the general top-level format of Coindesk API responses. type Response[T any] struct { StatusCode int `json:"statusCode"` Message string `json:"message"` Data T `json:"data"` } // PriceValues contains a series of timestamped prices for a particular asset. type PriceValues struct { ISO Asset `json:"iso"` Name string `json:"name"` Slug string `json:"slug"` IngestionStart Date `json:"ingestionStart"` Entries []TimestampPrice `json:"entries"` } // AssetTickers is a map from an asset to its ticker data. type AssetTickers map[Asset]AssetTicker // AssetTicker is a snapshot of pricing data for an asset. type AssetTicker struct { ISO Asset `json:"iso"` Name string `json:"name"` Slug string `json:"slug"` Change struct { Percent float64 `json:"percent"` Value float64 `json:"value"` } `json:"change"` OHLC struct { Opening Price `json:"o"` High Price `json:"h"` Low Price `json:"l"` Closing Price `json:"c"` } `json:"ohlc"` CirculatingSupply float64 `json:"circulatingSupply"` MarketCap Price `json:"marketCap"` Timestamp Timestamp `json:"ts"` } // TimestampPrice represents a JSON array with two elements: an integer Unix // timestamp expressed in milliseconds, and a floating-point USD price. type TimestampPrice struct { Timestamp Timestamp Price Price } func (t *TimestampPrice) UnmarshalJSON(b []byte) error { a := []interface{}{&t.Timestamp, &t.Price} return json.Unmarshal(b, &a) } // Timestamp represents an integer Unix timestamp expressed in milliseconds // which has been converted into a Golang time.Time object. type Timestamp time.Time func (t *Timestamp) UnmarshalJSON(b []byte) error { s := string(b) n := len(s) secsStr := s[0 : n-3] millisStr := s[n-3:] secs, err := strconv.ParseInt(secsStr, 10, 64) if err != nil { return err } millis, err := strconv.ParseInt(millisStr, 10, 64) if err != nil { return err } converted := time.Unix(secs, millis*1e6) *t = Timestamp(converted) return nil } // Price represents the USD price of an asset. type Price float64 // Date represents a date-only string which has been converted into a Golang // time.Time object. type Date time.Time func (d *Date) UnmarshalJSON(b []byte) error { s := string(b) s, _ = strings.CutPrefix(s, "\"") s, _ = strings.CutSuffix(s, "\"") t, err := time.Parse(time.DateOnly, s) if err != nil { return err } *d = Date(t) return nil }