Files
gocryptotrader/gdaxhttp.go
Scott f37bf8d89f Adds exchange name to account info retrieval
Adds walletRoutes.go
Adds method to get all exchange account stuff  in go
Adds method to get all exchange account stuff in server.js
2016-09-12 20:06:01 +10:00

722 lines
18 KiB
Go

package main
import (
"bytes"
"errors"
"fmt"
"log"
"net/url"
"strconv"
"time"
)
const (
GDAX_API_URL = "https://api.gdax.com/"
GDAX_API_VERISON = "0"
GDAX_PRODUCTS = "products"
GDAX_ORDERBOOK = "book"
GDAX_TICKER = "ticker"
GDAX_TRADES = "trades"
GDAX_HISTORY = "candles"
GDAX_STATS = "stats"
GDAX_CURRENCIES = "currencies"
GDAX_ACCOUNTS = "accounts"
GDAX_LEDGER = "ledger"
GDAX_HOLDS = "holds"
GDAX_ORDERS = "orders"
GDAX_FILLS = "fills"
GDAX_TRANSFERS = "transfers"
GDAX_REPORTS = "reports"
)
type GDAX struct {
Name string
Enabled bool
Verbose bool
Websocket bool
RESTPollingDelay time.Duration
AuthenticatedAPISupport bool
Password, APIKey, APISecret string
TakerFee, MakerFee float64
BaseCurrencies []string
AvailablePairs []string
EnabledPairs []string
}
type GDAXTicker struct {
TradeID int64 `json:"trade_id"`
Price float64 `json:"price,string"`
Size float64 `json:"size,string"`
Time string `json:"time"`
}
type GDAXProduct struct {
ID string `json:"id"`
BaseCurrency string `json:"base_currency"`
QuoteCurrency string `json:"quote_currency"`
BaseMinSize float64 `json:"base_min_size,string"`
BaseMaxSize int64 `json:"base_max_size,string"`
QuoteIncrement float64 `json:"quote_increment,string"`
DisplayName string `json:"string"`
}
type GDAXOrderL1L2 struct {
Price float64
Amount float64
NumOrders float64
}
type GDAXOrderL3 struct {
Price float64
Amount float64
OrderID string
}
type GDAXOrderbookL1L2 struct {
Sequence int64 `json:"sequence"`
Bids [][]GDAXOrderL1L2 `json:"asks"`
Asks [][]GDAXOrderL1L2 `json:"asks"`
}
type GDAXOrderbookL3 struct {
Sequence int64 `json:"sequence"`
Bids [][]GDAXOrderL3 `json:"asks"`
Asks [][]GDAXOrderL3 `json:"asks"`
}
type GDAXOrderbookResponse struct {
Sequence int64 `json:"sequence"`
Bids [][]interface{} `json:"bids"`
Asks [][]interface{} `json:"asks"`
}
type GDAXTrade 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 GDAXStats struct {
Open float64 `json:"open,string"`
High float64 `json:"high,string"`
Low float64 `json:"low,string"`
Volume float64 `json:"volume,string"`
}
type GDAXCurrency struct {
ID string
Name string
MinSize float64 `json:"min_size,string"`
}
type GDAXHistory struct {
Time int64
Low float64
High float64
Open float64
Close float64
Volume float64
}
func (g *GDAX) SetDefaults() {
g.Name = "GDAX"
g.Enabled = false
g.Verbose = false
g.TakerFee = 0.25
g.MakerFee = 0
g.Verbose = false
g.Websocket = false
g.RESTPollingDelay = 10
}
func (g *GDAX) GetName() string {
return g.Name
}
func (g *GDAX) SetEnabled(enabled bool) {
g.Enabled = enabled
}
func (g *GDAX) IsEnabled() bool {
return g.Enabled
}
func (g *GDAX) Setup(exch Exchanges) {
if !exch.Enabled {
g.SetEnabled(false)
} else {
g.Enabled = true
g.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
g.SetAPIKeys(exch.ClientID, exch.APIKey, exch.APISecret)
g.RESTPollingDelay = exch.RESTPollingDelay
g.Verbose = exch.Verbose
g.Websocket = exch.Websocket
g.BaseCurrencies = SplitStrings(exch.BaseCurrencies, ",")
g.AvailablePairs = SplitStrings(exch.AvailablePairs, ",")
g.EnabledPairs = SplitStrings(exch.EnabledPairs, ",")
}
}
func (k *GDAX) GetEnabledCurrencies() []string {
return k.EnabledPairs
}
func (g *GDAX) Start() {
go g.Run()
}
func (g *GDAX) GetFee(maker bool) float64 {
if maker {
return g.MakerFee
} else {
return g.TakerFee
}
}
func (g *GDAX) Run() {
if g.Verbose {
log.Printf("%s Websocket: %s. (url: %s).\n", g.GetName(), IsEnabled(g.Websocket), GDAX_WEBSOCKET_URL)
log.Printf("%s polling delay: %ds.\n", g.GetName(), g.RESTPollingDelay)
log.Printf("%s %d currencies enabled: %s.\n", g.GetName(), len(g.EnabledPairs), g.EnabledPairs)
}
if g.Websocket {
go g.WebsocketClient()
}
exchangeProducts, err := g.GetProducts()
if err != nil {
log.Printf("%s Failed to get available products.\n", g.GetName())
} else {
currencies := []string{}
for _, x := range exchangeProducts {
if x.ID != "BTC" && x.ID != "USD" && x.ID != "GBP" {
currencies = append(currencies, x.ID[0:3]+x.ID[4:])
}
}
diff := StringSliceDifference(g.AvailablePairs, currencies)
if len(diff) > 0 {
exch, err := GetExchangeConfig(g.Name)
if err != nil {
log.Println(err)
} else {
log.Printf("%s Updating available pairs. Difference: %s.\n", g.Name, diff)
exch.AvailablePairs = JoinStrings(currencies, ",")
UpdateExchangeConfig(exch)
}
}
}
for g.Enabled {
for _, x := range g.EnabledPairs {
currency := x[0:3] + "-" + x[3:]
go func() {
stats, err := g.GetStats(currency)
if err != nil {
log.Println(err)
return
}
ticker, err := g.GetTicker(currency)
if err != nil {
log.Println(err)
return
}
log.Printf("GDAX %s: Last %f High %f Low %f Volume %f\n", currency, ticker.Price, stats.High, stats.Low, stats.Volume)
AddExchangeInfo(g.GetName(), currency[0:3], currency[4:], ticker.Price, stats.Volume)
}()
}
time.Sleep(time.Second * g.RESTPollingDelay)
}
}
func (g *GDAX) SetAPIKeys(password, apiKey, apiSecret string) {
if !g.AuthenticatedAPISupport {
return
}
g.Password = password
g.APIKey = apiKey
result, err := Base64Decode(apiSecret)
if err != nil {
log.Printf("%s unable to decode secret key.", g.GetName())
g.Enabled = false
return
}
g.APISecret = string(result)
}
func (g *GDAX) GetProducts() ([]GDAXProduct, error) {
products := []GDAXProduct{}
err := SendHTTPGetRequest(GDAX_API_URL+GDAX_PRODUCTS, true, &products)
if err != nil {
return nil, err
}
return products, nil
}
func (g *GDAX) GetOrderbook(symbol string, level int) (interface{}, error) {
orderbook := GDAXOrderbookResponse{}
path := ""
if level > 0 {
levelStr := strconv.Itoa(level)
path = fmt.Sprintf("%s/%s/%s?level=%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_ORDERBOOK, levelStr)
} else {
path = fmt.Sprintf("%s/%s/%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_ORDERBOOK)
}
err := SendHTTPGetRequest(path, true, &orderbook)
if err != nil {
return nil, err
}
if level == 3 {
ob := GDAXOrderbookL3{}
ob.Sequence = orderbook.Sequence
for _, x := range orderbook.Asks {
price, err := strconv.ParseFloat((x[0].(string)), 64)
if err != nil {
continue
}
amount, err := strconv.ParseFloat((x[1].(string)), 64)
if err != nil {
continue
}
order := make([]GDAXOrderL3, 1)
order[0].Price = price
order[0].Amount = amount
order[0].OrderID = x[2].(string)
ob.Asks = append(ob.Asks, order)
}
for _, x := range orderbook.Bids {
price, err := strconv.ParseFloat((x[0].(string)), 64)
if err != nil {
continue
}
amount, err := strconv.ParseFloat((x[1].(string)), 64)
if err != nil {
continue
}
order := make([]GDAXOrderL3, 1)
order[0].Price = price
order[0].Amount = amount
order[0].OrderID = x[2].(string)
ob.Bids = append(ob.Bids, order)
}
return ob, nil
} else {
ob := GDAXOrderbookL1L2{}
ob.Sequence = orderbook.Sequence
for _, x := range orderbook.Asks {
price, err := strconv.ParseFloat((x[0].(string)), 64)
if err != nil {
continue
}
amount, err := strconv.ParseFloat((x[1].(string)), 64)
if err != nil {
continue
}
order := make([]GDAXOrderL1L2, 1)
order[0].Price = price
order[0].Amount = amount
order[0].NumOrders = x[2].(float64)
ob.Asks = append(ob.Asks, order)
}
for _, x := range orderbook.Bids {
price, err := strconv.ParseFloat((x[0].(string)), 64)
if err != nil {
continue
}
amount, err := strconv.ParseFloat((x[1].(string)), 64)
if err != nil {
continue
}
order := make([]GDAXOrderL1L2, 1)
order[0].Price = price
order[0].Amount = amount
order[0].NumOrders = x[2].(float64)
ob.Bids = append(ob.Bids, order)
}
return ob, nil
}
}
func (g *GDAX) GetTicker(symbol string) (GDAXTicker, error) {
ticker := GDAXTicker{}
path := fmt.Sprintf("%s/%s/%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_TICKER)
err := SendHTTPGetRequest(path, true, &ticker)
if err != nil {
return ticker, err
}
return ticker, nil
}
func (g *GDAX) GetTickerPrice(currency string) TickerPrice {
var tickerPrice TickerPrice
ticker, err := g.GetTicker(currency)
if err != nil {
log.Println(err)
return TickerPrice{}
}
tickerPrice.Ask = ticker.Price
tickerPrice.CryptoCurrency = currency
tickerPrice.Volume = ticker.Size
return tickerPrice
}
func (g *GDAX) GetTrades(symbol string) ([]GDAXTrade, error) {
trades := []GDAXTrade{}
path := fmt.Sprintf("%s/%s/%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_TRADES)
err := SendHTTPGetRequest(path, true, &trades)
if err != nil {
return nil, err
}
return trades, nil
}
func (g *GDAX) GetHistoricRates(symbol string, start, end, granularity int64) ([]GDAXHistory, error) {
history := []GDAXHistory{}
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 := EncodeURLValues(fmt.Sprintf("%s/%s/%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_HISTORY), values)
err := SendHTTPGetRequest(path, true, &history)
if err != nil {
return nil, err
}
return history, nil
}
func (g *GDAX) GetStats(symbol string) (GDAXStats, error) {
stats := GDAXStats{}
path := fmt.Sprintf("%s/%s/%s", GDAX_API_URL+GDAX_PRODUCTS, symbol, GDAX_STATS)
err := SendHTTPGetRequest(path, true, &stats)
if err != nil {
return stats, err
}
return stats, nil
}
func (g *GDAX) GetCurrencies() ([]GDAXCurrency, error) {
currencies := []GDAXCurrency{}
err := SendHTTPGetRequest(GDAX_API_URL+GDAX_CURRENCIES, true, &currencies)
if err != nil {
return nil, err
}
return currencies, nil
}
type GDAXAccountResponse struct {
ID string `json:"id"`
Balance float64 `json:"balance,string"`
Hold float64 `json:"hold,string"`
Available float64 `json:"available,string"`
Currency string `json:"currency"`
}
func (g *GDAX) GetAccounts() ([]GDAXAccountResponse, error) {
resp := []GDAXAccountResponse{}
err := g.SendAuthenticatedHTTPRequest("GET", GDAX_API_URL+GDAX_ACCOUNTS, nil, &resp)
if err != nil {
return nil, err
}
return resp, nil
}
func (g *GDAX) GetAccount(account string) (GDAXAccountResponse, error) {
resp := GDAXAccountResponse{}
path := fmt.Sprintf("%s/%s", GDAX_ACCOUNTS, account)
err := g.SendAuthenticatedHTTPRequest("GET", GDAX_API_URL+path, nil, &resp)
if err != nil {
return resp, err
}
return resp, nil
}
//GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the GDAX exchange
func (e *GDAX) GetExchangeAccountInfo() (ExchangeAccountInfo, error) {
var response ExchangeAccountInfo
response.ExchangeName = e.GetName()
accountBalance, err := e.GetAccounts()
if err != nil {
return response, err
}
for i := 0; i < len(accountBalance); i++ {
var exchangeCurrency ExchangeAccountCurrencyInfo
exchangeCurrency.CurrencyName = accountBalance[i].Currency
exchangeCurrency.TotalValue = accountBalance[i].Balance
exchangeCurrency.Hold = accountBalance[i].Hold
response.Currencies = append(response.Currencies, exchangeCurrency)
}
return response, nil
}
type GDAXAccountLedgerResponse struct {
ID string `json:"id"`
CreatedAt string `json:"created_at"`
Amount float64 `json:"amount,string"`
Balance float64 `json:"balance,string"`
Type string `json:"type"`
details interface{} `json:"details"`
}
func (g *GDAX) GetAccountHistory(accountID string) ([]GDAXAccountLedgerResponse, error) {
resp := []GDAXAccountLedgerResponse{}
path := fmt.Sprintf("%s/%s/%s", GDAX_ACCOUNTS, accountID, GDAX_LEDGER)
err := g.SendAuthenticatedHTTPRequest("GET", GDAX_API_URL+path, nil, &resp)
if err != nil {
return nil, err
}
return resp, nil
}
type GDAXAccountHolds struct {
ID string `json:"id"`
AccountID string `json:"account_id"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Amount float64 `json:"amount,string"`
Type string `json:"type"`
Reference string `json:"ref"`
}
func (g *GDAX) GetHolds(accountID string) ([]GDAXAccountHolds, error) {
resp := []GDAXAccountHolds{}
path := fmt.Sprintf("%s/%s/%s", GDAX_ACCOUNTS, accountID, GDAX_HOLDS)
err := g.SendAuthenticatedHTTPRequest("GET", GDAX_API_URL+path, nil, &resp)
if err != nil {
return nil, err
}
return resp, nil
}
func (g *GDAX) PlaceOrder(clientRef string, price, amount float64, side string, productID, stp string) (string, error) {
request := make(map[string]interface{})
if clientRef != "" {
request["client_oid"] = clientRef
}
request["price"] = strconv.FormatFloat(price, 'f', -1, 64)
request["size"] = strconv.FormatFloat(amount, 'f', -1, 64)
request["side"] = side
request["product_id"] = productID
if stp != "" {
request["stp"] = stp
}
type OrderResponse struct {
ID string `json:"id"`
}
resp := OrderResponse{}
err := g.SendAuthenticatedHTTPRequest("POST", GDAX_API_URL+GDAX_ORDERS, request, &resp)
if err != nil {
return "", err
}
return resp.ID, nil
}
func (g *GDAX) CancelOrder(orderID string) error {
path := fmt.Sprintf("%s/%s", GDAX_ORDERS, orderID)
err := g.SendAuthenticatedHTTPRequest("DELETE", GDAX_API_URL+path, nil, nil)
if err != nil {
return err
}
return nil
}
type GDAXOrdersResponse struct {
ID string `json:"id"`
Size float64 `json:"size,string"`
Price float64 `json:"price,string"`
ProductID string `json:"product_id"`
Status string `json:"status"`
FilledSize float64 `json:"filled_size,string"`
FillFees float64 `json:"fill_fees,string"`
Settled bool `json:"settled"`
Side string `json:"side"`
CreatedAt string `json:"created_at"`
}
func (g *GDAX) GetOrders(params url.Values) ([]GDAXOrdersResponse, error) {
path := EncodeURLValues(GDAX_API_URL+GDAX_ORDERS, params)
resp := []GDAXOrdersResponse{}
err := g.SendAuthenticatedHTTPRequest("GET", path, nil, &resp)
if err != nil {
return nil, err
}
return resp, nil
}
type GDAXOrderResponse struct {
ID string `json:"id"`
Size float64 `json:"size,string"`
Price float64 `json:"price,string"`
DoneReason string `json:"done_reason"`
Status string `json:"status"`
Settled bool `json:"settled"`
FilledSize float64 `json:"filled_size,string"`
ProductID string `json:"product_id"`
FillFees float64 `json:"fill_fees,string"`
Side string `json:"side"`
CreatedAt string `json:"created_at"`
DoneAt string `json:"done_at"`
}
func (g *GDAX) GetOrder(orderID string) (GDAXOrderResponse, error) {
path := fmt.Sprintf("%s/%s", GDAX_ORDERS, orderID)
resp := GDAXOrderResponse{}
err := g.SendAuthenticatedHTTPRequest("GET", GDAX_API_URL+path, nil, &resp)
if err != nil {
return resp, err
}
return resp, nil
}
type GDAXFillResponse struct {
TradeID int `json:"trade_id"`
ProductID string `json:"product_id"`
Price float64 `json:"price,string"`
Size float64 `json:"size,string"`
OrderID string `json:"order_id"`
CreatedAt string `json:"created_at"`
Liquidity string `json:"liquidity"`
Fee float64 `json:"fee,string"`
Settled bool `json:"settled"`
Side string `json:"side"`
}
func (g *GDAX) GetFills(params url.Values) ([]GDAXFillResponse, error) {
path := EncodeURLValues(GDAX_API_URL+GDAX_FILLS, params)
resp := []GDAXFillResponse{}
err := g.SendAuthenticatedHTTPRequest("GET", path, nil, &resp)
if err != nil {
return nil, err
}
return resp, nil
}
func (g *GDAX) Transfer(transferType string, amount float64, accountID string) error {
request := make(map[string]interface{})
request["type"] = transferType
request["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
request["GDAX_account_id"] = accountID
err := g.SendAuthenticatedHTTPRequest("POST", GDAX_API_URL+GDAX_TRANSFERS, request, nil)
if err != nil {
return err
}
return nil
}
type GDAXReportResponse struct {
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
CreatedAt string `json:"created_at"`
CompletedAt string `json:"completed_at"`
ExpiresAt string `json:"expires_at"`
FileURL string `json:"file_url"`
Params struct {
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
} `json:params"`
}
func (g *GDAX) GetReport(reportType, startDate, endDate string) (GDAXReportResponse, error) {
request := make(map[string]interface{})
request["type"] = reportType
request["start_date"] = startDate
request["end_date"] = endDate
resp := GDAXReportResponse{}
err := g.SendAuthenticatedHTTPRequest("POST", GDAX_API_URL+GDAX_REPORTS, request, &resp)
if err != nil {
return resp, err
}
return resp, nil
}
func (g *GDAX) GetReportStatus(reportID string) (GDAXReportResponse, error) {
path := fmt.Sprintf("%s/%s", GDAX_REPORTS, reportID)
resp := GDAXReportResponse{}
err := g.SendAuthenticatedHTTPRequest("POST", GDAX_API_URL+path, nil, &resp)
if err != nil {
return resp, err
}
return resp, nil
}
func (g *GDAX) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) (err error) {
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)[0:13]
payload := []byte("")
if params != nil {
payload, err = JSONEncode(params)
if err != nil {
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request")
}
if g.Verbose {
log.Printf("Request JSON: %s\n", payload)
}
}
message := timestamp + method + path + string(payload)
hmac := GetHMAC(HASH_SHA256, []byte(message), []byte(g.APISecret))
headers := make(map[string]string)
headers["CB-ACCESS-SIGN"] = Base64Encode([]byte(hmac))
headers["CB-ACCESS-TIMESTAMP"] = timestamp
headers["CB-ACCESS-KEY"] = g.APIKey
headers["CB-ACCESS-PASSPHRASE"] = g.Password
headers["Content-Type"] = "application/json"
resp, err := SendHTTPRequest(method, GDAX_API_URL+path, headers, bytes.NewBuffer(payload))
if g.Verbose {
log.Printf("Recieved raw: \n%s\n", resp)
}
err = JSONDecode([]byte(resp), &result)
if err != nil {
return errors.New("Unable to JSON Unmarshal response.")
}
return nil
}