Fixed linter issues, increased code cov & expanded functionality in Gemini.

This commit is contained in:
Ryan O'Hara-Reid
2017-09-07 19:01:51 +10:00
parent eef13c4515
commit dae90a2eaa
4 changed files with 661 additions and 224 deletions

View File

@@ -1,37 +1,32 @@
package coinut
import (
"testing"
"github.com/thrasher-/gocryptotrader/config"
)
const (
apiKey = ""
apiSecret = ""
)
var c COINUT
func TestSetDefaults(t *testing.T) {
c.SetDefaults()
}
func TestSetup(t *testing.T) {
exch := config.ExchangeConfig{}
c.Setup(exch)
exch.Enabled = true
exch.APIKey = apiKey
exch.APISecret = apiSecret
c.Setup(exch)
}
// func TestGetInstruments(t *testing.T) {
// c.Verbose = true
// resp, err := c.GetInstruments()
// if err == nil {
// t.Error("Test failed - GetInstruments() error", err)
// }
// log.Println(resp)
//
// const (
// apiKey = ""
// apiSecret = ""
// )
//
// var c COINUT
//
// func TestSetDefaults(t *testing.T) {
// c.SetDefaults()
// }
//
// func TestSetup(t *testing.T) {
// exch := config.ExchangeConfig{}
// c.Setup(exch)
//
// exch.Enabled = true
// exch.APIKey = apiKey
// exch.APISecret = apiSecret
// c.Setup(exch)
// }
//
// // func TestGetInstruments(t *testing.T) {
// // c.Verbose = true
// // resp, err := c.GetInstruments()
// // if err == nil {
// // t.Error("Test failed - GetInstruments() error", err)
// // }
// // log.Println(resp)
// // }

View File

