From 961d9c41146b9843e86b833f0a2e444f211d58a0 Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Wed, 11 Mar 2015 17:50:46 +1100 Subject: [PATCH] Added unauthenticated Coinbase Exchange HTTP API support. --- coinbase.go | 220 ++++++++++++++++++++++++++++++++++++++++++++ config_example.json | 10 ++ events.go | 1 + main.go | 24 +++++ 4 files changed, 255 insertions(+) create mode 100644 coinbase.go diff --git a/coinbase.go b/coinbase.go new file mode 100644 index 00000000..bd9febb0 --- /dev/null +++ b/coinbase.go @@ -0,0 +1,220 @@ +package main + +import ( + "log" + "fmt" + "strconv" + "net/url" +) + +const ( + COINBASE_API_URL = "https://api.exchange.coinbase.com/" + COINBASE_API_VERISON = "0" + COINBASE_PRODUCTS = "products" + COINBASE_ORDERBOOK = "book" + COINBASE_TICKER = "ticker" + COINBASE_TRADES = "trades" + COINBASE_HISTORY = "candles" + COINBASE_STATS = "stats" + COINBASE_CURRENCIES = "currencies" +) + +type Coinbase struct { + Name string + Enabled bool + Verbose bool + Password, APIKey, APISecret string + TakerFee, MakerFee float64 +} + +type CoinbaseTicker struct { + TradeID int64 `json:"trade_id"` + Price float64 `json:"price,string"` + Size float64 `json:"size,string"` + Time string `json:"time"` +} + +type CoinbaseProduct struct { + ID string `json:"id"` + BaseCurrency string `json:"base_currency"` + QuoteCurrency string `json:"quote_currency"` + BaseMinSize float64 `json:"base_min_size"` + BaseMaxSize int64 `json:"base_max_size"` + QuoteIncrement float64 `json:"quote_increment"` + DisplayName string `json:"string"` +} + +type CoinbaseOrderbook struct { + Asks [][]interface{} `json:"ask"` + Bids [][]interface{} `json:"bids"` + Sequence int64 `json:"sequence"` +} + +type CoinbaseTrade struct { + TradeID int64 `json:"trade_id"` + Price float64 `json:"price,string"` + Size float64 `json:"size,string"` + Time string `json:"time"` + Side string `json:"side"` +} + +type CoinbaseStats struct { + Open float64 `json:"open,string"` + High float64 `json:"high,string"` + Low float64 `json:"low,string"` + Volume float64 `json:"volume,string"` +} + +type CoinbaseCurrency struct { + ID string + Name string + MinSize float64 `json:"min_size,string"` +} + +type CoinbaseHistory struct { + Time int64 + Low float64 + High float64 + Open float64 + Close float64 + Volume float64 +} + +func (c *Coinbase) SetDefaults() { + c.Name = "Coinbase" + c.Enabled = true + c.Verbose = false + c.TakerFee = 0.25 + c.MakerFee = 0 +} + +func (c *Coinbase) GetName() (string) { + return c.Name +} + +func (c *Coinbase) SetEnabled(enabled bool) { + c.Enabled = enabled +} + +func (c *Coinbase) IsEnabled() (bool) { + return c.Enabled +} + +func (c *Coinbase) GetFee(maker bool) (float64) { + if maker { + return c.MakerFee + } else { + return c.TakerFee + } +} + +func (c *Coinbase) SetAPIKeys(password, apiKey, apiSecret string) { + c.Password = password + c.APIKey = apiKey + c.APISecret = apiSecret +} + +func (c *Coinbase) GetProducts() { + products := []CoinbaseProduct{} + err := SendHTTPRequest(COINBASE_API_URL + COINBASE_PRODUCTS, true, &products) + + if err != nil { + log.Println(err) + } + + log.Println(products) +} + +func (c *Coinbase) GetOrderbook(symbol string, level int) { + orderbook := CoinbaseOrderbook{} + path := "" + if level > 0 { + levelStr := strconv.Itoa(level) + path = fmt.Sprintf("%s/%s/%s?level=%s", COINBASE_API_URL + COINBASE_PRODUCTS, symbol, COINBASE_ORDERBOOK, levelStr) + } else { + path = fmt.Sprintf("%s/%s/%s", COINBASE_API_URL + COINBASE_PRODUCTS, symbol, COINBASE_ORDERBOOK) + } + + err := SendHTTPRequest(path, true, &orderbook) + + if err != nil { + log.Println(err) + } + log.Println(orderbook) +} + +func (c *Coinbase) GetTicker(symbol string) (CoinbaseTicker) { + ticker := CoinbaseTicker{} + path := fmt.Sprintf("%s/%s/%s", COINBASE_API_URL + COINBASE_PRODUCTS, symbol, COINBASE_TICKER) + err := SendHTTPRequest(path, true, &ticker) + + if err != nil { + log.Println(err) + return CoinbaseTicker{} + } + return ticker +} + +func (c *Coinbase) GetTrades(symbol string) { + trades := []CoinbaseTrade{} + path := fmt.Sprintf("%s/%s/%s", COINBASE_API_URL + COINBASE_PRODUCTS, symbol, COINBASE_TRADES) + err := SendHTTPRequest(path, true, &trades) + + if err != nil { + log.Println(err) + } + log.Println(trades) +} + +func (c *Coinbase) GetHistoricRates(symbol string, start, end, granularity int64) { + history := []CoinbaseHistory{} + values := url.Values{} + + if start > 0 { + values.Set("start", strconv.FormatInt(start, 10)) + } + + if end > 0 { + values.Set("end", strconv.FormatInt(end, 10)) + } + + if granularity > 0 { + values.Set("granularity", strconv.FormatInt(granularity, 10)) + } + + path := fmt.Sprintf("%s/%s/%s", COINBASE_API_URL + COINBASE_PRODUCTS, symbol, COINBASE_HISTORY) + encoded := values.Encode() + + if (len(encoded) > 0) { + path += encoded + } + + err := SendHTTPRequest(path, true, &history) + + if err != nil { + log.Println(err) + } + log.Println(history) +} + +func (c *Coinbase) GetStats(symbol string) (CoinbaseStats) { + stats := CoinbaseStats{} + path := fmt.Sprintf("%s/%s/%s", COINBASE_API_URL + COINBASE_PRODUCTS, symbol, COINBASE_STATS) + err := SendHTTPRequest(path, true, &stats) + + if err != nil { + log.Println(err) + return CoinbaseStats{} + } + return stats +} + +func (c *Coinbase) GetCurrencies() { + currencies := []CoinbaseCurrency{} + err := SendHTTPRequest(COINBASE_API_URL + COINBASE_CURRENCIES, true, ¤cies) + + if err != nil { + log.Println(err) + } + log.Println(currencies) +} \ No newline at end of file diff --git a/config_example.json b/config_example.json index 81d9732e..6297d08e 100644 --- a/config_example.json +++ b/config_example.json @@ -56,6 +56,16 @@ "Enabled": true, "Verbose": false }, + { + "Name": "Coinbase", + "Pairs": "BTCUSD", + "ClientID": "Password", + "APIKey": "Key", + "APISecret": "Secret", + "BaseCurrencies": "USD", + "Enabled": true, + "Verbose": false + }, { "Name": "Huobi", "Pairs": "BTCCNY,LTCCNY,LTCBTC", diff --git a/events.go b/events.go index f4644752..324c6c44 100644 --- a/events.go +++ b/events.go @@ -204,6 +204,7 @@ func IsValidExchange(Exchange string) (bool) { bot.exchange.btcchina.GetName() == Exchange && bot.exchange.btcchina.IsEnabled() || bot.exchange.btce.GetName() == Exchange && bot.exchange.btcchina.IsEnabled() || bot.exchange.btcmarkets.GetName() == Exchange && bot.exchange.btcmarkets.IsEnabled() || + bot.exchange.coinbase.GetName() == Exchange && bot.exchange.coinbase.IsEnabled() || bot.exchange.huobi.GetName() == Exchange && bot.exchange.huobi.IsEnabled() || bot.exchange.itbit.GetName() == Exchange && bot.exchange.itbit.IsEnabled() || bot.exchange.kraken.GetName() == Exchange && bot.exchange.kraken.IsEnabled() || diff --git a/main.go b/main.go index 5627ccb5..75421854 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ type Exchange struct { bitfinex Bitfinex btce BTCE btcmarkets BTCMarkets + coinbase Coinbase okcoinChina OKCoin okcoinIntl OKCoin itbit ItBit @@ -80,6 +81,7 @@ func main() { bot.exchange.bitfinex.SetDefaults() bot.exchange.btce.SetDefaults() bot.exchange.btcmarkets.SetDefaults() + bot.exchange.coinbase.SetDefaults() bot.exchange.okcoinChina.SetURL(OKCOIN_API_URL_CHINA) bot.exchange.okcoinChina.SetDefaults() bot.exchange.okcoinIntl.SetURL(OKCOIN_API_URL) @@ -167,6 +169,21 @@ func main() { log.Printf("%s Verbose output disabled.\n", exch.Name) } } + } else if bot.exchange.coinbase.GetName() == exch.Name { + if !exch.Enabled { + bot.exchange.coinbase.SetEnabled(false) + log.Printf("%s disabled.\n", exch.Name) + } else { + log.Printf("%s enabled.\n", exch.Name) + bot.exchange.coinbase.SetAPIKeys(exch.ClientID, exch.APIKey, exch.APISecret) + + if exch.Verbose { + bot.exchange.coinbase.Verbose = true + log.Printf("%s Verbose output enabled.\n", exch.Name) + } else { + log.Printf("%s Verbose output disabled.\n", exch.Name) + } + } } else if bot.exchange.okcoinChina.GetName() == exch.Name { if !exch.Enabled { bot.exchange.okcoinChina.SetEnabled(false) @@ -269,6 +286,13 @@ func main() { //temp until proper asynchronous method of getting pricing/order books is coded for { //spot + if bot.exchange.coinbase.IsEnabled() { + go func() { + CoinbaseStats := bot.exchange.coinbase.GetStats("BTC-USD") + CoinbaseTicker := bot.exchange.coinbase.GetTicker("BTC-USD") + log.Printf("Coinbase BTC: Last %f High %f Low %f Volume %f\n", CoinbaseTicker.Price, CoinbaseStats.High, CoinbaseStats.Low, CoinbaseStats.Volume) + }() + } if bot.exchange.kraken.IsEnabled() { go func() { KrakenBTC := bot.exchange.kraken.GetTicker("XBTUSD")