From c1910bf6b68640bc6b09c13426de85b74e4c81e6 Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Wed, 21 Oct 2015 10:14:34 +1100 Subject: [PATCH 1/2] Added common EncodeURLValues function. --- common.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/common.go b/common.go index 3a807dee..3fcc2e70 100644 --- a/common.go +++ b/common.go @@ -16,6 +16,7 @@ import ( "log" "math" "net/http" + "net/url" "strings" ) @@ -259,3 +260,11 @@ func JSONDecode(data []byte, to interface{}) error { return nil } + +func EncodeURLValues(url string, values url.Values) string { + path := url + if len(values) > 0 { + path += "?" + values.Encode() + } + return path +} From a7e90cb703c161d89a6671c93c8d9bb97c2f360b Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Tue, 20 Oct 2015 12:57:59 +1100 Subject: [PATCH 2/2] Updated Bitstamp REST API (complete coverage) --- bitstamphttp.go | 324 ++++++++++++++++++++++++++++++++++++------------ events.go | 7 +- 2 files changed, 254 insertions(+), 77 deletions(-) diff --git a/bitstamphttp.go b/bitstamphttp.go index e7e19cb6..19463038 100644 --- a/bitstamphttp.go +++ b/bitstamphttp.go @@ -13,13 +13,16 @@ const ( BITSTAMP_API_URL = "https://www.bitstamp.net/api/" BITSTAMP_API_VERSION = "0" BITSTAMP_API_TICKER = "ticker/" + BITSTAMP_API_TICKER_HOURLY = "ticker_hour/" BITSTAMP_API_ORDERBOOK = "order_book/" BITSTAMP_API_TRANSACTIONS = "transactions/" BITSTAMP_API_EURUSD = "eur_usd/" BITSTAMP_API_BALANCE = "balance/" BITSTAMP_API_USER_TRANSACTIONS = "user_transactions/" BITSTAMP_API_OPEN_ORDERS = "open_orders/" + BITSTAMP_API_ORDER_STATUS = "order_status" BITSTAMP_API_CANCEL_ORDER = "cancel_order/" + BITSTAMP_API_CANCEL_ALL_ORDERS = "cancel_all_orders/" BITSTAMP_API_BUY = "buy/" BITSTAMP_API_SELL = "sell/" BITSTAMP_API_WITHDRAWAL_REQUESTS = "withdrawal_requests/" @@ -38,10 +41,6 @@ type Bitstamp struct { RESTPollingDelay time.Duration AuthenticatedAPISupport bool ClientID, APIKey, APISecret string - Ticker BitstampTicker - Orderbook Orderbook - ConversionRate ConversionRate - Transactions []Transactions Balance BitstampAccountBalance TakerFee, MakerFee float64 BaseCurrencies []string @@ -69,20 +68,73 @@ type BitstampAccountBalance struct { USDAvailable float64 `json:"usd_available,string"` } -type Orderbook struct { - Timestamp string - Bids [][]string - Asks [][]string +type BitstampOrderbookBase struct { + Price float64 + Amount float64 } -type Transactions struct { - Date, Price, Amount string - Tid int64 +type BitstampOrderbook struct { + Timestamp int64 `json:"timestamp,string"` + Bids []BitstampOrderbookBase + Asks []BitstampOrderbookBase } -type ConversionRate struct { - Buy string - Sell string +type BitstampTransactions struct { + Date int64 `json:"date,string"` + TradeID int64 `json:"tid"` + Price float64 `json:"price,string"` + Type int `json:"type"` + Amount float64 `json:"amount,string"` +} + +type BitstampEURUSDConversionRate struct { + Buy float64 `json:"buy,string"` + Sell float64 `json:"sell,string"` +} + +type BitstampUserTransactions struct { + Date string `json:"datetime"` + TransID int64 `json:"id"` + Type int `json:"type"` + USD float64 `json:"usd,string"` + BTC float64 `json:"btc,string"` + BTCUSD float64 `json:"btc_usd,string"` + Fee float64 `json:"fee,string"` + OrderID interface{} `json:"order_id"` +} + +type BitstampOrder struct { + ID int64 `json:"id"` + Date string `json:"date"` + Type int `json:"type"` + Price float64 `json:"price"` + Amount float64 `json:"amount"` +} + +type BitstampOrderStatus struct { + Status string + Transactions []struct { + TradeID int64 `json:"tid"` + USD float64 `json:"usd,string"` + Price float64 `json:"price,string"` + Fee float64 `json:"fee,string"` + BTC float64 `json:"btc,string"` + } +} + +type BitstampWithdrawalRequests struct { + OrderID int64 `json:"id"` + Date string `json:"datetime"` + Type int `json:"type"` + Amount float64 `json:"amount,string"` + Status int `json:"status"` + Data interface{} +} + +type BitstampUnconfirmedBTCTransactions struct { + Amount float64 `json:"amount,string"` + Address string `json:"address"` + Confirmations int `json:"confirmations"` } func (b *Bitstamp) SetDefaults() { @@ -130,7 +182,11 @@ func (b *Bitstamp) Run() { for _, x := range b.EnabledPairs { currency := x go func() { - ticker := b.GetTicker() + ticker, err := b.GetTicker(true) + if err != nil { + log.Println(err) + return + } log.Printf("Bitstamp %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Last, ticker.High, ticker.Low, ticker.Volume) AddExchangeInfo(b.GetName(), currency[0:3], currency[3:], ticker.Last, ticker.Volume) }() @@ -139,141 +195,243 @@ func (b *Bitstamp) Run() { } } -func (b *Bitstamp) GetTicker() BitstampTicker { - err := SendHTTPGetRequest(BITSTAMP_API_URL+BITSTAMP_API_TICKER, true, &b.Ticker) +func (b *Bitstamp) GetTicker(hourly bool) (BitstampTicker, error) { + path := BITSTAMP_API_URL + ticker := BitstampTicker{} - if err != nil { - log.Println(err) - return BitstampTicker{} + if hourly { + path += BITSTAMP_API_TICKER_HOURLY + } else { + path += BITSTAMP_API_TICKER } - return b.Ticker -} - -func (b *Bitstamp) GetOrderbook() { - err := SendHTTPGetRequest(BITSTAMP_API_URL+BITSTAMP_API_ORDERBOOK, true, &b.Orderbook) + err := SendHTTPGetRequest(path, true, &ticker) if err != nil { - log.Println(err) - return + return ticker, err } + + return ticker, nil } -func (b *Bitstamp) GetTransactions() { - err := SendHTTPGetRequest(BITSTAMP_API_URL+BITSTAMP_API_TRANSACTIONS, true, &b.Transactions) +func (b *Bitstamp) GetOrderbook() (BitstampOrderbook, error) { + type response struct { + Timestamp int64 `json:"timestamp,string"` + Bids [][]string + Asks [][]string + } + + resp := response{} + err := SendHTTPGetRequest(BITSTAMP_API_URL+BITSTAMP_API_ORDERBOOK, true, &resp) + if err != nil { + return BitstampOrderbook{}, err + } + + orderbook := BitstampOrderbook{} + orderbook.Timestamp = resp.Timestamp + + for _, x := range resp.Bids { + price, err := strconv.ParseFloat(x[0], 64) + if err != nil { + log.Println(err) + continue + } + amount, err := strconv.ParseFloat(x[1], 64) + if err != nil { + log.Println(err) + continue + } + orderbook.Bids = append(orderbook.Bids, BitstampOrderbookBase{price, amount}) + } + + for _, x := range resp.Asks { + price, err := strconv.ParseFloat(x[0], 64) + if err != nil { + log.Println(err) + continue + } + amount, err := strconv.ParseFloat(x[1], 64) + if err != nil { + log.Println(err) + continue + } + orderbook.Asks = append(orderbook.Asks, BitstampOrderbookBase{price, amount}) + } + + return orderbook, nil +} + +func (b *Bitstamp) GetTransactions(values url.Values) ([]BitstampTransactions, error) { + transactions := []BitstampTransactions{} + path := BITSTAMP_API_URL + BITSTAMP_API_TRANSACTIONS + + if len(values) > 0 { + path += "?" + values.Encode() + } + + log.Println(path) + + err := SendHTTPGetRequest(path, true, &transactions) if err != nil { - log.Println(err) - return + return nil, err } + return transactions, nil } -func (b *Bitstamp) GetEURUSDConversionRate() { - err := SendHTTPGetRequest(BITSTAMP_API_URL+BITSTAMP_API_EURUSD, true, &b.ConversionRate) +func (b *Bitstamp) GetEURUSDConversionRate() (BitstampEURUSDConversionRate, error) { + rate := BitstampEURUSDConversionRate{} + err := SendHTTPGetRequest(BITSTAMP_API_URL+BITSTAMP_API_EURUSD, true, &rate) if err != nil { - log.Println(err) - return + return rate, err } + return rate, nil } -func (b *Bitstamp) GetBalance() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BALANCE, url.Values{}, &b.Balance) +func (b *Bitstamp) GetBalance() (BitstampAccountBalance, error) { + balance := BitstampAccountBalance{} + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BALANCE, url.Values{}, &balance) if err != nil { - log.Println(err) + return balance, err } + return balance, nil } -func (b *Bitstamp) GetUserTransactions(offset, limit, sort int64) { - var req = url.Values{} - - req.Add("offset", strconv.FormatInt(offset, 10)) - req.Add("limit", strconv.FormatInt(limit, 10)) - req.Add("sort", strconv.FormatInt(sort, 10)) - - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_USER_TRANSACTIONS, req, nil) +func (b *Bitstamp) GetUserTransactions(values url.Values) ([]BitstampUserTransactions, error) { + response := []BitstampUserTransactions{} + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_USER_TRANSACTIONS, values, &response) if err != nil { - log.Println(err) + return nil, err } + + return response, nil } -func (b *Bitstamp) CancelOrder(OrderID int64) { +func (b *Bitstamp) GetOpenOrders() ([]BitstampOrder, error) { + resp := []BitstampOrder{} + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_OPEN_ORDERS, nil, &resp) + + if err != nil { + return nil, err + } + + return resp, nil +} + +func (b *Bitstamp) GetOrderStatus(OrderID int64) (BitstampOrderStatus, error) { var req = url.Values{} req.Add("id", strconv.FormatInt(OrderID, 10)) + resp := BitstampOrderStatus{} - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_CANCEL_ORDER, req, nil) + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_CANCEL_ORDER, req, &resp) if err != nil { - log.Println(err) + return resp, err } + + return resp, nil } -func (b *Bitstamp) GetOpenOrders() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_OPEN_ORDERS, url.Values{}, nil) +func (b *Bitstamp) CancelOrder(OrderID int64) (bool, error) { + var req = url.Values{} + result := false + req.Add("id", strconv.FormatInt(OrderID, 10)) + + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_CANCEL_ORDER, req, &result) if err != nil { - log.Println(err) + return result, err } + + return result, nil } -func (b *Bitstamp) PlaceOrder(price float64, amount float64, Type int) { +func (b *Bitstamp) CancelAllOrders() (bool, error) { + result := false + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_CANCEL_ALL_ORDERS, nil, &result) + + if err != nil { + return result, err + } + + return result, nil +} + +func (b *Bitstamp) PlaceOrder(price float64, amount float64, buy bool) (BitstampOrder, error) { var req = url.Values{} req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64)) req.Add("price", strconv.FormatFloat(price, 'f', -1, 64)) + response := BitstampOrder{} orderType := BITSTAMP_API_BUY - if Type == 1 { + if !buy { orderType = BITSTAMP_API_SELL } - log.Printf("Placing %s order at price %f for %f amount.\n", orderType, price, amount) - - err := b.SendAuthenticatedHTTPRequest(orderType, req, nil) + err := b.SendAuthenticatedHTTPRequest(orderType, req, &response) if err != nil { - log.Println(err) + return response, err } + + return response, nil } -func (b *Bitstamp) GetWithdrawalRequests() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_WITHDRAWAL_REQUESTS, url.Values{}, nil) +func (b *Bitstamp) GetWithdrawalRequests() ([]BitstampWithdrawalRequests, error) { + resp := []BitstampWithdrawalRequests{} + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_WITHDRAWAL_REQUESTS, url.Values{}, &resp) if err != nil { - log.Println(err) + return nil, err } + + return resp, nil } -func (b *Bitstamp) BitcoinWithdrawal(amount float64, address string) { +func (b *Bitstamp) BitcoinWithdrawal(amount float64, address string) (string, error) { var req = url.Values{} req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64)) req.Add("address", address) - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BITCOIN_WITHDRAWAL, req, nil) + type response struct { + ID string `json:"id"` + } + + resp := response{} + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BITCOIN_WITHDRAWAL, req, &resp) if err != nil { - log.Println(err) + return "", err } + + return resp.ID, nil } -func (b *Bitstamp) BitcoinDepositAddress() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BITCOIN_DEPOSIT, url.Values{}, nil) +func (b *Bitstamp) GetBitcoinDepositAddress() (string, error) { + address := "" + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_BITCOIN_DEPOSIT, url.Values{}, &address) if err != nil { - log.Println(err) + return address, err } + return address, nil } -func (b *Bitstamp) UnconfirmedBitcoin() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_UNCONFIRMED_BITCOIN, url.Values{}, nil) +func (b *Bitstamp) GetUnconfirmedBitcoinDeposits() ([]BitstampUnconfirmedBTCTransactions, error) { + response := []BitstampUnconfirmedBTCTransactions{} + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_UNCONFIRMED_BITCOIN, nil, &response) if err != nil { - log.Println(err) + return nil, err } + + return response, nil } -func (b *Bitstamp) RippleWithdrawal(amount float64, address, currency string) { +func (b *Bitstamp) RippleWithdrawal(amount float64, address, currency string) (bool, error) { var req = url.Values{} req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64)) req.Add("address", address) @@ -282,20 +440,34 @@ func (b *Bitstamp) RippleWithdrawal(amount float64, address, currency string) { err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_RIPPLE_WITHDRAWAL, req, nil) if err != nil { - log.Println(err) + return false, err } + + return true, nil } -func (b *Bitstamp) RippleDepositAddress() { - err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_RIPPLE_DESPOIT, url.Values{}, nil) +func (b *Bitstamp) GetRippleDepositAddress() (string, error) { + type response struct { + Address string + } + + resp := response{} + err := b.SendAuthenticatedHTTPRequest(BITSTAMP_API_RIPPLE_DESPOIT, nil, &resp) if err != nil { - log.Println(err) + return "", err } + + return resp.Address, nil } func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, values url.Values, result interface{}) (err error) { nonce := strconv.FormatInt(time.Now().UnixNano(), 10) + + if values == nil { + values = url.Values{} + } + values.Set("key", b.APIKey) values.Set("nonce", nonce) hmac := GetHMAC(HASH_SHA256, []byte(nonce+b.ClientID+b.APIKey), []byte(b.APISecret)) diff --git a/events.go b/events.go index 1f9e2f26..d8ddb17e 100644 --- a/events.go +++ b/events.go @@ -123,7 +123,12 @@ func (e *Event) CheckCondition() bool { lastPrice = result.Last } } else if bot.exchange.bitstamp.GetName() == e.Exchange { - lastPrice = bot.exchange.bitstamp.GetTicker().Last + result, err := bot.exchange.bitstamp.GetTicker(false) + if err != nil { + lastPrice = 0 + } else { + lastPrice = result.Last + } } else if bot.exchange.coinbase.GetName() == e.Exchange { lastPrice = bot.exchange.coinbase.GetTicker("BTC-USD").Price } else if bot.exchange.cryptsy.GetName() == e.Exchange {