@@ -16,30 +16,88 @@ import (
)
const (
GEMINI_API_URL = "https://api.gemini.com"
GEMINI_API_VERSION = "1"
geminiAPIURL = "https://api.gemini.com"
geminiSandboxAPIURL = "https://api.sandbox.gemini.com"
geminiAPIVersion = "1"
GEMINI_SYMBOLS = "symbols"
GEMINI_TICKER = "pubticker"
GEMINI_AUCTION = "auction"
GEMINI_AUCTION_HISTORY = "history"
GEMINI_ORDERBOOK = "book"
GEMINI_TRADES = "trades"
GEMINI_ORDERS = "orders"
GEMINI_ORDER_NEW = "order/new"
GEMINI_ORDER_CANCEL = "order/cancel"
GEMINI_ORDER_CANCEL_SESSION = "order/cancel/session"
GEMINI_ORDER_CANCEL_ALL = "order/cancel/all"
GEMINI_ORDER_STATUS = "order/status"
GEMINI_MYTRADES = "mytrades"
GEMINI_BALANCES = "balances"
GEMINI_HEARTBEAT = "heartbeat"
geminiSymbols = "symbols"
geminiTicker = "pubticker"
geminiAuction = "auction"
geminiAuctionHistory = "history"
geminiOrderbook = "book"
geminiTrades = "trades"
geminiOrders = "orders"
geminiOrderNew = "order/new"
geminiOrderCancel = "order/cancel"
geminiOrderCancelSession = "order/cancel/session"
geminiOrderCancelAll = "order/cancel/all"
geminiOrderStatus = "order/status"
geminiMyTrades = "mytrades"
geminiBalances = "balances"
geminiTradeVolume = "tradevolume"
geminiDeposit = "deposit"
geminiNewAddress = "newAddress"
geminiWithdraw = "withdraw/"
geminiHeartbeat = "heartbeat"
// rate limits per minute
geminiPublicRate = 120
geminiPrivateRate = 600
// rates limits per second
geminiPublicRateSec = 1
geminiPrivateRateSec = 5
// Too many requests returns this
geminiRateError = "429"
// Assigned API key roles on creation
geminiRoleTrader = "trader"
geminiRoleFundManager = "fundmanager"
)
// SessionID map guides
var (
sessionAPIKey map[int]string // map[sessionID]APIKEY
sessionAPISecret map[int]string // map[sessionID]APIKEY
sessionRole map[string]string // map[sessionID]Roles
sessionHeartbeat map[int]bool // map[sessionID]RequiresHeartBeat
IsSession bool
)
// Gemini is the overarching type across the Gemini package, create multiple
// instances with differing APIkeys for segregation of roles for authenticated
// requests & sessions by appending the session function, if sandbox test is
// needed append the sandbox function as well.
type Gemini struct {
exchange.Base
}
// AddSession adds a new session to the gemini base
func (g *Gemini) AddSession(sessionID int, apiKey, apiSecret, role string, needsHeartbeat bool) error {
if sessionAPIKey == nil {
IsSession = true
sessionAPIKey = make(map[int]string)
sessionAPISecret = make(map[int]string)
sessionRole = make(map[string]string)
sessionHeartbeat = make(map[int]bool)
}
_, ok := sessionAPIKey[sessionID]
if ok {
return errors.New("sessionID already being used")
}
sessionAPIKey[sessionID] = apiKey
sessionAPISecret[sessionID] = apiSecret
sessionRole[apiKey] = role
sessionHeartbeat[sessionID] = needsHeartbeat
return nil
}
//return session function?
// SetDefaults sets package defaults for gemini exchange
func (g *Gemini) SetDefaults() {
g.Name = "Gemini"
g.Enabled = false
@@ -53,6 +111,7 @@ func (g *Gemini) SetDefaults() {
g.AssetTypes = []string{ticker.Spot}
}
// Setup sets exchange configuration paramaters
func (g *Gemini) Setup(exch config.ExchangeConfig) {
if !exch.Enabled {
g.SetEnabled(false)
@@ -77,7 +136,35 @@ func (g *Gemini) Setup(exch config.ExchangeConfig) {
}
}
func (g *Gemini) GetTicker(currency string) (GeminiTicker, error) {
// Session is a session manager for differing APIKeys and roles, use this for all function
// calls in this package
func (g *Gemini) Session(sessionID int) *Gemini {
g.APIUrl = geminiAPIURL
_, ok := sessionAPIKey[sessionID]
if !ok {
return nil
}
g.APIKey = sessionAPIKey[sessionID]
g.APISecret = sessionAPISecret[sessionID]
return g
}
// Sandbox diverts the apiURL to the sandbox API for testing purposes
func (g *Gemini) Sandbox() *Gemini {
g.APIUrl = geminiSandboxAPIURL
return g
}
// GetSymbols returns all available symbols for trading
func (g *Gemini) GetSymbols() ([]string, error) {
symbols := []string{}
path := fmt.Sprintf("%s/v%s/%s", geminiAPIURL, geminiAPIVersion, geminiSymbols)
return symbols, common.SendHTTPGetRequest(path, true, &symbols)
}
// GetTicker returns information about recent trading activity for the symbol
func (g *Gemini) GetTicker(currencyPair string) (Ticker, error) {
type TickerResponse struct {
Ask float64 `json:"ask,string"`
@@ -86,9 +173,9 @@ func (g *Gemini) GetTicker(currency string) (GeminiTicker, error) {
Volume map[string]interface{}
}
ticker := GeminiTicker{}
ticker := Ticker{}
resp := TickerResponse{}
path := fmt.Sprintf("%s/v%s/%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_TICKER, currency)
path := fmt.Sprintf("%s/v%s/%s/%s", geminiAPIURL, geminiAPIVersion, geminiTicker, currencyPair)
err := common.SendHTTPGetRequest(path, true, &resp)
if err != nil {
@@ -99,7 +186,7 @@ func (g *Gemini) GetTicker(currency string) (GeminiTicker, error) {
ticker.Bid = resp.Bid
ticker.Last = resp.Last
ticker.Volume.Currency, _ = strconv.ParseFloat(resp.Volume[currency[0:3]].(string), 64)
ticker.Volume.Currency, _ = strconv.ParseFloat(resp.Volume[currencyPair[0:3]].(string), 64)
ticker.Volume.USD, _ = strconv.ParseFloat(resp.Volume["USD"].(string), 64)
time, _ := resp.Volume["timestamp"].(float64)
@@ -108,59 +195,77 @@ func (g *Gemini) GetTicker(currency string) (GeminiTicker, error) {
return ticker, nil
}
func (g *Gemini) GetSymbols() ([]string, error) {
symbols := []string{}
path := fmt.Sprintf("%s/v%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_SYMBOLS)
err := common.SendHTTPGetRequest(path, true, &symbols)
if err != nil {
return nil, err
}
return symbols, nil
// GetOrderbook returns the current order book, as two arrays, one of bids, and
// one of asks
//
// params - limit_bids or limit_asks [OPTIONAL] default 50, 0 returns all Values
// Type is an integer ie "params.Set("limit_asks", 30)"
func (g *Gemini) GetOrderbook(currencyPair string, params url.Values) (Orderbook, error) {
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s", geminiAPIURL, geminiAPIVersion, geminiOrderbook, currencyPair), params)
orderbook := Orderbook{}
return orderbook, common.SendHTTPGetRequest(path, true, &orderbook)
}
func (g *Gemini) GetAuction(currency string) (GeminiAuction, error) {
path := fmt.Sprintf("%s/v%s/%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_AUCTION, currency)
auction := GeminiAuction{}
err := common.SendHTTPGetRequest(path, true, &auction)
if err != nil {
return auction, err
}
return auction, nil
// GetTrades eturn the trades that have executed since the specified timestamp.
// Timestamps are either seconds or milliseconds since the epoch (1970-01-01).
//
// currencyPair - example "btcusd"
// params --
// since, timestamp [optional]
// limit_trades integer Optional. The maximum number of trades to return.
// include_breaks boolean Optional. Whether to display broken trades. False by
// default. Can be '1' or 'true' to activate
func (g *Gemini) GetTrades(currencyPair string, params url.Values) ([]Trade, error) {
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s", geminiAPIURL, geminiAPIVersion, geminiTrades, currencyPair), params)
trades := []Trade{}
return trades, common.SendHTTPGetRequest(path, true, &trades)
}
func (g *Gemini) GetAuctionHistory(currency string, params url.Values) ([]GeminiAuctionHistory, error) {
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_AUCTION, currency, GEMINI_AUCTION_HISTORY), params)
auctionHist := []GeminiAuctionHistory{}
err := common.SendHTTPGetRequest(path, true, &auctionHist)
if err != nil {
return nil, err
}
return auctionHist, nil
// GetAuction returns auction infomation
func (g *Gemini) GetAuction(currencyPair string) (Auction, error) {
path := fmt.Sprintf("%s/v%s/%s/%s", geminiAPIURL, geminiAPIVersion, geminiAuction, currencyPair)
auction := Auction{}
return auction, common.SendHTTPGetRequest(path, true, &auction)
}
func (g *Gemini) GetOrderbook(currency string, params url.Values) (GeminiOrderbook, error) {
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_ORDERBOOK, currency), params)
orderbook := GeminiOrderbook{}
err := common.SendHTTPGetRequest(path, true, &orderbook)
if err != nil {
return GeminiOrderbook{}, err
}
// GetAuctionHistory returns the auction events, optionally including
// publications of indicative prices, since the specific timestamp.
//
// currencyPair - example "btcusd"
// params -- [optional]
// since - [timestamp] Only returns auction events after the specified
// timestamp.
// limit_auction_results - [integer] The maximum number of auction
// events to return.
// include_indicative - [bool] Whether to include publication of
// indicative prices and quantities.
func (g *Gemini) GetAuctionHistory(currencyPair string, params url.Values) ([]AuctionHistory, error) {
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s/%s", geminiAPIURL, geminiAPIVersion, geminiAuction, currencyPair, geminiAuctionHistory), params)
auctionHist := []AuctionHistory{}
return orderbook, nil
return auctionHist, common.SendHTTPGetRequest(path, true, &auctionHist)
}
func (g *Gemini) GetTrades(currency string, params url.Values) ([]GeminiTrade, error) {
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s", GEMINI_API_URL, GEMINI_API_VERSION, GEMINI_TRADES, currency), params)
trades := []GeminiTrade{}
err := common.SendHTTPGetRequest(path, true, &trades)
if err != nil {
return []GeminiTrade{}, err
func (g *Gemini) isCorrectSession(role string) error {
if !IsSession {
return errors.New("session not set")
}
return trades, nil
if sessionRole[g.APIKey] != role {
return errors.New("incorrect role for APIKEY cannot use this function")
}
return nil
}
// NewOrder Only limit orders are supported through the API at present.
// returns order ID if successful
func (g *Gemini) NewOrder(symbol string, amount, price float64, side, orderType string) (int64, error) {
if err := g.isCorrectSession(geminiRoleTrader); err != nil {
return 0, err
}
request := make(map[string]interface{})
request["symbol"] = symbol
request["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
@@ -168,96 +273,127 @@ func (g *Gemini) NewOrder(symbol string, amount, price float64, side, orderType
request["side"] = side
request["type"] = orderType
response := GeminiOrder{}
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_ORDER_NEW, request, &response)
response := Order{}
err := g.SendAuthenticatedHTTPRequest("POST", geminiOrderNew, request, &response)
if err != nil {
return 0, err
}
return response.OrderID, nil
}
func (g *Gemini) CancelOrder(OrderID int64) (GeminiOrder, error) {
// CancelOrder will cancel an order. If the order is already canceled, the
// message will succeed but have no effect.
func (g *Gemini) CancelOrder(OrderID int64) (Order, error) {
request := make(map[string]interface{})
request["order_id"] = OrderID
response := GeminiOrder{}
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_ORDER_CANCEL, request, &response)
response := Order{}
err := g.SendAuthenticatedHTTPRequest("POST", geminiOrderCancel, request, &response)
if err != nil {
return GeminiOrder{}, err
return Order{}, err
}
return response, nil
}
func (g *Gemini) CancelOrders(sessions bool) ([]GeminiOrderResult, error) {
response := []GeminiOrderResult{}
path := GEMINI_ORDER_CANCEL_ALL
if sessions {
path = GEMINI_ORDER_CANCEL_SESSION
// CancelOrders will cancel all outstanding orders created by all sessions owned
// by this account, including interactive orders placed through the UI. If
// sessions = true will only cancel the order that is called on this session
// asssociated with the APIKEY
func (g *Gemini) CancelOrders(CancelBySession bool) (OrderResult, error) {
response := OrderResult{}
path := geminiOrderCancelAll
if CancelBySession {
path = geminiOrderCancelSession
}
err := g.SendAuthenticatedHTTPRequest("POST", path, nil, &response)
if err != nil {
return nil, err
}
return response, nil
return response, g.SendAuthenticatedHTTPRequest("POST", path, nil, &response)
}
func (g *Gemini) GetOrderStatus(orderID int64) (GeminiOrder, error) {
// GetOrderStatus returns the status for an order
func (g *Gemini) GetOrderStatus(orderID int64) (Order, error) {
request := make(map[string]interface{})
request["order_id"] = orderID
response := GeminiOrder{}
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_ORDER_STATUS, request, &response)
if err != nil {
return GeminiOrder{}, err
}
return response, nil
response := Order{}
return response,
g.SendAuthenticatedHTTPRequest("POST", geminiOrderStatus, request, &response)
}
func (g *Gemini) GetOrders() ([]GeminiOrder, error) {
response := []GeminiOrder{}
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_ORDERS, nil, &response)
if err != nil {
return nil, err
}
return response, nil
// GetOrders returns active orders in the market
func (g *Gemini) GetOrders() ([]Order, error) {
response := []Order{}
return response,
g.SendAuthenticatedHTTPRequest("POST", geminiOrders, nil, &response)
}
func (g *Gemini) GetTradeHistory(symbol string, timestamp int64) ([]GeminiTradeHistory, error) {
// GetTradeHistory returns an array of trades that have been on the exchange
//
// currencyPair - example "btcusd"
// timestamp - [optional] Only return trades on or after this timestamp.
func (g *Gemini) GetTradeHistory(currencyPair string, timestamp int64) ([]TradeHistory, error) {
response := []TradeHistory{}
request := make(map[string]interface{})
request["symbol"] = symbol
request["timestamp"] = timestamp
request["symbol"] = currencyPair
response := []GeminiTradeHistory{}
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_MYTRADES, request, &response)
if err != nil {
return nil, err
if timestamp != 0 {
request["timestamp"] = timestamp
}
return response, nil
return response,
g.SendAuthenticatedHTTPRequest("POST", geminiMyTrades, request, &response)
}
func (g *Gemini) GetBalances() ([]GeminiBalance, error) {
response := []GeminiBalance{}
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_BALANCES, nil, &response)
if err != nil {
return nil, err
}
return response, nil
// GetTradeVolume returns a multi-arrayed volume response
func (g *Gemini) GetTradeVolume() ([][]TradeVolume, error) {
response := [][]TradeVolume{}
return response,
g.SendAuthenticatedHTTPRequest("POST", geminiTradeVolume, nil, &response)
}
func (g *Gemini) PostHeartbeat() (bool, error) {
// GetBalances returns available balances in the supported currencies
func (g *Gemini) GetBalances() ([]Balance, error) {
response := []Balance{}
return response,
g.SendAuthenticatedHTTPRequest("POST", geminiBalances, nil, &response)
}
// GetDepositAddress returns a deposit address
func (g *Gemini) GetDepositAddress(depositAddlabel, currency string) (DepositAddress, error) {
response := DepositAddress{}
return response,
g.SendAuthenticatedHTTPRequest("POST", geminiDeposit+"/"+currency+"/"+geminiNewAddress, nil, &response)
}
// WithdrawCrypto withdraws crypto currency to a whitelisted address
func (g *Gemini) WithdrawCrypto(address, currency string, amount float64) (WithdrawelAddress, error) {
response := WithdrawelAddress{}
request := make(map[string]interface{})
request["address"] = address
request["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
return response,
g.SendAuthenticatedHTTPRequest("POST", geminiWithdraw+currency, nil, &response)
}
// PostHeartbeat sends a maintenance heartbeat to the exchange for all heartbeat
// maintaned sessions
func (g *Gemini) PostHeartbeat() (string, error) {
type Response struct {
Result bool `json:"result"`
Result string `json:"result"`
}
response := Response{}
err := g.SendAuthenticatedHTTPRequest("POST", GEMINI_HEARTBEAT, nil, &response)
if err != nil {
return false, err
}
return response.Result, nil
return response.Result,
g.SendAuthenticatedHTTPRequest("POST", geminiHeartbeat, nil, &response)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the
// exchange and returns an error
func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) (err error) {
if !g.AuthenticatedAPISupport {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, g.Name)
@@ -269,8 +405,9 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
g.Nonce.Inc()
}
headers := make(map[string]string)
request := make(map[string]interface{})
request["request"] = fmt.Sprintf("/v%s/%s", GEMINI_API_VERSION, path)
request["request"] = fmt.Sprintf("/v%s/%s", geminiAPIVersion, path)
request["nonce"] = g.Nonce.Get()
if params != nil {
@@ -280,7 +417,6 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
}
PayloadJSON, err := common.JSONEncode(request)
if err != nil {
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request")
}
@@ -291,21 +427,32 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
PayloadBase64 := common.Base64Encode(PayloadJSON)
hmac := common.GetHMAC(common.HashSHA512_384, []byte(PayloadBase64), []byte(g.APISecret))
headers := make(map[string]string)
headers["X-GEMINI-APIKEY"] = g.APIKey
headers["X-GEMINI-PAYLOAD"] = PayloadBase64
headers["X-GEMINI-SIGNATURE"] = common.HexEncodeToString(hmac)
resp, err := common.SendHTTPRequest(method, GEMINI_API_URL+path, headers, strings.NewReader(""))
resp, err := common.SendHTTPRequest(method, g.APIUrl+"/v1/"+path, headers, strings.NewReader(""))
if err != nil {
return err
}
if g.Verbose {
log.Printf("Received raw: \n%s\n", resp)
}
err = common.JSONDecode([]byte(resp), &result)
captureErr := ErrorCapture{}
if err = common.JSONDecode([]byte(resp), &captureErr); err == nil {
if len(captureErr.Message) != 0 || len(captureErr.Result) != 0 || len(captureErr.Reason) != 0 {
if captureErr.Result != "ok" {
return errors.New(captureErr.Message)
}
}
}
err = common.JSONDecode([]byte(resp), &result)
if err != nil {
return errors.New("unable to JSON Unmarshal response")
return err
}
return nil

View File

@@ -0,0 +1,224 @@
package gemini
import (
"net/url"
"testing"
"github.com/thrasher-/gocryptotrader/config"
)
var (
g Gemini
)
// Please enter sandbox API keys & assigned roles for better testing procedures
const (
apiKey1 = ""
apiSecret1 = ""
apiKeyRole1 = ""
sessionHeartBeat1 = false
apiKey2 = ""
apiSecret2 = ""
apiKeyRole2 = ""
sessionHeartBeat2 = false
)
func TestAddSession(t *testing.T) {
err := g.AddSession(1, apiKey1, apiSecret1, apiKeyRole1, true)
if err != nil {
t.Error("Test failed - AddSession() error")
}
err = g.AddSession(1, apiKey1, apiSecret1, apiKeyRole1, true)
if err == nil {
t.Error("Test failed - AddSession() error")
}
err = g.AddSession(2, apiKey2, apiSecret2, apiKeyRole2, false)
if err != nil {
t.Error("Test failed - AddSession() error")
}
}
func TestSetDefaults(t *testing.T) {
g.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.dat")
geminiConfig, err := cfg.GetExchangeConfig("Gemini")
if err != nil {
t.Error("Test Failed - Gemini Setup() init error")
}
geminiConfig.AuthenticatedAPISupport = true
g.Setup(geminiConfig)
}
func TestSession(t *testing.T) {
t.Parallel()
if g.Session(1) == nil {
t.Error("Test Failed - Session() error")
}
if g.Session(1337) != nil {
t.Error("Test Failed - Session() error")
}
}
func TestSandbox(t *testing.T) {
t.Parallel()
g.APIUrl = geminiAPIURL
if g.Sandbox().APIUrl != geminiSandboxAPIURL {
t.Error("Test Failed - Sandbox() error")
}
}
func TestGetSymbols(t *testing.T) {
t.Parallel()
_, err := g.GetSymbols()
if err != nil {
t.Error("Test Failed - GetSymbols() error", err)
}
}
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := g.GetTicker("BTCUSD")
if err != nil {
t.Error("Test Failed - GetTicker() error", err)
}
_, err = g.GetTicker("bla")
if err == nil {
t.Error("Test Failed - GetTicker() error", err)
}
}
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := g.GetOrderbook("btcusd", url.Values{})
if err != nil {
t.Error("Test Failed - GetOrderbook() error", err)
}
}
func TestGetTrades(t *testing.T) {
t.Parallel()
_, err := g.GetTrades("btcusd", url.Values{})
if err != nil {
t.Error("Test Failed - GetTrades() error", err)
}
}
func TestGetAuction(t *testing.T) {
t.Parallel()
_, err := g.GetAuction("btcusd")
if err != nil {
t.Error("Test Failed - GetAuction() error", err)
}
}
func TestGetAuctionHistory(t *testing.T) {
t.Parallel()
_, err := g.GetAuctionHistory("btcusd", url.Values{})
if err != nil {
t.Error("Test Failed - GetAuctionHistory() error", err)
}
}
func TestNewOrder(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().NewOrder("btcusd", 1, 4500, "buy", "exchange limit")
if err == nil {
t.Error("Test Failed - NewOrder() error", err)
}
_, err = g.Session(2).Sandbox().NewOrder("btcusd", 1, 4500, "buy", "exchange limit")
if err == nil {
t.Error("Test Failed - NewOrder() error", err)
}
}
func TestCancelOrder(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().CancelOrder(1337)
if err == nil {
t.Error("Test Failed - CancelOrder() error", err)
}
}
func TestCancelOrders(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().CancelOrders(false)
if err == nil {
t.Error("Test Failed - CancelOrders() error", err)
}
_, err = g.Session(2).Sandbox().CancelOrders(true)
if err == nil {
t.Error("Test Failed - CancelOrders() error", err)
}
}
func TestGetOrderStatus(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().GetOrderStatus(1337)
if err == nil {
t.Error("Test Failed - GetOrderStatus() error", err)
}
}
func TestGetOrders(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().GetOrders()
if err == nil {
t.Error("Test Failed - GetOrders() error", err)
}
}
func TestGetTradeHistory(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().GetTradeHistory("btcusd", 0)
if err == nil {
t.Error("Test Failed - GetTradeHistory() error", err)
}
}
func TestGetTradeVolume(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().GetTradeVolume()
if err == nil {
t.Error("Test Failed - GetTradeVolume() error", err)
}
}
func TestGetBalances(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().GetBalances()
if err == nil {
t.Error("Test Failed - GetBalances() error", err)
}
}
func TestGetDepositAddress(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().GetDepositAddress("LOL123", "btc")
if err == nil {
t.Error("Test Failed - GetDepositAddress() error", err)
}
}
func TestWithdrawCrypto(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().WithdrawCrypto("LOL123", "btc", 1)
if err == nil {
t.Error("Test Failed - WithdrawCrypto() error", err)
}
}
func TestPostHeartbeat(t *testing.T) {
t.Parallel()
_, err := g.Session(1).Sandbox().PostHeartbeat()
if err == nil {
t.Error("Test Failed - PostHeartbeat() error", err)
}
}

View File

@@ -1,66 +1,7 @@
package gemini
type GeminiOrderbookEntry struct {
Price float64 `json:"price,string"`
Amount float64 `json:"amount,string"`
}
type GeminiOrderbook struct {
Bids []GeminiOrderbookEntry `json:"bids"`
Asks []GeminiOrderbookEntry `json:"asks"`
}
type GeminiTrade struct {
Timestamp int64 `json:"timestamp"`
TID int64 `json:"tid"`
Price float64 `json:"price"`
Amount float64 `json:"amount"`
Side string `json:"taker"`
}
type GeminiOrder struct {
OrderID int64 `json:"order_id"`
ClientOrderID string `json:"client_order_id"`
Symbol string `json:"symbol"`
Exchange string `json:"exchange"`
Price float64 `json:"price,string"`
AvgExecutionPrice float64 `json:"avg_execution_price,string"`
Side string `json:"side"`
Type string `json:"type"`
Timestamp int64 `json:"timestamp"`
TimestampMS int64 `json:"timestampms"`
IsLive bool `json:"is_live"`
IsCancelled bool `json:"is_cancelled"`
WasForced bool `json:"was_forced"`
ExecutedAmount float64 `json:"executed_amount,string"`
RemainingAmount float64 `json:"remaining_amount,string"`
OriginalAmount float64 `json:"original_amount,string"`
}
type GeminiOrderResult struct {
Result bool `json:"result"`
}
type GeminiTradeHistory struct {
Price float64 `json:"price"`
Amount float64 `json:"amount"`
Timestamp int64 `json:"timestamp"`
TimestampMS int64 `json:"timestampms"`
Type string `json:"type"`
FeeCurrency string `json:"fee_currency"`
FeeAmount float64 `json:"fee_amount"`
TID int64 `json:"tid"`
OrderID int64 `json:"order_id"`
ClientOrderID string `json:"client_order_id"`
}
type GeminiBalance struct {
Currency string `json:"currency"`
Amount float64 `json:"amount"`
Available float64 `json:"available"`
}
type GeminiTicker struct {
// Ticker holds returned ticker data from the exchange
type Ticker struct {
Ask float64 `json:"ask,string"`
Bid float64 `json:"bid,string"`
Last float64 `json:"last,string"`
@@ -71,16 +12,47 @@ type GeminiTicker struct {
}
}
type GeminiAuction struct {
LastAuctionPrice float64 `json:"last_auction_price,string"`
LastAuctionQuantity float64 `json:"last_auction_quantity,string"`
LastHighestBidPrice float64 `json:"last_highest_bid_price,string"`
LastLowestAskPrice float64 `json:"last_lowest_ask_price,string"`
NextUpdateMS int64 `json:"next_update_ms"`
NextAuctionMS int64 `json:"next_auction_ms"`
LastAuctionEID int64 `json:"last_auction_eid"`
// Orderbook contains orderbook information for both bid and ask side
type Orderbook struct {
Bids []OrderbookEntry `json:"bids"`
Asks []OrderbookEntry `json:"asks"`
}
type GeminiAuctionHistory struct {
// OrderbookEntry subtype of orderbook information
type OrderbookEntry struct {
Price float64 `json:"price,string"`
Amount float64 `json:"amount,string"`
}
// Trade holds trade history for a specific currency pair
type Trade struct {
Timestamp int64 `json:"timestamp"`
Timestampms int64 `json:"timestampms"`
TID int64 `json:"tid"`
Price float64 `json:"price,string"`
Amount float64 `json:"amount,string"`
Exchange string `json:"exchange"`
Side string `json:"type"`
}
// Auction is generalized response type
type Auction struct {
LastAuctionEID int64 `json:"last_auction_eid"`
ClosedUntilMs int64 `json:"closed_until_ms"`
LastAuctionPrice float64 `json:"last_auction_price,string"`
LastAuctionQuantity float64 `json:"last_auction_quantity,string"`
LastHighestBidPrice float64 `json:"last_highest_bid_price,string"`
LastLowestAskPrice float64 `json:"last_lowest_ask_price,string"`
NextAuctionMS int64 `json:"next_auction_ms"`
NextUpdateMS int64 `json:"next_update_ms"`
MostRecentIndicativePrice float64 `json:"most_recent_indicative_price,string"`
MostRecentIndicativeQuantity float64 `json:"most_recent_indicative_quantity,string"`
MostRecentHighestBidPrice float64 `json:"most_recent_highest_bid_price,string"`
MostRecentLowestAskPrice float64 `json:"most_recent_lowest_ask_price,string"`
}
// AuctionHistory holds auction history information
type AuctionHistory struct {
AuctionID int64 `json:"auction_id"`
AuctionPrice float64 `json:"auction_price,string"`
AuctionQuantity float64 `json:"auction_quantity,string"`
@@ -92,3 +64,102 @@ type GeminiAuctionHistory struct {
TimestampMS int64 `json:"timestampms"`
EventType string `json:"event_type"`
}
// OrderResult holds cancelled order information
type OrderResult struct {
Result string `json:"result"`
Details struct {
CancelledOrders []string `json:"cancelledOrders"`
CancelRejects []string `json:"cancelRejects"`
} `json:"details"`
}
// Order contains order information
type Order struct {
OrderID int64 `json:"order_id,string"`
ID int64 `json:"id,string"`
ClientOrderID string `json:"client_order_id"`
Symbol string `json:"symbol"`
Exchange string `json:"exchange"`
Price float64 `json:"price,string"`
AvgExecutionPrice float64 `json:"avg_execution_price,string"`
Side string `json:"side"`
Type string `json:"type"`
Timestamp int64 `json:"timestamp,string"`
TimestampMS int64 `json:"timestampms"`
IsLive bool `json:"is_live"`
IsCancelled bool `json:"is_cancelled"`
IsHidden bool `json:"is_hidden"`
Options []string `json:"options"`
WasForced bool `json:"was_forced"`
ExecutedAmount float64 `json:"executed_amount,string"`
RemainingAmount float64 `json:"remaining_amount,string"`
OriginalAmount float64 `json:"original_amount,string"`
}
// TradeHistory holds trade history information
type TradeHistory struct {
Price float64 `json:"price,string"`
Amount float64 `json:"amount,string"`
Timestamp int64 `json:"timestamp"`
TimestampMS int64 `json:"timestampms"`
Type string `json:"type"`
FeeCurrency string `json:"fee_currency"`
FeeAmount float64 `json:"fee_amount,string"`
TID int64 `json:"tid"`
OrderID int64 `json:"order_id,string"`
Exchange string `json:"exchange"`
IsAuctionFilled bool `json:"is_auction_fill"`
ClientOrderID string `json:"client_order_id"`
}
// TradeVolume holds Volume information
type TradeVolume struct {
AccountID int64 `json:"account_id"`
Symbol string `json:"symbol"`
BaseCurrency string `json:"base_currency"`
NotionalCurrency string `json:"notional_currency"`
Date string `json:"date_date"`
TotalVolumeBase float64 `json:"total_volume_base"`
MakerBuySellRatio float64 `json:"maker_buy_sell_ratio"`
BuyMakerBase float64 `json:"buy_maker_base"`
BuyMakerNotional float64 `json:"buy_maker_notional"`
BuyMakerCount float64 `json:"buy_maker_count"`
SellMakerBase float64 `json:"sell_maker_base"`
SellMakerNotional float64 `json:"sell_maker_notional"`
SellMakerCount float64 `json:"sell_maker_count"`
BuyTakerBase float64 `json:"buy_taker_base"`
BuyTakerNotional float64 `json:"buy_taker_notional"`
BuyTakerCount float64 `json:"buy_taker_count"`
SellTakerBase float64 `json:"sell_taker_base"`
SellTakerNotional float64 `json:"sell_taker_notional"`
SellTakerCount float64 `json:"sell_taker_count"`
}
// Balance is a simple balance type
type Balance struct {
Currency string `json:"currency"`
Amount float64 `json:"amount,string"`
Available float64 `json:"available,string"`
}
// DepositAddress holds assigned deposit address for a specific currency
type DepositAddress struct {
Currency string `json:"currency"`
Address string `json:"address"`
Label string `json:"label"`
}
// WithdrawelAddress holds withdrawel information
type WithdrawelAddress struct {
Address string `json:"address"`
Amount float64 `json:"amount"`
TXHash string `json:"txHash"`
}
// ErrorCapture is a generlized error response from the server
type ErrorCapture struct {
Result string `json:"result"`
Reason string `json:"reason"`
Message string `json:"message"`
}