diff --git a/common/convert/convert.go b/common/convert/convert.go index 5bf7e740..1993de89 100644 --- a/common/convert/convert.go +++ b/common/convert/convert.go @@ -1,8 +1,11 @@ package convert import ( + "errors" "fmt" + "math" "strconv" + "strings" "time" ) @@ -77,3 +80,30 @@ func UnixMillis(t time.Time) int64 { func RecvWindow(d time.Duration) int64 { return int64(d) / int64(time.Millisecond) } + +// SplitFloatDecimals takes in a float64 and splits +// the decimals into their own integers +// Warning. Passing in numbers with many decimals +// can lead to a loss of accuracy +func SplitFloatDecimals(input float64) (baseNum, decimalNum int64, err error) { + if input > float64(math.MaxInt64) { + return 0, 0, errors.New("number too large to split into integers") + } + if input == float64(int64(input)) { + return int64(input), 0, nil + } + decStr := strconv.FormatFloat(input, 'f', -1, 64) + splitNum := strings.Split(decStr, ".") + baseNum, err = strconv.ParseInt(splitNum[0], 10, 64) + if err != nil { + return 0, 0, err + } + decimalNum, err = strconv.ParseInt(splitNum[1], 10, 64) + if err != nil { + return 0, 0, err + } + if baseNum < 0 { + decimalNum *= -1 + } + return baseNum, decimalNum, nil +} diff --git a/common/convert/convert_test.go b/common/convert/convert_test.go index 03aabb3d..60d84153 100644 --- a/common/convert/convert_test.go +++ b/common/convert/convert_test.go @@ -1,6 +1,7 @@ package convert import ( + "math" "testing" "time" ) @@ -149,3 +150,58 @@ func TestRecvWindow(t *testing.T) { expectedOutput, actualOutput) } } + +// TestSplitFloatDecimals ensures SplitFloatDecimals +// accurately splits decimals into integers +func TestSplitFloatDecimals(t *testing.T) { + x, y, err := SplitFloatDecimals(1.2) + if err != nil { + t.Error(err) + } + if x != 1 && y != 2 { + t.Error("Conversion error") + } + x, y, err = SplitFloatDecimals(123456.654321) + if err != nil { + t.Error(err) + } + if x != 123456 && y != 654321 { + t.Error("Conversion error") + } + x, y, err = SplitFloatDecimals(123.111000) + if err != nil { + t.Error(err) + } + if x != 123 && y != 111 { + t.Error("Conversion error") + } + x, y, err = SplitFloatDecimals(0123.111001) + if err != nil { + t.Error(err) + } + if x != 123 && y != 111001 { + t.Error("Conversion error") + } + x, y, err = SplitFloatDecimals(1) + if err != nil { + t.Error(err) + } + if x != 1 && y != 0 { + t.Error("Conversion error") + } + _, _, err = SplitFloatDecimals(float64(math.MaxInt64) + 1) + if err == nil { + t.Error("Expected conversion error") + } + _, _, err = SplitFloatDecimals(1797693134862315700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111) + if err == nil { + t.Error("Expected conversion error") + } + x, y, err = SplitFloatDecimals(-1.2) + if err != nil { + t.Error(err) + } + if x != -1 && y != -2 { + t.Error("Conversion error") + } +} diff --git a/currency/pair_test.go b/currency/pair_test.go index 837d52e2..b3856528 100644 --- a/currency/pair_test.go +++ b/currency/pair_test.go @@ -343,10 +343,10 @@ func TestNewPairDelimiter(t *testing.T) { // specific index func TestNewPairFromIndex(t *testing.T) { t.Parallel() - currency := defaultPair + curr := defaultPair index := "BTC" - pair, err := NewPairFromIndex(currency, index) + pair, err := NewPairFromIndex(curr, index) if err != nil { t.Error("NewPairFromIndex() error", err) } @@ -362,9 +362,9 @@ func TestNewPairFromIndex(t *testing.T) { ) } - currency = "DOGEBTC" + curr = "DOGEBTC" - pair, err = NewPairFromIndex(currency, index) + pair, err = NewPairFromIndex(curr, index) if err != nil { t.Error("NewPairFromIndex() error", err) } diff --git a/currency/storage.go b/currency/storage.go index b0452f0e..3ed5aec0 100644 --- a/currency/storage.go +++ b/currency/storage.go @@ -175,19 +175,15 @@ func (s *Storage) SetupConversionRates() { // SetDefaultFiatCurrencies assigns the default fiat currency list and adds it // to the running list func (s *Storage) SetDefaultFiatCurrencies(c ...Code) { - for _, currency := range c { - s.defaultFiatCurrencies = append(s.defaultFiatCurrencies, currency) - s.fiatCurrencies = append(s.fiatCurrencies, currency) - } + s.defaultFiatCurrencies = append(s.defaultFiatCurrencies, c...) + s.fiatCurrencies = append(s.fiatCurrencies, c...) } // SetDefaultCryptocurrencies assigns the default cryptocurrency list and adds // it to the running list func (s *Storage) SetDefaultCryptocurrencies(c ...Code) { - for _, currency := range c { - s.defaultCryptoCurrencies = append(s.defaultCryptoCurrencies, currency) - s.cryptocurrencies = append(s.cryptocurrencies, currency) - } + s.defaultCryptoCurrencies = append(s.defaultCryptoCurrencies, c...) + s.cryptocurrencies = append(s.cryptocurrencies, c...) } // SetupForexProviders sets up a new instance of the forex providers @@ -507,8 +503,8 @@ func (s *Storage) GetTotalMarketCryptocurrencies() (Currencies, error) { // IsDefaultCurrency returns if a currency is a default currency func (s *Storage) IsDefaultCurrency(c Code) bool { t, _ := GetTranslation(c) - for _, d := range s.defaultFiatCurrencies { - if d.Match(c) || d.Match(t) { + for i := range s.defaultFiatCurrencies { + if s.defaultFiatCurrencies[i].Match(c) || s.defaultFiatCurrencies[i].Match(t) { return true } } diff --git a/engine/orders.go b/engine/orders.go index 303df534..aa50406e 100644 --- a/engine/orders.go +++ b/engine/orders.go @@ -264,11 +264,11 @@ func (o *orderManager) processOrders() { } for x := range result { - order := &result[x] - result := o.orderStore.Add(order) + ord := &result[x] + result := o.orderStore.Add(ord) if result != ErrOrdersAlreadyExists { msg := fmt.Sprintf("Order manager: Exchange %s added order ID=%v pair=%v price=%v amount=%v side=%v type=%v.", - order.Exchange, order.ID, order.CurrencyPair, order.Price, order.Amount, order.OrderSide, order.OrderType) + ord.Exchange, ord.ID, ord.CurrencyPair, ord.Price, ord.Amount, ord.OrderSide, ord.OrderType) log.Debugf(log.OrderMgr, "%v\n", msg) Bot.CommsManager.PushEvent(base.Event{ Type: "order", diff --git a/engine/syncer.go b/engine/syncer.go index 6807dd9c..d900160e 100644 --- a/engine/syncer.go +++ b/engine/syncer.go @@ -310,16 +310,17 @@ func (e *ExchangeCurrencyPairSyncer) worker() { } for y := range assetTypes { - for _, p := range Bot.Exchanges[x].GetEnabledPairs(assetTypes[y]) { + enabledPairs := Bot.Exchanges[x].GetEnabledPairs(assetTypes[y]) + for i := range enabledPairs { if atomic.LoadInt32(&e.shutdown) == 1 { return } - if !e.exists(exchangeName, p, assetTypes[y]) { + if !e.exists(exchangeName, enabledPairs[i], assetTypes[y]) { c := CurrencyPairSyncAgent{ AssetType: assetTypes[y], Exchange: exchangeName, - Pair: p, + Pair: enabledPairs[i], } if e.Cfg.SyncTicker { @@ -346,7 +347,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() { e.add(&c) } - c, err := e.get(exchangeName, p, assetTypes[y]) + c, err := e.get(exchangeName, enabledPairs[i], assetTypes[y]) if err != nil { log.Errorf(log.SyncMgr, "failed to get item. Err: %s\n", err) continue @@ -354,7 +355,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() { if switchedToRest && usingWebsocket { log.Infof(log.SyncMgr, "%s %s: Websocket re-enabled, switching from rest to websocket\n", - c.Exchange, FormatCurrency(p).String()) + c.Exchange, FormatCurrency(enabledPairs[i]).String()) switchedToRest = false } if e.Cfg.SyncTicker { @@ -371,7 +372,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() { c.Ticker.IsUsingREST = true log.Warnf(log.SyncMgr, "%s %s: No ticker update after 10 seconds, switching from websocket to rest\n", - c.Exchange, FormatCurrency(p).String()) + c.Exchange, FormatCurrency(enabledPairs[i]).String()) switchedToRest = true e.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemTicker, false) } @@ -521,14 +522,15 @@ func (e *ExchangeCurrencyPairSyncer) Start() { } for y := range assetTypes { - for _, p := range Bot.Exchanges[x].GetEnabledPairs(assetTypes[y]) { - if e.exists(exchangeName, p, assetTypes[y]) { + enabledPairs := Bot.Exchanges[x].GetEnabledPairs(assetTypes[y]) + for i := range enabledPairs { + if e.exists(exchangeName, enabledPairs[i], assetTypes[y]) { continue } c := CurrencyPairSyncAgent{ AssetType: assetTypes[y], Exchange: exchangeName, - Pair: p, + Pair: enabledPairs[i], } if e.Cfg.SyncTicker { diff --git a/exchanges/alphapoint/alphapoint_test.go b/exchanges/alphapoint/alphapoint_test.go index 964cd0d8..910e9880 100644 --- a/exchanges/alphapoint/alphapoint_test.go +++ b/exchanges/alphapoint/alphapoint_test.go @@ -2,6 +2,8 @@ package alphapoint import ( "encoding/json" + "log" + "os" "testing" "github.com/thrasher-corp/gocryptotrader/common" @@ -17,45 +19,37 @@ const ( canManipulateRealOrders = false ) -func TestSetDefaults(t *testing.T) { - t.Parallel() - SetDefaults := Alphapoint{} +var a Alphapoint - SetDefaults.SetDefaults() - if SetDefaults.API.Endpoints.URL != "https://sim3.alphapoint.com:8400" { - t.Error("SetDefaults: String Incorrect -", SetDefaults.API.Endpoints.URL) - } - if SetDefaults.API.Endpoints.WebsocketURL != "wss://sim3.alphapoint.com:8401/v1/GetTicker/" { - t.Error("SetDefaults: String Incorrect -", SetDefaults.API.Endpoints.WebsocketURL) - } -} - -func testSetAPIKey(a *Alphapoint) { +func TestMain(m *testing.M) { + a.SetDefaults() a.API.Credentials.Key = apiKey a.API.Credentials.Secret = apiSecret a.API.AuthenticatedSupport = true -} - -func testIsAPIKeysSet(a *Alphapoint) bool { - if apiKey != "" && apiSecret != "" && a.API.AuthenticatedSupport { - return true + if a.API.Endpoints.URL != "https://sim3.alphapoint.com:8400" { + log.Fatal("SetDefaults: String Incorrect -", a.API.Endpoints.URL) } - return false + if a.API.Endpoints.WebsocketURL != "wss://sim3.alphapoint.com:8401/v1/GetTicker/" { + log.Fatal("SetDefaults: String Incorrect -", a.API.Endpoints.WebsocketURL) + } + os.Exit(m.Run()) } -func TestGetTicker(t *testing.T) { - alpha := Alphapoint{} - alpha.SetDefaults() +func areTestAPIKeysSet() bool { + return a.ValidateAPICredentials() +} + +func TestGetTicker(t *testing.T) { + t.Parallel() var ticker Ticker var err error - if onlineTest { - ticker, err = alpha.GetTicker("BTCUSD") + ticker, err = a.GetTicker("BTCUSD") if err != nil { t.Fatal("Alphapoint GetTicker init error: ", err) } - _, err = alpha.GetTicker("wigwham") + _, err = a.GetTicker("wigwham") if err == nil { t.Error("Alphapoint GetTicker Expected error") } @@ -80,19 +74,16 @@ func TestGetTicker(t *testing.T) { } func TestGetTrades(t *testing.T) { - alpha := Alphapoint{} - alpha.SetDefaults() - + t.Parallel() var trades Trades var err error - if onlineTest { - trades, err = alpha.GetTrades("BTCUSD", 0, 10) + trades, err = a.GetTrades("BTCUSD", 0, 10) if err != nil { t.Fatalf("Init error: %s", err) } - _, err = alpha.GetTrades("wigwham", 0, 10) + _, err = a.GetTrades("wigwham", 0, 10) if err == nil { t.Fatal("GetTrades Expected error") } @@ -121,18 +112,15 @@ func TestGetTrades(t *testing.T) { } func TestGetTradesByDate(t *testing.T) { - alpha := Alphapoint{} - alpha.SetDefaults() - + t.Parallel() var trades Trades var err error - if onlineTest { - trades, err = alpha.GetTradesByDate("BTCUSD", 1414799400, 1414800000) + trades, err = a.GetTradesByDate("BTCUSD", 1414799400, 1414800000) if err != nil { t.Errorf("Init error: %s", err) } - _, err = alpha.GetTradesByDate("wigwham", 1414799400, 1414800000) + _, err = a.GetTradesByDate("wigwham", 1414799400, 1414800000) if err == nil { t.Error("GetTradesByDate Expected error") } @@ -168,19 +156,16 @@ func TestGetTradesByDate(t *testing.T) { } func TestGetOrderbook(t *testing.T) { - alpha := Alphapoint{} - alpha.SetDefaults() - + t.Parallel() var orderBook Orderbook var err error - if onlineTest { - orderBook, err = alpha.GetOrderbook("BTCUSD") + orderBook, err = a.GetOrderbook("BTCUSD") if err != nil { t.Errorf("Init error: %s", err) } - _, err = alpha.GetOrderbook("wigwham") + _, err = a.GetOrderbook("wigwham") if err == nil { t.Error("GetOrderbook() Expected error") } @@ -213,14 +198,12 @@ func TestGetOrderbook(t *testing.T) { } func TestGetProductPairs(t *testing.T) { - alpha := Alphapoint{} - alpha.SetDefaults() - + t.Parallel() var products ProductPairs var err error if onlineTest { - products, err = alpha.GetProductPairs() + products, err = a.GetProductPairs() if err != nil { t.Errorf("Init error: %s", err) } @@ -253,14 +236,12 @@ func TestGetProductPairs(t *testing.T) { } func TestGetProducts(t *testing.T) { - alpha := Alphapoint{} - alpha.SetDefaults() - + t.Parallel() var products Products var err error if onlineTest { - products, err = alpha.GetProducts() + products, err = a.GetProducts() if err != nil { t.Errorf("Init error: %s", err) } @@ -293,12 +274,9 @@ func TestGetProducts(t *testing.T) { } func TestCreateAccount(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } err := a.CreateAccount("test", "account", "something@something.com", "0292383745", "lolcat123") @@ -316,12 +294,9 @@ func TestCreateAccount(t *testing.T) { } func TestGetUserInfo(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } _, err := a.GetUserInfo() @@ -331,12 +306,9 @@ func TestGetUserInfo(t *testing.T) { } func TestSetUserInfo(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } _, err := a.SetUserInfo("bla", "bla", "1", "meh", true, true) @@ -346,12 +318,9 @@ func TestSetUserInfo(t *testing.T) { } func TestGetAccountInfo(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } _, err := a.GetAccountInfo() @@ -361,12 +330,9 @@ func TestGetAccountInfo(t *testing.T) { } func TestGetAccountTrades(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } _, err := a.GetAccountTrades("", 1, 2) @@ -376,12 +342,9 @@ func TestGetAccountTrades(t *testing.T) { } func TestGetDepositAddresses(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } _, err := a.GetDepositAddresses() @@ -391,12 +354,9 @@ func TestGetDepositAddresses(t *testing.T) { } func TestWithdrawCoins(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } err := a.WithdrawCoins("", "", "", 0.01) @@ -406,12 +366,9 @@ func TestWithdrawCoins(t *testing.T) { } func TestCreateOrder(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } _, err := a.CreateOrder("", "", order.Limit.String(), 0.01, 0) @@ -421,12 +378,9 @@ func TestCreateOrder(t *testing.T) { } func TestModifyExistingOrder(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } _, err := a.ModifyExistingOrder("", 1, 1) @@ -436,12 +390,9 @@ func TestModifyExistingOrder(t *testing.T) { } func TestCancelAllExistingOrders(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } err := a.CancelAllExistingOrders("") @@ -451,12 +402,9 @@ func TestCancelAllExistingOrders(t *testing.T) { } func TestGetOrders(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } _, err := a.GetOrders() @@ -466,12 +414,9 @@ func TestGetOrders(t *testing.T) { } func TestGetOrderFee(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - testSetAPIKey(a) - - if !testIsAPIKeysSet(a) { - return + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") } _, err := a.GetOrderFee("", "", 1, 1) @@ -481,45 +426,38 @@ func TestGetOrderFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() + t.Parallel() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.WithdrawCryptoWith2FAText + " & " + exchange.NoFiatWithdrawalsText - withdrawPermissions := a.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - + t.Parallel() var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } _, err := a.GetActiveOrders(&getOrdersRequest) - if areTestAPIKeysSet(a) && err != nil { + if areTestAPIKeysSet() && err != nil { t.Errorf("Could not get open orders: %s", err) - } else if !areTestAPIKeysSet(a) && err == nil { + } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") } } func TestGetOrderHistory(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - + t.Parallel() var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } _, err := a.GetOrderHistory(&getOrdersRequest) - if areTestAPIKeysSet(a) && err != nil { + if areTestAPIKeysSet() && err != nil { t.Errorf("Could not get order history: %s", err) - } else if !areTestAPIKeysSet(a) && err == nil { + } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") } } @@ -527,15 +465,9 @@ func TestGetOrderHistory(t *testing.T) { // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- -func areTestAPIKeysSet(a *Alphapoint) bool { - return a.ValidateAPICredentials() -} - func TestSubmitOrder(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - - if areTestAPIKeysSet(a) && !canManipulateRealOrders { + t.Parallel() + if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -553,10 +485,10 @@ func TestSubmitOrder(t *testing.T) { } response, err := a.SubmitOrder(orderSubmission) - if !areTestAPIKeysSet(a) && err == nil { + if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") } - if areTestAPIKeysSet(a) && err != nil { + if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) if !response.IsOrderPlaced { @@ -566,15 +498,12 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - - if areTestAPIKeysSet(a) && !canManipulateRealOrders { + t.Parallel() + if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.BTC, currency.LTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -583,24 +512,21 @@ func TestCancelExchangeOrder(t *testing.T) { } err := a.CancelOrder(orderCancellation) - if !areTestAPIKeysSet(a) && err == nil { + if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") } - if areTestAPIKeysSet(a) && err != nil { + if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) } } func TestCancelAllExchangeOrders(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - - if areTestAPIKeysSet(a) && !canManipulateRealOrders { + t.Parallel() + if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.BTC, currency.LTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -609,11 +535,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { } resp, err := a.CancelAllOrders(orderCancellation) - - if !areTestAPIKeysSet(a) && err == nil { + if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") } - if areTestAPIKeysSet(a) && err != nil { + if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) } @@ -623,9 +548,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestModifyOrder(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - + t.Parallel() + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := a.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -633,10 +559,8 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() + t.Parallel() var withdrawCryptoRequest = exchange.CryptoWithdrawRequest{} - _, err := a.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest) if err != common.ErrNotYetImplemented { t.Errorf("Expected 'Not implemented', received %v", err) @@ -644,10 +568,8 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - - if areTestAPIKeysSet(a) && !canManipulateRealOrders { + t.Parallel() + if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -659,10 +581,8 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - a := &Alphapoint{} - a.SetDefaults() - - if areTestAPIKeysSet(a) && !canManipulateRealOrders { + t.Parallel() + if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index 2eb89200..4df2c5d5 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -212,16 +212,18 @@ func (a *Alphapoint) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) s.OrderSide.String(), s.Amount, s.Price) - + if err != nil { + return submitOrderResponse, err + } if response > 0 { submitOrderResponse.OrderID = strconv.FormatInt(response, 10) } - - if err == nil { - submitOrderResponse.IsOrderPlaced = true + if s.OrderType == order.Market { + submitOrderResponse.FullyMatched = true } + submitOrderResponse.IsOrderPlaced = true - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/anx/anx_test.go b/exchanges/anx/anx_test.go index e6b29f10..c817c725 100644 --- a/exchanges/anx/anx_test.go +++ b/exchanges/anx/anx_test.go @@ -86,7 +86,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { t.Parallel() var feeBuilder = setFeeBuilder() a.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -171,9 +171,7 @@ func TestFormatWithdrawPermissions(t *testing.T) { t.Parallel() expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawCryptoWith2FAText + " & " + exchange.WithdrawCryptoWithEmailText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - withdrawPermissions := a.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } @@ -254,7 +252,6 @@ func TestCancelExchangeOrder(t *testing.T) { } currencyPair := currency.NewPair(currency.BTC, currency.LTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -280,7 +277,6 @@ func TestCancelAllExchangeOrders(t *testing.T) { } currencyPair := currency.NewPair(currency.BTC, currency.LTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", diff --git a/exchanges/anx/anx_wrapper.go b/exchanges/anx/anx_wrapper.go index 96b657ff..2a393832 100644 --- a/exchanges/anx/anx_wrapper.go +++ b/exchanges/anx/anx_wrapper.go @@ -2,6 +2,7 @@ package anx import ( "fmt" + "strconv" "strings" "sync" "time" @@ -346,16 +347,18 @@ func (a *ANX) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { false, "", false) - + if err != nil { + return submitOrderResponse, err + } if response != "" { submitOrderResponse.OrderID = response } - - if err == nil { - submitOrderResponse.IsOrderPlaced = true + if s.OrderType == order.Market { + submitOrderResponse.FullyMatched = true } + submitOrderResponse.IsOrderPlaced = true - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -391,9 +394,9 @@ func (a *ANX) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) return cancelAllOrdersResponse, err } - for _, order := range resp.OrderCancellationResponses { - if order.Error != CancelRequestSubmitted { - cancelAllOrdersResponse.Status[order.UUID] = order.Error + for i := range resp.OrderCancellationResponses { + if resp.OrderCancellationResponses[i].Error != CancelRequestSubmitted { + cancelAllOrdersResponse.Status[resp.OrderCancellationResponses[i].UUID] = resp.OrderCancellationResponses[i].Error } } @@ -414,7 +417,7 @@ func (a *ANX) GetDepositAddress(cryptocurrency currency.Code, _ string) (string, // WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is // submitted func (a *ANX) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.CryptoWithdrawRequest) (string, error) { - return a.Send(withdrawRequest.Currency.String(), withdrawRequest.Address, "", fmt.Sprintf("%v", withdrawRequest.Amount)) + return a.Send(withdrawRequest.Currency.String(), withdrawRequest.Address, "", strconv.FormatFloat(withdrawRequest.Amount, 'f', -1, 64)) } // WithdrawFiatFunds returns a withdrawal ID when a withdrawal is diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index 0f142019..0934d278 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -194,7 +194,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() b.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -282,7 +282,6 @@ func TestFormatWithdrawPermissions(t *testing.T) { t.Parallel() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText - withdrawPermissions := b.FormatWithdrawPermissions() if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) @@ -382,7 +381,6 @@ func TestCancelExchangeOrder(t *testing.T) { if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -407,7 +405,6 @@ func TestCancelAllExchangeOrders(t *testing.T) { if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", diff --git a/exchanges/binance/binance_websocket.go b/exchanges/binance/binance_websocket.go index ab437935..21d40cd3 100644 --- a/exchanges/binance/binance_websocket.go +++ b/exchanges/binance/binance_websocket.go @@ -53,8 +53,9 @@ func (b *Binance) WsConnect() error { kline + "/" + depth - for _, ePair := range b.GetEnabledPairs(asset.Spot) { - err = b.SeedLocalCache(ePair) + enabledPairs := b.GetEnabledPairs(asset.Spot) + for i := range enabledPairs { + err = b.SeedLocalCache(enabledPairs[i]) if err != nil { return err } diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index 7d66a87b..59203ff1 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -426,16 +426,18 @@ func (b *Binance) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { } response, err := b.NewOrder(&orderRequest) - + if err != nil { + return submitOrderResponse, err + } if response.OrderID > 0 { submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10) } - - if err == nil { - submitOrderResponse.IsOrderPlaced = true + if response.ExecutedQty == response.OrigQty { + submitOrderResponse.FullyMatched = true } + submitOrderResponse.IsOrderPlaced = true - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/bitfinex/bitfinex.go b/exchanges/bitfinex/bitfinex.go index f85e4c9d..2877b2cb 100644 --- a/exchanges/bitfinex/bitfinex.go +++ b/exchanges/bitfinex/bitfinex.go @@ -85,8 +85,9 @@ const ( // depending on some factors (e.g. servers load, endpoint, etc.). type Bitfinex struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection - WebsocketSubdChannels map[int]WebsocketChanInfo + WebsocketConn *wshandler.WebsocketConnection + AuthenticatedWebsocketConn *wshandler.WebsocketConnection + WebsocketSubdChannels map[int]WebsocketChanInfo } // GetPlatformStatus returns the Bifinex platform status @@ -358,11 +359,11 @@ func (b *Bitfinex) GetTradesV2(currencyPair string, timestampStart, timestampEnd } var tempHistory TradeStructureV2 - for _, data := range resp { - tempHistory.TID = int64(data[0].(float64)) - tempHistory.Timestamp = int64(data[1].(float64)) - tempHistory.Amount = data[2].(float64) - tempHistory.Price = data[3].(float64) + for i := range resp { + tempHistory.TID = int64(resp[i][0].(float64)) + tempHistory.Timestamp = int64(resp[i][1].(float64)) + tempHistory.Amount = resp[i][2].(float64) + tempHistory.Price = resp[i][3].(float64) tempHistory.Exchange = b.Name tempHistory.Type = order.Buy.String() diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index 04f44be4..ce29001d 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -1,8 +1,10 @@ package bitfinex import ( + "log" "net/http" "net/url" + "os" "reflect" "testing" "time" @@ -25,37 +27,43 @@ const ( ) var b Bitfinex +var wsAuthExecuted bool -func TestSetup(t *testing.T) { +func TestMain(m *testing.M) { b.SetDefaults() cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("Bitfinex load config error", err) + log.Fatal("Bitfinex load config error", err) } bfxConfig, err := cfg.GetExchangeConfig("Bitfinex") if err != nil { - t.Error("Bitfinex Setup() init error") + log.Fatal("Bitfinex Setup() init error") } err = b.Setup(bfxConfig) if err != nil { - t.Fatal("Bitfinex setup error", err) + log.Fatal("Bitfinex setup error", err) } b.API.Credentials.Key = apiKey b.API.Credentials.Secret = apiSecret if !b.Enabled || b.API.AuthenticatedSupport || b.Verbose || b.Websocket.IsEnabled() || len(b.BaseCurrencies) < 1 { - t.Error("Bitfinex Setup values not set correctly") + log.Fatal("Bitfinex Setup values not set correctly") + } + + if areTestAPIKeysSet() { + b.API.AuthenticatedSupport = true + b.API.AuthenticatedWebsocketSupport = true } - b.API.AuthenticatedSupport = true - b.API.AuthenticatedWebsocketSupport = true // custom rate limit for testing b.Requester.SetRateLimit(true, time.Millisecond*300, 1) b.Requester.SetRateLimit(false, time.Millisecond*300, 1) + os.Exit(m.Run()) } func TestAppendOptionalDelimiter(t *testing.T) { + t.Parallel() curr1 := currency.NewPairFromString("BTCUSD") b.appendOptionalDelimiter(&curr1) if curr1.Delimiter != "" { @@ -71,7 +79,6 @@ func TestAppendOptionalDelimiter(t *testing.T) { func TestGetPlatformStatus(t *testing.T) { t.Parallel() - result, err := b.GetPlatformStatus() if err != nil { t.Errorf("TestGetPlatformStatus error: %s", err) @@ -653,7 +660,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() b.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -665,11 +672,10 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - b.SetDefaults() - TestSetup(t) var feeBuilder = setFeeBuilder() + t.Parallel() - if apiKey != "" || apiSecret != "" { + if areTestAPIKeysSet() { // CryptocurrencyTradeFee Basic if resp, err := b.GetFee(feeBuilder); resp != float64(0.002) || err != nil { t.Error(err) @@ -738,20 +744,16 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - b.SetDefaults() + t.Parallel() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.AutoWithdrawFiatWithAPIPermissionText - withdrawPermissions := b.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -765,9 +767,7 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -787,9 +787,7 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -806,23 +804,25 @@ func TestSubmitOrder(t *testing.T) { ClientID: "meowOrder", } response, err := b.SubmitOrder(orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { - t.Errorf("Order failed to be placed: %v", err) - } else if !areTestAPIKeysSet() && err == nil { + + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not cancel orders: %v", err) + } + if areTestAPIKeysSet() && !response.IsOrderPlaced { + t.Error("Order not placed") + } + if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") } } func TestCancelExchangeOrder(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -840,15 +840,12 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrdera(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -871,6 +868,10 @@ func TestCancelAllExchangeOrdera(t *testing.T) { } func TestModifyOrder(t *testing.T) { + t.Parallel() + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := b.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -878,8 +879,7 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - b.SetDefaults() - TestSetup(t) + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -903,9 +903,7 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -938,9 +936,7 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -979,6 +975,7 @@ func TestWithdrawInternationalBank(t *testing.T) { } func TestGetDepositAddress(t *testing.T) { + t.Parallel() if areTestAPIKeysSet() { _, err := b.GetDepositAddress(currency.BTC, "deposit") if err != nil { @@ -992,40 +989,168 @@ func TestGetDepositAddress(t *testing.T) { } } -// TestWsAuth dials websocket, sends login request. -func TestWsAuth(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) - } - b.WebsocketConn = &wshandler.WebsocketConnection{ +func setupWs() { + b.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ ExchangeName: b.Name, - URL: b.Websocket.GetWebsocketURL(), + URL: authenticatedBitfinexWebsocketEndpoint, Verbose: b.Verbose, ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, } var dialer websocket.Dialer - err := b.WebsocketConn.Dial(&dialer, http.Header{}) + err := b.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{}) if err != nil { - t.Fatal(err) + log.Fatal(err) } b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() b.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() + go b.WsReadData(b.AuthenticatedWebsocketConn) go b.WsDataHandler() - err = b.WsSendAuth() +} + +// TestWsAuth dials websocket, sends login request. +func TestWsAuth(t *testing.T) { + if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") + } + runAuth(t) +} + +func runAuth(t *testing.T) { + setupWs() + err := b.WsSendAuth() if err != nil { t.Error(err) } timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout) select { case resp := <-b.Websocket.DataHandler: - if resp.(map[string]interface{})["event"] != "auth" && resp.(map[string]interface{})["status"] != "OK" { - t.Error("expected successful login") + if logResponse, ok := resp.(map[string]interface{}); ok { + if logResponse["event"] != "auth" && logResponse["status"] != "OK" { + t.Error("expected successful login") + } + } else { + t.Error("Unexpected response") } case <-timer.C: t.Error("Have not received a response") } timer.Stop() + wsAuthExecuted = true +} + +// TestWsPlaceOrder dials websocket, sends order request. +func TestWsPlaceOrder(t *testing.T) { + if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") + } + if !wsAuthExecuted { + runAuth(t) + } + _, err := b.WsNewOrder(&WsNewOrderRequest{ + CustomID: 1337, + Type: order.Buy.String(), + Symbol: "tBTCUSD", + Amount: 10, + Price: -10, + }) + if err != nil { + t.Error(err) + } +} + +// TestWsCancelOrder dials websocket, sends cancel request. +func TestWsCancelOrder(t *testing.T) { + if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") + } + if !wsAuthExecuted { + runAuth(t) + } + err := b.WsCancelOrder(1234) + if err != nil { + t.Error(err) + } +} + +// TestWsCancelOrder dials websocket, sends modify request. +func TestWsUpdateOrder(t *testing.T) { + if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") + } + if !wsAuthExecuted { + runAuth(t) + } + err := b.WsModifyOrder(&WsUpdateOrderRequest{ + OrderID: 1234, + Price: -111, + Amount: 111, + }) + if err != nil { + t.Error(err) + } +} + +// TestWsCancelAllOrders dials websocket, sends cancel all request. +func TestWsCancelAllOrders(t *testing.T) { + if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") + } + if !wsAuthExecuted { + runAuth(t) + } + err := b.WsCancelAllOrders() + if err != nil { + t.Error(err) + } +} + +// TestWsCancelAllOrders dials websocket, sends cancel all request. +func TestWsCancelMultiOrders(t *testing.T) { + if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") + } + if !wsAuthExecuted { + runAuth(t) + } + err := b.WsCancelMultiOrders([]int64{1, 2, 3, 4}) + if err != nil { + t.Error(err) + } +} + +// TestWsNewOffer dials websocket, sends new offer request. +func TestWsNewOffer(t *testing.T) { + if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") + } + if !wsAuthExecuted { + runAuth(t) + } + err := b.WsNewOffer(&WsNewOfferRequest{ + Type: order.Limit.String(), + Symbol: "fBTC", + Amount: -10, + Rate: 10, + Period: 30, + }) + if err != nil { + t.Error(err) + } + time.Sleep(time.Second) +} + +// TestWsCancelOffer dials websocket, sends cancel offer request. +func TestWsCancelOffer(t *testing.T) { + if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { + t.Skip("API keys not set, skipping") + } + if !wsAuthExecuted { + runAuth(t) + } + err := b.WsCancelOffer(1234) + if err != nil { + t.Error(err) + } + time.Sleep(time.Second) } diff --git a/exchanges/bitfinex/bitfinex_types.go b/exchanges/bitfinex/bitfinex_types.go index a54e7584..23864bb4 100644 --- a/exchanges/bitfinex/bitfinex_types.go +++ b/exchanges/bitfinex/bitfinex_types.go @@ -385,7 +385,7 @@ type WebsocketChanInfo struct { // WebsocketBook holds booking information type WebsocketBook struct { Price float64 - Count int + ID int Amount float64 } @@ -397,6 +397,16 @@ type WebsocketTrade struct { Amount float64 } +// WebsocketCandle candle data +type WebsocketCandle struct { + Timestamp int64 + Open float64 + Close float64 + High float64 + Low float64 + Volume float64 +} + // WebsocketTicker holds ticker information type WebsocketTicker struct { Bid float64 @@ -416,7 +426,11 @@ type WebsocketPosition struct { Amount float64 Price float64 MarginFunding float64 - MarginFundingType int + MarginFundingType int64 + ProfitLoss float64 + ProfitLossPercent float64 + LiquidationPrice float64 + Leverage float64 } // WebsocketWallet holds wallet information @@ -459,6 +473,9 @@ type WebsocketTradeData struct { OrderID int64 AmountExecuted float64 PriceExecuted float64 + OrderType string + OrderPrice float64 + Maker bool Fee float64 FeeCurrency string } @@ -468,21 +485,215 @@ type ErrorCapture struct { Message string `json:"message"` } -// TimeInterval represents interval enum. -type TimeInterval string +// WebsocketHandshake defines the communication between the websocket API for +// initial connection +type WebsocketHandshake struct { + Event string `json:"event"` + Code int64 `json:"code"` + Version float64 `json:"version"` +} -// TimeInvterval vars -var ( - TimeIntervalMinute = TimeInterval("1m") - TimeIntervalFiveMinutes = TimeInterval("5m") - TimeIntervalFifteenMinutes = TimeInterval("15m") - TimeIntervalThirtyMinutes = TimeInterval("30m") - TimeIntervalHour = TimeInterval("1h") - TimeIntervalThreeHours = TimeInterval("3h") - TimeIntervalSixHours = TimeInterval("6h") - TimeIntervalTwelveHours = TimeInterval("12h") - TimeIntervalDay = TimeInterval("1d") - TimeIntervalSevenDays = TimeInterval("7d") - TimeIntervalFourteenDays = TimeInterval("14d") - TimeIntervalMonth = TimeInterval("1M") +const ( + authenticatedBitfinexWebsocketEndpoint = "wss://api.bitfinex.com/ws/2" + publicBitfinexWebsocketEndpoint = "wss://api-pub.bitfinex.com/ws/2" + pong = "pong" + wsHeartbeat = "hb" + wsPositionSnapshot = "ps" + wsPositionNew = "pn" + wsPositionUpdate = "pu" + wsPositionClose = "pc" + wsWalletSnapshot = "ws" + wsWalletUpdate = "wu" + wsTradeExecutionUpdate = "tu" + wsTradeExecuted = "te" + wsFundingCreditSnapshot = "fcs" + wsFundingCreditNew = "fcn" + wsFundingCreditUpdate = "fcu" + wsFundingCreditCancel = "fcc" + wsFundingLoanSnapshot = "fls" + wsFundingLoanNew = "fln" + wsFundingLoanUpdate = "flu" + wsFundingLoanCancel = "flc" + wsFundingTradeExecuted = "fte" + wsFundingTradeUpdate = "ftu" + wsFundingInfoUpdate = "fiu" + wsBalanceUpdate = "bu" + wsMarginInfoUpdate = "miu" + wsNotification = "n" + wsOrderNew = "on" + wsOrderUpdate = "ou" + wsOrderCancel = "oc" + wsFundingOrderSnapshot = "fos" + wsFundingOrderNew = "fon" + wsFundingOrderUpdate = "fou" + wsFundingOrderCancel = "foc" + wsCancelMultipleOrders = "oc_multi" + wsBook = "book" + wsCandles = "candles" + wsTicker = "ticker" + wsTrades = "trades" + wsError = "error" ) + +// WsAuthRequest container for WS auth request +type WsAuthRequest struct { + Event string `json:"event"` + APIKey string `json:"apiKey"` + AuthPayload string `json:"authPayload"` + AuthSig string `json:"authSig"` + AuthNonce string `json:"authNonce"` + DeadManSwitch int64 `json:"dms,omitempty"` +} + +// WsFundingOffer funding offer received via websocket +type WsFundingOffer struct { + ID int64 + Symbol string + Created int64 + Updated int64 + Amount float64 + AmountOrig float64 + Type string + Flags interface{} + Status string + Rate float64 + Period int64 + Notify bool + Hidden bool + Insure bool + Renew bool + RateReal float64 +} + +// WsCredit credit details received via websocket +type WsCredit struct { + ID int64 + Symbol string + Side string + Created int64 + Updated int64 + Amount float64 + Flags interface{} + Status string + Rate float64 + Period int64 + Opened int64 + LastPayout int64 + Notify bool + Hidden bool + Insure bool + Renew bool + RateReal float64 + NoClose bool + PositionPair string +} + +// WsWallet wallet update details received via websocket +type WsWallet struct { + Type string + Currency string + Balance float64 + UnsettledInterest float64 + BalanceAvailable float64 +} + +// WsBalanceInfo the total and net assets in your account received via websocket +type WsBalanceInfo struct { + TotalAssetsUnderManagement float64 + NetAssetsUnderManagement float64 +} + +// WsFundingInfo account funding info received via websocket +type WsFundingInfo struct { + Symbol string + YieldLoan float64 + YieldLend float64 + DurationLoan float64 + DurationLend float64 +} + +// WsMarginInfoBase account margin info received via websocket +type WsMarginInfoBase struct { + UserProfitLoss float64 + UserSwaps float64 + MarginBalance float64 + MarginNet float64 +} + +// WsMarginInfoBase recent funding trades received via websocket +type WsFundingTrade struct { + ID int64 + Symbol string + MTSCreated int64 + OfferID int64 + Amount float64 + Rate float64 + Period int64 + Maker bool +} + +// WsNewOrderRequest new order request... +type WsNewOrderRequest struct { + GroupID int64 `json:"gid,omitempty"` + CustomID int64 `json:"cid,omitempty"` + Type string `json:"type"` + Symbol string `json:"symbol"` + Amount float64 `json:"amount,string"` + Price float64 `json:"price,string"` + Leverage int64 `json:"lev,omitempty"` + TrailingPrice float64 `json:"price_trailing,string,omitempty"` + AuxiliaryLimitPrice float64 `json:"price_aux_limit,string,omitempty"` + StopPrice float64 `json:"price_oco_stop,string,omitempty"` + Flags int64 `json:"flags,omitempty"` + TimeInForce string `json:"tif,omitempty"` +} + +// WsUpdateOrderRequest update order request... +type WsUpdateOrderRequest struct { + OrderID int64 `json:"id,omitempty"` + CustomID int64 `json:"cid,omitempty"` + CustomIDDate string `json:"cid_date,omitempty"` + GroupID int64 `json:"gid,omitempty"` + Price float64 `json:"price,string,omitempty"` + Amount float64 `json:"amount,string,omitempty"` + Leverage int64 `json:"lev,omitempty"` + Delta float64 `json:"delta,string,omitempty"` + AuxiliaryLimitPrice float64 `json:"price_aux_limit,string,omitempty"` + TrailingPrice float64 `json:"price_trailing,string,omitempty"` + Flags int64 `json:"flags,omitempty"` + TimeInForce string `json:"tif,omitempty"` +} + +// WsCancelOrderRequest cancel order request... +type WsCancelOrderRequest struct { + OrderID int64 `json:"id,omitempty"` + CustomID int64 `json:"cid,omitempty"` + CustomIDDate string `json:"cid_date,omitempty"` +} + +// WsCancelGroupOrdersRequest cancel orders request... +type WsCancelGroupOrdersRequest struct { + OrderID []int64 `json:"id,omitempty"` + CustomID [][]int64 `json:"cid,omitempty"` + GroupOrderID []int64 `json:"gid,omitempty"` +} + +// WsNewOfferRequest new offer request +type WsNewOfferRequest struct { + Type string `json:"type,omitempty"` + Symbol string `json:"symbol,omitempty"` + Amount float64 `json:"amount,string,omitempty"` + Rate float64 `json:"rate,string,omitempty"` + Period float64 `json:"period,omitempty"` + Flags int64 `json:"flags,omitempty"` +} + +// WsCancelOfferRequest cancel offer request +type WsCancelOfferRequest struct { + OrderID int64 `json:"id"` +} + +// WsCancelAllOrdersRequest cancel all orders request +type WsCancelAllOrdersRequest struct { + All int64 `json:"all"` +} diff --git a/exchanges/bitfinex/bitfinex_websocket.go b/exchanges/bitfinex/bitfinex_websocket.go index 3fb9a7f9..2b54be05 100644 --- a/exchanges/bitfinex/bitfinex_websocket.go +++ b/exchanges/bitfinex/bitfinex_websocket.go @@ -7,6 +7,7 @@ import ( "net/http" "reflect" "strconv" + "strings" "time" "github.com/gorilla/websocket" @@ -21,98 +22,7 @@ import ( log "github.com/thrasher-corp/gocryptotrader/logger" ) -const ( - bitfinexWebsocket = "wss://api.bitfinex.com/ws" - bitfinexWebsocketVersion = "1.1" - bitfinexWebsocketPositionSnapshot = "ps" - bitfinexWebsocketPositionNew = "pn" - bitfinexWebsocketPositionUpdate = "pu" - bitfinexWebsocketPositionClose = "pc" - bitfinexWebsocketWalletSnapshot = "ws" - bitfinexWebsocketWalletUpdate = "wu" - bitfinexWebsocketOrderSnapshot = "os" - bitfinexWebsocketOrderNew = "on" - bitfinexWebsocketOrderUpdate = "ou" - bitfinexWebsocketOrderCancel = "oc" - bitfinexWebsocketTradeExecuted = "te" - bitfinexWebsocketTradeExecutionUpdate = "tu" - bitfinexWebsocketTradeSnapshots = "ts" - bitfinexWebsocketHeartbeat = "hb" - bitfinexWebsocketAlertRestarting = "20051" - bitfinexWebsocketAlertRefreshing = "20060" - bitfinexWebsocketAlertResume = "20061" - bitfinexWebsocketUnknownEvent = "10000" - bitfinexWebsocketUnknownPair = "10001" - bitfinexWebsocketSubscriptionFailed = "10300" - bitfinexWebsocketAlreadySubscribed = "10301" - bitfinexWebsocketUnknownChannel = "10302" -) - -// WebsocketHandshake defines the communication between the websocket API for -// initial connection -type WebsocketHandshake struct { - Event string `json:"event"` - Code int64 `json:"code"` - Version float64 `json:"version"` -} - -var pongReceive chan struct{} - -// WsPingHandler sends a ping request to the websocket server -func (b *Bitfinex) WsPingHandler() error { - req := make(map[string]string) - req["event"] = "ping" - return b.WebsocketConn.SendMessage(req) -} - -// WsSendAuth sends a autheticated event payload -func (b *Bitfinex) WsSendAuth() error { - if !b.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", b.Name) - } - req := make(map[string]interface{}) - payload := "AUTH" + strconv.FormatInt(time.Now().UnixNano(), 10)[:13] - req["event"] = "auth" - req["apiKey"] = b.API.Credentials.Key - - req["authSig"] = crypto.HexEncodeToString( - crypto.GetHMAC( - crypto.HashSHA512_384, - []byte(payload), - []byte(b.API.Credentials.Secret))) - - req["authPayload"] = payload - - err := b.WebsocketConn.SendMessage(req) - if err != nil { - b.Websocket.SetCanUseAuthenticatedEndpoints(false) - return err - } - return nil -} - -// WsSendUnauth sends an unauthenticated payload -func (b *Bitfinex) WsSendUnauth() error { - req := make(map[string]string) - req["event"] = "unauth" - - return b.WebsocketConn.SendMessage(req) -} - -// WsAddSubscriptionChannel adds a new subscription channel to the -// WebsocketSubdChannels map in bitfinex.go (Bitfinex struct) -func (b *Bitfinex) WsAddSubscriptionChannel(chanID int, channel, pair string) { - chanInfo := WebsocketChanInfo{Pair: pair, Channel: channel} - b.WebsocketSubdChannels[chanID] = chanInfo - - if b.Verbose { - log.Debugf(log.ExchangeSys, "%s Subscribed to Channel: %s Pair: %s ChannelID: %d\n", - b.Name, - channel, - pair, - chanID) - } -} +var comms = make(chan wshandler.WebsocketResponse) // WsConnect starts a new websocket connection func (b *Bitfinex) WsConnect() error { @@ -121,70 +31,64 @@ func (b *Bitfinex) WsConnect() error { } var dialer websocket.Dialer - b.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: b.Name, - URL: b.Websocket.GetWebsocketURL(), - ProxyURL: b.Websocket.GetProxyAddress(), - Verbose: b.Verbose, - } err := b.WebsocketConn.Dial(&dialer, http.Header{}) if err != nil { return fmt.Errorf("%v unable to connect to Websocket. Error: %s", b.Name, err) } + go b.WsReadData(b.WebsocketConn) - resp, err := b.WebsocketConn.ReadMessage() - if err != nil { - b.Websocket.ReadMessageErrors <- err - return fmt.Errorf("%v unable to read from Websocket. Error: %s", b.Name, err) - } - b.Websocket.TrafficAlert <- struct{}{} - var hs WebsocketHandshake - err = json.Unmarshal(resp.Raw, &hs) - if err != nil { - return err - } - - err = b.WsSendAuth() - if err != nil { - log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", b.Name, err) - } - - b.GenerateDefaultSubscriptions() - if hs.Event == "info" { - if b.Verbose { - log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", b.Name) + if b.Websocket.CanUseAuthenticatedEndpoints() { + err = b.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{}) + if err != nil { + log.Errorf(log.ExchangeSys, "%v unable to connect to authenticated Websocket. Error: %s", b.Name, err) + b.Websocket.SetCanUseAuthenticatedEndpoints(false) + } + go b.WsReadData(b.AuthenticatedWebsocketConn) + err = b.WsSendAuth() + if err != nil { + log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", b.Name, err) + b.Websocket.SetCanUseAuthenticatedEndpoints(false) } } - pongReceive = make(chan struct{}, 1) - + b.GenerateDefaultSubscriptions() go b.WsDataHandler() - return nil } +// WsReadData funnels both auth and public ws data into one manageable place +func (b *Bitfinex) WsReadData(ws *wshandler.WebsocketConnection) { + b.Websocket.Wg.Add(1) + defer b.Websocket.Wg.Done() + for { + select { + case <-b.Websocket.ShutdownC: + return + default: + resp, err := ws.ReadMessage() + if err != nil { + b.Websocket.DataHandler <- err + return + } + b.Websocket.TrafficAlert <- struct{}{} + comms <- resp + } + } +} + // WsDataHandler handles data from WsReadData func (b *Bitfinex) WsDataHandler() { b.Websocket.Wg.Add(1) - defer b.Websocket.Wg.Done() for { select { case <-b.Websocket.ShutdownC: return - - default: - stream, err := b.WebsocketConn.ReadMessage() - if err != nil { - b.Websocket.ReadMessageErrors <- err - return - } - b.Websocket.TrafficAlert <- struct{}{} - + case stream := <-comms: if stream.Type == websocket.TextMessage { var result interface{} - err = json.Unmarshal(stream.Raw, &result) + err := json.Unmarshal(stream.Raw, &result) if err != nil { b.Websocket.DataHandler <- err return @@ -195,10 +99,17 @@ func (b *Bitfinex) WsDataHandler() { event := eventData["event"] switch event { case "subscribed": - b.WsAddSubscriptionChannel(int(eventData["chanId"].(float64)), - eventData["channel"].(string), - eventData["pair"].(string)) - + if symbol, ok := eventData["pair"].(string); ok { + b.WsAddSubscriptionChannel(int(eventData["chanId"].(float64)), + eventData["channel"].(string), + symbol, + ) + } else if key, ok := eventData["key"].(string); ok { + b.WsAddSubscriptionChannel(int(eventData["chanId"].(float64)), + eventData["channel"].(string), + key, + ) + } case "auth": status := eventData["status"].(string) if status == "OK" { @@ -209,201 +120,104 @@ func (b *Bitfinex) WsDataHandler() { eventData["code"].(string)) } } - case "[]interface {}": chanData := result.([]interface{}) chanID := int(chanData[0].(float64)) - chanInfo, ok := b.WebsocketSubdChannels[chanID] - if !ok { + if !ok && chanID != 0 { b.Websocket.DataHandler <- fmt.Errorf("bitfinex.go error - Unable to locate chanID: %d", chanID) continue } - if len(chanData) == 2 { - if reflect.TypeOf(chanData[1]).String() == "string" { - if chanData[1].(string) == bitfinexWebsocketHeartbeat { - continue - } else if chanData[1].(string) == "pong" { - pongReceive <- struct{}{} - continue - } - } - } switch chanInfo.Channel { - case "book": + case wsBook: var newOrderbook []WebsocketBook curr := currency.NewPairFromString(chanInfo.Pair) - switch len(chanData) { - case 2: - data := chanData[1].([]interface{}) - for i := range data { - y := data[i].([]interface{}) + if obSnapBundle, ok := chanData[1].([]interface{}); ok { + switch snapshot := obSnapBundle[0].(type) { + case []interface{}: + for i := range snapshot { + obSnap := snapshot[i].([]interface{}) + newOrderbook = append(newOrderbook, WebsocketBook{ + ID: int(obSnap[0].(float64)), + Price: obSnap[1].(float64), + Amount: obSnap[2].(float64)}) + } + err := b.WsInsertSnapshot(curr, + asset.Spot, + newOrderbook) + if err != nil { + b.Websocket.DataHandler <- fmt.Errorf("bitfinex_websocket.go inserting snapshot error: %s", + err) + } + case float64: newOrderbook = append(newOrderbook, WebsocketBook{ - Price: y[0].(float64), - Count: int(y[1].(float64)), - Amount: y[2].(float64)}) - } - - err := b.WsInsertSnapshot(curr, - asset.Spot, - newOrderbook) - - if err != nil { - b.Websocket.DataHandler <- fmt.Errorf("bitfinex_websocket.go inserting snapshot error: %s", - err) - } - case 4: - newOrderbook = append(newOrderbook, WebsocketBook{ - Price: chanData[1].(float64), - Count: int(chanData[2].(float64)), - Amount: chanData[3].(float64)}) - err := b.WsUpdateOrderbook(curr, - asset.Spot, - newOrderbook) - - if err != nil { - b.Websocket.DataHandler <- fmt.Errorf("bitfinex_websocket.go updating orderbook error: %s", - err) + ID: int(snapshot), + Price: obSnapBundle[1].(float64), + Amount: obSnapBundle[2].(float64)}) + err := b.WsUpdateOrderbook(curr, + asset.Spot, + newOrderbook) + if err != nil { + b.Websocket.DataHandler <- fmt.Errorf("bitfinex_websocket.go inserting snapshot error: %s", + err) + } } } - case "ticker": + continue + case wsCandles: + curr := currency.NewPairFromString(chanInfo.Pair) + if candleBundle, ok := chanData[1].([]interface{}); ok { + if len(candleBundle) == 0 { + continue + } + switch candleBundle[0].(type) { + case []interface{}: + for i := range candleBundle { + candle := candleBundle[i].([]interface{}) + b.Websocket.DataHandler <- wshandler.KlineData{ + Timestamp: time.Unix(0, candle[0].(int64)), + Exchange: b.Name, + AssetType: asset.Spot, + Pair: curr, + OpenPrice: candle[1].(float64), + ClosePrice: candle[2].(float64), + HighPrice: candle[3].(float64), + LowPrice: candle[4].(float64), + Volume: candle[5].(float64), + } + } + case float64: + b.Websocket.DataHandler <- wshandler.KlineData{ + Timestamp: time.Unix(0, candleBundle[0].(int64)), + Exchange: b.Name, + AssetType: asset.Spot, + Pair: curr, + OpenPrice: candleBundle[1].(float64), + ClosePrice: candleBundle[2].(float64), + HighPrice: candleBundle[3].(float64), + LowPrice: candleBundle[4].(float64), + Volume: candleBundle[5].(float64), + } + } + } + continue + case wsTicker: + tickerData := chanData[1].([]interface{}) b.Websocket.DataHandler <- wshandler.TickerData{ Exchange: b.Name, - Volume: chanData[8].(float64), - High: chanData[9].(float64), - Low: chanData[10].(float64), - Bid: chanData[1].(float64), - Ask: chanData[3].(float64), - Last: chanData[7].(float64), + Bid: tickerData[0].(float64), + Ask: tickerData[2].(float64), + Last: tickerData[6].(float64), + Volume: tickerData[7].(float64), + High: tickerData[8].(float64), + Low: tickerData[9].(float64), AssetType: asset.Spot, Pair: currency.NewPairFromString(chanInfo.Pair), } - - case "account": - switch chanData[1].(string) { - case bitfinexWebsocketPositionSnapshot: - var positionSnapshot []WebsocketPosition - data := chanData[2].([]interface{}) - for i := range data { - y := data[i].([]interface{}) - positionSnapshot = append(positionSnapshot, - WebsocketPosition{ - Pair: y[0].(string), - Status: y[1].(string), - Amount: y[2].(float64), - Price: y[3].(float64), - MarginFunding: y[4].(float64), - MarginFundingType: int(y[5].(float64))}) - } - - if len(positionSnapshot) == 0 { - continue - } - - b.Websocket.DataHandler <- positionSnapshot - - case bitfinexWebsocketPositionNew, bitfinexWebsocketPositionUpdate, bitfinexWebsocketPositionClose: - data := chanData[2].([]interface{}) - position := WebsocketPosition{ - Pair: data[0].(string), - Status: data[1].(string), - Amount: data[2].(float64), - Price: data[3].(float64), - MarginFunding: data[4].(float64), - MarginFundingType: int(data[5].(float64))} - - b.Websocket.DataHandler <- position - - case bitfinexWebsocketWalletSnapshot: - data := chanData[2].([]interface{}) - var walletSnapshot []WebsocketWallet - for i := range data { - y := data[i].([]interface{}) - walletSnapshot = append(walletSnapshot, - WebsocketWallet{ - Name: y[0].(string), - Currency: y[1].(string), - Balance: y[2].(float64), - UnsettledInterest: y[3].(float64)}) - } - - b.Websocket.DataHandler <- walletSnapshot - - case bitfinexWebsocketWalletUpdate: - data := chanData[2].([]interface{}) - wallet := WebsocketWallet{ - Name: data[0].(string), - Currency: data[1].(string), - Balance: data[2].(float64), - UnsettledInterest: data[3].(float64)} - - b.Websocket.DataHandler <- wallet - - case bitfinexWebsocketOrderSnapshot: - var orderSnapshot []WebsocketOrder - data := chanData[2].([]interface{}) - for i := range data { - y := data[i].([]interface{}) - orderSnapshot = append(orderSnapshot, - WebsocketOrder{ - OrderID: int64(y[0].(float64)), - Pair: y[1].(string), - Amount: y[2].(float64), - OrigAmount: y[3].(float64), - OrderType: y[4].(string), - Status: y[5].(string), - Price: y[6].(float64), - PriceAvg: y[7].(float64), - Timestamp: y[8].(string)}) - } - - b.Websocket.DataHandler <- orderSnapshot - - case bitfinexWebsocketOrderNew, bitfinexWebsocketOrderUpdate, bitfinexWebsocketOrderCancel: - data := chanData[2].([]interface{}) - order := WebsocketOrder{ - OrderID: int64(data[0].(float64)), - Pair: data[1].(string), - Amount: data[2].(float64), - OrigAmount: data[3].(float64), - OrderType: data[4].(string), - Status: data[5].(string), - Price: data[6].(float64), - PriceAvg: data[7].(float64), - Timestamp: data[8].(string), - Notify: int(data[9].(float64))} - - b.Websocket.DataHandler <- order - - case bitfinexWebsocketTradeExecuted: - data := chanData[2].([]interface{}) - trade := WebsocketTradeExecuted{ - TradeID: int64(data[0].(float64)), - Pair: data[1].(string), - Timestamp: int64(data[2].(float64)), - OrderID: int64(data[3].(float64)), - AmountExecuted: data[4].(float64), - PriceExecuted: data[5].(float64)} - - b.Websocket.DataHandler <- trade - case bitfinexWebsocketTradeSnapshots, bitfinexWebsocketTradeExecutionUpdate: - data := chanData[2].([]interface{}) - trade := WebsocketTradeData{ - TradeID: int64(data[0].(float64)), - Pair: data[1].(string), - Timestamp: int64(data[2].(float64)), - OrderID: int64(data[3].(float64)), - AmountExecuted: data[4].(float64), - PriceExecuted: data[5].(float64), - Fee: data[6].(float64), - FeeCurrency: data[7].(string)} - - b.Websocket.DataHandler <- trade - } - - case "trades": + continue + case wsTrades: var trades []WebsocketTrade switch len(chanData) { case 2: @@ -413,42 +227,370 @@ func (b *Bitfinex) WsDataHandler() { if _, ok := y[0].(string); ok { continue } - - id, _ := y[0].(float64) - trades = append(trades, WebsocketTrade{ - ID: int64(id), + ID: int64(y[0].(float64)), Timestamp: int64(y[1].(float64)), - Price: y[2].(float64), - Amount: y[3].(float64)}) + Price: y[3].(float64), + Amount: y[2].(float64)}) } - - case 7: - trade := WebsocketTrade{ - ID: int64(chanData[3].(float64)), - Timestamp: int64(chanData[4].(float64)), - Price: chanData[5].(float64), - Amount: chanData[6].(float64)} - trades = append(trades, trade) + case 3: + if chanData[1].(string) == wsTradeExecuted { + // the te update contains less data then the "tu" + continue + } + data := chanData[2].([]interface{}) + trades = append(trades, WebsocketTrade{ + ID: int64(data[0].(float64)), + Timestamp: int64(data[1].(float64)), + Price: data[3].(float64), + Amount: data[2].(float64)}) } - if len(trades) > 0 { - side := order.Buy - newAmount := trades[0].Amount - if newAmount < 0 { - side = order.Sell - newAmount *= -1 + for i := range trades { + side := order.Buy.String() + newAmount := trades[i].Amount + if newAmount < 0 { + side = order.Sell.String() + newAmount *= -1 + } + b.Websocket.DataHandler <- wshandler.TradeData{ + CurrencyPair: currency.NewPairFromString(chanInfo.Pair), + Timestamp: time.Unix(trades[i].Timestamp, 0), + Price: trades[i].Price, + Amount: newAmount, + Exchange: b.Name, + AssetType: asset.Spot, + Side: side, + } } - - b.Websocket.DataHandler <- wshandler.TradeData{ - CurrencyPair: currency.NewPairFromString(chanInfo.Pair), - Timestamp: time.Unix(trades[0].Timestamp, 0), - Price: trades[0].Price, - Amount: newAmount, - Exchange: b.Name, - AssetType: asset.Spot, - Side: side.String(), + continue + } + } + if authResp, ok := chanData[1].(string); ok { + switch authResp { + case wsHeartbeat, pong: + continue + case wsNotification: + notification := chanData[2].([]interface{}) + if data, ok := notification[4].([]interface{}); ok { + channelName := notification[1].(string) + switch { + case strings.Contains(channelName, wsOrderUpdate), + strings.Contains(channelName, wsOrderCancel), + strings.Contains(channelName, wsFundingOrderCancel): + if data[0] != nil && data[0].(float64) > 0 { + b.AuthenticatedWebsocketConn.AddResponseWithID(int64(data[0].(float64)), stream.Raw) + continue + } + case strings.Contains(channelName, wsOrderNew): + if data[2] != nil && data[2].(float64) > 0 { + b.AuthenticatedWebsocketConn.AddResponseWithID(int64(data[2].(float64)), stream.Raw) + continue + } + } + b.Websocket.DataHandler <- fmt.Errorf("%s - Unexpected data returned %s", b.Name, stream.Raw) + continue + } + if notification[5] != nil && strings.EqualFold(notification[5].(string), wsError) { + b.Websocket.DataHandler <- fmt.Errorf("%s - Error %s", b.Name, notification[6].(string)) + } + case wsPositionSnapshot: + var snapshot []WebsocketPosition + if snapBundle, ok := chanData[2].([]interface{}); ok && len(snapBundle) > 0 { + if _, ok := snapBundle[0].([]interface{}); ok { + for i := range snapBundle { + positionData := snapBundle[i].([]interface{}) + position := WebsocketPosition{ + Pair: positionData[0].(string), + Status: positionData[1].(string), + Amount: positionData[2].(float64), + Price: positionData[3].(float64), + MarginFunding: positionData[4].(float64), + MarginFundingType: int64(positionData[5].(float64)), + ProfitLoss: positionData[6].(float64), + ProfitLossPercent: positionData[7].(float64), + LiquidationPrice: positionData[8].(float64), + Leverage: positionData[9].(float64), + } + snapshot = append(snapshot, position) + } + b.Websocket.DataHandler <- snapshot + } + } + case wsPositionNew, wsPositionUpdate, wsPositionClose: + if positionData, ok := chanData[2].([]interface{}); ok && len(positionData) > 0 { + position := WebsocketPosition{ + Pair: positionData[0].(string), + Status: positionData[1].(string), + Amount: positionData[2].(float64), + Price: positionData[3].(float64), + MarginFunding: positionData[4].(float64), + MarginFundingType: int64(positionData[5].(float64)), + ProfitLoss: positionData[6].(float64), + ProfitLossPercent: positionData[7].(float64), + LiquidationPrice: positionData[8].(float64), + Leverage: positionData[9].(float64), + } + b.Websocket.DataHandler <- position + } + case wsTradeExecutionUpdate: + if tradeData, ok := chanData[2].([]interface{}); ok && len(tradeData) > 4 { + b.Websocket.DataHandler <- WebsocketTradeData{ + TradeID: int64(tradeData[0].(float64)), + Pair: tradeData[1].(string), + Timestamp: int64(tradeData[2].(float64)), + OrderID: int64(tradeData[3].(float64)), + AmountExecuted: tradeData[4].(float64), + PriceExecuted: tradeData[5].(float64), + OrderType: tradeData[6].(string), + OrderPrice: tradeData[7].(float64), + Maker: tradeData[8].(float64) == 1, + Fee: tradeData[9].(float64), + FeeCurrency: tradeData[10].(string), + } + } + case wsFundingOrderSnapshot: + var snapshot []WsFundingOffer + if snapBundle, ok := chanData[2].([]interface{}); ok && len(snapBundle) > 0 { + if _, ok := snapBundle[0].([]interface{}); ok { + for i := range snapBundle { + data := snapBundle[i].([]interface{}) + offer := WsFundingOffer{ + ID: int64(data[0].(float64)), + Symbol: data[1].(string), + Created: int64(data[2].(float64)), + Updated: int64(data[3].(float64)), + Amount: data[4].(float64), + AmountOrig: data[5].(float64), + Type: data[6].(string), + Flags: data[9].(float64), + Status: data[10].(string), + Rate: data[14].(float64), + Period: int64(data[15].(float64)), + Notify: data[16].(float64) == 1, + Hidden: data[17].(float64) == 1, + Insure: data[18].(float64) == 1, + Renew: data[19].(float64) == 1, + RateReal: data[20].(float64), + } + snapshot = append(snapshot, offer) + } + b.Websocket.DataHandler <- snapshot + } + } + case wsFundingOrderNew, wsFundingOrderUpdate, wsFundingOrderCancel: + if data, ok := chanData[2].([]interface{}); ok && len(data) > 0 { + b.Websocket.DataHandler <- WsFundingOffer{ + ID: int64(data[0].(float64)), + Symbol: data[1].(string), + Created: int64(data[2].(float64)), + Updated: int64(data[3].(float64)), + Amount: data[4].(float64), + AmountOrig: data[5].(float64), + Type: data[6].(string), + Flags: data[9].(float64), + Status: data[10].(string), + Rate: data[14].(float64), + Period: int64(data[15].(float64)), + Notify: data[16].(float64) == 1, + Hidden: data[17].(float64) == 1, + Insure: data[18].(float64) == 1, + Renew: data[19].(float64) == 1, + RateReal: data[20].(float64), + } + } + case wsFundingCreditSnapshot: + var snapshot []WsCredit + if snapBundle, ok := chanData[2].([]interface{}); ok && len(snapBundle) > 0 { + if _, ok := snapBundle[0].([]interface{}); ok { + for i := range snapBundle { + data := snapBundle[i].([]interface{}) + credit := WsCredit{ + ID: int64(data[0].(float64)), + Symbol: data[1].(string), + Side: data[2].(string), + Created: int64(data[3].(float64)), + Updated: int64(data[4].(float64)), + Amount: data[5].(float64), + Flags: data[6].(string), + Status: data[7].(string), + Rate: data[11].(float64), + Period: int64(data[12].(float64)), + Opened: int64(data[13].(float64)), + LastPayout: int64(data[14].(float64)), + Notify: data[15].(float64) == 1, + Hidden: data[16].(float64) == 1, + Insure: data[17].(float64) == 1, + Renew: data[18].(float64) == 1, + RateReal: data[19].(float64), + NoClose: data[20].(float64) == 1, + PositionPair: data[21].(string), + } + snapshot = append(snapshot, credit) + } + b.Websocket.DataHandler <- snapshot + } + } + case wsFundingCreditNew, wsFundingCreditUpdate, wsFundingCreditCancel: + if data, ok := chanData[2].([]interface{}); ok && len(data) > 0 { + b.Websocket.DataHandler <- WsCredit{ + ID: int64(data[0].(float64)), + Symbol: data[1].(string), + Side: data[2].(string), + Created: int64(data[3].(float64)), + Updated: int64(data[4].(float64)), + Amount: data[5].(float64), + Flags: data[6].(string), + Status: data[7].(string), + Rate: data[11].(float64), + Period: int64(data[12].(float64)), + Opened: int64(data[13].(float64)), + LastPayout: int64(data[14].(float64)), + Notify: data[15].(float64) == 1, + Hidden: data[16].(float64) == 1, + Insure: data[17].(float64) == 1, + Renew: data[18].(float64) == 1, + RateReal: data[19].(float64), + NoClose: data[20].(float64) == 1, + PositionPair: data[21].(string), + } + } + case wsFundingLoanSnapshot: + var snapshot []WsCredit + if snapBundle, ok := chanData[2].([]interface{}); ok && len(snapBundle) > 0 { + if _, ok := snapBundle[0].([]interface{}); ok { + for i := range snapBundle { + data := snapBundle[i].([]interface{}) + credit := WsCredit{ + ID: int64(data[0].(float64)), + Symbol: data[1].(string), + Side: data[2].(string), + Created: int64(data[3].(float64)), + Updated: int64(data[4].(float64)), + Amount: data[5].(float64), + Flags: data[6].(string), + Status: data[7].(string), + Rate: data[11].(float64), + Period: int64(data[12].(float64)), + Opened: int64(data[13].(float64)), + LastPayout: int64(data[14].(float64)), + Notify: data[15].(float64) == 1, + Hidden: data[16].(float64) == 1, + Insure: data[17].(float64) == 1, + Renew: data[18].(float64) == 1, + RateReal: data[19].(float64), + NoClose: data[20].(float64) == 1, + } + snapshot = append(snapshot, credit) + } + b.Websocket.DataHandler <- snapshot + } + } + case wsFundingLoanNew, wsFundingLoanUpdate, wsFundingLoanCancel: + if data, ok := chanData[2].([]interface{}); ok && len(data) > 0 { + b.Websocket.DataHandler <- WsCredit{ + ID: int64(data[0].(float64)), + Symbol: data[1].(string), + Side: data[2].(string), + Created: int64(data[3].(float64)), + Updated: int64(data[4].(float64)), + Amount: data[5].(float64), + Flags: data[6].(string), + Status: data[7].(string), + Rate: data[11].(float64), + Period: int64(data[12].(float64)), + Opened: int64(data[13].(float64)), + LastPayout: int64(data[14].(float64)), + Notify: data[15].(float64) == 1, + Hidden: data[16].(float64) == 1, + Insure: data[17].(float64) == 1, + Renew: data[18].(float64) == 1, + RateReal: data[19].(float64), + NoClose: data[20].(float64) == 1, + } + } + case wsWalletSnapshot: + var snapshot []WsWallet + if snapBundle, ok := chanData[2].([]interface{}); ok && len(snapBundle) > 0 { + if _, ok := snapBundle[0].([]interface{}); ok { + for i := range snapBundle { + data := snapBundle[i].([]interface{}) + var balanceAvailable float64 + if _, ok := data[4].(float64); ok { + balanceAvailable = data[4].(float64) + } + wallet := WsWallet{ + Type: data[0].(string), + Currency: data[1].(string), + Balance: data[2].(float64), + UnsettledInterest: data[3].(float64), + BalanceAvailable: balanceAvailable, + } + snapshot = append(snapshot, wallet) + } + b.Websocket.DataHandler <- snapshot + } + } + case wsWalletUpdate: + if data, ok := chanData[2].([]interface{}); ok && len(data) > 0 { + var balanceAvailable float64 + if _, ok := data[4].(float64); ok { + balanceAvailable = data[4].(float64) + } + b.Websocket.DataHandler <- WsWallet{ + Type: data[0].(string), + Currency: data[1].(string), + Balance: data[2].(float64), + UnsettledInterest: data[3].(float64), + BalanceAvailable: balanceAvailable, + } + } + case wsBalanceUpdate: + if data, ok := chanData[2].([]interface{}); ok && len(data) > 0 { + b.Websocket.DataHandler <- WsBalanceInfo{ + TotalAssetsUnderManagement: data[0].(float64), + NetAssetsUnderManagement: data[1].(float64), + } + } + case wsMarginInfoUpdate: + if data, ok := chanData[2].([]interface{}); ok && len(data) > 0 { + if data[0].(string) == "base" { + if infoBase, ok := chanData[2].([]interface{}); ok && len(infoBase) > 0 { + baseData := data[1].([]interface{}) + b.Websocket.DataHandler <- WsMarginInfoBase{ + UserProfitLoss: baseData[0].(float64), + UserSwaps: baseData[1].(float64), + MarginBalance: baseData[2].(float64), + MarginNet: baseData[3].(float64), + } + } + } + } + case wsFundingInfoUpdate: + if data, ok := chanData[2].([]interface{}); ok && len(data) > 0 { + if data[0].(string) == "sym" { + symbolData := data[1].([]interface{}) + b.Websocket.DataHandler <- WsFundingInfo{ + YieldLoan: symbolData[0].(float64), + YieldLend: symbolData[1].(float64), + DurationLoan: symbolData[2].(float64), + DurationLend: symbolData[3].(float64), + } + } + } + case wsFundingTradeExecuted, wsFundingTradeUpdate: + if data, ok := chanData[2].([]interface{}); ok && len(data) > 0 { + b.Websocket.DataHandler <- WsFundingTrade{ + ID: int64(data[0].(float64)), + Symbol: data[1].(string), + MTSCreated: int64(data[2].(float64)), + OfferID: int64(data[3].(float64)), + Amount: data[4].(float64), + Rate: data[5].(float64), + Period: int64(data[6].(float64)), + Maker: data[7].(float64) == 1, + } } } } @@ -504,7 +646,7 @@ func (b *Bitfinex) WsUpdateOrderbook(p currency.Pair, assetType asset.Item, book for i := 0; i < len(book); i++ { switch { - case book[i].Count > 0: + case book[i].Price > 0: if book[i].Amount > 0 { // update bid orderbookUpdate.Bids = append(orderbookUpdate.Bids, orderbook.Item{Amount: book[i].Amount, Price: book[i].Price}) @@ -512,7 +654,7 @@ func (b *Bitfinex) WsUpdateOrderbook(p currency.Pair, assetType asset.Item, book // update ask orderbookUpdate.Asks = append(orderbookUpdate.Asks, orderbook.Item{Amount: book[i].Amount * -1, Price: book[i].Price}) } - case book[i].Count == 0: + case book[i].Price == 0: if book[i].Amount == 1 { // delete bid orderbookUpdate.Bids = append(orderbookUpdate.Bids, orderbook.Item{Amount: 0, Price: book[i].Price}) @@ -522,7 +664,6 @@ func (b *Bitfinex) WsUpdateOrderbook(p currency.Pair, assetType asset.Item, book } } } - orderbookUpdate.UpdateTime = time.Now() err := b.Websocket.Orderbook.Update(&orderbookUpdate) if err != nil { return err @@ -537,17 +678,29 @@ func (b *Bitfinex) WsUpdateOrderbook(p currency.Pair, assetType asset.Item, book // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() func (b *Bitfinex) GenerateDefaultSubscriptions() { - var channels = []string{"book", "trades", "ticker"} + var channels = []string{ + wsBook, + wsTrades, + wsTicker, + wsCandles, + } var subscriptions []wshandler.WebsocketChannelSubscription for i := range channels { enabledPairs := b.GetEnabledPairs(asset.Spot) for j := range enabledPairs { + if strings.HasPrefix(enabledPairs[j].Base.String(), "f") { + log.Warnf(log.WebsocketMgr, + "%v - Websocket does not support funding currency %v, skipping", + b.Name, enabledPairs[j]) + continue + } b.appendOptionalDelimiter(&enabledPairs[j]) params := make(map[string]interface{}) - if channels[i] == "book" { - params["prec"] = "P0" + if channels[i] == wsBook { + params["prec"] = "R0" params["len"] = "100" } + subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ Channel: channels[i], Currency: enabledPairs[j], @@ -564,8 +717,14 @@ func (b *Bitfinex) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscr req["event"] = "subscribe" req["channel"] = channelToSubscribe.Channel if channelToSubscribe.Currency.String() != "" { - req["pair"] = b.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String() + if channelToSubscribe.Channel == wsCandles { + // TODO: Add ability to select timescale + req["key"] = fmt.Sprintf("trade:1D:%v", + b.FormatExchangeCurrency(channelToSubscribe.Currency, asset.Spot).String()) + } else { + req["symbol"] = b.FormatExchangeCurrency(channelToSubscribe.Currency, + asset.Spot).String() + } } if len(channelToSubscribe.Params) > 0 { for k, v := range channelToSubscribe.Params { @@ -588,3 +747,196 @@ func (b *Bitfinex) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubs } return b.WebsocketConn.SendMessage(req) } + +// WsSendAuth sends a autheticated event payload +func (b *Bitfinex) WsSendAuth() error { + if !b.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { + return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", b.Name) + } + nonce := strconv.FormatInt(time.Now().Unix(), 10) + payload := "AUTH" + nonce + request := WsAuthRequest{ + Event: "auth", + APIKey: b.API.Credentials.Key, + AuthPayload: payload, + AuthSig: crypto.HexEncodeToString( + crypto.GetHMAC( + crypto.HashSHA512_384, + []byte(payload), + []byte(b.API.Credentials.Secret))), + AuthNonce: nonce, + DeadManSwitch: 0, + } + err := b.AuthenticatedWebsocketConn.SendMessage(request) + if err != nil { + b.Websocket.SetCanUseAuthenticatedEndpoints(false) + return err + } + return nil +} + +// WsSendUnauth sends an unauthenticated payload +func (b *Bitfinex) WsSendUnauth() error { + req := make(map[string]string) + req["event"] = "unauth" + + return b.WebsocketConn.SendMessage(req) +} + +// WsAddSubscriptionChannel adds a new subscription channel to the +// WebsocketSubdChannels map in bitfinex.go (Bitfinex struct) +func (b *Bitfinex) WsAddSubscriptionChannel(chanID int, channel, pair string) { + chanInfo := WebsocketChanInfo{Pair: pair, Channel: channel} + b.WebsocketSubdChannels[chanID] = chanInfo + + if b.Verbose { + log.Debugf(log.ExchangeSys, "%s Subscribed to Channel: %s Pair: %s ChannelID: %d\n", + b.Name, + channel, + pair, + chanID) + } +} + +// WsNewOrder authenticated new order request +func (b *Bitfinex) WsNewOrder(data *WsNewOrderRequest) (string, error) { + data.CustomID = b.AuthenticatedWebsocketConn.GenerateMessageID(false) + request := makeRequestInterface(wsOrderNew, data) + resp, err := b.AuthenticatedWebsocketConn.SendMessageReturnResponse(data.CustomID, request) + if err != nil { + return "", err + } + if resp == nil { + return "", errors.New(b.Name + " - Order message not returned") + } + var respData []interface{} + err = json.Unmarshal(resp, &respData) + if err != nil { + return "", err + } + responseDataDetail := respData[2].([]interface{}) + responseOrderDetail := responseDataDetail[4].([]interface{}) + var orderID string + if responseOrderDetail[0] != nil && responseOrderDetail[0].(float64) > 0 { + orderID = strconv.FormatFloat(responseOrderDetail[0].(float64), 'f', -1, 64) + } + errCode := responseDataDetail[6].(string) + errorMessage := responseDataDetail[7].(string) + + if strings.EqualFold(errCode, wsError) { + return orderID, errors.New(b.Name + " - " + errCode + ": " + errorMessage) + } + + return orderID, nil +} + +// WsModifyOrder authenticated modify order request +func (b *Bitfinex) WsModifyOrder(data *WsUpdateOrderRequest) error { + request := makeRequestInterface(wsOrderUpdate, data) + resp, err := b.AuthenticatedWebsocketConn.SendMessageReturnResponse(data.OrderID, request) + if err != nil { + return err + } + if resp == nil { + return errors.New(b.Name + " - Order message not returned") + } + + var responseData []interface{} + err = json.Unmarshal(resp, &responseData) + if err != nil { + return err + } + responseOrderData := responseData[2].([]interface{}) + errCode := responseOrderData[6].(string) + errorMessage := responseOrderData[7].(string) + if strings.EqualFold(errCode, wsError) { + return errors.New(b.Name + " - " + errCode + ": " + errorMessage) + } + + return nil +} + +// WsCancelMultiOrders authenticated cancel multi order request +func (b *Bitfinex) WsCancelMultiOrders(orderIDs []int64) error { + cancel := WsCancelGroupOrdersRequest{ + OrderID: orderIDs, + } + request := makeRequestInterface(wsCancelMultipleOrders, cancel) + return b.AuthenticatedWebsocketConn.SendMessage(request) +} + +// WsCancelOrder authenticated cancel order request +func (b *Bitfinex) WsCancelOrder(orderID int64) error { + cancel := WsCancelOrderRequest{ + OrderID: orderID, + } + request := makeRequestInterface(wsOrderCancel, cancel) + resp, err := b.AuthenticatedWebsocketConn.SendMessageReturnResponse(orderID, request) + if err != nil { + return err + } + if resp == nil { + return fmt.Errorf("%v - Order %v failed to cancel", b.Name, orderID) + } + var responseData []interface{} + err = json.Unmarshal(resp, &responseData) + if err != nil { + return err + } + responseOrderData := responseData[2].([]interface{}) + errCode := responseOrderData[6].(string) + errorMessage := responseOrderData[7].(string) + if strings.EqualFold(errCode, wsError) { + return errors.New(b.Name + " - " + errCode + ": " + errorMessage) + } + + return nil +} + +// WsCancelAllOrders authenticated cancel all orders request +func (b *Bitfinex) WsCancelAllOrders() error { + cancelAll := WsCancelAllOrdersRequest{All: 1} + request := makeRequestInterface(wsCancelMultipleOrders, cancelAll) + return b.AuthenticatedWebsocketConn.SendMessage(request) +} + +// WsNewOffer authenticated new offer request +func (b *Bitfinex) WsNewOffer(data *WsNewOfferRequest) error { + request := makeRequestInterface(wsFundingOrderNew, data) + return b.AuthenticatedWebsocketConn.SendMessage(request) +} + +// WsCancelOffer authenticated cancel offer request +func (b *Bitfinex) WsCancelOffer(orderID int64) error { + cancel := WsCancelOrderRequest{ + OrderID: orderID, + } + request := makeRequestInterface(wsFundingOrderCancel, cancel) + resp, err := b.AuthenticatedWebsocketConn.SendMessageReturnResponse(orderID, request) + if err != nil { + return err + } + if resp == nil { + return fmt.Errorf("%v - Order %v failed to cancel", b.Name, orderID) + } + var responseData []interface{} + err = json.Unmarshal(resp, &responseData) + if err != nil { + return err + } + responseOrderData := responseData[2].([]interface{}) + errCode := responseOrderData[6].(string) + var errorMessage string + if responseOrderData[7] != nil { + errorMessage = responseOrderData[7].(string) + } + if strings.EqualFold(errCode, wsError) { + return errors.New(b.Name + " - " + errCode + ": " + errorMessage) + } + + return nil +} + +func makeRequestInterface(channelName string, data interface{}) []interface{} { + return []interface{}{0, channelName, nil, data} +} diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index f5337b75..95838ffc 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -87,7 +87,6 @@ func (b *Bitfinex) SetDefaults() { CancelOrder: true, SubmitOrder: true, SubmitOrders: true, - ModifyOrder: true, DepositHistory: true, WithdrawalHistory: true, TradeFetching: true, @@ -99,12 +98,21 @@ func (b *Bitfinex) SetDefaults() { CryptoWithdrawalFee: true, }, WebsocketCapabilities: protocol.Features{ + AccountBalance: true, + CancelOrders: true, + CancelOrder: true, + SubmitOrder: true, + ModifyOrder: true, TickerFetching: true, + KlineFetching: true, TradeFetching: true, OrderbookFetching: true, + AccountInfo: true, Subscribe: true, Unsubscribe: true, AuthenticatedEndpoints: true, + MessageCorrelation: true, + DeadMansSwitch: true, }, WithdrawPermissions: exchange.AutoWithdrawCryptoWithAPIPermission | exchange.AutoWithdrawFiatWithAPIPermission, @@ -121,7 +129,7 @@ func (b *Bitfinex) SetDefaults() { b.API.Endpoints.URLDefault = bitfinexAPIURLBase b.API.Endpoints.URL = b.API.Endpoints.URLDefault - b.API.Endpoints.WebsocketURL = bitfinexWebsocket + b.API.Endpoints.WebsocketURL = publicBitfinexWebsocketEndpoint b.Websocket = wshandler.New() b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout @@ -146,7 +154,7 @@ func (b *Bitfinex) Setup(exch *config.ExchangeConfig) error { Verbose: exch.Verbose, AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: bitfinexWebsocket, + DefaultURL: publicBitfinexWebsocketEndpoint, ExchangeName: exch.Name, RunningURL: exch.API.Endpoints.WebsocketURL, Connector: b.WsConnect, @@ -166,6 +174,14 @@ func (b *Bitfinex) Setup(exch *config.ExchangeConfig) error { ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, } + b.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ + ExchangeName: b.Name, + URL: authenticatedBitfinexWebsocketEndpoint, + ProxyURL: b.Websocket.GetProxyAddress(), + Verbose: b.Verbose, + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + } b.Websocket.Orderbook.Setup( exch.WebsocketOrderbookBufferLimit, @@ -318,6 +334,7 @@ func (b *Bitfinex) UpdateOrderbook(p currency.Pair, assetType asset.Item) (order func (b *Bitfinex) GetAccountInfo() (exchange.AccountInfo, error) { var response exchange.AccountInfo response.Exchange = b.Name + accountBalance, err := b.GetAccountBalance() if err != nil { return response, err @@ -358,39 +375,67 @@ func (b *Bitfinex) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([] } // SubmitOrder submits a new order -func (b *Bitfinex) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { +func (b *Bitfinex) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) { var submitOrderResponse order.SubmitResponse - if err := s.Validate(); err != nil { + err := o.Validate() + if err != nil { return submitOrderResponse, err } + if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + submitOrderResponse.OrderID, err = b.WsNewOrder(&WsNewOrderRequest{ + CustomID: b.AuthenticatedWebsocketConn.GenerateMessageID(false), + Type: o.OrderType.String(), + Symbol: b.FormatExchangeCurrency(o.Pair, asset.Spot).String(), + Amount: o.Amount, + Price: o.Price, + }) + if err != nil { + return submitOrderResponse, err + } + } else { + var response Order + isBuying := o.OrderSide == order.Buy + b.appendOptionalDelimiter(&o.Pair) + response, err = b.NewOrder(o.Pair.String(), + o.Amount, + o.Price, + isBuying, + o.OrderType.String(), + false) + if err != nil { + return submitOrderResponse, err + } + if response.OrderID > 0 { + submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10) + } + if response.RemainingAmount == 0 { + submitOrderResponse.FullyMatched = true + } - var isBuying bool - if s.OrderSide == order.Buy { - isBuying = true - } - b.appendOptionalDelimiter(&s.Pair) - response, err := b.NewOrder(s.Pair.String(), - s.Amount, - s.Price, - isBuying, - s.OrderType.String(), - false) - - if response.OrderID > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10) - } - - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } // ModifyOrder will allow of changing orderbook placement and limit to // market conversion func (b *Bitfinex) ModifyOrder(action *order.Modify) (string, error) { - return "", common.ErrFunctionNotSupported + orderIDInt, err := strconv.ParseInt(action.OrderID, 10, 64) + if err != nil { + return action.OrderID, err + } + if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + if action.Side == order.Sell && action.Amount > 0 { + action.Amount = -1 * action.Amount + } + err = b.WsModifyOrder(&WsUpdateOrderRequest{ + OrderID: orderIDInt, + Price: action.Price, + Amount: action.Amount, + }) + return action.OrderID, err + } + return "", common.ErrNotYetImplemented } // CancelOrder cancels an order by its corresponding ID number @@ -399,13 +444,22 @@ func (b *Bitfinex) CancelOrder(order *order.Cancel) error { if err != nil { return err } - _, err = b.CancelExistingOrder(orderIDInt) + if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + err = b.WsCancelOrder(orderIDInt) + } else { + _, err = b.CancelExistingOrder(orderIDInt) + } return err } // CancelAllOrders cancels all orders associated with a currency pair func (b *Bitfinex) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) { - _, err := b.CancelAllExistingOrders() + var err error + if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + err = b.WsCancelAllOrders() + } else { + _, err = b.CancelAllExistingOrders() + } return order.CancelAllResponse{}, err } @@ -422,7 +476,8 @@ func (b *Bitfinex) GetDepositAddress(cryptocurrency currency.Code, accountID str return "", err } - resp, err := b.NewDeposit(method, accountID, 0) + var resp DepositResponse + resp, err = b.NewDeposit(method, accountID, 0) if err != nil { return "", err } @@ -547,7 +602,7 @@ func (b *Bitfinex) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, orderDetail.Status = order.UnknownStatus } - // API docs discrepency. Example contains prefixed "exchange " + // API docs discrepancy. Example contains prefixed "exchange " // Return type suggests “market” / “limit” / “stop” / “trailing-stop” orderType := strings.Replace(resp[i].Type, "exchange ", "", 1) if orderType == "trailing-stop" { diff --git a/exchanges/bitflyer/bitflyer_test.go b/exchanges/bitflyer/bitflyer_test.go index 3364b944..31a87a5c 100644 --- a/exchanges/bitflyer/bitflyer_test.go +++ b/exchanges/bitflyer/bitflyer_test.go @@ -1,6 +1,8 @@ package bitflyer import ( + "log" + "os" "testing" "github.com/thrasher-corp/gocryptotrader/common" @@ -9,7 +11,6 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" - log "github.com/thrasher-corp/gocryptotrader/logger" ) // Please supply your own keys here for due diligence testing @@ -21,19 +22,16 @@ const ( var b Bitflyer -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { b.SetDefaults() -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("Bitflyer load config error", err) + log.Fatal("Bitflyer load config error", err) } bitflyerConfig, err := cfg.GetExchangeConfig("Bitflyer") if err != nil { - t.Error("bitflyer Setup() init error") + log.Fatal("bitflyer Setup() init error") } bitflyerConfig.API.AuthenticatedSupport = true @@ -42,8 +40,10 @@ func TestSetup(t *testing.T) { err = b.Setup(bitflyerConfig) if err != nil { - t.Fatal("Bitflyer setup error", err) + log.Fatal("Bitflyer setup error", err) } + + os.Exit(m.Run()) } func TestGetLatestBlockCA(t *testing.T) { @@ -85,7 +85,7 @@ func TestGetAddressInfoCA(t *testing.T) { t.Error("Bitflyer - GetAddressInfoCA() error:", err) } if v.UnconfirmedBalance == 0 || v.ConfirmedBalance == 0 { - log.Warn(log.ExchangeSys, "Donation wallet is empty :( - please consider donating") + t.Log("Donation wallet is empty :( - please consider donating") } } @@ -171,7 +171,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() b.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -183,11 +183,10 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - b.SetDefaults() - TestSetup(t) + t.Parallel() var feeBuilder = setFeeBuilder() - if apiKey != "" || apiSecret != "" { + if areTestAPIKeysSet() { // CryptocurrencyTradeFee Basic if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { t.Error(err) @@ -256,20 +255,16 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - b.SetDefaults() + t.Parallel() expectedResult := exchange.AutoWithdrawFiatText + " & " + exchange.WithdrawCryptoViaWebsiteOnlyText - withdrawPermissions := b.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -283,9 +278,7 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -303,9 +296,7 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -328,9 +319,7 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -351,9 +340,7 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -374,8 +361,11 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestWithdraw(t *testing.T) { - b.SetDefaults() - TestSetup(t) + t.Parallel() + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } + withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -385,10 +375,6 @@ func TestWithdraw(t *testing.T) { Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", } - if areTestAPIKeysSet() && !canManipulateRealOrders { - t.Skip("API keys set, canManipulateRealOrders false, skipping test") - } - _, err := b.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest) if err != common.ErrNotYetImplemented { t.Errorf("Expected 'Not Yet Implemented', received %v", err) @@ -396,6 +382,10 @@ func TestWithdraw(t *testing.T) { } func TestModifyOrder(t *testing.T) { + t.Parallel() + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := b.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -403,9 +393,7 @@ func TestModifyOrder(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -419,9 +407,7 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } diff --git a/exchanges/bithumb/bithumb_test.go b/exchanges/bithumb/bithumb_test.go index 8f82f248..b2331ba2 100644 --- a/exchanges/bithumb/bithumb_test.go +++ b/exchanges/bithumb/bithumb_test.go @@ -1,6 +1,8 @@ package bithumb import ( + "log" + "os" "testing" "github.com/thrasher-corp/gocryptotrader/common" @@ -20,19 +22,16 @@ const ( var b Bithumb -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { b.SetDefaults() -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("Bithumb load config error", err) + log.Fatal("Bithumb load config error", err) } bitConfig, err := cfg.GetExchangeConfig("Bithumb") if err != nil { - t.Error("Bithumb Setup() init error") + log.Fatal("Bithumb Setup() init error") } bitConfig.API.AuthenticatedSupport = true @@ -41,8 +40,10 @@ func TestSetup(t *testing.T) { err = b.Setup(bitConfig) if err != nil { - t.Fatal("Bithumb setup error", err) + log.Fatal("Bithumb setup error", err) } + + os.Exit(m.Run()) } func TestGetTradablePairs(t *testing.T) { @@ -87,7 +88,7 @@ func TestGetTransactionHistory(t *testing.T) { func TestGetAccountBalance(t *testing.T) { t.Parallel() - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { t.Skip() } @@ -98,11 +99,11 @@ func TestGetAccountBalance(t *testing.T) { } func TestGetWalletAddress(t *testing.T) { - if apiKey == "" || apiSecret == "" { + t.Parallel() + if !areTestAPIKeysSet() { t.Skip() } - t.Parallel() _, err := b.GetWalletAddress("") if err == nil { t.Error("Bithumb GetWalletAddress() Expected error") @@ -167,7 +168,7 @@ func TestWithdrawCrypto(t *testing.T) { func TestRequestKRWDepositDetails(t *testing.T) { t.Parallel() - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { t.Skip() } _, err := b.RequestKRWDepositDetails() @@ -213,7 +214,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() b.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -225,10 +226,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - b.SetDefaults() - TestSetup(t) var feeBuilder = setFeeBuilder() - // CryptocurrencyTradeFee Basic if resp, err := b.GetFee(feeBuilder); resp != float64(0.0025) || err != nil { t.Error(err) @@ -296,20 +294,16 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - b.SetDefaults() + t.Parallel() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.AutoWithdrawFiatText - withdrawPermissions := b.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, OrderSide: order.Sell, @@ -324,9 +318,7 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -346,9 +338,7 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -373,15 +363,12 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -399,15 +386,12 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -431,7 +415,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { func TestGetAccountInfo(t *testing.T) { t.Parallel() - if apiKey != "" || apiSecret != "" { + if areTestAPIKeysSet() { _, err := b.GetAccountInfo() if err != nil { t.Error("Bithumb GetAccountInfo() error", err) @@ -445,6 +429,7 @@ func TestGetAccountInfo(t *testing.T) { } func TestModifyOrder(t *testing.T) { + t.Parallel() curr := currency.NewPairFromString("BTCUSD") _, err := b.ModifyOrder(&order.Modify{ OrderID: "1337", @@ -458,8 +443,7 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - b.SetDefaults() - TestSetup(t) + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -483,9 +467,7 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -519,9 +501,7 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - b.SetDefaults() - TestSetup(t) - + t.Parallel() if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -534,7 +514,8 @@ func TestWithdrawInternationalBank(t *testing.T) { } func TestGetDepositAddress(t *testing.T) { - if apiKey != "" && apiSecret != "" { + t.Parallel() + if areTestAPIKeysSet() { _, err := b.GetDepositAddress(currency.BTC, "") if err != nil { t.Error("GetDepositAddress() error", err) diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 91f8f983..8d08da2b 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -220,9 +220,9 @@ func (b *Bithumb) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderbo // UpdateOrderbook updates and returns the orderbook for a currency pair func (b *Bithumb) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) { var orderBook orderbook.Base - currency := p.Base.String() + curr := p.Base.String() - orderbookNew, err := b.GetOrderBook(currency) + orderbookNew, err := b.GetOrderBook(curr) if err != nil { return orderBook, err } @@ -311,20 +311,25 @@ func (b *Bithumb) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { if s.OrderSide == order.Buy { var result MarketBuy result, err = b.MarketBuyOrder(s.Pair.Base.String(), s.Amount) + if err != nil { + return submitOrderResponse, err + } orderID = result.OrderID } else if s.OrderSide == order.Sell { var result MarketSell result, err = b.MarketSellOrder(s.Pair.Base.String(), s.Amount) + if err != nil { + return submitOrderResponse, err + } orderID = result.OrderID } - if orderID != "" { submitOrderResponse.OrderID = orderID + submitOrderResponse.FullyMatched = true } - if err == nil { - submitOrderResponse.IsOrderPlaced = true - } - return submitOrderResponse, err + submitOrderResponse.IsOrderPlaced = true + + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -358,12 +363,13 @@ func (b *Bithumb) CancelAllOrders(orderCancellation *order.Cancel) (order.Cancel } var allOrders []OrderData - for _, currency := range b.GetEnabledPairs(asset.Spot) { + currs := b.GetEnabledPairs(asset.Spot) + for i := range currs { orders, err := b.GetOrders("", orderCancellation.Side.String(), "100", "", - currency.Base.String()) + currs[i].Base.String()) if err != nil { return cancelAllOrdersResponse, err } diff --git a/exchanges/bitmex/bitmex_test.go b/exchanges/bitmex/bitmex_test.go index c1dd5b64..f92fdee0 100644 --- a/exchanges/bitmex/bitmex_test.go +++ b/exchanges/bitmex/bitmex_test.go @@ -1,7 +1,9 @@ package bitmex import ( + "log" "net/http" + "os" "sync" "testing" "time" @@ -25,19 +27,16 @@ const ( var b Bitmex -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { b.SetDefaults() -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("Bitmex load config error", err) + log.Fatal("Bitmex load config error", err) } bitmexConfig, err := cfg.GetExchangeConfig("Bitmex") if err != nil { - t.Error("Bitmex Setup() init error") + log.Fatal("Bitmex Setup() init error") } bitmexConfig.API.AuthenticatedSupport = true @@ -47,8 +46,9 @@ func TestSetup(t *testing.T) { err = b.Setup(bitmexConfig) if err != nil { - t.Fatal("Bitmex setup error", err) + log.Fatal("Bitmex setup error", err) } + os.Exit(m.Run()) } func TestStart(t *testing.T) { @@ -386,7 +386,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() b.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -398,9 +398,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - b.SetDefaults() - TestSetup(t) - var feeBuilder = setFeeBuilder() // CryptocurrencyTradeFee Basic if resp, err := b.GetFee(feeBuilder); resp != float64(0.00075) || err != nil { @@ -469,21 +466,15 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - b.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.WithdrawCryptoWith2FAText + " & " + exchange.WithdrawCryptoWithEmailText + " & " + exchange.NoFiatWithdrawalsText - withdrawPermissions := b.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - b.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -497,9 +488,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - b.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.LTC, @@ -521,9 +509,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -548,15 +533,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "123456789012345678901234567890123456", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -574,15 +555,11 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "123456789012345678901234567890123456", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -605,7 +582,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestGetAccountInfo(t *testing.T) { - if apiKey != "" || apiSecret != "" { + if areTestAPIKeysSet() { _, err := b.GetAccountInfo() if err != nil { t.Error("GetAccountInfo() error", err) @@ -619,6 +596,9 @@ func TestGetAccountInfo(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := b.ModifyOrder(&order.Modify{OrderID: "1337"}) if err == nil { t.Error("ModifyOrder() error") @@ -626,9 +606,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - b.SetDefaults() - TestSetup(t) - withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -653,9 +630,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -668,9 +642,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -698,8 +669,6 @@ func TestGetDepositAddress(t *testing.T) { // TestWsAuth dials websocket, sends login request. func TestWsAuth(t *testing.T) { - b.SetDefaults() - TestSetup(t) if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(wshandler.WebsocketNotEnabled) } diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index 306a8a9a..4f65011d 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -432,15 +432,18 @@ func (b *Bitmex) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { } response, err := b.CreateOrder(&orderNewParams) + if err != nil { + return submitOrderResponse, err + } if response.OrderID != "" { submitOrderResponse.OrderID = response.OrderID } - - if err == nil { - submitOrderResponse.IsOrderPlaced = true + if s.OrderType == order.Market { + submitOrderResponse.FullyMatched = true } + submitOrderResponse.IsOrderPlaced = true - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/bitstamp/bitstamp_test.go b/exchanges/bitstamp/bitstamp_test.go index 77d8b575..39d7dfd4 100644 --- a/exchanges/bitstamp/bitstamp_test.go +++ b/exchanges/bitstamp/bitstamp_test.go @@ -38,7 +38,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() b.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, @@ -319,7 +319,6 @@ func TestFormatWithdrawPermissions(t *testing.T) { expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.AutoWithdrawFiatText - withdrawPermissions := b.FormatWithdrawPermissions() if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index facc2950..5b74ddf6 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -364,16 +364,18 @@ func (b *Bitstamp) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { s.Amount, buy, market) - + if err != nil { + return submitOrderResponse, err + } if response.ID > 0 { submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10) } - if err == nil { - submitOrderResponse.IsOrderPlaced = true + submitOrderResponse.IsOrderPlaced = true + if s.OrderType == order.Market { + submitOrderResponse.FullyMatched = true } - - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/bittrex/bittrex.go b/exchanges/bittrex/bittrex.go index 4d9489d5..a564dd16 100644 --- a/exchanges/bittrex/bittrex.go +++ b/exchanges/bittrex/bittrex.go @@ -329,7 +329,7 @@ func (b *Bittrex) Withdraw(currency, paymentID, address string, quantity float64 var id UUID values := url.Values{} values.Set("currency", currency) - values.Set("quantity", fmt.Sprintf("%v", quantity)) + values.Set("quantity", strconv.FormatFloat(quantity, 'f', -1, 64)) values.Set("address", address) if len(paymentID) > 0 { values.Set("paymentid", paymentID) diff --git a/exchanges/bittrex/bittrex_test.go b/exchanges/bittrex/bittrex_test.go index 976fe165..5cc15712 100644 --- a/exchanges/bittrex/bittrex_test.go +++ b/exchanges/bittrex/bittrex_test.go @@ -1,6 +1,8 @@ package bittrex import ( + "log" + "os" "testing" "github.com/thrasher-corp/gocryptotrader/common" @@ -19,22 +21,16 @@ const ( var b Bittrex -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { b.SetDefaults() - if b.Name != "Bittrex" { - t.Error("Bittrex - SetDefaults() error") - } -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("Bittrex load config error", err) + log.Fatal("Bittrex load config error", err) } bConfig, err := cfg.GetExchangeConfig("Bittrex") if err != nil { - t.Error("Bittrex Setup() init error") + log.Fatal("Bittrex Setup() init error") } bConfig.API.Credentials.Key = apiKey bConfig.API.Credentials.Secret = apiSecret @@ -42,13 +38,15 @@ func TestSetup(t *testing.T) { err = b.Setup(bConfig) if err != nil { - t.Fatal("Bittrex setup error", err) + log.Fatal("Bittrex setup error", err) } if !b.IsEnabled() || !b.API.AuthenticatedSupport || b.Verbose || len(b.BaseCurrencies) < 1 { - t.Error("Bittrex Setup values not set correctly") + log.Fatal("Bittrex Setup values not set correctly") } + + os.Exit(m.Run()) } func TestGetMarkets(t *testing.T) { @@ -236,7 +234,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() b.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -248,11 +246,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - b.SetDefaults() - TestSetup(t) - var feeBuilder = setFeeBuilder() - // CryptocurrencyTradeFee Basic if resp, err := b.GetFee(feeBuilder); resp != float64(0.0025) || err != nil { t.Error(err) @@ -320,20 +314,14 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - b.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.NoFiatWithdrawalsText - withdrawPermissions := b.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - b.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.LTC, @@ -351,9 +339,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - b.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -373,9 +358,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -401,15 +383,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -427,15 +405,11 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -458,6 +432,9 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := b.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -465,8 +442,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - b.SetDefaults() - TestSetup(t) withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -490,9 +465,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -506,9 +478,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - b.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index da8a543b..0af7bb87 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -329,7 +329,6 @@ func (b *Bittrex) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { } buy := s.OrderSide == order.Buy - if s.OrderType != order.Limit { return submitOrderResponse, errors.New("limit orders only supported on exchange") @@ -346,16 +345,16 @@ func (b *Bittrex) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { s.Amount, s.Price) } - + if err != nil { + return submitOrderResponse, err + } if response.Result.ID != "" { submitOrderResponse.OrderID = response.Result.ID } - if err == nil { - submitOrderResponse.IsOrderPlaced = true - } + submitOrderResponse.IsOrderPlaced = true - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/btse/btse_test.go b/exchanges/btse/btse_test.go index d97130b0..d0cd92fa 100644 --- a/exchanges/btse/btse_test.go +++ b/exchanges/btse/btse_test.go @@ -205,7 +205,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } b.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -320,7 +320,6 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-") - var orderCancellation = &order.Cancel{ OrderID: "b334ecef-2b42-4998-b8a4-b6b14f6d2671", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -341,7 +340,6 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-") - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go index 5a5ca079..02444533 100644 --- a/exchanges/btse/btse_wrapper.go +++ b/exchanges/btse/btse_wrapper.go @@ -3,6 +3,7 @@ package btse import ( "errors" "fmt" + "strconv" "strings" "sync" "time" @@ -357,7 +358,9 @@ func (b *BTSE) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { resp.IsOrderPlaced = true resp.OrderID = *r } - + if s.OrderType == order.Market { + resp.FullyMatched = true + } return resp, nil } @@ -475,7 +478,7 @@ func (b *BTSE) GetOrderInfo(orderID string) (order.Detail, error) { } od.Trades = append(od.Trades, order.TradeHistory{ Timestamp: createdAt, - TID: fills[i].ID, + TID: strconv.FormatInt(fills[i].ID, 10), Price: fills[i].Price, Amount: fills[i].Amount, Exchange: b.Name, @@ -569,7 +572,7 @@ func (b *BTSE) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, err } openOrder.Trades = append(openOrder.Trades, order.TradeHistory{ Timestamp: createdAt, - TID: fills[i].ID, + TID: strconv.FormatInt(fills[i].ID, 10), Price: fills[i].Price, Amount: fills[i].Amount, Exchange: b.Name, diff --git a/exchanges/coinbasepro/coinbasepro_test.go b/exchanges/coinbasepro/coinbasepro_test.go index 95d84f4a..b594603c 100644 --- a/exchanges/coinbasepro/coinbasepro_test.go +++ b/exchanges/coinbasepro/coinbasepro_test.go @@ -1,7 +1,9 @@ package coinbasepro import ( + "log" "net/http" + "os" "testing" "time" @@ -25,20 +27,18 @@ const ( testPair = "BTC-USD" ) -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { c.SetDefaults() c.Requester.SetRateLimit(false, time.Second, 1) -} -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("coinbasepro load config error", err) + log.Fatal("coinbasepro load config error", err) } gdxConfig, err := cfg.GetExchangeConfig("CoinbasePro") if err != nil { - t.Error("coinbasepro Setup() init error") + log.Fatal("coinbasepro Setup() init error") } gdxConfig.API.Credentials.Key = apiKey gdxConfig.API.Credentials.Secret = apiSecret @@ -47,8 +47,10 @@ func TestSetup(t *testing.T) { gdxConfig.API.AuthenticatedWebsocketSupport = true err = c.Setup(gdxConfig) if err != nil { - t.Fatal("CoinbasePro setup error", err) + log.Fatal("CoinbasePro setup error", err) } + + os.Exit(m.Run()) } func TestGetProducts(t *testing.T) { @@ -198,7 +200,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() c.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -210,12 +212,9 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - c.SetDefaults() - TestSetup(t) - var feeBuilder = setFeeBuilder() - if apiKey != "" || apiSecret != "" { + if areTestAPIKeysSet() { // CryptocurrencyTradeFee Basic if resp, err := c.GetFee(feeBuilder); resp != float64(0.003) || err != nil { t.Error(err) @@ -371,7 +370,6 @@ func TestCalculateTradingFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - c.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.AutoWithdrawFiatWithAPIPermissionText withdrawPermissions := c.FormatWithdrawPermissions() if withdrawPermissions != expectedResult { @@ -380,9 +378,6 @@ func TestFormatWithdrawPermissions(t *testing.T) { } func TestGetActiveOrders(t *testing.T) { - c.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.BTC, @@ -398,9 +393,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - c.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.BTC, @@ -422,9 +414,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - c.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -450,15 +439,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - c.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -476,15 +461,11 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - c.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -507,6 +488,9 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := c.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -514,8 +498,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - c.SetDefaults() - TestSetup(t) withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -539,9 +521,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - c.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -564,9 +543,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - c.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -597,8 +573,6 @@ func TestGetDepositAddress(t *testing.T) { // TestWsAuth dials websocket, sends login request. func TestWsAuth(t *testing.T) { - c.SetDefaults() - TestSetup(t) if !c.Websocket.IsEnabled() && !c.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(wshandler.WebsocketNotEnabled) } diff --git a/exchanges/coinbasepro/coinbasepro_websocket.go b/exchanges/coinbasepro/coinbasepro_websocket.go index 96c80b44..6994211b 100644 --- a/exchanges/coinbasepro/coinbasepro_websocket.go +++ b/exchanges/coinbasepro/coinbasepro_websocket.go @@ -3,7 +3,6 @@ package coinbasepro import ( "encoding/json" "errors" - "fmt" "net/http" "strconv" "time" @@ -311,7 +310,7 @@ func (c *CoinbasePro) Subscribe(channelToSubscribe wshandler.WebsocketChannelSub }, } if channelToSubscribe.Channel == "user" || channelToSubscribe.Channel == "full" { - n := fmt.Sprintf("%v", time.Now().Unix()) + n := strconv.FormatInt(time.Now().Unix(), 10) message := n + "GET" + "/users/self/verify" hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(message), []byte(c.API.Credentials.Secret)) diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index a66ebf41..959a3de8 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -400,16 +400,19 @@ func (c *CoinbasePro) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) default: err = errors.New("order type not supported") } - + if err != nil { + return submitOrderResponse, err + } + if s.OrderType == order.Market { + submitOrderResponse.FullyMatched = true + } if response != "" { submitOrderResponse.OrderID = response } - if err == nil { - submitOrderResponse.IsOrderPlaced = true - } + submitOrderResponse.IsOrderPlaced = true - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -509,7 +512,7 @@ func (c *CoinbasePro) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Deta var orders []order.Detail for i := range respOrders { - currency := currency.NewPairDelimiter(respOrders[i].ProductID, + curr := currency.NewPairDelimiter(respOrders[i].ProductID, c.GetPairFormat(asset.Spot, false).Delimiter) orderSide := order.Side(strings.ToUpper(respOrders[i].Side)) orderType := order.Type(strings.ToUpper(respOrders[i].Type)) @@ -530,7 +533,7 @@ func (c *CoinbasePro) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Deta OrderType: orderType, OrderDate: orderDate, OrderSide: orderSide, - CurrencyPair: currency, + CurrencyPair: curr, Exchange: c.Name, }) } @@ -545,9 +548,9 @@ func (c *CoinbasePro) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Deta // Can Limit response to specific order status func (c *CoinbasePro) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error) { var respOrders []GeneralizedOrderResponse - for _, currency := range req.Currencies { + for i := range req.Currencies { resp, err := c.GetOrders([]string{"done", "settled"}, - c.FormatExchangeCurrency(currency, asset.Spot).String()) + c.FormatExchangeCurrency(req.Currencies[i], asset.Spot).String()) if err != nil { return nil, err } @@ -556,7 +559,7 @@ func (c *CoinbasePro) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Deta var orders []order.Detail for i := range respOrders { - currency := currency.NewPairDelimiter(respOrders[i].ProductID, + curr := currency.NewPairDelimiter(respOrders[i].ProductID, c.GetPairFormat(asset.Spot, false).Delimiter) orderSide := order.Side(strings.ToUpper(respOrders[i].Side)) orderType := order.Type(strings.ToUpper(respOrders[i].Type)) @@ -577,7 +580,7 @@ func (c *CoinbasePro) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Deta OrderType: orderType, OrderDate: orderDate, OrderSide: orderSide, - CurrencyPair: currency, + CurrencyPair: curr, Exchange: c.Name, }) } diff --git a/exchanges/coinbene/coinbene.go b/exchanges/coinbene/coinbene.go index 4bb0653e..482edab7 100644 --- a/exchanges/coinbene/coinbene.go +++ b/exchanges/coinbene/coinbene.go @@ -14,6 +14,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" ) @@ -27,8 +28,6 @@ const ( coinbeneAPIURL = "https://openapi-exchange.coinbene.com/api/exchange/" coinbeneAuthPath = "/api/exchange/v2" coinbeneAPIVersion = "v2" - buy = "buy" - sell = "sell" // Public endpoints coinbeneFetchTicker = "/market/ticker/one" @@ -114,9 +113,9 @@ func (c *Coinbene) PlaceOrder(price, quantity float64, symbol, direction, client params := url.Values{} params.Set("symbol", symbol) switch direction { - case sell: + case order.Buy.Lower(): params.Set("direction", "2") - case buy: + case order.Sell.Lower(): params.Set("direction", "1") default: return resp, diff --git a/exchanges/coinbene/coinbene_test.go b/exchanges/coinbene/coinbene_test.go index b870ce71..5c77b5f1 100644 --- a/exchanges/coinbene/coinbene_test.go +++ b/exchanges/coinbene/coinbene_test.go @@ -7,6 +7,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) // Please supply your own keys here for due diligence testing @@ -103,7 +104,7 @@ func TestPlaceOrder(t *testing.T) { if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") } - _, err := c.PlaceOrder(140, 1, btcusdt, "buy", "") + _, err := c.PlaceOrder(1, 1, btcusdt, order.Buy.Lower(), "") if err != nil { t.Error(err) } diff --git a/exchanges/coinbene/coinbene_wrapper.go b/exchanges/coinbene/coinbene_wrapper.go index d88c751f..373222f1 100644 --- a/exchanges/coinbene/coinbene_wrapper.go +++ b/exchanges/coinbene/coinbene_wrapper.go @@ -3,6 +3,7 @@ package coinbene import ( "fmt" "strconv" + "strings" "sync" "time" @@ -504,9 +505,9 @@ func (c *Coinbene) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([] for y := range tempData.OpenOrders { tempResp.Exchange = c.Name tempResp.CurrencyPair = getOrdersRequest.Currencies[x] - tempResp.OrderSide = buy - if tempData.OpenOrders[y].OrderType == sell { - tempResp.OrderSide = sell + tempResp.OrderSide = order.Buy + if strings.EqualFold(tempData.OpenOrders[y].OrderType, order.Sell.String()) { + tempResp.OrderSide = order.Sell } t, err = time.Parse(time.RFC3339, tempData.OpenOrders[y].OrderTime) if err != nil { @@ -556,7 +557,7 @@ func (c *Coinbene) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([] tempResp.Exchange = c.Name tempResp.CurrencyPair = getOrdersRequest.Currencies[x] tempResp.OrderSide = order.Buy - if tempData.Data[y].OrderType == sell { + if strings.EqualFold(tempData.Data[y].OrderType, order.Sell.String()) { tempResp.OrderSide = order.Sell } t, err = time.Parse(time.RFC3339, tempData.Data[y].OrderTime) diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index 4ba2ec97..7cb7157e 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net/http" + "strconv" "strings" "github.com/thrasher-corp/gocryptotrader/common/crypto" @@ -105,9 +106,8 @@ func (c *COINUT) GetTrades(instrumentID int) (Trades, error) { } // GetUserBalance returns the full user balance -func (c *COINUT) GetUserBalance() (UserBalance, error) { - result := UserBalance{} - +func (c *COINUT) GetUserBalance() (*UserBalance, error) { + var result *UserBalance return result, c.SendHTTPRequest(coinutBalance, nil, true, &result) } @@ -117,26 +117,16 @@ func (c *COINUT) NewOrder(instrumentID int64, quantity, price float64, buy bool, params := make(map[string]interface{}) params["inst_id"] = instrumentID if price > 0 { - params["price"] = fmt.Sprintf("%v", price) + params["price"] = strconv.FormatFloat(price, 'f', -1, 64) } - params["qty"] = fmt.Sprintf("%v", quantity) + params["qty"] = strconv.FormatFloat(quantity, 'f', -1, 64) params["side"] = order.Buy.String() if !buy { params["side"] = order.Sell.String() } params["client_ord_id"] = orderID - err := c.SendHTTPRequest(coinutOrder, params, true, &result) - if _, ok := result.(OrderRejectResponse); ok { - return result.(OrderRejectResponse), err - } - if _, ok := result.(OrderFilledResponse); ok { - return result.(OrderFilledResponse), err - } - if _, ok := result.(OrdersBase); ok { - return result.(OrdersBase), err - } - return result, err + return result, c.SendHTTPRequest(coinutOrder, params, true, &result) } // NewOrders places multiple orders on the exchange @@ -425,3 +415,77 @@ func getInternationalBankDepositFee(c currency.Code, amount float64) float64 { return fee } + +// IsLoaded returns whether or not the instrument map has been seeded +func (i *instrumentMap) IsLoaded() bool { + i.m.Lock() + defer i.m.Unlock() + return i.Loaded +} + +// Seed seeds the instrument map +func (i *instrumentMap) Seed(curr string, id int64) { + i.m.Lock() + defer i.m.Unlock() + + if !i.Loaded { + i.Instruments = make(map[string]int64) + } + + // check to see if the instrument already exists + _, ok := i.Instruments[curr] + if ok { + return + } + + i.Instruments[curr] = id + i.Loaded = true +} + +// LookupInstrument looks up an instrument based on an id +func (i *instrumentMap) LookupInstrument(id int64) string { + i.m.Lock() + defer i.m.Unlock() + + if !i.Loaded { + return "" + } + + for k, v := range i.Instruments { + if v == id { + return k + } + } + return "" +} + +// LookupID looks up an ID based on a string +func (i *instrumentMap) LookupID(curr string) int64 { + i.m.Lock() + defer i.m.Unlock() + + if !i.Loaded { + return 0 + } + + if ic, ok := i.Instruments[curr]; ok { + return ic + } + return 0 +} + +// GetInstrumentIDs returns a list of IDs +func (i *instrumentMap) GetInstrumentIDs() []int64 { + i.m.Lock() + defer i.m.Unlock() + + if !i.Loaded { + return nil + } + + var instruments []int64 + for _, x := range i.Instruments { + instruments = append(instruments, x) + } + return instruments +} diff --git a/exchanges/coinut/coinut_test.go b/exchanges/coinut/coinut_test.go index 622d75c1..c3616708 100644 --- a/exchanges/coinut/coinut_test.go +++ b/exchanges/coinut/coinut_test.go @@ -1,7 +1,9 @@ package coinut import ( + "log" "net/http" + "os" "testing" "github.com/gorilla/websocket" @@ -24,45 +26,42 @@ const ( canManipulateRealOrders = false ) -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { c.SetDefaults() -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("Coinut load config error", err) + log.Fatal("Coinut load config error", err) } bConfig, err := cfg.GetExchangeConfig("COINUT") if err != nil { - t.Error("Coinut Setup() init error") + log.Fatal("Coinut Setup() init error") } bConfig.API.AuthenticatedSupport = true bConfig.API.AuthenticatedWebsocketSupport = true bConfig.API.Credentials.Key = apiKey bConfig.API.Credentials.ClientID = clientID - bConfig.Verbose = true err = c.Setup(bConfig) if err != nil { - t.Fatal("Coinut setup error", err) + log.Fatal("Coinut setup error", err) } - if !c.IsEnabled() || !c.Verbose || - c.Websocket.IsEnabled() || len(c.BaseCurrencies) < 1 { - t.Error("Coinut Setup values not set correctly") - } + c.SeedInstruments() + + os.Exit(m.Run()) } func setupWSTestAuth(t *testing.T) { if wsSetupRan { return } - c.SetDefaults() - TestSetup(t) + if !c.Websocket.IsEnabled() && !c.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(wshandler.WebsocketNotEnabled) } + if areTestAPIKeysSet() { + c.Websocket.SetCanUseAuthenticatedEndpoints(true) + } c.WebsocketConn = &wshandler.WebsocketConnection{ ExchangeName: c.Name, URL: coinutWebsocketURL, @@ -71,6 +70,7 @@ func setupWSTestAuth(t *testing.T) { ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, } + var dialer websocket.Dialer err := c.WebsocketConn.Dial(&dialer, http.Header{}) if err != nil { @@ -84,6 +84,10 @@ func setupWSTestAuth(t *testing.T) { t.Error(err) } wsSetupRan = true + _, err = c.WsGetInstruments() + if err != nil { + t.Error(err) + } } func TestGetInstruments(t *testing.T) { @@ -130,12 +134,8 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - c.SetDefaults() - TestSetup(t) t.Parallel() - var feeBuilder = setFeeBuilder() - // CryptocurrencyTradeFee Basic if resp, err := c.GetFee(feeBuilder); resp != float64(0.001) || err != nil { t.Error(err) @@ -248,38 +248,29 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - c.SetDefaults() expectedResult := exchange.WithdrawCryptoViaWebsiteOnlyText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - withdrawPermissions := c.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - c.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } - _, err := c.GetActiveOrders(&getOrdersRequest) if areTestAPIKeysSet() && err != nil { t.Errorf("Could not get open orders: %s", err) } } -func TestGetOrderHistory(t *testing.T) { - c.SetDefaults() - TestSetup(t) - +func TestGetOrderHistoryWrapper(t *testing.T) { + setupWSTestAuth(t) var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.BTC, - currency.LTC)}, + currency.USD)}, } _, err := c.GetOrderHistory(&getOrdersRequest) @@ -295,9 +286,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - c.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -311,7 +299,7 @@ func TestSubmitOrder(t *testing.T) { OrderType: order.Limit, Price: 1, Amount: 1, - ClientID: "meowOrder", + ClientID: "123", } response, err := c.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { @@ -322,15 +310,10 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - c.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - - currencyPair := currency.NewPair(currency.LTC, currency.BTC) - + currencyPair := currency.NewPair(currency.BTC, currency.USD) var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -348,15 +331,11 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - c.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -393,6 +372,9 @@ func TestGetAccountInfo(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := c.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -400,8 +382,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - c.SetDefaults() - TestSetup(t) withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -422,9 +402,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - c.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -437,9 +414,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - c.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -473,14 +447,14 @@ func TestWsAuthSubmitOrder(t *testing.T) { if !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - order := WsSubmitOrderParameters{ + ord := WsSubmitOrderParameters{ Amount: 1, Currency: currency.NewPair(currency.LTC, currency.BTC), OrderID: 1, Price: 1, Side: order.Buy, } - _, err := c.wsSubmitOrder(&order) + _, err := c.wsSubmitOrder(&ord) if err != nil { t.Error(err) } @@ -513,12 +487,13 @@ func TestWsAuthSubmitOrders(t *testing.T) { } // TestWsAuthCancelOrders dials websocket, cancels orders +// doesn't care about if the order cancellations fail func TestWsAuthCancelOrders(t *testing.T) { setupWSTestAuth(t) if !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - order := WsCancelOrderParameters{ + ord := WsCancelOrderParameters{ Currency: currency.NewPair(currency.LTC, currency.BTC), OrderID: 1, } @@ -526,9 +501,28 @@ func TestWsAuthCancelOrders(t *testing.T) { Currency: currency.NewPair(currency.LTC, currency.BTC), OrderID: 2, } - _, errs := c.wsCancelOrders([]WsCancelOrderParameters{order, order2}) - if len(errs) > 0 { - t.Error(errs) + resp, err := c.wsCancelOrders([]WsCancelOrderParameters{ord, order2}) + if err != nil { + t.Error(err) + } + if resp.Status[0] != "OK" { + t.Error("Order failed to cancel") + } +} + +// TestWsAuthCancelOrders dials websocket, cancels orders +// Checks that the wrapper oversight works +func TestWsAuthCancelOrdersWrapper(t *testing.T) { + setupWSTestAuth(t) + if !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } + orderDetails := order.Cancel{ + CurrencyPair: currency.NewPair(currency.LTC, currency.BTC), + } + _, err := c.CancelAllOrders(&orderDetails) + if err != nil { + t.Error(err) } } @@ -538,20 +532,23 @@ func TestWsAuthCancelOrder(t *testing.T) { if !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - order := WsCancelOrderParameters{ + ord := &WsCancelOrderParameters{ Currency: currency.NewPair(currency.LTC, currency.BTC), OrderID: 1, } - err := c.wsCancelOrder(order) + resp, err := c.wsCancelOrder(ord) if err != nil { t.Error(err) } + if len(resp.Status) >= 1 && resp.Status[0] != "OK" { + t.Errorf("Failed to cancel order") + } } // TestWsAuthGetOpenOrders dials websocket, retrieves open orders func TestWsAuthGetOpenOrders(t *testing.T) { setupWSTestAuth(t) - err := c.wsGetOpenOrders(currency.NewPair(currency.LTC, currency.BTC)) + _, err := c.wsGetOpenOrders(currency.NewPair(currency.LTC, currency.BTC).String()) if err != nil { t.Error(err) } @@ -573,7 +570,6 @@ func TestCurrencyMapIsLoaded(t *testing.T) { func TestCurrencyMapSeed(t *testing.T) { t.Parallel() var i instrumentMap - // Test non-seeded lookups if id := i.LookupInstrument(1234); id != "" { t.Error("unexpected result") diff --git a/exchanges/coinut/coinut_types.go b/exchanges/coinut/coinut_types.go index b42d3425..a647afdf 100644 --- a/exchanges/coinut/coinut_types.go +++ b/exchanges/coinut/coinut_types.go @@ -1,7 +1,6 @@ package coinut import ( - "strings" "sync" "github.com/thrasher-corp/gocryptotrader/currency" @@ -177,7 +176,7 @@ type CancelOrdersResponse struct { Results []struct { OrderID int64 `json:"order_id"` Status string `json:"status"` - InstrumentID int `json:"inst_id"` + InstrumentID int64 `json:"inst_id"` } `json:"results"` } @@ -372,10 +371,10 @@ type WsTradeUpdate struct { // WsInstrumentList defines instrument list type WsInstrumentList struct { - Spot map[string][]WsSupportedCurrency `json:"SPOT"` - Nonce int64 `json:"nonce"` - Reply string `json:"inst_list"` - Status []interface{} `json:"status"` + Spot map[string][]InstrumentBase `json:"SPOT"` + Nonce int64 `json:"nonce,omitempty"` + Reply string `json:"inst_list,omitempty"` + Status []interface{} `json:"status,omitempty"` } // WsSupportedCurrency defines supported currency on the exchange @@ -536,14 +535,15 @@ type WsOrderFilledResponse struct { // WsOrderData ws response data type WsOrderData struct { - ClientOrdID int64 `json:"client_ord_id"` - InstID int64 `json:"inst_id"` - OpenQty float64 `json:"open_qty,string"` - OrderID int64 `json:"order_id"` - Price float64 `json:"price,string"` - Qty float64 `json:"qty,string"` - Side string `json:"side"` - Timestamp int64 `json:"timestamp"` + ClientOrdID int64 `json:"client_ord_id"` + InstID int64 `json:"inst_id"` + OpenQty float64 `json:"open_qty,string"` + OrderID int64 `json:"order_id"` + Price float64 `json:"price,string"` + Qty float64 `json:"qty,string"` + Side string `json:"side"` + Timestamp int64 `json:"timestamp"` + Status []string `json:"status"` } // WsOrderFilledCommissionData ws response data @@ -656,20 +656,20 @@ type WsNewOrderResponse struct { // WsGetAccountBalanceResponse contains values of each currency type WsGetAccountBalanceResponse struct { - BCH string `json:"BCH"` - BTC string `json:"BTC"` - BTG string `json:"BTG"` - CAD string `json:"CAD"` - ETC string `json:"ETC"` - ETH string `json:"ETH"` - LCH string `json:"LCH"` - LTC string `json:"LTC"` - MYR string `json:"MYR"` - SGD string `json:"SGD"` - USD string `json:"USD"` - USDT string `json:"USDT"` - XMR string `json:"XMR"` - ZEC string `json:"ZEC"` + BCH float64 `json:"BCH,string"` + BTC float64 `json:"BTC,string"` + BTG float64 `json:"BTG,string"` + CAD float64 `json:"CAD,string"` + ETC float64 `json:"ETC,string"` + ETH float64 `json:"ETH,string"` + LCH float64 `json:"LCH,string"` + LTC float64 `json:"LTC,string"` + MYR float64 `json:"MYR,string"` + SGD float64 `json:"SGD,string"` + USD float64 `json:"USD,string"` + USDT float64 `json:"USDT,string"` + XMR float64 `json:"XMR,string"` + ZEC float64 `json:"ZEC,string"` Nonce int64 `json:"nonce"` Reply string `json:"reply"` Status []string `json:"status"` @@ -681,79 +681,3 @@ type instrumentMap struct { Loaded bool m sync.Mutex } - -// IsLoaded returns whether or not the instrument map has been seeded -func (i *instrumentMap) IsLoaded() bool { - i.m.Lock() - defer i.m.Unlock() - return i.Loaded -} - -// Seed seeds the instrument map -func (i *instrumentMap) Seed(currency string, id int64) { - i.m.Lock() - defer i.m.Unlock() - - if !i.Loaded { - i.Instruments = make(map[string]int64) - } - - // check to see if the instrument already exists - _, ok := i.Instruments[currency] - if ok { - return - } - - i.Instruments[currency] = id - i.Loaded = true -} - -// LookupInstrument looks up an instrument based on an id -func (i *instrumentMap) LookupInstrument(id int64) string { - i.m.Lock() - defer i.m.Unlock() - - if !i.Loaded { - return "" - } - - for k, v := range i.Instruments { - if v == id { - return k - } - } - return "" -} - -// LookupID looks up an ID based on a string -func (i *instrumentMap) LookupID(currency string) int64 { - i.m.Lock() - defer i.m.Unlock() - - if !i.Loaded { - return 0 - } - - for k, v := range i.Instruments { - if strings.EqualFold(currency, k) { - return v - } - } - return 0 -} - -// GetInstrumentIDs returns a list of IDs -func (i *instrumentMap) GetInstrumentIDs() []int64 { - i.m.Lock() - defer i.m.Unlock() - - if !i.Loaded { - return nil - } - - var instruments []int64 - for _, x := range i.Instruments { - instruments = append(instruments, x) - } - return instruments -} diff --git a/exchanges/coinut/coinut_websocket.go b/exchanges/coinut/coinut_websocket.go index 6d242239..c5dfbb19 100644 --- a/exchanges/coinut/coinut_websocket.go +++ b/exchanges/coinut/coinut_websocket.go @@ -17,6 +17,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" + log "github.com/thrasher-corp/gocryptotrader/logger" ) const ( @@ -25,8 +26,7 @@ const ( ) var ( - channels map[string]chan []byte - wsInstrumentMap instrumentMap + channels map[string]chan []byte ) // NOTE for speed considerations @@ -46,13 +46,17 @@ func (c *COINUT) WsConnect() error { } go c.WsHandleData() - if !wsInstrumentMap.IsLoaded() { - err = c.WsSetInstrumentList() + if !c.instrumentMap.IsLoaded() { + _, err = c.WsGetInstruments() if err != nil { return err } } - c.wsAuthenticate() + err = c.wsAuthenticate() + if err != nil { + c.Websocket.SetCanUseAuthenticatedEndpoints(false) + log.Error(log.WebsocketMgr, err) + } c.GenerateDefaultSubscriptions() // define bi-directional communication @@ -135,7 +139,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) { return } - currencyPair := wsInstrumentMap.LookupInstrument(ticker.InstID) + currencyPair := c.instrumentMap.LookupInstrument(ticker.InstID) c.Websocket.DataHandler <- wshandler.TickerData{ Exchange: c.Name, Volume: ticker.Volume24, @@ -164,7 +168,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) { c.Websocket.DataHandler <- err return } - currencyPair := wsInstrumentMap.LookupInstrument(orderbooksnapshot.InstID) + currencyPair := c.instrumentMap.LookupInstrument(orderbooksnapshot.InstID) c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ Exchange: c.Name, Asset: asset.Spot, @@ -184,7 +188,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) { c.Websocket.DataHandler <- err return } - currencyPair := wsInstrumentMap.LookupInstrument(orderbookUpdate.InstID) + currencyPair := c.instrumentMap.LookupInstrument(orderbookUpdate.InstID) c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ Exchange: c.Name, Asset: asset.Spot, @@ -207,7 +211,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) { c.Websocket.DataHandler <- err return } - currencyPair := wsInstrumentMap.LookupInstrument(tradeUpdate.InstID) + currencyPair := c.instrumentMap.LookupInstrument(tradeUpdate.InstID) c.Websocket.DataHandler <- wshandler.TradeData{ Timestamp: time.Unix(tradeUpdate.Timestamp, 0), CurrencyPair: currency.NewPairFromFormattedPairs(currencyPair, @@ -238,8 +242,9 @@ func (c *COINUT) GetNonce() int64 { return int64(c.Nonce.Get()) } -// WsSetInstrumentList fetches instrument list and propagates a local cache -func (c *COINUT) WsSetInstrumentList() error { +// WsGetInstruments fetches instrument list and propagates a local cache +func (c *COINUT) WsGetInstruments() (Instruments, error) { + var list Instruments request := wsRequest{ Request: "inst_list", SecType: strings.ToUpper(asset.Spot.String()), @@ -247,20 +252,19 @@ func (c *COINUT) WsSetInstrumentList() error { } resp, err := c.WebsocketConn.SendMessageReturnResponse(request.Nonce, request) if err != nil { - return err + return list, err } - var list WsInstrumentList err = json.Unmarshal(resp, &list) if err != nil { - return err + return list, err } - for curr, data := range list.Spot { - wsInstrumentMap.Seed(curr, data[0].InstID) + for curr, data := range list.Instruments { + c.instrumentMap.Seed(curr, data[0].InstID) } - if len(wsInstrumentMap.GetInstrumentIDs()) == 0 { - return errors.New("instrument list failed to populate") + if len(c.instrumentMap.GetInstrumentIDs()) == 0 { + return list, errors.New("instrument list failed to populate") } - return nil + return list, nil } // WsProcessOrderbookSnapshot processes the orderbook snapshot @@ -285,7 +289,7 @@ func (c *COINUT) WsProcessOrderbookSnapshot(ob *WsOrderbookSnapshot) error { newOrderBook.Asks = asks newOrderBook.Bids = bids newOrderBook.Pair = currency.NewPairFromFormattedPairs( - wsInstrumentMap.LookupInstrument(ob.InstID), + c.instrumentMap.LookupInstrument(ob.InstID), c.GetEnabledPairs(asset.Spot), c.GetPairFormat(asset.Spot, true), ) @@ -298,7 +302,7 @@ func (c *COINUT) WsProcessOrderbookSnapshot(ob *WsOrderbookSnapshot) error { // WsProcessOrderbookUpdate process an orderbook update func (c *COINUT) WsProcessOrderbookUpdate(update *WsOrderbookUpdate) error { p := currency.NewPairFromFormattedPairs( - wsInstrumentMap.LookupInstrument(update.InstID), + c.instrumentMap.LookupInstrument(update.InstID), c.GetEnabledPairs(asset.Spot), c.GetPairFormat(asset.Spot, true), ) @@ -335,7 +339,7 @@ func (c *COINUT) GenerateDefaultSubscriptions() { func (c *COINUT) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { subscribe := wsRequest{ Request: channelToSubscribe.Channel, - InstID: wsInstrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency, + InstID: c.instrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency, asset.Spot).String()), Subscribe: true, Nonce: c.WebsocketConn.GenerateMessageID(false), @@ -347,7 +351,7 @@ func (c *COINUT) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscrip func (c *COINUT) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { subscribe := wsRequest{ Request: channelToSubscribe.Channel, - InstID: wsInstrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency, + InstID: c.instrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency, asset.Spot).String()), Subscribe: false, Nonce: c.WebsocketConn.GenerateMessageID(false), @@ -406,7 +410,7 @@ func (c *COINUT) wsAuthenticate() error { return nil } -func (c *COINUT) wsGetAccountBalance() (*WsGetAccountBalanceResponse, error) { +func (c *COINUT) wsGetAccountBalance() (*UserBalance, error) { if !c.Websocket.CanUseAuthenticatedEndpoints() { return nil, fmt.Errorf("%v not authorised to submit order", c.Name) } @@ -418,7 +422,7 @@ func (c *COINUT) wsGetAccountBalance() (*WsGetAccountBalanceResponse, error) { if err != nil { return nil, err } - var response WsGetAccountBalanceResponse + var response UserBalance err = json.Unmarshal(resp, &response) if err != nil { return nil, err @@ -429,21 +433,21 @@ func (c *COINUT) wsGetAccountBalance() (*WsGetAccountBalanceResponse, error) { return &response, nil } -func (c *COINUT) wsSubmitOrder(order *WsSubmitOrderParameters) (*WsStandardOrderResponse, error) { +func (c *COINUT) wsSubmitOrder(o *WsSubmitOrderParameters) (*WsStandardOrderResponse, error) { if !c.Websocket.CanUseAuthenticatedEndpoints() { return nil, fmt.Errorf("%v not authorised to submit order", c.Name) } - curr := c.FormatExchangeCurrency(order.Currency, asset.Spot).String() + curr := c.FormatExchangeCurrency(o.Currency, asset.Spot).String() var orderSubmissionRequest WsSubmitOrderRequest orderSubmissionRequest.Request = "new_order" orderSubmissionRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) - orderSubmissionRequest.InstID = wsInstrumentMap.LookupID(curr) - orderSubmissionRequest.Qty = order.Amount - orderSubmissionRequest.Price = order.Price - orderSubmissionRequest.Side = string(order.Side) + orderSubmissionRequest.InstID = c.instrumentMap.LookupID(curr) + orderSubmissionRequest.Qty = o.Amount + orderSubmissionRequest.Price = o.Price + orderSubmissionRequest.Side = string(o.Side) - if order.OrderID > 0 { - orderSubmissionRequest.OrderID = order.OrderID + if o.OrderID > 0 { + orderSubmissionRequest.OrderID = o.OrderID } resp, err := c.WebsocketConn.SendMessageReturnResponse(orderSubmissionRequest.Nonce, orderSubmissionRequest) if err != nil { @@ -548,7 +552,7 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]WsStandardO Qty: orders[i].Amount, Price: orders[i].Price, Side: string(orders[i].Side), - InstID: wsInstrumentMap.LookupID(curr), + InstID: c.instrumentMap.LookupID(curr), ClientOrdID: i + 1, }) } @@ -585,7 +589,7 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]WsStandardO if len(standardOrder.Reasons) > 0 && standardOrder.Reasons[0] != "" { errors = append(errors, fmt.Errorf("%v order submission failed for currency %v and orderID %v, message %v ", c.Name, - wsInstrumentMap.LookupInstrument(standardOrder.InstID), + c.instrumentMap.LookupInstrument(standardOrder.InstID), standardOrder.OrderID, standardOrder.Reasons[0])) @@ -597,73 +601,73 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]WsStandardO return ordersResponse, errors } -func (c *COINUT) wsGetOpenOrders(p currency.Pair) error { +func (c *COINUT) wsGetOpenOrders(curr string) (*WsUserOpenOrdersResponse, error) { + var response *WsUserOpenOrdersResponse if !c.Websocket.CanUseAuthenticatedEndpoints() { - return fmt.Errorf("%v not authorised to get open orders", c.Name) + return response, fmt.Errorf("%v not authorised to get open orders", c.Name) } - curr := c.FormatExchangeCurrency(p, asset.Spot).String() var openOrdersRequest WsGetOpenOrdersRequest openOrdersRequest.Request = "user_open_orders" openOrdersRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) - openOrdersRequest.InstID = wsInstrumentMap.LookupID(curr) + openOrdersRequest.InstID = c.instrumentMap.LookupID(curr) resp, err := c.WebsocketConn.SendMessageReturnResponse(openOrdersRequest.Nonce, openOrdersRequest) if err != nil { - return err + return response, err } - var response map[string]interface{} err = json.Unmarshal(resp, &response) if err != nil { - return err + return response, err } - if response["status"].([]interface{})[0] != "OK" { - return fmt.Errorf("%v get open orders failed for currency %v", + if response.Status[0] != "OK" { + return response, fmt.Errorf("%v get open orders failed for currency %v", c.Name, - p) + curr) } - return nil + return response, nil } -func (c *COINUT) wsCancelOrder(cancellation WsCancelOrderParameters) error { +func (c *COINUT) wsCancelOrder(cancellation *WsCancelOrderParameters) (*CancelOrdersResponse, error) { + var response *CancelOrdersResponse if !c.Websocket.CanUseAuthenticatedEndpoints() { - return fmt.Errorf("%v not authorised to cancel order", c.Name) + return response, fmt.Errorf("%v not authorised to cancel order", c.Name) } - currency := c.FormatExchangeCurrency(cancellation.Currency, asset.Spot).String() + curr := c.FormatExchangeCurrency(cancellation.Currency, asset.Spot).String() var cancellationRequest WsCancelOrderRequest cancellationRequest.Request = "cancel_order" - cancellationRequest.InstID = wsInstrumentMap.LookupID(currency) + cancellationRequest.InstID = c.instrumentMap.LookupID(curr) cancellationRequest.OrderID = cancellation.OrderID cancellationRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) resp, err := c.WebsocketConn.SendMessageReturnResponse(cancellationRequest.Nonce, cancellationRequest) if err != nil { - return err + return response, err } - var response map[string]interface{} err = json.Unmarshal(resp, &response) if err != nil { - return err + return response, err } - if response["status"].([]interface{})[0] != "OK" { - return fmt.Errorf("%v order cancellation failed for currency %v and orderID %v, message %v", + if response.Status[0] != "OK" { + return response, fmt.Errorf("%v order cancellation failed for currency %v and orderID %v, message %v", c.Name, cancellation.Currency, cancellation.OrderID, - response["status"]) + response.Status[0]) } - return nil + return response, nil } -func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) (*WsCancelOrdersResponse, []error) { - var errors []error +func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) (*CancelOrdersResponse, error) { + var err error + var response *CancelOrdersResponse if !c.Websocket.CanUseAuthenticatedEndpoints() { - return nil, errors + return nil, err } - cancelOrderRequest := WsCancelOrdersRequest{} + var cancelOrderRequest WsCancelOrdersRequest for i := range cancellations { - currency := c.FormatExchangeCurrency(cancellations[i].Currency, asset.Spot).String() + curr := c.FormatExchangeCurrency(cancellations[i].Currency, asset.Spot).String() cancelOrderRequest.Entries = append(cancelOrderRequest.Entries, WsCancelOrdersRequestEntry{ - InstID: wsInstrumentMap.LookupID(currency), + InstID: c.instrumentMap.LookupID(curr), OrderID: cancellations[i].OrderID, }) } @@ -672,53 +676,40 @@ func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) (*WsCan cancelOrderRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) resp, err := c.WebsocketConn.SendMessageReturnResponse(cancelOrderRequest.Nonce, cancelOrderRequest) if err != nil { - return nil, []error{err} + return response, err } - var response WsCancelOrdersResponse err = json.Unmarshal(resp, &response) if err != nil { - return nil, []error{err} + return response, err } - if response.Status[0] != "OK" { - return &response, []error{err} - } - for i := range response.Results { - if response.Results[i].Status != "OK" { - errors = append(errors, fmt.Errorf("%v order cancellation failed for currency %v and orderID %v, message %v", - c.Name, - wsInstrumentMap.LookupInstrument(response.Results[i].InstID), - response.Results[i].OrderID, - response.Results[i].Status)) - } - } - return &response, errors + return response, err } -func (c *COINUT) wsGetTradeHistory(p currency.Pair, start, limit int64) error { +func (c *COINUT) wsGetTradeHistory(p currency.Pair, start, limit int64) (*WsTradeHistoryResponse, error) { + var response *WsTradeHistoryResponse if !c.Websocket.CanUseAuthenticatedEndpoints() { - return fmt.Errorf("%v not authorised to get trade history", c.Name) + return response, fmt.Errorf("%v not authorised to get trade history", c.Name) } curr := c.FormatExchangeCurrency(p, asset.Spot).String() var request WsTradeHistoryRequest request.Request = "trade_history" - request.InstID = wsInstrumentMap.LookupID(curr) + request.InstID = c.instrumentMap.LookupID(curr) request.Nonce = c.WebsocketConn.GenerateMessageID(false) request.Start = start request.Limit = limit resp, err := c.WebsocketConn.SendMessageReturnResponse(request.Nonce, request) if err != nil { - return err + return response, err } - var response map[string]interface{} err = json.Unmarshal(resp, &response) if err != nil { - return err + return response, err } - if response["status"].([]interface{})[0] != "OK" { - return fmt.Errorf("%v get trade history failed for %v", + if response.Status[0] != "OK" { + return response, fmt.Errorf("%v get trade history failed for %v", c.Name, request) } - return nil + return response, nil } diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index c5c3e03e..aaa0b0e3 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -88,15 +88,20 @@ func (c *COINUT) SetDefaults() { FiatWithdrawalFee: true, }, WebsocketCapabilities: protocol.Features{ + AccountBalance: true, + GetOrders: true, + CancelOrders: true, + CancelOrder: true, + SubmitOrder: true, + SubmitOrders: true, + UserTradeHistory: true, TickerFetching: true, - OrderbookFetching: true, TradeFetching: true, + OrderbookFetching: true, + AccountInfo: true, Subscribe: true, Unsubscribe: true, AuthenticatedEndpoints: true, - SubmitOrder: true, - SubmitOrders: true, - CancelOrder: true, MessageCorrelation: true, }, WithdrawPermissions: exchange.WithdrawCryptoViaWebsiteOnly | @@ -217,15 +222,25 @@ func (c *COINUT) Run() { // FetchTradablePairs returns a list of the exchanges tradable pairs func (c *COINUT) FetchTradablePairs(asset asset.Item) ([]string, error) { - i, err := c.GetInstruments() - if err != nil { - return nil, err + var instruments map[string][]InstrumentBase + var resp Instruments + var err error + if c.Websocket.IsConnected() { + resp, err = c.WsGetInstruments() + if err != nil { + return nil, err + } + } else { + resp, err = c.GetInstruments() + if err != nil { + return nil, err + } } - + instruments = resp.Instruments var pairs []string - for _, y := range i.Instruments { - c.instrumentMap.Seed(y[0].Base+y[0].Quote, y[0].InstID) - p := y[0].Base + c.GetPairFormat(asset, false).Delimiter + y[0].Quote + for i := range instruments { + c.instrumentMap.Seed(instruments[i][0].Base+instruments[i][0].Quote, instruments[i][0].InstID) + p := instruments[i][0].Base + c.GetPairFormat(asset, false).Delimiter + instruments[i][0].Quote pairs = append(pairs, p) } @@ -248,9 +263,20 @@ func (c *COINUT) UpdateTradablePairs(forceUpdate bool) error { // COINUT exchange func (c *COINUT) GetAccountInfo() (exchange.AccountInfo, error) { var info exchange.AccountInfo - bal, err := c.GetUserBalance() - if err != nil { - return info, err + var bal *UserBalance + var err error + if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + var resp *UserBalance + resp, err = c.wsGetAccountBalance() + if err != nil { + return info, err + } + bal = resp + } else { + bal, err = c.GetUserBalance() + if err != nil { + return info, err + } } var balances = []exchange.AccountCurrencyInfo{ @@ -322,12 +348,9 @@ func (c *COINUT) GetAccountInfo() (exchange.AccountInfo, error) { // UpdateTicker updates and returns the ticker for a currency pair func (c *COINUT) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { var tickerPrice ticker.Price - - if !c.instrumentMap.IsLoaded() { - err := c.SeedInstruments() - if err != nil { - return tickerPrice, err - } + err := c.loadInstrumentsIfNotLoaded() + if err != nil { + return tickerPrice, err } instID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(p, @@ -335,8 +358,8 @@ func (c *COINUT) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pri if instID == 0 { return tickerPrice, errors.New("unable to lookup instrument ID") } - - tick, err := c.GetInstrumentTicker(instID) + var tick Ticker + tick, err = c.GetInstrumentTicker(instID) if err != nil { return tickerPrice, err } @@ -379,12 +402,9 @@ func (c *COINUT) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderboo // UpdateOrderbook updates and returns the orderbook for a currency pair func (c *COINUT) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) { var orderBook orderbook.Base - - if !c.instrumentMap.IsLoaded() { - err := c.SeedInstruments() - if err != nil { - return orderBook, err - } + err := c.loadInstrumentsIfNotLoaded() + if err != nil { + return orderBook, err } instID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(p, @@ -430,69 +450,74 @@ func (c *COINUT) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]ex } // SubmitOrder submits a new order -func (c *COINUT) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { +func (c *COINUT) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) { var submitOrderResponse order.SubmitResponse - if err := s.Validate(); err != nil { - return submitOrderResponse, err + var err error + if _, err = strconv.Atoi(o.ClientID); err != nil { + return submitOrderResponse, fmt.Errorf("%s - ClientID must be a number, received: %s", c.Name, o.ClientID) } + err = o.Validate() - var APIresponse interface{} - isBuyOrder := s.OrderSide == order.Buy - clientIDInt, err := strconv.ParseUint(s.ClientID, 0, 32) if err != nil { return submitOrderResponse, err } - clientIDUint := uint32(clientIDInt) - - if !c.instrumentMap.IsLoaded() { - err = c.SeedInstruments() + if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + var response *WsStandardOrderResponse + response, err = c.wsSubmitOrder(&WsSubmitOrderParameters{ + Currency: o.Pair, + Side: o.OrderSide, + Amount: o.Amount, + Price: o.Price, + }) if err != nil { return submitOrderResponse, err } - } - - currencyID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(s.Pair, - asset.Spot).String()) - if currencyID == 0 { - return submitOrderResponse, errLookupInstrumentID - } - - switch s.OrderType { - case order.Limit: - APIresponse, err = c.NewOrder(currencyID, - s.Amount, - s.Price, - isBuyOrder, - clientIDUint) - case order.Market: - APIresponse, err = c.NewOrder(currencyID, - s.Amount, - 0, - isBuyOrder, - clientIDUint) - } - - switch apiResp := APIresponse.(type) { - case OrdersBase: - orderResult := apiResp - submitOrderResponse.OrderID = strconv.FormatInt(orderResult.OrderID, 10) - case OrderFilledResponse: - orderResult := apiResp - submitOrderResponse.OrderID = strconv.FormatInt(orderResult.Order.OrderID, 10) - case OrderRejectResponse: - orderResult := apiResp - submitOrderResponse.OrderID = strconv.FormatInt(orderResult.OrderID, 10) - err = fmt.Errorf("orderID: %d was rejected: %v", - orderResult.OrderID, - orderResult.Reasons) - } - - if err == nil { + submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10) submitOrderResponse.IsOrderPlaced = true - } + } else { + err = c.loadInstrumentsIfNotLoaded() + if err != nil { + return submitOrderResponse, err + } - return submitOrderResponse, err + currencyID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(o.Pair, + asset.Spot).String()) + if currencyID == 0 { + return submitOrderResponse, errLookupInstrumentID + } + + var APIResponse interface{} + var clientIDInt uint64 + isBuyOrder := o.OrderSide == order.Buy + clientIDInt, err = strconv.ParseUint(o.ClientID, 0, 32) + if err != nil { + return submitOrderResponse, err + } + clientIDUint := uint32(clientIDInt) + APIResponse, err = c.NewOrder(currencyID, o.Amount, o.Price, + isBuyOrder, clientIDUint) + if err != nil { + return submitOrderResponse, err + } + responseMap := APIResponse.(map[string]interface{}) + switch responseMap["reply"].(string) { + case "order_rejected": + return submitOrderResponse, fmt.Errorf("clientOrderID: %v was rejected: %v", o.ClientID, responseMap["reasons"]) + case "order_filled": + orderID := responseMap["order_id"].(float64) + submitOrderResponse.OrderID = strconv.FormatFloat(orderID, 'f', -1, 64) + submitOrderResponse.IsOrderPlaced = true + submitOrderResponse.FullyMatched = true + return submitOrderResponse, nil + case "order_accepted": + orderID := responseMap["order_id"].(float64) + submitOrderResponse.OrderID = strconv.FormatFloat(orderID, 'f', -1, 64) + submitOrderResponse.IsOrderPlaced = true + return submitOrderResponse, nil + } + } + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -502,76 +527,108 @@ func (c *COINUT) ModifyOrder(action *order.Modify) (string, error) { } // CancelOrder cancels an order by its corresponding ID number -func (c *COINUT) CancelOrder(order *order.Cancel) error { - orderIDInt, err := strconv.ParseInt(order.OrderID, 10, 64) +func (c *COINUT) CancelOrder(o *order.Cancel) error { + err := c.loadInstrumentsIfNotLoaded() + if err != nil { + return err + } + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } - if !c.instrumentMap.IsLoaded() { - err = c.SeedInstruments() + currencyID := c.instrumentMap.LookupID(c.FormatExchangeCurrency( + o.CurrencyPair, + asset.Spot).String(), + ) + if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + var resp *CancelOrdersResponse + resp, err = c.wsCancelOrder(&WsCancelOrderParameters{ + Currency: o.CurrencyPair, + OrderID: orderIDInt, + }) + if err != nil { + return err + } + if len(resp.Status) >= 1 && resp.Status[0] != "OK" { + return errors.New(c.Name + " - Failed to cancel order " + o.OrderID) + } + } else { + if currencyID == 0 { + return errLookupInstrumentID + } + _, err = c.CancelExistingOrder(currencyID, orderIDInt) if err != nil { return err } } - currencyID := c.instrumentMap.LookupID(c.FormatExchangeCurrency( - order.CurrencyPair, - asset.Spot).String(), - ) - if currencyID == 0 { - return errLookupInstrumentID - } - _, err = c.CancelExistingOrder(currencyID, orderIDInt) - return err + return nil } // CancelAllOrders cancels all orders associated with a currency pair -func (c *COINUT) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) { - // TODO, this is a terrible implementation. Requires DB to improve - // Coinut provides no way of retrieving orders without a currency - // So we need to retrieve all currencies, then retrieve orders for each - // currency then cancel. Advisable to never use this until DB due to - // performance. - cancelAllOrdersResponse := order.CancelAllResponse{ - Status: make(map[string]string), +func (c *COINUT) CancelAllOrders(details *order.Cancel) (order.CancelAllResponse, error) { + var cancelAllOrdersResponse order.CancelAllResponse + err := c.loadInstrumentsIfNotLoaded() + if err != nil { + return cancelAllOrdersResponse, err } - - if !c.instrumentMap.IsLoaded() { - err := c.SeedInstruments() + cancelAllOrdersResponse.Status = make(map[string]string) + if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + openOrders, err := c.wsGetOpenOrders(details.CurrencyPair.String()) if err != nil { return cancelAllOrdersResponse, err } - } - - var allTheOrders []OrderResponse - ids := c.instrumentMap.GetInstrumentIDs() - for x := range ids { - openOrders, err := c.GetOpenOrders(ids[x]) + var ordersToCancel []WsCancelOrderParameters + for i := range openOrders.Orders { + if openOrders.Orders[i].InstID == c.instrumentMap.LookupID(c.FormatExchangeCurrency(details.CurrencyPair, asset.Spot).String()) { + ordersToCancel = append(ordersToCancel, WsCancelOrderParameters{ + Currency: details.CurrencyPair, + OrderID: openOrders.Orders[i].OrderID, + }) + } + } + resp, err := c.wsCancelOrders(ordersToCancel) if err != nil { return cancelAllOrdersResponse, err } - allTheOrders = append(allTheOrders, openOrders.Orders...) - } - - var allTheOrdersToCancel []CancelOrders - for _, orderToCancel := range allTheOrders { - cancelOrder := CancelOrders{ - InstrumentID: orderToCancel.InstrumentID, - OrderID: orderToCancel.OrderID, + for i := range resp.Results { + if openOrders.Orders[i].Status[0] != "OK" { + cancelAllOrdersResponse.Status[strconv.FormatInt(openOrders.Orders[i].OrderID, 10)] = strings.Join(openOrders.Orders[i].Status, ",") + } } - allTheOrdersToCancel = append(allTheOrdersToCancel, cancelOrder) - } - - if len(allTheOrdersToCancel) > 0 { - resp, err := c.CancelOrders(allTheOrdersToCancel) - if err != nil { - return cancelAllOrdersResponse, err + } else { + var allTheOrders []OrderResponse + ids := c.instrumentMap.GetInstrumentIDs() + for x := range ids { + if ids[x] == c.instrumentMap.LookupID(c.FormatExchangeCurrency(details.CurrencyPair, asset.Spot).String()) { + openOrders, err := c.GetOpenOrders(ids[x]) + if err != nil { + return cancelAllOrdersResponse, err + } + allTheOrders = append(allTheOrders, openOrders.Orders...) + } } - for _, order := range resp.Results { - if order.Status != "OK" { - cancelAllOrdersResponse.Status[strconv.FormatInt(order.OrderID, 10)] = order.Status + var allTheOrdersToCancel []CancelOrders + for i := range allTheOrders { + cancelOrder := CancelOrders{ + InstrumentID: allTheOrders[i].InstrumentID, + OrderID: allTheOrders[i].OrderID, + } + allTheOrdersToCancel = append(allTheOrdersToCancel, cancelOrder) + } + + if len(allTheOrdersToCancel) > 0 { + resp, err := c.CancelOrders(allTheOrdersToCancel) + if err != nil { + return cancelAllOrdersResponse, err + } + + for i := range resp.Results { + if resp.Results[i].Status != "OK" { + cancelAllOrdersResponse.Status[strconv.FormatInt(resp.Results[i].OrderID, 10)] = resp.Results[i].Status + } } } } @@ -623,51 +680,81 @@ func (c *COINUT) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) // GetActiveOrders retrieves any orders that are active/open func (c *COINUT) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) { - if !c.instrumentMap.IsLoaded() { - err := c.SeedInstruments() - if err != nil { - return nil, err - } + err := c.loadInstrumentsIfNotLoaded() + if err != nil { + return nil, err } - - var instrumentsToUse []int64 - if len(req.Currencies) > 0 { - for x := range req.Currencies { - currency := c.FormatExchangeCurrency(req.Currencies[x], - asset.Spot).String() - instrumentsToUse = append(instrumentsToUse, - c.instrumentMap.LookupID(currency)) + var orders []order.Detail + var currenciesToCheck []string + if len(req.Currencies) == 0 { + for i := range req.Currencies { + currenciesToCheck = append(currenciesToCheck, c.FormatExchangeCurrency(req.Currencies[i], asset.Spot).String()) } } else { - instrumentsToUse = c.instrumentMap.GetInstrumentIDs() - } - - if len(instrumentsToUse) == 0 { - return nil, errors.New("no instrument IDs to use") - } - - var orders []order.Detail - for x := range instrumentsToUse { - openOrders, err := c.GetOpenOrders(instrumentsToUse[x]) - if err != nil { - return nil, err + for k := range c.instrumentMap.Instruments { + currenciesToCheck = append(currenciesToCheck, k) } - for y := range openOrders.Orders { - curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x]) - p := currency.NewPairFromFormattedPairs(curr, - c.GetEnabledPairs(asset.Spot), - c.GetPairFormat(asset.Spot, true)) - orderSide := order.Side(strings.ToUpper(openOrders.Orders[y].Side)) - orderDate := time.Unix(openOrders.Orders[y].Timestamp, 0) - orders = append(orders, order.Detail{ - ID: strconv.FormatInt(openOrders.Orders[y].OrderID, 10), - Amount: openOrders.Orders[y].Quantity, - Price: openOrders.Orders[y].Price, - Exchange: c.Name, - OrderSide: orderSide, - OrderDate: orderDate, - CurrencyPair: p, - }) + } + if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + for x := range currenciesToCheck { + openOrders, err := c.wsGetOpenOrders(currenciesToCheck[x]) + if err != nil { + return nil, err + } + for i := range openOrders.Orders { + orders = append(orders, order.Detail{ + Exchange: c.Name, + ID: strconv.FormatInt(openOrders.Orders[i].OrderID, 10), + CurrencyPair: c.FormatExchangeCurrency(currency.NewPairFromString(currenciesToCheck[x]), asset.Spot), + OrderSide: order.Side(openOrders.Orders[i].Side), + OrderDate: time.Unix(0, openOrders.Orders[i].Timestamp), + Status: order.Active, + Price: openOrders.Orders[i].Price, + Amount: openOrders.Orders[i].Qty, + ExecutedAmount: openOrders.Orders[i].Qty - openOrders.Orders[i].OpenQty, + RemainingAmount: openOrders.Orders[i].OpenQty, + }) + } + } + } else { + var instrumentsToUse []int64 + if len(req.Currencies) > 0 { + for x := range req.Currencies { + curr := c.FormatExchangeCurrency(req.Currencies[x], + asset.Spot).String() + instrumentsToUse = append(instrumentsToUse, + c.instrumentMap.LookupID(curr)) + } + } else { + instrumentsToUse = c.instrumentMap.GetInstrumentIDs() + } + + if len(instrumentsToUse) == 0 { + return nil, errors.New("no instrument IDs to use") + } + + for x := range instrumentsToUse { + openOrders, err := c.GetOpenOrders(instrumentsToUse[x]) + if err != nil { + return nil, err + } + for y := range openOrders.Orders { + curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x]) + p := currency.NewPairFromFormattedPairs(curr, + c.GetEnabledPairs(asset.Spot), + c.GetPairFormat(asset.Spot, true)) + orderSide := order.Side(strings.ToUpper(openOrders.Orders[y].Side)) + orderDate := time.Unix(openOrders.Orders[y].Timestamp, 0) + orders = append(orders, order.Detail{ + ID: strconv.FormatInt(openOrders.Orders[y].OrderID, 10), + Amount: openOrders.Orders[y].Quantity, + Price: openOrders.Orders[y].Price, + Exchange: c.Name, + OrderSide: orderSide, + OrderDate: orderDate, + CurrencyPair: p, + }) + } } } @@ -679,51 +766,78 @@ func (c *COINUT) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e // GetOrderHistory retrieves account order information // Can Limit response to specific order status func (c *COINUT) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error) { - if !c.instrumentMap.IsLoaded() { - err := c.SeedInstruments() - if err != nil { - return nil, err - } + err := c.loadInstrumentsIfNotLoaded() + if err != nil { + return nil, err } - - var instrumentsToUse []int64 - if len(req.Currencies) > 0 { - for x := range req.Currencies { - currency := c.FormatExchangeCurrency(req.Currencies[x], - asset.Spot).String() - instrumentsToUse = append(instrumentsToUse, - c.instrumentMap.LookupID(currency)) + var allOrders []order.Detail + if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + for i := range req.Currencies { + for j := int64(0); ; j += 100 { + trades, err := c.wsGetTradeHistory(req.Currencies[i], j, 100) + if err != nil { + return allOrders, err + } + for x := range trades.Trades { + curr := c.instrumentMap.LookupInstrument(trades.Trades[x].InstID) + allOrders = append(allOrders, order.Detail{ + Exchange: c.Name, + ID: strconv.FormatInt(trades.Trades[x].OrderID, 10), + CurrencyPair: currency.NewPairFromString(curr), + OrderSide: order.Side(trades.Trades[x].Side), + OrderDate: time.Unix(0, trades.Trades[x].Timestamp), + Status: order.Filled, + Price: trades.Trades[x].Price, + Amount: trades.Trades[x].Qty, + ExecutedAmount: trades.Trades[x].Qty, + RemainingAmount: trades.Trades[x].OpenQty, + }) + } + if len(trades.Trades) < 100 { + break + } + } } } else { - instrumentsToUse = c.instrumentMap.GetInstrumentIDs() - } - - if len(instrumentsToUse) == 0 { - return nil, errors.New("no instrument IDs to use") - } - - var allOrders []order.Detail - for x := range instrumentsToUse { - orders, err := c.GetTradeHistory(instrumentsToUse[x], -1, -1) - if err != nil { - return nil, err + var instrumentsToUse []int64 + if len(req.Currencies) > 0 { + for x := range req.Currencies { + curr := c.FormatExchangeCurrency(req.Currencies[x], + asset.Spot).String() + instrumentID := c.instrumentMap.LookupID(curr) + if instrumentID > 0 { + instrumentsToUse = append(instrumentsToUse, instrumentID) + } + } + } else { + instrumentsToUse = c.instrumentMap.GetInstrumentIDs() } - for y := range orders.Trades { - curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x]) - p := currency.NewPairFromFormattedPairs(curr, - c.GetEnabledPairs(asset.Spot), - c.GetPairFormat(asset.Spot, true)) - orderSide := order.Side(strings.ToUpper(orders.Trades[y].Order.Side)) - orderDate := time.Unix(orders.Trades[y].Order.Timestamp, 0) - allOrders = append(allOrders, order.Detail{ - ID: strconv.FormatInt(orders.Trades[y].Order.OrderID, 10), - Amount: orders.Trades[y].Order.Quantity, - Price: orders.Trades[y].Order.Price, - Exchange: c.Name, - OrderSide: orderSide, - OrderDate: orderDate, - CurrencyPair: p, - }) + + if len(instrumentsToUse) == 0 { + return nil, errors.New("no instrument IDs to use") + } + for x := range instrumentsToUse { + orders, err := c.GetTradeHistory(instrumentsToUse[x], -1, -1) + if err != nil { + return nil, err + } + for y := range orders.Trades { + curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x]) + p := currency.NewPairFromFormattedPairs(curr, + c.GetEnabledPairs(asset.Spot), + c.GetPairFormat(asset.Spot, true)) + orderSide := order.Side(strings.ToUpper(orders.Trades[y].Order.Side)) + orderDate := time.Unix(orders.Trades[y].Order.Timestamp, 0) + allOrders = append(allOrders, order.Detail{ + ID: strconv.FormatInt(orders.Trades[y].Order.OrderID, 10), + Amount: orders.Trades[y].Order.Quantity, + Price: orders.Trades[y].Order.Price, + Exchange: c.Name, + OrderSide: orderSide, + OrderDate: orderDate, + CurrencyPair: p, + }) + } } } @@ -755,3 +869,20 @@ func (c *COINUT) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, e func (c *COINUT) AuthenticateWebsocket() error { return c.wsAuthenticate() } + +func (c *COINUT) loadInstrumentsIfNotLoaded() error { + if !c.instrumentMap.IsLoaded() { + if c.Websocket.IsConnected() { + _, err := c.WsGetInstruments() + if err != nil { + return err + } + } else { + err := c.SeedInstruments() + if err != nil { + return err + } + } + } + return nil +} diff --git a/exchanges/exchange_types.go b/exchanges/exchange_types.go index 8b5e8839..804f7921 100644 --- a/exchanges/exchange_types.go +++ b/exchanges/exchange_types.go @@ -14,27 +14,8 @@ import ( const ( RestAuthentication uint8 = 0 WebsocketAuthentication uint8 = 1 -) - -// FeeType custom type for calculating fees based on method -type FeeType uint8 - -// Const declarations for fee types -const ( - BankFee FeeType = iota - InternationalBankDepositFee - InternationalBankWithdrawalFee - CryptocurrencyTradeFee - CyptocurrencyDepositFee - CryptocurrencyWithdrawalFee - OfflineTradeFee -) - -// InternationalBankTransactionType custom type for calculating fees based on fiat transaction types -type InternationalBankTransactionType uint8 - -// Const declarations for international transaction types -const ( + // Repeated exchange strings + // FeeType custom type for calculating fees based on method WireTransfer InternationalBankTransactionType = iota PerfectMoney Neteller @@ -53,26 +34,15 @@ const ( WesternUnion MoneyGram Contact -) - -// FeeBuilder is the type which holds all parameters required to calculate a fee -// for an exchange -type FeeBuilder struct { - FeeType FeeType - // Used for calculating crypto trading fees, deposits & withdrawals - Pair currency.Pair - IsMaker bool - // Fiat currency used for bank deposits & withdrawals - FiatCurrency currency.Code - BankTransactionType InternationalBankTransactionType - // Used to multiply for fee calculations - PurchasePrice float64 - Amount float64 -} - -// Definitions for each type of withdrawal method for a given exchange -const ( - // No withdraw + // Const declarations for fee types + BankFee FeeType = iota + InternationalBankDepositFee + InternationalBankWithdrawalFee + CryptocurrencyTradeFee + CyptocurrencyDepositFee + CryptocurrencyWithdrawalFee + OfflineTradeFee + // Definitions for each type of withdrawal method for a given exchange NoAPIWithdrawalMethods uint32 = 0 NoAPIWithdrawalMethodsText string = "NONE, WEBSITE ONLY" AutoWithdrawCrypto uint32 = (1 << 0) @@ -113,10 +83,29 @@ const ( WithdrawFiatViaWebsiteOnlyText string = "WITHDRAW FIAT VIA WEBSITE ONLY" NoFiatWithdrawals uint32 = (1 << 18) NoFiatWithdrawalsText string = "NO FIAT WITHDRAWAL" - - UnknownWithdrawalTypeText string = "UNKNOWN" + UnknownWithdrawalTypeText string = "UNKNOWN" ) +type FeeType uint8 + +// InternationalBankTransactionType custom type for calculating fees based on fiat transaction types +type InternationalBankTransactionType uint8 + +// FeeBuilder is the type which holds all parameters required to calculate a fee +// for an exchange +type FeeBuilder struct { + FeeType FeeType + // Used for calculating crypto trading fees, deposits & withdrawals + Pair currency.Pair + IsMaker bool + // Fiat currency used for bank deposits & withdrawals + FiatCurrency currency.Code + BankTransactionType InternationalBankTransactionType + // Used to multiply for fee calculations + PurchasePrice float64 + Amount float64 +} + // AccountInfo is a Generic type to hold each exchange's holdings in // all enabled currencies type AccountInfo struct { diff --git a/exchanges/exmo/exmo_test.go b/exchanges/exmo/exmo_test.go index 0d009164..2a5fa1d9 100644 --- a/exchanges/exmo/exmo_test.go +++ b/exchanges/exmo/exmo_test.go @@ -2,6 +2,7 @@ package exmo import ( "log" + "os" "testing" "github.com/thrasher-corp/gocryptotrader/common" @@ -21,11 +22,8 @@ var ( e EXMO ) -func TestDefault(t *testing.T) { +func TestMain(m *testing.M) { e.SetDefaults() -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { @@ -33,17 +31,19 @@ func TestSetup(t *testing.T) { } exmoConf, err := cfg.GetExchangeConfig("EXMO") if err != nil { - t.Error("Exmo Setup() init error") + log.Fatal("Exmo Setup() init error") } err = e.Setup(exmoConf) if err != nil { - t.Fatal("Exmo setup error", err) + log.Fatal("Exmo setup error", err) } e.API.AuthenticatedSupport = true e.API.Credentials.Key = APIKey e.API.Credentials.Secret = APISecret + + os.Exit(m.Run()) } func TestGetTrades(t *testing.T) { @@ -88,10 +88,9 @@ func TestGetCurrency(t *testing.T) { func TestGetUserInfo(t *testing.T) { t.Parallel() - if APIKey == "" || APISecret == "" { + if !areTestAPIKeysSet() { t.Skip() } - TestSetup(t) _, err := e.GetUserInfo() if err != nil { t.Errorf("Err: %s", err) @@ -100,10 +99,9 @@ func TestGetUserInfo(t *testing.T) { func TestGetRequiredAmount(t *testing.T) { t.Parallel() - if APIKey == "" || APISecret == "" { + if !areTestAPIKeysSet() { t.Skip() } - TestSetup(t) _, err := e.GetRequiredAmount("BTC_USD", 100) if err != nil { t.Errorf("Err: %s", err) @@ -125,7 +123,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() e.GetFeeByType(feeBuilder) - if APIKey == "" || APISecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -137,8 +135,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - e.SetDefaults() - TestSetup(t) t.Parallel() var feeBuilder = setFeeBuilder() @@ -255,20 +251,14 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - e.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.NoFiatWithdrawalsText - withdrawPermissions := e.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - e.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -282,9 +272,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - e.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -307,8 +294,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - e.SetDefaults() - TestSetup(t) if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -334,14 +319,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - e.SetDefaults() - TestSetup(t) if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -359,9 +341,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - e.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -389,6 +368,9 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := e.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -396,8 +378,10 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - e.SetDefaults() - TestSetup(t) + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } + withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -407,10 +391,6 @@ func TestWithdraw(t *testing.T) { Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", } - if areTestAPIKeysSet() && !canManipulateRealOrders { - t.Skip("API keys set, canManipulateRealOrders false, skipping test") - } - _, err := e.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -421,9 +401,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - e.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -436,9 +413,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - e.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index e228fc7a..374cbf81 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -238,10 +238,10 @@ func (e *EXMO) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook if err != nil { return orderBook, err } - - for _, x := range e.GetEnabledPairs(assetType) { - currency := e.FormatExchangeCurrency(x, assetType) - data, ok := result[currency.String()] + enabledPairs := e.GetEnabledPairs(assetType) + for i := range enabledPairs { + curr := e.FormatExchangeCurrency(enabledPairs[i], assetType) + data, ok := result[curr.String()] if !ok { continue } @@ -266,7 +266,7 @@ func (e *EXMO) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook } orderBook.Bids = obItems - orderBook.Pair = x + orderBook.Pair = enabledPairs[i] orderBook.ExchangeName = e.Name orderBook.AssetType = assetType @@ -344,15 +344,18 @@ func (e *EXMO) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { oT, s.Price, s.Amount) + if err != nil { + return submitOrderResponse, err + } if response > 0 { submitOrderResponse.OrderID = strconv.FormatInt(response, 10) } - if err == nil { - submitOrderResponse.IsOrderPlaced = true + submitOrderResponse.IsOrderPlaced = true + if s.OrderType == order.Market { + submitOrderResponse.FullyMatched = true } - - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -382,10 +385,10 @@ func (e *EXMO) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) return cancelAllOrdersResponse, err } - for _, order := range openOrders { - err = e.CancelExistingOrder(order.OrderID) + for i := range openOrders { + err = e.CancelExistingOrder(openOrders[i].OrderID) if err != nil { - cancelAllOrdersResponse.Status[strconv.FormatInt(order.OrderID, 10)] = err.Error() + cancelAllOrdersResponse.Status[strconv.FormatInt(openOrders[i].OrderID, 10)] = err.Error() } } @@ -486,13 +489,13 @@ func (e *EXMO) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, err } var allTrades []UserTrades - for _, currency := range req.Currencies { - resp, err := e.GetUserTrades(e.FormatExchangeCurrency(currency, asset.Spot).String(), "", "10000") + for i := range req.Currencies { + resp, err := e.GetUserTrades(e.FormatExchangeCurrency(req.Currencies[i], asset.Spot).String(), "", "10000") if err != nil { return nil, err } - for _, order := range resp { - allTrades = append(allTrades, order...) + for j := range resp { + allTrades = append(allTrades, resp[j]...) } } diff --git a/exchanges/gateio/gateio_test.go b/exchanges/gateio/gateio_test.go index 7437e6da..cfa6cf2f 100644 --- a/exchanges/gateio/gateio_test.go +++ b/exchanges/gateio/gateio_test.go @@ -1,7 +1,9 @@ package gateio import ( + "log" "net/http" + "os" "testing" "github.com/gorilla/websocket" @@ -25,29 +27,28 @@ const ( var g Gateio var wsSetupRan bool -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { g.SetDefaults() -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("GateIO load config error", err) + log.Fatal("GateIO load config error", err) } - gateioConfig, err := cfg.GetExchangeConfig("GateIO") + gConf, err := cfg.GetExchangeConfig("GateIO") if err != nil { - t.Error("GateIO Setup() init error") + log.Fatal("GateIO Setup() init error") } - gateioConfig.API.AuthenticatedSupport = true - gateioConfig.API.AuthenticatedWebsocketSupport = true - gateioConfig.API.Credentials.Key = apiKey - gateioConfig.API.Credentials.Secret = apiSecret + gConf.API.AuthenticatedSupport = true + gConf.API.AuthenticatedWebsocketSupport = true + gConf.API.Credentials.Key = apiKey + gConf.API.Credentials.Secret = apiSecret - err = g.Setup(gateioConfig) + err = g.Setup(gConf) if err != nil { - t.Fatal("GateIO setup error", err) + log.Fatal("GateIO setup error", err) } + + os.Exit(m.Run()) } func TestGetSymbols(t *testing.T) { @@ -100,7 +101,7 @@ func TestCancelExistingOrder(t *testing.T) { func TestGetBalances(t *testing.T) { t.Parallel() - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { t.Skip() } @@ -173,7 +174,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() g.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -185,9 +186,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - g.SetDefaults() - TestSetup(t) - var feeBuilder = setFeeBuilder() if areTestAPIKeysSet() { // CryptocurrencyTradeFee Basic @@ -265,20 +263,14 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - g.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText - withdrawPermissions := g.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - g.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -292,9 +284,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - g.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -318,9 +307,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - g.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip() } @@ -346,15 +332,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - g.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip() } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -372,15 +354,11 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - g.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip() } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -417,6 +395,9 @@ func TestGetAccountInfo(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := g.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -424,8 +405,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - g.SetDefaults() - TestSetup(t) withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -449,9 +428,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - g.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -464,9 +440,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - g.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -492,9 +465,6 @@ func TestGetDepositAddress(t *testing.T) { } } func TestGetOrderInfo(t *testing.T) { - g.SetDefaults() - TestSetup(t) - if !areTestAPIKeysSet() { t.Skip("no API keys set skipping test") } @@ -509,8 +479,6 @@ func TestGetOrderInfo(t *testing.T) { // TestWsGetBalance dials websocket, sends balance request. func TestWsGetBalance(t *testing.T) { - g.SetDefaults() - TestSetup(t) if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(wshandler.WebsocketNotEnabled) } @@ -541,12 +509,14 @@ func TestWsGetBalance(t *testing.T) { if err != nil { t.Error(err) } + _, err = g.wsGetBalance([]string{}) + if err != nil { + t.Error(err) + } } // TestWsGetOrderInfo dials websocket, sends order info request. func TestWsGetOrderInfo(t *testing.T) { - g.SetDefaults() - TestSetup(t) if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(wshandler.WebsocketNotEnabled) } @@ -573,7 +543,7 @@ func TestWsGetOrderInfo(t *testing.T) { if resp.Result.Status != "success" { t.Fatal("Unsuccessful login") } - _, err = g.wsGetOrderInfo("EOS_USDT", 0, 10) + _, err = g.wsGetOrderInfo("EOS_USDT", 0, 1000) if err != nil { t.Error(err) } @@ -583,8 +553,6 @@ func setupWSTestAuth(t *testing.T) { if wsSetupRan { return } - g.SetDefaults() - TestSetup(t) if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport { t.Skip(wshandler.WebsocketNotEnabled) } diff --git a/exchanges/gateio/gateio_types.go b/exchanges/gateio/gateio_types.go index 8336d8e5..283cfbf4 100644 --- a/exchanges/gateio/gateio_types.go +++ b/exchanges/gateio/gateio_types.go @@ -445,19 +445,19 @@ type WebSocketOrderQueryResult struct { // WebSocketOrderQueryRecords contains order information from a order.query websocket request type WebSocketOrderQueryRecords struct { - ID int `json:"id"` + ID int64 `json:"id"` Market string `json:"market"` - User int `json:"user"` + User int64 `json:"user"` Ctime float64 `json:"ctime"` Mtime float64 `json:"mtime"` - Price string `json:"price"` - Amount string `json:"amount"` - Left string `json:"left"` - DealFee string `json:"dealFee"` - OrderType int `json:"orderType"` - Type int `json:"type"` - FilledAmount string `json:"filledAmount"` - FilledTotal string `json:"filledTotal"` + Price float64 `json:"price,string"` + Amount float64 `json:"amount,string"` + Left float64 `json:"left,string"` + DealFee float64 `json:"dealFee,string"` + OrderType int64 `json:"orderType"` + Type int64 `json:"type"` + FilledAmount float64 `json:"filledAmount,string"` + FilledTotal float64 `json:"filledTotal,string"` } // WebsocketAuthenticationResponse contains the result of a login request @@ -473,14 +473,14 @@ type WebsocketAuthenticationResponse struct { type wsGetBalanceRequest struct { ID int64 `json:"id"` Method string `json:"method"` - Params []string `json:"params,omitempty"` + Params []string `json:"params"` } // WsGetBalanceResponse stores WS GetBalance response type WsGetBalanceResponse struct { - Error interface{} `json:"error"` - Result map[currency.Code]WsGetBalanceResponseData `json:"result,omitempty"` - ID int64 `json:"id"` + Error interface{} `json:"error"` + Result map[string]WsGetBalanceResponseData `json:"result"` + ID int64 `json:"id"` } // WsGetBalanceResponseData contains currency data diff --git a/exchanges/gateio/gateio_websocket.go b/exchanges/gateio/gateio_websocket.go index 04545179..8959f1f1 100644 --- a/exchanges/gateio/gateio_websocket.go +++ b/exchanges/gateio/gateio_websocket.go @@ -40,6 +40,7 @@ func (g *Gateio) WsConnect() error { _, err = g.wsServerSignIn() if err != nil { log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", g.Name, err) + g.Websocket.SetCanUseAuthenticatedEndpoints(false) } g.GenerateAuthenticatedSubscriptions() g.GenerateDefaultSubscriptions() @@ -416,7 +417,7 @@ func (g *Gateio) wsGetOrderInfo(market string, offset, limit int) (*WebSocketOrd if !g.Websocket.CanUseAuthenticatedEndpoints() { return nil, fmt.Errorf("%v not authorised to get order info", g.Name) } - order := WebsocketRequest{ + ord := WebsocketRequest{ ID: g.WebsocketConn.GenerateMessageID(true), Method: "order.query", Params: []interface{}{ @@ -425,7 +426,7 @@ func (g *Gateio) wsGetOrderInfo(market string, offset, limit int) (*WebSocketOrd limit, }, } - resp, err := g.WebsocketConn.SendMessageReturnResponse(order.ID, order) + resp, err := g.WebsocketConn.SendMessageReturnResponse(ord.ID, ord) if err != nil { return nil, err } diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index 91970c94..5d0d8384 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -9,6 +9,7 @@ import ( "time" "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" @@ -99,6 +100,8 @@ func (g *Gateio) SetDefaults() { Unsubscribe: true, AuthenticatedEndpoints: true, MessageCorrelation: true, + GetOrder: true, + AccountBalance: true, }, WithdrawPermissions: exchange.AutoWithdrawCrypto | exchange.NoFiatWithdrawals, @@ -269,9 +272,9 @@ func (g *Gateio) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderboo // UpdateOrderbook updates and returns the orderbook for a currency pair func (g *Gateio) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) { var orderBook orderbook.Base - currency := g.FormatExchangeCurrency(p, assetType).String() + curr := g.FormatExchangeCurrency(p, assetType).String() - orderbookNew, err := g.GetOrderbook(currency) + orderbookNew, err := g.GetOrderbook(curr) if err != nil { return orderBook, err } @@ -306,61 +309,78 @@ func (g *Gateio) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbo // ZB exchange func (g *Gateio) GetAccountInfo() (exchange.AccountInfo, error) { var info exchange.AccountInfo - - balance, err := g.GetBalances() - if err != nil { - return info, err - } - var balances []exchange.AccountCurrencyInfo - switch l := balance.Locked.(type) { - case map[string]interface{}: - for x := range l { - lockedF, err := strconv.ParseFloat(l[x].(string), 64) - if err != nil { - return info, err - } - - balances = append(balances, exchange.AccountCurrencyInfo{ - CurrencyName: currency.NewCode(x), - Hold: lockedF, + if g.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + resp, err := g.wsGetBalance([]string{}) + if err != nil { + return info, err + } + var currData []exchange.AccountCurrencyInfo + for k := range resp.Result { + currData = append(currData, exchange.AccountCurrencyInfo{ + CurrencyName: currency.NewCode(k), + TotalValue: resp.Result[k].Available + resp.Result[k].Freeze, + Hold: resp.Result[k].Freeze, }) } - default: - break - } + info.Accounts = append(info.Accounts, exchange.Account{ + Currencies: currData, + }) + } else { + balance, err := g.GetBalances() + if err != nil { + return info, err + } - switch v := balance.Available.(type) { - case map[string]interface{}: - for x := range v { - availAmount, err := strconv.ParseFloat(v[x].(string), 64) - if err != nil { - return info, err - } - - var updated bool - for i := range balances { - if balances[i].CurrencyName == currency.NewCode(x) { - balances[i].TotalValue = balances[i].Hold + availAmount - updated = true - break + switch l := balance.Locked.(type) { + case map[string]interface{}: + for x := range l { + lockedF, err := strconv.ParseFloat(l[x].(string), 64) + if err != nil { + return info, err } - } - if !updated { + balances = append(balances, exchange.AccountCurrencyInfo{ CurrencyName: currency.NewCode(x), - TotalValue: availAmount, + Hold: lockedF, }) } + default: + break } - default: - break - } - info.Accounts = append(info.Accounts, exchange.Account{ - Currencies: balances, - }) + switch v := balance.Available.(type) { + case map[string]interface{}: + for x := range v { + availAmount, err := strconv.ParseFloat(v[x].(string), 64) + if err != nil { + return info, err + } + + var updated bool + for i := range balances { + if balances[i].CurrencyName == currency.NewCode(x) { + balances[i].TotalValue = balances[i].Hold + availAmount + updated = true + break + } + } + if !updated { + balances = append(balances, exchange.AccountCurrencyInfo{ + CurrencyName: currency.NewCode(x), + TotalValue: availAmount, + }) + } + } + default: + break + } + + info.Accounts = append(info.Accounts, exchange.Account{ + Currencies: balances, + }) + } info.Exchange = g.Name @@ -401,15 +421,18 @@ func (g *Gateio) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { } response, err := g.SpotNewOrder(spotNewOrderRequestParams) + if err != nil { + return submitOrderResponse, err + } if response.OrderNumber > 0 { submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10) } - - if err == nil { - submitOrderResponse.IsOrderPlaced = true + if response.LeftAmount == 0 { + submitOrderResponse.FullyMatched = true } + submitOrderResponse.IsOrderPlaced = true - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -457,7 +480,6 @@ func (g *Gateio) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, erro // GetOrderInfo returns information on a current open order func (g *Gateio) GetOrderInfo(orderID string) (order.Detail, error) { var orderDetail order.Detail - orders, err := g.GetOpenOrders("") if err != nil { return orderDetail, errors.New("failed to get open orders") @@ -534,40 +556,79 @@ func (g *Gateio) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) // GetActiveOrders retrieves any orders that are active/open func (g *Gateio) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) { + var orders []order.Detail var currPair string if len(req.Currencies) == 1 { currPair = req.Currencies[0].String() } + if g.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + for i := 0; ; i += 100 { + resp, err := g.wsGetOrderInfo(req.OrderType.String(), i, 100) + if err != nil { + return orders, err + } - resp, err := g.GetOpenOrders(currPair) - if err != nil { - return nil, err - } - - var orders []order.Detail - for i := range resp.Orders { - if resp.Orders[i].Status != "open" { - continue + for j := range resp.WebSocketOrderQueryRecords { + orderSide := order.Buy + if resp.WebSocketOrderQueryRecords[j].Type == 1 { + orderSide = order.Sell + } + orderType := order.Market + if resp.WebSocketOrderQueryRecords[j].OrderType == 1 { + orderType = order.Limit + } + firstNum, decNum, err := convert.SplitFloatDecimals(resp.WebSocketOrderQueryRecords[j].Ctime) + if err != nil { + return orders, err + } + orderDate := time.Unix(firstNum, decNum) + orders = append(orders, order.Detail{ + Exchange: g.Name, + AccountID: strconv.FormatInt(resp.WebSocketOrderQueryRecords[j].User, 10), + ID: strconv.FormatInt(resp.WebSocketOrderQueryRecords[j].ID, 10), + CurrencyPair: currency.NewPairFromString(resp.WebSocketOrderQueryRecords[j].Market), + OrderSide: orderSide, + OrderType: orderType, + OrderDate: orderDate, + Price: resp.WebSocketOrderQueryRecords[j].Price, + Amount: resp.WebSocketOrderQueryRecords[j].Amount, + ExecutedAmount: resp.WebSocketOrderQueryRecords[j].FilledAmount, + RemainingAmount: resp.WebSocketOrderQueryRecords[j].Left, + Fee: resp.WebSocketOrderQueryRecords[j].DealFee, + }) + } + if len(resp.WebSocketOrderQueryRecords) < 100 { + break + } + } + } else { + resp, err := g.GetOpenOrders(currPair) + if err != nil { + return nil, err } - symbol := currency.NewPairDelimiter(resp.Orders[i].CurrencyPair, - g.GetPairFormat(asset.Spot, false).Delimiter) - side := order.Side(strings.ToUpper(resp.Orders[i].Type)) - orderDate := time.Unix(resp.Orders[i].Timestamp, 0) + for i := range resp.Orders { + if resp.Orders[i].Status != "open" { + continue + } - orders = append(orders, order.Detail{ - ID: resp.Orders[i].OrderNumber, - Amount: resp.Orders[i].Amount, - Price: resp.Orders[i].Rate, - RemainingAmount: resp.Orders[i].FilledAmount, - OrderDate: orderDate, - OrderSide: side, - Exchange: g.Name, - CurrencyPair: symbol, - Status: order.Status(resp.Orders[i].Status), - }) + symbol := currency.NewPairDelimiter(resp.Orders[i].CurrencyPair, + g.GetPairFormat(asset.Spot, false).Delimiter) + side := order.Side(strings.ToUpper(resp.Orders[i].Type)) + orderDate := time.Unix(resp.Orders[i].Timestamp, 0) + orders = append(orders, order.Detail{ + ID: resp.Orders[i].OrderNumber, + Amount: resp.Orders[i].Amount, + Price: resp.Orders[i].Rate, + RemainingAmount: resp.Orders[i].FilledAmount, + OrderDate: orderDate, + OrderSide: side, + Exchange: g.Name, + CurrencyPair: symbol, + Status: order.Status(resp.Orders[i].Status), + }) + } } - order.FilterOrdersByTickRange(&orders, req.StartTicks, req.EndTicks) order.FilterOrdersBySide(&orders, req.OrderSide) return orders, nil @@ -577,8 +638,8 @@ func (g *Gateio) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e // Can Limit response to specific order status func (g *Gateio) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error) { var trades []TradesResponse - for _, currency := range req.Currencies { - resp, err := g.GetTradeHistory(currency.String()) + for i := range req.Currencies { + resp, err := g.GetTradeHistory(req.Currencies[i].String()) if err != nil { return nil, err } diff --git a/exchanges/gemini/gemini_test.go b/exchanges/gemini/gemini_test.go index dec36bab..02704567 100644 --- a/exchanges/gemini/gemini_test.go +++ b/exchanges/gemini/gemini_test.go @@ -336,9 +336,7 @@ func TestFormatWithdrawPermissions(t *testing.T) { exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - withdrawPermissions := g.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, @@ -425,7 +423,6 @@ func TestCancelExchangeOrder(t *testing.T) { if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var orderCancellation = &order.Cancel{ OrderID: "266029865", } @@ -448,7 +445,6 @@ func TestCancelAllExchangeOrders(t *testing.T) { } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", diff --git a/exchanges/gemini/gemini_websocket.go b/exchanges/gemini/gemini_websocket.go index 9815ab36..36bf2cb2 100644 --- a/exchanges/gemini/gemini_websocket.go +++ b/exchanges/gemini/gemini_websocket.go @@ -32,7 +32,7 @@ const ( ) // Instantiates a communications channel between websocket connections -var comms = make(chan ReadData, 1) +var comms = make(chan ReadData) var responseMaxLimit time.Duration var responseCheckTimeout time.Duration @@ -62,13 +62,13 @@ func (g *Gemini) WsConnect() error { // WsSubscribe subscribes to the full websocket suite on gemini exchange func (g *Gemini) WsSubscribe(dialer *websocket.Dialer) error { enabledCurrencies := g.GetEnabledPairs(asset.Spot) - for i, c := range enabledCurrencies { + for i := range enabledCurrencies { val := url.Values{} val.Set("heartbeat", "true") endpoint := fmt.Sprintf("%s%s/%s?%s", g.API.Endpoints.WebsocketURL, geminiWsMarketData, - c.String(), + enabledCurrencies[i].String(), val.Encode()) connection := &wshandler.WebsocketConnection{ ExchangeName: g.Name, @@ -82,7 +82,7 @@ func (g *Gemini) WsSubscribe(dialer *websocket.Dialer) error { return fmt.Errorf("%v Websocket connection %v error. Error %v", g.Name, endpoint, err) } - go g.WsReadData(connection, c) + go g.WsReadData(connection, enabledCurrencies[i]) if len(enabledCurrencies)-1 == i { return nil } diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 16e66d45..d730b463 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -333,13 +333,16 @@ func (g *Gemini) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { s.Price, s.OrderSide.String(), "exchange limit") + if err != nil { + return submitOrderResponse, err + } if response > 0 { submitOrderResponse.OrderID = strconv.FormatInt(response, 10) } - if err == nil { - submitOrderResponse.IsOrderPlaced = true - } - return submitOrderResponse, err + + submitOrderResponse.IsOrderPlaced = true + + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -369,8 +372,8 @@ func (g *Gemini) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, erro return cancelAllOrdersResponse, err } - for _, order := range resp.Details.CancelRejects { - cancelAllOrdersResponse.Status[order] = "Could not cancel order" + for i := range resp.Details.CancelRejects { + cancelAllOrdersResponse.Status[resp.Details.CancelRejects[i]] = "Could not cancel order" } return cancelAllOrdersResponse, nil diff --git a/exchanges/hitbtc/hitbtc.go b/exchanges/hitbtc/hitbtc.go index f6145843..63649393 100644 --- a/exchanges/hitbtc/hitbtc.go +++ b/exchanges/hitbtc/hitbtc.go @@ -315,7 +315,7 @@ func (h *HitBTC) GetOpenOrders(currency string) ([]OrderHistoryResponse, error) // PlaceOrder places an order on the exchange func (h *HitBTC) PlaceOrder(currency string, rate, amount float64, orderType, side string) (OrderResponse, error) { - result := OrderResponse{} + var result OrderResponse values := url.Values{} values.Set("symbol", currency) diff --git a/exchanges/hitbtc/hitbtc_test.go b/exchanges/hitbtc/hitbtc_test.go index 976de532..208d5fdc 100644 --- a/exchanges/hitbtc/hitbtc_test.go +++ b/exchanges/hitbtc/hitbtc_test.go @@ -1,7 +1,9 @@ package hitbtc import ( + "log" "net/http" + "os" "testing" "time" @@ -26,19 +28,16 @@ const ( canManipulateRealOrders = false ) -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { h.SetDefaults() -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("HitBTC load config error", err) + log.Fatal("HitBTC load config error", err) } hitbtcConfig, err := cfg.GetExchangeConfig("HitBTC") if err != nil { - t.Error("HitBTC Setup() init error") + log.Fatal("HitBTC Setup() init error") } hitbtcConfig.API.AuthenticatedSupport = true hitbtcConfig.API.AuthenticatedWebsocketSupport = true @@ -47,8 +46,10 @@ func TestSetup(t *testing.T) { err = h.Setup(hitbtcConfig) if err != nil { - t.Fatal("HitBTC setup error", err) + log.Fatal("HitBTC setup error", err) } + + os.Exit(m.Run()) } func TestGetOrderbook(t *testing.T) { @@ -94,7 +95,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() h.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -106,9 +107,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestUpdateTicker(t *testing.T) { - h.SetDefaults() - TestSetup(t) - h.CurrencyPairs.StorePairs(asset.Spot, currency.NewPairsFromStrings([]string{"BTC-USD", "XRP-USD"}), true) _, err := h.UpdateTicker(currency.NewPair(currency.BTC, currency.USD), asset.Spot) if err != nil { @@ -136,9 +134,6 @@ func TestGetSingularTicker(t *testing.T) { } func TestGetFee(t *testing.T) { - h.SetDefaults() - TestSetup(t) - var feeBuilder = setFeeBuilder() if areTestAPIKeysSet() { // CryptocurrencyTradeFee Basic @@ -219,20 +214,14 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - h.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText - withdrawPermissions := h.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - h.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.ETH, currency.BTC)}, @@ -247,9 +236,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - h.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.ETH, currency.BTC)}, @@ -270,9 +256,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - h.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -297,15 +280,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - h.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -323,15 +302,11 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - h.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -354,6 +329,9 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := h.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -361,8 +339,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - h.SetDefaults() - TestSetup(t) withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -386,9 +362,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - h.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -401,9 +374,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - h.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -432,8 +402,6 @@ func setupWsAuth(t *testing.T) { if wsSetupRan { return } - TestSetDefaults(t) - TestSetup(t) if !h.Websocket.IsEnabled() && !h.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(wshandler.WebsocketNotEnabled) } diff --git a/exchanges/hitbtc/hitbtc_websocket.go b/exchanges/hitbtc/hitbtc_websocket.go index e77522ff..aa24a6dc 100644 --- a/exchanges/hitbtc/hitbtc_websocket.go +++ b/exchanges/hitbtc/hitbtc_websocket.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net/http" + "strconv" "strings" "time" @@ -395,7 +396,7 @@ func (h *HitBTC) wsLogin() error { return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", h.Name) } h.Websocket.SetCanUseAuthenticatedEndpoints(true) - nonce := fmt.Sprintf("%v", time.Now().Unix()) + nonce := strconv.FormatInt(time.Now().Unix(), 10) hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(nonce), []byte(h.API.Credentials.Secret)) request := WsLoginRequest{ Method: "login", @@ -483,7 +484,7 @@ func (h *HitBTC) wsReplaceOrder(clientOrderID string, quantity, price float64) ( Method: "cancelReplaceOrder", Params: WsReplaceOrderRequestData{ ClientOrderID: clientOrderID, - RequestClientID: fmt.Sprintf("%v", time.Now().Unix()), + RequestClientID: strconv.FormatInt(time.Now().Unix(), 10), Quantity: quantity, Price: price, }, diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index bd823364..c1cc92b6 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -373,24 +373,42 @@ func (h *HitBTC) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]ex } // SubmitOrder submits a new order -func (h *HitBTC) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { +func (h *HitBTC) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) { var submitOrderResponse order.SubmitResponse - if err := s.Validate(); err != nil { + err := o.Validate() + if err != nil { return submitOrderResponse, err } + if h.Websocket.IsConnected() && h.Websocket.CanUseAuthenticatedEndpoints() { + var response *WsSubmitOrderSuccessResponse + response, err = h.wsPlaceOrder(o.Pair, o.OrderSide.String(), o.Amount, o.Price) + if err != nil { + return submitOrderResponse, err + } + submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10) + if response.Result.CumQuantity == o.Amount { + submitOrderResponse.FullyMatched = true + } + } else { + var response OrderResponse + response, err = h.PlaceOrder(o.Pair.String(), + o.Price, + o.Amount, + strings.ToLower(o.OrderType.String()), + strings.ToLower(o.OrderSide.String())) + if err != nil { + return submitOrderResponse, err + } + if response.OrderNumber > 0 { + submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10) + } + if o.OrderType == order.Market { + submitOrderResponse.FullyMatched = true + } + } + submitOrderResponse.IsOrderPlaced = true - response, err := h.PlaceOrder(s.Pair.String(), - s.Price, - s.Amount, - s.OrderType.Lower(), - s.OrderSide.Lower()) - if response.OrderNumber > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10) - } - if err == nil { - submitOrderResponse.IsOrderPlaced = true - } - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/huobi/huobi.go b/exchanges/huobi/huobi.go index f01482ea..22121506 100644 --- a/exchanges/huobi/huobi.go +++ b/exchanges/huobi/huobi.go @@ -44,7 +44,7 @@ const ( huobiOrderCancel = "order/orders/%s/submitcancel" huobiOrderCancelBatch = "order/orders/batchcancel" huobiBatchCancelOpenOrders = "order/orders/batchCancelOpenOrders" - huobiGetOrder = "order/orders/%s" + huobiGetOrder = "order/orders/getClientOrder" huobiGetOrderMatch = "order/orders/%s/matchresults" huobiGetOrders = "order/orders" huobiGetOpenOrders = "order/openOrders" @@ -443,8 +443,9 @@ func (h *HUOBI) GetOrder(orderID int64) (OrderInfo, error) { } var result response - endpoint := fmt.Sprintf(huobiGetOrder, strconv.FormatInt(orderID, 10)) - err := h.SendAuthenticatedHTTPRequest(http.MethodGet, endpoint, url.Values{}, nil, &result) + urlVal := url.Values{} + urlVal.Set("clientOrderId", strconv.FormatInt(orderID, 10)) + err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiGetOrder, urlVal, nil, &result) if result.ErrorMessage != "" { return result.Order, errors.New(result.ErrorMessage) @@ -514,7 +515,7 @@ func (h *HUOBI) GetOrders(symbol, types, start, end, states, from, direct, size } // GetOpenOrders returns a list of orders -func (h *HUOBI) GetOpenOrders(accountID, symbol, side string, size int) ([]OrderInfo, error) { +func (h *HUOBI) GetOpenOrders(accountID, symbol, side string, size int64) ([]OrderInfo, error) { type response struct { Response Orders []OrderInfo `json:"data"` @@ -526,7 +527,7 @@ func (h *HUOBI) GetOpenOrders(accountID, symbol, side string, size int) ([]Order if len(side) > 0 { vals.Set("side", side) } - vals.Set("size", fmt.Sprintf("%v", size)) + vals.Set("size", strconv.FormatInt(size, 10)) var result response err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiGetOpenOrders, vals, nil, &result) @@ -855,8 +856,7 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url values.Set("PrivateSignature", crypto.Base64Encode(privSig)) } - urlPath := fmt.Sprintf("%s%s", common.EncodeURLValues(h.API.Endpoints.URL, values), - endpoint) + urlPath := h.API.Endpoints.URL + common.EncodeURLValues(endpoint, values) var body []byte diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index 91079f85..714af623 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -7,6 +7,7 @@ import ( "encoding/pem" "io/ioutil" "log" + "os" "strconv" "strings" "testing" @@ -33,11 +34,8 @@ const ( var h HUOBI var wsSetupRan bool -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { h.SetDefaults() -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { @@ -45,7 +43,7 @@ func TestSetup(t *testing.T) { } hConfig, err := cfg.GetExchangeConfig("Huobi") if err != nil { - t.Error("Huobi Setup() init error") + log.Fatal("Huobi Setup() init error") } hConfig.API.AuthenticatedSupport = true hConfig.API.AuthenticatedWebsocketSupport = true @@ -54,16 +52,16 @@ func TestSetup(t *testing.T) { err = h.Setup(hConfig) if err != nil { - t.Fatal("Huobi setup error", err) + log.Fatal("Huobi setup error", err) } + + os.Exit(m.Run()) } func setupWsTests(t *testing.T) { if wsSetupRan { return } - TestSetDefaults(t) - TestSetup(t) if !h.Websocket.IsEnabled() && !h.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(wshandler.WebsocketNotEnabled) } @@ -195,8 +193,7 @@ func TestGetTimestamp(t *testing.T) { func TestGetAccounts(t *testing.T) { t.Parallel() - - if !h.ValidateAPICredentials() { + if !h.ValidateAPICredentials() || !canManipulateRealOrders { t.Skip() } @@ -208,8 +205,7 @@ func TestGetAccounts(t *testing.T) { func TestGetAccountBalance(t *testing.T) { t.Parallel() - - if !h.ValidateAPICredentials() { + if !h.ValidateAPICredentials() || !canManipulateRealOrders { t.Skip() } @@ -227,7 +223,6 @@ func TestGetAccountBalance(t *testing.T) { func TestGetAggregatedBalance(t *testing.T) { t.Parallel() - if !h.ValidateAPICredentials() { t.Skip() } @@ -240,8 +235,7 @@ func TestGetAggregatedBalance(t *testing.T) { func TestSpotNewOrder(t *testing.T) { t.Parallel() - - if !h.ValidateAPICredentials() { + if !h.ValidateAPICredentials() || !canManipulateRealOrders { t.Skip() } @@ -261,7 +255,9 @@ func TestSpotNewOrder(t *testing.T) { func TestCancelExistingOrder(t *testing.T) { t.Parallel() - + if !h.ValidateAPICredentials() || !canManipulateRealOrders { + t.Skip() + } _, err := h.CancelExistingOrder(1337) if err == nil { t.Error("Huobi TestCancelExistingOrder Expected error") @@ -270,16 +266,17 @@ func TestCancelExistingOrder(t *testing.T) { func TestGetOrder(t *testing.T) { t.Parallel() - + if !h.ValidateAPICredentials() || !canManipulateRealOrders { + t.Skip() + } _, err := h.GetOrder(1337) - if err == nil { - t.Error("Huobi TestCancelOrder Expected error") + if err != nil { + t.Error(err) } } func TestGetMarginLoanOrders(t *testing.T) { t.Parallel() - if !h.ValidateAPICredentials() { t.Skip() } @@ -292,7 +289,6 @@ func TestGetMarginLoanOrders(t *testing.T) { func TestGetMarginAccountBalance(t *testing.T) { t.Parallel() - if !h.ValidateAPICredentials() { t.Skip() } @@ -305,7 +301,9 @@ func TestGetMarginAccountBalance(t *testing.T) { func TestCancelWithdraw(t *testing.T) { t.Parallel() - + if !h.ValidateAPICredentials() || !canManipulateRealOrders { + t.Skip() + } _, err := h.CancelWithdraw(1337) if err == nil { t.Error("Huobi TestCancelWithdraw Expected error") @@ -314,7 +312,6 @@ func TestCancelWithdraw(t *testing.T) { func TestPEMLoadAndSign(t *testing.T) { t.Parallel() - pemKey := strings.NewReader(h.API.Credentials.PEMKey) pemBytes, err := ioutil.ReadAll(pemKey) if err != nil { @@ -355,7 +352,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() h.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -443,20 +440,14 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - h.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.NoFiatWithdrawalsText - withdrawPermissions := h.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - h.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.BTC, currency.USDT)}, @@ -471,9 +462,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - h.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.BTC, currency.USDT)}, @@ -494,9 +482,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - h.SetDefaults() - TestSetup(t) - if !h.ValidateAPICredentials() { t.Skip() } @@ -528,15 +513,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - h.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -555,8 +536,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - h.SetDefaults() - TestSetup(t) if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -584,7 +563,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestGetAccountInfo(t *testing.T) { - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { _, err := h.GetAccountInfo() if err == nil { t.Error("GetAccountInfo() Expected error") @@ -598,6 +577,9 @@ func TestGetAccountInfo(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := h.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -605,8 +587,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - h.SetDefaults() - TestSetup(t) withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -630,9 +610,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - h.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -645,9 +622,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - h.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -669,7 +643,7 @@ func TestGetDepositAddress(t *testing.T) { // TestWsGetAccountsList connects to WS, logs in, gets account list func TestWsGetAccountsList(t *testing.T) { setupWsTests(t) - resp, err := h.wsGetAccountsList(currency.NewPairFromString("ethbtc")) + resp, err := h.wsGetAccountsList() if err != nil { t.Fatal(err) } diff --git a/exchanges/huobi/huobi_types.go b/exchanges/huobi/huobi_types.go index c4d516bc..9ad0f9f2 100644 --- a/exchanges/huobi/huobi_types.go +++ b/exchanges/huobi/huobi_types.go @@ -170,24 +170,23 @@ type CancelOrderBatch struct { // OrderInfo stores the order info type OrderInfo struct { - ID int `json:"id"` + ID int64 `json:"id"` Symbol string `json:"symbol"` - AccountID float64 `json:"account-id"` + AccountID int64 `json:"account-id"` Amount float64 `json:"amount,string"` Price float64 `json:"price,string"` CreatedAt int64 `json:"created-at"` Type string `json:"type"` FieldAmount float64 `json:"field-amount,string"` FieldCashAmount float64 `json:"field-cash-amount,string"` - Fieldees float64 `json:"field-fees,string"` FilledAmount float64 `json:"filled-amount,string"` FilledCashAmount float64 `json:"filled-cash-amount,string"` FilledFees float64 `json:"filled-fees,string"` FinishedAt int64 `json:"finished-at"` - UserID int `json:"user-id"` + UserID int64 `json:"user-id"` Source string `json:"source"` State string `json:"state"` - CanceledAt int `json:"canceled-at"` + CanceledAt int64 `json:"canceled-at"` Exchange string `json:"exchange"` Batch string `json:"batch"` } @@ -547,31 +546,13 @@ type WsAuthenticatedAccountsListResponseDataList struct { // WsAuthenticatedOrdersListResponse response from OrdersList authenticated endpoint type WsAuthenticatedOrdersListResponse struct { WsAuthenticatedDataResponse - Data []WsAuthenticatedOrdersListResponseData `json:"data"` -} - -// WsAuthenticatedOrdersListResponseData contains order details -type WsAuthenticatedOrdersListResponseData struct { - ID int64 `json:"id"` - Symbol string `json:"symbol"` - AccountID int64 `json:"account-id"` - Amount float64 `json:"amount,string"` - Price float64 `json:"price,string"` - CreatedAt int64 `json:"created-at"` - Type string `json:"type"` - FilledAmount float64 `json:"filled-amount,string"` - FilledCashAmount float64 `json:"filled-cash-amount,string"` - FilledFees float64 `json:"filled-fees,string"` - FinishedAt int64 `json:"finished-at"` - Source string `json:"source"` - State string `json:"state"` - CanceledAt int64 `json:"canceled-at"` + Data []OrderInfo `json:"data"` } // WsAuthenticatedOrderDetailResponse response from OrderDetail authenticated endpoint type WsAuthenticatedOrderDetailResponse struct { WsAuthenticatedDataResponse - Data WsAuthenticatedOrdersListResponseData `json:"data"` + Data OrderInfo `json:"data"` } // WsPong sent for pong messages diff --git a/exchanges/huobi/huobi_websocket.go b/exchanges/huobi/huobi_websocket.go index fa7022f4..93dda0a8 100644 --- a/exchanges/huobi/huobi_websocket.go +++ b/exchanges/huobi/huobi_websocket.go @@ -49,7 +49,7 @@ const ( ) // Instantiates a communications channel between websocket connections -var comms = make(chan WsMessage, 1) +var comms = make(chan WsMessage) // WsConnect initiates a new websocket connection func (h *HUOBI) WsConnect() error { @@ -68,6 +68,7 @@ func (h *HUOBI) WsConnect() error { err = h.wsLogin() if err != nil { log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", h.Name, err) + h.Websocket.SetCanUseAuthenticatedEndpoints(false) } go h.WsHandleData() @@ -434,7 +435,7 @@ func (h *HUOBI) wsAuthenticatedSubscribe(operation, endpoint, topic string) erro return h.AuthenticatedWebsocketConn.SendMessage(request) } -func (h *HUOBI) wsGetAccountsList(pair currency.Pair) (*WsAuthenticatedAccountsListResponse, error) { +func (h *HUOBI) wsGetAccountsList() (*WsAuthenticatedAccountsListResponse, error) { if !h.Websocket.CanUseAuthenticatedEndpoints() { return nil, fmt.Errorf("%v not authenticated cannot get accounts list", h.Name) } @@ -446,7 +447,6 @@ func (h *HUOBI) wsGetAccountsList(pair currency.Pair) (*WsAuthenticatedAccountsL SignatureVersion: signatureVersion, Timestamp: timestamp, Topic: wsAccountsList, - Symbol: h.FormatExchangeCurrency(pair, asset.Spot).String(), } hmac := h.wsGenerateSignature(timestamp, wsAccountListEndpoint) request.Signature = crypto.Base64Encode(hmac) diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index e83c5e8b..c2429488 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "strconv" + "strings" "sync" "time" @@ -96,6 +97,8 @@ func (h *HUOBI) SetDefaults() { AuthenticatedEndpoints: true, AccountInfo: true, MessageCorrelation: true, + GetOrder: true, + GetOrders: true, }, WithdrawPermissions: exchange.AutoWithdrawCryptoWithSetup | exchange.NoFiatWithdrawals, @@ -393,62 +396,83 @@ func (h *HUOBI) GetAccountID() ([]Account, error) { func (h *HUOBI) GetAccountInfo() (exchange.AccountInfo, error) { var info exchange.AccountInfo info.Exchange = h.Name - - accounts, err := h.GetAccountID() - if err != nil { - return info, err - } - - for i := range accounts { - var acc exchange.Account - acc.ID = strconv.FormatInt(accounts[i].ID, 10) - balances, err := h.GetAccountBalance(acc.ID) + if h.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + resp, err := h.wsGetAccountsList() if err != nil { return info, err } - var currencyDetails []exchange.AccountCurrencyInfo - for j := range balances { - var frozen bool - if balances[j].Type == "frozen" { - frozen = true + for i := range resp.Data { + if len(resp.Data[i].List) == 0 { + continue + } + currData := exchange.AccountCurrencyInfo{ + CurrencyName: currency.NewCode(resp.Data[i].List[0].Currency), + TotalValue: resp.Data[i].List[0].Balance, + } + if len(resp.Data[i].List) > 1 && resp.Data[i].List[1].Type == "frozen" { + currData.Hold = resp.Data[i].List[1].Balance + } + currencyDetails = append(currencyDetails, currData) + } + var acc exchange.Account + acc.Currencies = currencyDetails + info.Accounts = append(info.Accounts, acc) + } else { + accounts, err := h.GetAccountID() + if err != nil { + return info, err + } + for i := range accounts { + var acc exchange.Account + acc.ID = strconv.FormatInt(accounts[i].ID, 10) + balances, err := h.GetAccountBalance(acc.ID) + if err != nil { + return info, err } - var updated bool - for i := range currencyDetails { - if currencyDetails[i].CurrencyName == currency.NewCode(balances[j].Currency) { - if frozen { - currencyDetails[i].Hold = balances[j].Balance - } else { - currencyDetails[i].TotalValue = balances[j].Balance + var currencyDetails []exchange.AccountCurrencyInfo + for j := range balances { + var frozen bool + if balances[j].Type == "frozen" { + frozen = true + } + + var updated bool + for i := range currencyDetails { + if currencyDetails[i].CurrencyName == currency.NewCode(balances[j].Currency) { + if frozen { + currencyDetails[i].Hold = balances[j].Balance + } else { + currencyDetails[i].TotalValue = balances[j].Balance + } + updated = true } - updated = true + } + + if updated { + continue + } + + if frozen { + currencyDetails = append(currencyDetails, + exchange.AccountCurrencyInfo{ + CurrencyName: currency.NewCode(balances[j].Currency), + Hold: balances[j].Balance, + }) + } else { + currencyDetails = append(currencyDetails, + exchange.AccountCurrencyInfo{ + CurrencyName: currency.NewCode(balances[j].Currency), + TotalValue: balances[j].Balance, + }) } } - if updated { - continue - } - - if frozen { - currencyDetails = append(currencyDetails, - exchange.AccountCurrencyInfo{ - CurrencyName: currency.NewCode(balances[j].Currency), - Hold: balances[j].Balance, - }) - } else { - currencyDetails = append(currencyDetails, - exchange.AccountCurrencyInfo{ - CurrencyName: currency.NewCode(balances[j].Currency), - TotalValue: balances[j].Balance, - }) - } + acc.Currencies = currencyDetails + info.Accounts = append(info.Accounts, acc) } - - acc.Currencies = currencyDetails - info.Accounts = append(info.Accounts, acc) } - return info, nil } @@ -498,13 +522,18 @@ func (h *HUOBI) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { params.Type = formattedType response, err := h.SpotNewOrder(params) + if err != nil { + return submitOrderResponse, err + } if response > 0 { submitOrderResponse.OrderID = strconv.FormatInt(response, 10) } - if err == nil { - submitOrderResponse.IsOrderPlaced = true + + submitOrderResponse.IsOrderPlaced = true + if s.OrderType == order.Market { + submitOrderResponse.FullyMatched = true } - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -527,9 +556,10 @@ func (h *HUOBI) CancelOrder(order *order.Cancel) error { // CancelAllOrders cancels all orders associated with a currency pair func (h *HUOBI) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) { var cancelAllOrdersResponse order.CancelAllResponse - for _, currency := range h.GetEnabledPairs(asset.Spot) { + enabledPairs := h.GetEnabledPairs(asset.Spot) + for i := range enabledPairs { resp, err := h.CancelOpenOrdersBatch(orderCancellation.AccountID, - h.FormatExchangeCurrency(currency, asset.Spot).String()) + h.FormatExchangeCurrency(enabledPairs[i], asset.Spot).String()) if err != nil { return cancelAllOrdersResponse, err } @@ -551,7 +581,56 @@ func (h *HUOBI) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAl // GetOrderInfo returns information on a current open order func (h *HUOBI) GetOrderInfo(orderID string) (order.Detail, error) { var orderDetail order.Detail - return orderDetail, common.ErrNotYetImplemented + var respData *OrderInfo + if h.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + resp, err := h.wsGetOrderDetails(orderID) + if err != nil { + return orderDetail, err + } + respData = &resp.Data + } else { + oID, err := strconv.ParseInt(orderID, 10, 64) + if err != nil { + return orderDetail, err + } + resp, err := h.GetOrder(oID) + if err != nil { + return orderDetail, err + } + respData = &resp + } + if respData.ID == 0 { + return orderDetail, fmt.Errorf("%s - order not found for orderid %s", h.Name, orderID) + } + + typeDetails := strings.Split(respData.Type, "-") + orderSide, err := order.StringToOrderSide(typeDetails[0]) + if err != nil { + return orderDetail, err + } + orderType, err := order.StringToOrderType(typeDetails[1]) + if err != nil { + return orderDetail, err + } + orderStatus, err := order.StringToOrderStatus(respData.State) + if err != nil { + return orderDetail, err + } + orderDetail = order.Detail{ + Exchange: h.Name, + ID: strconv.FormatInt(respData.ID, 10), + AccountID: strconv.FormatInt(respData.AccountID, 10), + CurrencyPair: currency.NewPairFromString(respData.Symbol), + OrderType: orderType, + OrderSide: orderSide, + OrderDate: time.Unix(respData.CreatedAt, 0), + Status: orderStatus, + Price: respData.Price, + Amount: respData.Amount, + ExecutedAmount: respData.FilledAmount, + Fee: respData.FilledFees, + } + return orderDetail, nil } // GetDepositAddress returns a deposit address for a specified currency @@ -607,32 +686,72 @@ func (h *HUOBI) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, er var orders []order.Detail - for i := range req.Currencies { - resp, err := h.GetOpenOrders(h.API.Credentials.ClientID, - req.Currencies[i].Lower().String(), - side, - 500) - if err != nil { - return nil, err + if h.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + for i := range req.Currencies { + resp, err := h.wsGetOrdersList(-1, req.Currencies[i]) + if err != nil { + return orders, err + } + for j := range resp.Data { + sideData := strings.Split(resp.Data[j].OrderState, "-") + side = sideData[0] + orderSide, err := order.StringToOrderSide(side) + if err != nil { + return orders, err + } + orderType, err := order.StringToOrderType(sideData[1]) + if err != nil { + return orders, err + } + orderStatus, err := order.StringToOrderStatus(resp.Data[j].OrderState) + if err != nil { + return orders, err + } + orders = append(orders, order.Detail{ + Exchange: h.Name, + AccountID: strconv.FormatInt(resp.Data[j].AccountID, 10), + ID: strconv.FormatInt(resp.Data[j].OrderID, 10), + CurrencyPair: req.Currencies[i], + OrderType: orderType, + OrderSide: orderSide, + OrderDate: time.Unix(resp.Data[j].CreatedAt, 0), + Status: orderStatus, + Price: resp.Data[j].Price, + Amount: resp.Data[j].OrderAmount, + ExecutedAmount: resp.Data[j].FilledAmount, + RemainingAmount: resp.Data[j].UnfilledAmount, + Fee: resp.Data[j].FilledFees, + }) + } } - - for i := range resp { - orderDetail := order.Detail{ - ID: strconv.FormatInt(int64(resp[i].ID), 10), - Price: resp[i].Price, - Amount: resp[i].Amount, - CurrencyPair: req.Currencies[i], - Exchange: h.Name, - ExecutedAmount: resp[i].FilledAmount, - OrderDate: time.Unix(0, resp[i].CreatedAt*int64(time.Millisecond)), - Status: order.Status(resp[i].State), - AccountID: strconv.FormatFloat(resp[i].AccountID, 'f', -1, 64), - Fee: resp[i].FilledFees, + } else { + for i := range req.Currencies { + resp, err := h.GetOpenOrders(h.API.Credentials.ClientID, + req.Currencies[i].Lower().String(), + side, + 500) + if err != nil { + return nil, err } - setOrderSideAndType(resp[i].Type, &orderDetail) + for i := range resp { + orderDetail := order.Detail{ + ID: strconv.FormatInt(resp[i].ID, 10), + Price: resp[i].Price, + Amount: resp[i].Amount, + CurrencyPair: req.Currencies[i], + Exchange: h.Name, + ExecutedAmount: resp[i].FilledAmount, + OrderDate: time.Unix(0, resp[i].CreatedAt*int64(time.Millisecond)), + Status: order.Status(resp[i].State), + AccountID: strconv.FormatInt(resp[i].AccountID, 10), + Fee: resp[i].FilledFees, + } - orders = append(orders, orderDetail) + setOrderSideAndType(resp[i].Type, &orderDetail) + + orders = append(orders, orderDetail) + } } } @@ -664,7 +783,7 @@ func (h *HUOBI) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er for i := range resp { orderDetail := order.Detail{ - ID: strconv.FormatInt(int64(resp[i].ID), 10), + ID: strconv.FormatInt(resp[i].ID, 10), Price: resp[i].Price, Amount: resp[i].Amount, CurrencyPair: req.Currencies[i], @@ -672,7 +791,7 @@ func (h *HUOBI) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er ExecutedAmount: resp[i].FilledAmount, OrderDate: time.Unix(0, resp[i].CreatedAt*int64(time.Millisecond)), Status: order.Status(resp[i].State), - AccountID: strconv.FormatFloat(resp[i].AccountID, 'f', -1, 64), + AccountID: strconv.FormatInt(resp[i].AccountID, 10), Fee: resp[i].FilledFees, } diff --git a/exchanges/itbit/itbit_test.go b/exchanges/itbit/itbit_test.go index 42f29425..85cfd4d6 100644 --- a/exchanges/itbit/itbit_test.go +++ b/exchanges/itbit/itbit_test.go @@ -1,7 +1,9 @@ package itbit import ( + "log" "net/url" + "os" "testing" "github.com/thrasher-corp/gocryptotrader/common" @@ -21,19 +23,16 @@ const ( canManipulateRealOrders = false ) -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { i.SetDefaults() -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("Itbit load config error", err) + log.Fatal("Itbit load config error", err) } itbitConfig, err := cfg.GetExchangeConfig("ITBIT") if err != nil { - t.Error("Itbit Setup() init error") + log.Fatal("Itbit Setup() init error") } itbitConfig.API.AuthenticatedSupport = true itbitConfig.API.Credentials.Key = apiKey @@ -42,8 +41,10 @@ func TestSetup(t *testing.T) { err = i.Setup(itbitConfig) if err != nil { - t.Fatal("Itbit setup error", err) + log.Fatal("Itbit setup error", err) } + + os.Exit(m.Run()) } func TestGetTicker(t *testing.T) { @@ -167,7 +168,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() i.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -255,20 +256,14 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - i.SetDefaults() expectedResult := exchange.WithdrawCryptoViaWebsiteOnlyText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - withdrawPermissions := i.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - i.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -282,9 +277,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - i.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -304,8 +296,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - i.SetDefaults() - TestSetup(t) if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -330,15 +320,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - i.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -357,15 +343,11 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - i.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -388,7 +370,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestGetAccountInfo(t *testing.T) { - if apiKey != "" || apiSecret != "" || clientID != "" { + if areTestAPIKeysSet() { _, err := i.GetAccountInfo() if err == nil { t.Error("GetAccountInfo() Expected error") @@ -397,6 +379,9 @@ func TestGetAccountInfo(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := i.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -404,8 +389,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - i.SetDefaults() - TestSetup(t) withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -426,9 +409,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - i.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -441,9 +421,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - i.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index 1168ebc4..918b877d 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -331,14 +331,18 @@ func (i *ItBit) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { s.Price, s.Pair.String(), "") + if err != nil { + return submitOrderResponse, err + } if response.ID != "" { submitOrderResponse.OrderID = response.ID } - if err == nil { - submitOrderResponse.IsOrderPlaced = true - } - return submitOrderResponse, err + if response.AmountFilled == s.Amount { + submitOrderResponse.FullyMatched = true + } + submitOrderResponse.IsOrderPlaced = true + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/kraken/kraken.go b/exchanges/kraken/kraken.go index 9bc63241..46e532db 100644 --- a/exchanges/kraken/kraken.go +++ b/exchanges/kraken/kraken.go @@ -47,6 +47,7 @@ const ( krakenDepositAddresses = "DepositAddresses" krakenWithdrawStatus = "WithdrawStatus" krakenWithdrawCancel = "WithdrawCancel" + krakenWebsocketToken = "GetWebSocketsToken" krakenAuthRate = 0 krakenUnauthRate = 0 @@ -57,8 +58,9 @@ var assetPairMap map[string]string // Kraken is the overarching type across the alphapoint package type Kraken struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection - wsRequestMtx sync.Mutex + WebsocketConn *wshandler.WebsocketConnection + AuthenticatedWebsocketConn *wshandler.WebsocketConnection + wsRequestMtx sync.Mutex } // GetServerTime returns current server time @@ -1036,3 +1038,14 @@ func (k *Kraken) WithdrawCancel(c currency.Code, refID string) (bool, error) { return response.Result, GetError(response.Error) } + +func (k *Kraken) GetWebsocketToken() (string, error) { + var response WsTokenResponse + if err := k.SendAuthenticatedHTTPRequest(krakenWebsocketToken, url.Values{}, &response); err != nil { + return "", err + } + if len(response.Error) > 0 { + return "", fmt.Errorf("%s - %v", k.Name, response.Error) + } + return response.Result.Token, nil +} diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index 41b22ed7..227bde0b 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -3,6 +3,8 @@ package kraken import ( "log" "net/http" + "os" + "strings" "testing" "github.com/gorilla/websocket" @@ -25,13 +27,9 @@ const ( canManipulateRealOrders = false ) -// TestSetDefaults setup func -func TestSetDefaults(t *testing.T) { - k.SetDefaults() -} - // TestSetup setup func -func TestSetup(t *testing.T) { +func TestMain(m *testing.M) { + k.SetDefaults() cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { @@ -39,19 +37,19 @@ func TestSetup(t *testing.T) { } krakenConfig, err := cfg.GetExchangeConfig("Kraken") if err != nil { - t.Error("kraken Setup() init error", err) + log.Fatal("kraken Setup() init error", err) } krakenConfig.API.AuthenticatedSupport = true krakenConfig.API.Credentials.Key = apiKey krakenConfig.API.Credentials.Secret = apiSecret krakenConfig.API.Credentials.ClientID = clientID krakenConfig.API.Endpoints.WebsocketURL = k.API.Endpoints.WebsocketURL - subscribeToDefaultChannels = false - err = k.Setup(krakenConfig) if err != nil { - t.Fatal("Kraken setup error", err) + log.Fatal("Kraken setup error", err) } + + os.Exit(m.Run()) } // TestGetServerTime API endpoint test @@ -278,7 +276,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() k.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -290,8 +288,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - k.SetDefaults() - TestSetup(t) var feeBuilder = setFeeBuilder() if areTestAPIKeysSet() { @@ -373,11 +369,8 @@ func TestGetFee(t *testing.T) { // TestFormatWithdrawPermissions logic test func TestFormatWithdrawPermissions(t *testing.T) { - k.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawCryptoWith2FAText + " & " + exchange.AutoWithdrawFiatWithSetupText + " & " + exchange.WithdrawFiatWith2FAText - withdrawPermissions := k.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } @@ -385,9 +378,6 @@ func TestFormatWithdrawPermissions(t *testing.T) { // TestGetActiveOrders wrapper test func TestGetActiveOrders(t *testing.T) { - k.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -402,9 +392,6 @@ func TestGetActiveOrders(t *testing.T) { // TestGetOrderHistory wrapper test func TestGetOrderHistory(t *testing.T) { - k.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -417,6 +404,21 @@ func TestGetOrderHistory(t *testing.T) { } } +// TestGetOrderHistory wrapper test +func TestGetOrderInfo(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } + + _, err := k.GetOrderInfo("ImACoolOrderID") + if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting error") + } + if areTestAPIKeysSet() && !strings.Contains(err.Error(), "- Order ID not found:") { + t.Error("Expected Order ID not found error") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -425,9 +427,6 @@ func areTestAPIKeysSet() bool { // TestSubmitOrder wrapper test func TestSubmitOrder(t *testing.T) { - k.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -453,15 +452,11 @@ func TestSubmitOrder(t *testing.T) { // TestCancelExchangeOrder wrapper test func TestCancelExchangeOrder(t *testing.T) { - k.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -480,15 +475,11 @@ func TestCancelExchangeOrder(t *testing.T) { // TestCancelAllExchangeOrders wrapper test func TestCancelAllExchangeOrders(t *testing.T) { - k.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -512,7 +503,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { // TestGetAccountInfo wrapper test func TestGetAccountInfo(t *testing.T) { - if apiKey != "" || apiSecret != "" || clientID != "" { + if areTestAPIKeysSet() || clientID != "" { _, err := k.GetAccountInfo() if err != nil { t.Error("GetAccountInfo() error", err) @@ -527,6 +518,9 @@ func TestGetAccountInfo(t *testing.T) { // TestModifyOrder wrapper test func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := k.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -535,9 +529,6 @@ func TestModifyOrder(t *testing.T) { // TestWithdraw wrapper test func TestWithdraw(t *testing.T) { - k.SetDefaults() - TestSetup(t) - withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -563,9 +554,6 @@ func TestWithdraw(t *testing.T) { // TestWithdrawFiat wrapper test func TestWithdrawFiat(t *testing.T) { - k.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -590,9 +578,6 @@ func TestWithdrawFiat(t *testing.T) { // TestWithdrawInternationalBank wrapper test func TestWithdrawInternationalBank(t *testing.T) { - k.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -632,9 +617,6 @@ func TestGetDepositAddress(t *testing.T) { // TestWithdrawStatus wrapper test func TestWithdrawStatus(t *testing.T) { - k.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() { _, err := k.WithdrawStatus(currency.BTC, "") if err != nil { @@ -650,8 +632,6 @@ func TestWithdrawStatus(t *testing.T) { // TestWithdrawCancel wrapper test func TestWithdrawCancel(t *testing.T) { - k.SetDefaults() - TestSetup(t) _, err := k.WithdrawCancel(currency.BTC, "") if areTestAPIKeysSet() && err == nil { t.Error("WithdrawCancel() error cannot be nil") @@ -666,12 +646,11 @@ func setupWsTests(t *testing.T) { if wsSetupRan { return } - TestSetDefaults(t) - TestSetup(t) if !k.Websocket.IsEnabled() && !k.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(wshandler.WebsocketNotEnabled) } k.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() + comms = make(chan wshandler.WebsocketResponse, sharedtestvalues.WebsocketChannelOverrideCapacity) k.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() k.WebsocketConn = &wshandler.WebsocketConnection{ ExchangeName: k.Name, @@ -680,12 +659,33 @@ func setupWsTests(t *testing.T) { ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, } + k.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ + ExchangeName: k.Name, + URL: krakenAuthWSURL, + Verbose: k.Verbose, + ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, + ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, + } var dialer websocket.Dialer err := k.WebsocketConn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } + err = k.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{}) + if err != nil { + t.Fatal(err) + } + + token, err := k.GetWebsocketToken() + if err != nil { + t.Error(err) + } + authToken = token + + go k.WsReadData(k.WebsocketConn) + go k.WsReadData(k.AuthenticatedWebsocketConn) go k.WsHandleData() + go k.wsPingHandler() wsSetupRan = true } @@ -700,3 +700,38 @@ func TestWebsocketSubscribe(t *testing.T) { t.Error(err) } } + +func TestGetWSToken(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("API keys required, skipping") + } + resp, err := k.GetWebsocketToken() + if err != nil { + t.Error(err) + } + if resp == "" { + t.Error("Token not returned") + } +} + +func TestWsAddOrder(t *testing.T) { + setupWsTests(t) + _, err := k.wsAddOrder(&WsAddOrderRequest{ + OrderType: order.Limit.Lower(), + OrderSide: order.Buy.Lower(), + Pair: "XBT/USD", + Price: -100, + }) + if err != nil { + t.Error(err) + } +} + +func TestWsCancelOrder(t *testing.T) { + setupWsTests(t) + err := k.wsCancelOrders([]string{"1337"}) + if err != nil { + t.Error(err) + } +} diff --git a/exchanges/kraken/kraken_types.go b/exchanges/kraken/kraken_types.go index e816afd4..d950a6a9 100644 --- a/exchanges/kraken/kraken_types.go +++ b/exchanges/kraken/kraken_types.go @@ -1,6 +1,10 @@ package kraken -import "github.com/thrasher-corp/gocryptotrader/currency" +import ( + "time" + + "github.com/thrasher-corp/gocryptotrader/currency" +) // TimeResponse type type TimeResponse struct { @@ -391,7 +395,7 @@ type WithdrawStatusResponse struct { type WebsocketSubscriptionEventRequest struct { Event string `json:"event"` // subscribe RequestID int64 `json:"reqid,omitempty"` // Optional, client originated ID reflected in response message. - Pairs []string `json:"pair"` // Array of currency pairs (pair1,pair2,pair3). + Pairs []string `json:"pair,omitempty"` // Array of currency pairs (pair1,pair2,pair3). Subscription WebsocketSubscriptionData `json:"subscription,omitempty"` } @@ -413,6 +417,8 @@ type WebsocketSubscriptionData struct { Name string `json:"name,omitempty"` // ticker|ohlc|trade|book|spread|*, * for all (ohlc interval value is 1 if all channels subscribed) Interval int64 `json:"interval,omitempty"` // Optional - Time interval associated with ohlc subscription in minutes. Default 1. Valid Interval values: 1|5|15|30|60|240|1440|10080|21600 Depth int64 `json:"depth,omitempty"` // Optional - depth associated with book subscription in number of levels each side, default 10. Valid Options are: 10, 25, 100, 500, 1000 + Token string `json:"token,omitempty"` // Optional used for authenticated requests + } // WebsocketEventResponse holds all data response types @@ -459,3 +465,103 @@ type WebsocketChannelData struct { Pair currency.Pair ChannelID int64 } + +// WsTokenResponse holds the WS auth token +type WsTokenResponse struct { + Error []string `json:"error"` + Result struct { + Expires int64 `json:"expires"` + Token string `json:"token"` + } `json:"result"` +} + +// WsOwnTrade ws auth owntrade data +type WsOwnTrade struct { + Cost float64 `json:"cost,string"` + Fee float64 `json:"fee,string"` + Margin float64 `json:"margin,string"` + OrderTransactionID string `json:"ordertxid"` + OrderType string `json:"ordertype"` + Pair string `json:"pair"` + PostTransactionID string `json:"postxid"` + Price float64 `json:"price,string"` + Time time.Time `json:"time"` + Type string `json:"type"` + Vol float64 `json:"vol,string"` +} + +// WsOpenOrders ws auth open order data +type WsOpenOrders struct { + Cost float64 `json:"cost,string"` + Description WsOpenOrderDescription `json:"descr"` + ExpireTime time.Time `json:"expiretm"` + Fee float64 `json:"fee,string"` + LimitPrice float64 `json:"limitprice,string"` + Misc string `json:"misc"` + OFlags string `json:"oflags"` + OpenTime time.Time `json:"opentm"` + Price float64 `json:"price,string"` + RefID string `json:"refid"` + StartTime time.Time `json:"starttm"` + Status string `json:"status"` + StopPrice float64 `json:"stopprice,string"` + UserReference float64 `json:"userref"` + Volume float64 `json:"vol,string"` + ExecutedVolume float64 `json:"vol_exec,string"` +} + +// WsOpenOrderDescription additional data for WsOpenOrders +type WsOpenOrderDescription struct { + Close string `json:"close"` + Leverage string `json:"leverage"` + Order string `json:"order"` + OrderType string `json:"ordertype"` + Pair string `json:"pair"` + Price float64 `json:"price,string"` + Price2 float64 `json:"price2,string"` + Type string `json:"type"` +} + +// WsAddOrderRequest request type for ws adding order +type WsAddOrderRequest struct { + Event string `json:"event"` + Token string `json:"token"` + OrderType string `json:"ordertype"` + OrderSide string `json:"type"` + Pair string `json:"pair"` + Price float64 `json:"price,omitempty"` // optional + Price2 float64 `json:"price2,omitempty"` // optional + Volume float64 `json:"volume,omitempty"` + Leverage float64 `json:"leverage,omitempty"` // optional + OFlags string `json:"oflags,omitempty"` // optional + StartTime string `json:"starttm,omitempty"` // optional + ExpireTime string `json:"expiretm,omitempty"` // optional + UserReferenceID string `json:"userref,omitempty"` // optional + Validate string `json:"validate,omitempty"` // optional + CloseOrderType string `json:"close[ordertype],omitempty"` // optional + ClosePrice float64 `json:"close[price],omitempty"` // optional + ClosePrice2 float64 `json:"close[price2],omitempty"` // optional +} + +// WsAddOrderResponse response data for ws order +type WsAddOrderResponse struct { + Description string `json:"descr"` + Event string `json:"event"` + Status string `json:"status"` + TransactionID string `json:"txid"` + ErrorMessage string `json:"errorMessage"` +} + +// WsCancelOrderRequest request for ws cancel order +type WsCancelOrderRequest struct { + Event string `json:"event"` + Token string `json:"token"` + TransactionIDs []string `json:"txid"` +} + +// WsCancelOrderResponse response data for ws cancel order +type WsCancelOrderResponse struct { + Event string `json:"event"` + Status string `json:"status"` + ErrorMessage string `json:"errorMessage"` +} diff --git a/exchanges/kraken/kraken_websocket.go b/exchanges/kraken/kraken_websocket.go index 1be268b1..8faa8761 100644 --- a/exchanges/kraken/kraken_websocket.go +++ b/exchanges/kraken/kraken_websocket.go @@ -10,7 +10,9 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/currency" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" @@ -21,11 +23,9 @@ import ( // List of all websocket channels to subscribe to const ( krakenWSURL = "wss://ws.kraken.com" + krakenAuthWSURL = "wss://ws-auth.kraken.com" krakenWSSandboxURL = "wss://sandbox.kraken.com" - krakenWSSupportedVersion = "0.2.0" - // If a checksum fails, then resubscribing to the channel fails, fatal after these attempts - krakenWsResubscribeFailureLimit = 3 - krakenWsResubscribeDelayInSeconds = 3 + krakenWSSupportedVersion = "0.3.0" // WS endpoints krakenWsHeartbeat = "heartbeat" krakenWsPing = "ping" @@ -39,18 +39,22 @@ const ( krakenWsTrade = "trade" krakenWsSpread = "spread" krakenWsOrderbook = "book" - - orderbookBufferLimit = 3 - krakenWsRateLimit = 50 + krakenWsOwnTrades = "ownTrades" + krakenWsOpenOrders = "openOrders" + krakenWsAddOrder = "addOrder" + krakenWsCancelOrder = "cancelOrder" + krakenWsRateLimit = 50 ) // orderbookMutex Ensures if two entries arrive at once, only one can be processed at a time var subscriptionChannelPair []WebsocketChannelData -var subscribeToDefaultChannels = true +var comms = make(chan wshandler.WebsocketResponse) +var authToken string // Channels require a topic and a currency // Format [[ticker,but-t4u],[orderbook,nce-btt]] var defaultSubscribedChannels = []string{krakenWsTicker, krakenWsTrade, krakenWsOrderbook, krakenWsOHLC, krakenWsSpread} +var authenticatedChannels = []string{krakenWsOwnTrades, krakenWsOpenOrders} // WsConnect initiates a websocket connection func (k *Kraken) WsConnect() error { @@ -62,15 +66,86 @@ func (k *Kraken) WsConnect() error { if err != nil { return err } - go k.WsHandleData() - go k.wsPingHandler() - if subscribeToDefaultChannels { - k.GenerateDefaultSubscriptions() + if k.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { + authToken, err = k.GetWebsocketToken() + if err != nil { + k.Websocket.SetCanUseAuthenticatedEndpoints(false) + log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", k.Name, err) + } + err = k.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{}) + if err != nil { + k.Websocket.SetCanUseAuthenticatedEndpoints(false) + log.Errorf(log.ExchangeSys, "%v - failed to connect to authenticated endpoint: %v\n", k.Name, err) + } + go k.WsReadData(k.AuthenticatedWebsocketConn) + k.GenerateAuthenticatedSubscriptions() } + go k.WsReadData(k.WebsocketConn) + go k.WsHandleData() + go k.wsPingHandler() + k.GenerateDefaultSubscriptions() + return nil } +// WsReadData funnels both auth and public ws data into one manageable place +func (k *Kraken) WsReadData(ws *wshandler.WebsocketConnection) { + k.Websocket.Wg.Add(1) + defer k.Websocket.Wg.Done() + for { + select { + case <-k.Websocket.ShutdownC: + return + default: + resp, err := ws.ReadMessage() + if err != nil { + k.Websocket.DataHandler <- err + return + } + k.Websocket.TrafficAlert <- struct{}{} + comms <- resp + } + } +} + +// WsHandleData handles the read data from the websocket connection +func (k *Kraken) WsHandleData() { + k.Websocket.Wg.Add(1) + defer func() { + k.Websocket.Wg.Done() + }() + + for { + select { + case <-k.Websocket.ShutdownC: + return + default: + resp := <-comms + // event response handling + var eventResponse WebsocketEventResponse + err := json.Unmarshal(resp.Raw, &eventResponse) + if err == nil && eventResponse.Event != "" { + k.WsHandleEventResponse(&eventResponse, resp.Raw) + continue + } + // Data response handling + var dataResponse WebsocketDataResponse + err = json.Unmarshal(resp.Raw, &dataResponse) + if err != nil { + log.Error(log.WebsocketMgr, fmt.Errorf("%s - unhandled websocket data: %v", k.Name, err)) + continue + } + if _, ok := dataResponse[0].(float64); ok { + k.WsHandleDataResponse(dataResponse) + } + if _, ok := dataResponse[1].(string); ok { + k.wsHandleAuthDataResponse(dataResponse) + } + } + } +} + // wsPingHandler sends a message "ping" every 27 to maintain the connection to the websocket func (k *Kraken) wsPingHandler() { k.Websocket.Wg.Add(1) @@ -96,82 +171,47 @@ func (k *Kraken) wsPingHandler() { } } -// WsHandleData handles the read data from the websocket connection -func (k *Kraken) WsHandleData() { - k.Websocket.Wg.Add(1) - defer func() { - k.Websocket.Wg.Done() - }() - - for { - select { - case <-k.Websocket.ShutdownC: - return - default: - resp, err := k.WebsocketConn.ReadMessage() - if err != nil { - k.Websocket.ReadMessageErrors <- err - return - } - k.Websocket.TrafficAlert <- struct{}{} - // event response handling - var eventResponse WebsocketEventResponse - err = json.Unmarshal(resp.Raw, &eventResponse) - if err == nil && eventResponse.Event != "" { - k.WsHandleEventResponse(&eventResponse, resp.Raw) - continue - } - // Data response handling - var dataResponse WebsocketDataResponse - err = json.Unmarshal(resp.Raw, &dataResponse) - if err == nil && dataResponse[0].(float64) >= 0 { - k.WsHandleDataResponse(dataResponse) - continue - } - continue - } - } -} - // WsHandleDataResponse classifies the WS response and sends to appropriate handler func (k *Kraken) WsHandleDataResponse(response WebsocketDataResponse) { - channelID := int64(response[0].(float64)) - channelData := getSubscriptionChannelData(channelID) - switch channelData.Subscription { - case krakenWsTicker: - if k.Verbose { - log.Debugf(log.ExchangeSys, "%v Websocket ticker data received", - k.Name) + if cID, ok := response[0].(float64); ok { + channelID := int64(cID) + channelData := getSubscriptionChannelData(channelID) + switch channelData.Subscription { + case krakenWsTicker: + if k.Verbose { + log.Debugf(log.ExchangeSys, "%v Websocket ticker data received", + k.Name) + } + k.wsProcessTickers(&channelData, response[1].(map[string]interface{})) + case krakenWsOHLC: + if k.Verbose { + log.Debugf(log.ExchangeSys, "%v Websocket OHLC data received", + k.Name) + } + k.wsProcessCandles(&channelData, response[1].([]interface{})) + case krakenWsOrderbook: + if k.Verbose { + log.Debugf(log.ExchangeSys, "%v Websocket Orderbook data received", + k.Name) + } + k.wsProcessOrderBook(&channelData, response[1].(map[string]interface{})) + case krakenWsSpread: + if k.Verbose { + log.Debugf(log.ExchangeSys, "%v Websocket Spread data received", + k.Name) + } + k.wsProcessSpread(&channelData, response[1].([]interface{})) + case krakenWsTrade: + if k.Verbose { + log.Debugf(log.ExchangeSys, "%v Websocket Trade data received", + k.Name) + } + k.wsProcessTrades(&channelData, response[1].([]interface{})) + default: + log.Errorf(log.ExchangeSys, "%v Unidentified websocket data received: %v", + k.Name, + response) } - k.wsProcessTickers(&channelData, response[1].(map[string]interface{})) - case krakenWsOHLC: - if k.Verbose { - log.Debugf(log.ExchangeSys, "%v Websocket OHLC data received", - k.Name) - } - k.wsProcessCandles(&channelData, response[1].([]interface{})) - case krakenWsOrderbook: - if k.Verbose { - log.Debugf(log.ExchangeSys, "%v Websocket Orderbook data received", - k.Name) - } - k.wsProcessOrderBook(&channelData, response[1].(map[string]interface{})) - case krakenWsSpread: - if k.Verbose { - log.Debugf(log.ExchangeSys, "%v Websocket Spread data received", - k.Name) - } - k.wsProcessSpread(&channelData, response[1].([]interface{})) - case krakenWsTrade: - if k.Verbose { - log.Debugf(log.ExchangeSys, "%v Websocket Trade data received", - k.Name) - } - k.wsProcessTrades(&channelData, response[1].([]interface{})) - default: - log.Errorf(log.ExchangeSys, "%v Unidentified websocket data received: %v", - k.Name, - response) } } @@ -197,7 +237,7 @@ func (k *Kraken) WsHandleEventResponse(response *WebsocketEventResponse, rawResp k.Websocket.DataHandler <- fmt.Errorf("%v Websocket status '%v'", k.Name, response.Status) } - if response.WebsocketStatusResponse.Version != krakenWSSupportedVersion { + if response.WebsocketStatusResponse.Version > krakenWSSupportedVersion { log.Warnf(log.ExchangeSys, "%v New version of Websocket API released. Was %v Now %v", k.Name, krakenWSSupportedVersion, response.WebsocketStatusResponse.Version) } @@ -214,6 +254,192 @@ func (k *Kraken) WsHandleEventResponse(response *WebsocketEventResponse, rawResp } } +func (k *Kraken) wsHandleAuthDataResponse(response WebsocketDataResponse) { + if chName, ok := response[1].(string); ok { + switch chName { + case krakenWsOwnTrades: + if k.Verbose { + log.Debugf(log.ExchangeSys, "%v Websocket auth own trade data received", + k.Name) + } + k.wsProcessOwnTrades(&response[0]) + case krakenWsOpenOrders: + if k.Verbose { + log.Debugf(log.ExchangeSys, "%v Websocket auth open order data received", + k.Name) + } + k.wsProcessOpenOrders(&response[0]) + } + } +} + +func (k *Kraken) wsProcessOwnTrades(ownOrders interface{}) { + if data, ok := ownOrders.([]interface{}); ok { + for i := range data { + ownTrade := data[i].(map[string]interface{}) + for _, val := range ownTrade { + tradeData := val.(map[string]interface{}) + cost, err := strconv.ParseFloat(tradeData["cost"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + fee, err := strconv.ParseFloat(tradeData["fee"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + margin, err := strconv.ParseFloat(tradeData["margin"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + vol, err := strconv.ParseFloat(tradeData["vol"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + price, err := strconv.ParseFloat(tradeData["price"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + timeTogether, err := strconv.ParseFloat(tradeData["time"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + first, second, err := convert.SplitFloatDecimals(timeTogether) + if err != nil { + k.Websocket.DataHandler <- err + } + k.Websocket.DataHandler <- WsOwnTrade{ + Cost: cost, + Fee: fee, + Margin: margin, + OrderTransactionID: tradeData["ordertxid"].(string), + OrderType: tradeData["ordertype"].(string), + Pair: tradeData["pair"].(string), + PostTransactionID: tradeData["postxid"].(string), + Price: price, + Time: time.Unix(first, second), + Type: tradeData["type"].(string), + Vol: vol, + } + } + } + } else { + k.Websocket.DataHandler <- errors.New(k.Name + " - Invalid own trades data") + } +} + +func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) { + if data, ok := ownOrders.([]interface{}); ok { + for i := range data { + ownTrade := data[i].(map[string]interface{}) + for key, val := range ownTrade { + tradeData := val.(map[string]interface{}) + if len(tradeData) == 1 { + // just a status update + if status, ok := tradeData["status"].(string); ok { + k.Websocket.DataHandler <- k.Name + " - Order " + key + " " + status + } + } + startTimeConv, err := strconv.ParseFloat(tradeData["starttm"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + startTime, startTimeNano, err := convert.SplitFloatDecimals(startTimeConv) + if err != nil { + k.Websocket.DataHandler <- err + } + openTimeConv, err := strconv.ParseFloat(tradeData["opentm"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + openTime, openTimeNano, err := convert.SplitFloatDecimals(openTimeConv) + if err != nil { + k.Websocket.DataHandler <- err + } + expireTimeConv, err := strconv.ParseFloat(tradeData["expiretm"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + expireTime, expireTimeNano, err := convert.SplitFloatDecimals(expireTimeConv) + if err != nil { + k.Websocket.DataHandler <- err + } + cost, err := strconv.ParseFloat(tradeData["cost"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + executedVolume, err := strconv.ParseFloat(tradeData["vol_exec"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + volume, err := strconv.ParseFloat(tradeData["vol"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + userReference, err := strconv.ParseFloat(tradeData["userref"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + stopPrice, err := strconv.ParseFloat(tradeData["stopprice"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + price, err := strconv.ParseFloat(tradeData["price"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + limitPrice, err := strconv.ParseFloat(tradeData["limitprice"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + fee, err := strconv.ParseFloat(tradeData["fee"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + descriptionSubData := tradeData["description"].(map[string]interface{}) + descriptionPrice, err := strconv.ParseFloat(descriptionSubData["price"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + descriptionPrice2, err := strconv.ParseFloat(descriptionSubData["price2"].(string), 64) + if err != nil { + k.Websocket.DataHandler <- err + } + description := WsOpenOrderDescription{ + Close: descriptionSubData["close"].(string), + Leverage: descriptionSubData["leverage"].(string), + Order: descriptionSubData["order"].(string), + OrderType: descriptionSubData["ordertype"].(string), + Pair: descriptionSubData["pair"].(string), + Price: descriptionPrice, + Price2: descriptionPrice2, + Type: descriptionSubData["type"].(string), + } + + k.Websocket.DataHandler <- WsOpenOrders{ + Cost: cost, + ExpireTime: time.Unix(expireTime, expireTimeNano), + Description: description, + Fee: fee, + LimitPrice: limitPrice, + Misc: tradeData["misc"].(string), + OFlags: tradeData["oflags"].(string), + OpenTime: time.Unix(openTime, openTimeNano), + Price: price, + RefID: tradeData["refid"].(string), + StartTime: time.Unix(startTime, startTimeNano), + Status: tradeData["status"].(string), + StopPrice: stopPrice, + UserReference: userReference, + Volume: volume, + ExecutedVolume: executedVolume, + } + } + } + } else { + k.Websocket.DataHandler <- errors.New(k.Name + " - Invalid own trades data") + } +} + // addNewSubscriptionChannelData stores channel ids, pairs and subscription types to an array // allowing correlation between subscriptions and returned data func addNewSubscriptionChannelData(response *WebsocketEventResponse) { @@ -623,22 +849,39 @@ func (k *Kraken) GenerateDefaultSubscriptions() { k.Websocket.SubscribeToChannels(subscriptions) } +// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() +func (k *Kraken) GenerateAuthenticatedSubscriptions() { + var subscriptions []wshandler.WebsocketChannelSubscription + for i := range authenticatedChannels { + params := make(map[string]interface{}) + subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + Channel: authenticatedChannels[i], + Params: params, + }) + } + k.Websocket.SubscribeToChannels(subscriptions) +} + // Subscribe sends a websocket message to receive data from the channel func (k *Kraken) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - var depth int64 - if channelToSubscribe.Channel == "book" { - depth = 1000 - } - resp := WebsocketSubscriptionEventRequest{ Event: krakenWsSubscribe, - Pairs: []string{channelToSubscribe.Currency.String()}, Subscription: WebsocketSubscriptionData{ - Name: channelToSubscribe.Channel, - Depth: depth, + Name: channelToSubscribe.Channel, }, RequestID: k.WebsocketConn.GenerateMessageID(false), } + if channelToSubscribe.Channel == "book" { + // TODO: Add ability to make depth customisable + resp.Subscription.Depth = 1000 + } + if !channelToSubscribe.Currency.IsEmpty() { + resp.Pairs = []string{channelToSubscribe.Currency.String()} + } + if channelToSubscribe.Params != nil { + resp.Subscription.Token = authToken + } + _, err := k.WebsocketConn.SendMessageReturnResponse(resp.RequestID, resp) return err } @@ -656,3 +899,32 @@ func (k *Kraken) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscr _, err := k.WebsocketConn.SendMessageReturnResponse(resp.RequestID, resp) return err } + +func (k *Kraken) wsAddOrder(request *WsAddOrderRequest) (string, error) { + id := k.AuthenticatedWebsocketConn.GenerateMessageID(false) + request.UserReferenceID = strconv.FormatInt(id, 10) + request.Event = krakenWsAddOrder + request.Token = authToken + jsonResp, err := k.AuthenticatedWebsocketConn.SendMessageReturnResponse(id, request) + if err != nil { + return "", err + } + var resp WsAddOrderResponse + err = json.Unmarshal(jsonResp, &resp) + if err != nil { + return "", err + } + if resp.ErrorMessage != "" { + return "", fmt.Errorf(k.Name + " - " + resp.ErrorMessage) + } + return resp.TransactionID, nil +} + +func (k *Kraken) wsCancelOrders(orderIDs []string) error { + request := WsCancelOrderRequest{ + Event: krakenWsCancelOrder, + Token: authToken, + TransactionIDs: orderIDs, + } + return k.AuthenticatedWebsocketConn.SendMessage(request) +} diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 2831e760..fa820996 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -9,6 +9,7 @@ import ( "time" "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" @@ -106,6 +107,9 @@ func (k *Kraken) SetDefaults() { Subscribe: true, Unsubscribe: true, MessageCorrelation: true, + SubmitOrder: true, + CancelOrder: true, + CancelOrders: true, }, WithdrawPermissions: exchange.AutoWithdrawCryptoWithSetup | exchange.WithdrawCryptoWith2FA | @@ -171,6 +175,16 @@ func (k *Kraken) Setup(exch *config.ExchangeConfig) error { ResponseMaxLimit: exch.WebsocketResponseMaxLimit, } + k.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ + ExchangeName: k.Name, + URL: krakenAuthWSURL, + ProxyURL: k.Websocket.GetProxyAddress(), + Verbose: k.Verbose, + RateLimit: krakenWsRateLimit, + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + } + k.Websocket.Orderbook.Setup( exch.WebsocketOrderbookBufferLimit, true, @@ -397,25 +411,47 @@ func (k *Kraken) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]ex // SubmitOrder submits a new order func (k *Kraken) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { var submitOrderResponse order.SubmitResponse - if err := s.Validate(); err != nil { + err := s.Validate() + if err != nil { return submitOrderResponse, err } - response, err := k.AddOrder(s.Pair.String(), - s.OrderSide.String(), - s.OrderType.String(), - s.Amount, - s.Price, - 0, - 0, - &AddOrderOptions{}) - if len(response.TransactionIds) > 0 { - submitOrderResponse.OrderID = strings.Join(response.TransactionIds, ", ") - } - if err == nil { + if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + var resp string + resp, err = k.wsAddOrder(&WsAddOrderRequest{ + OrderType: s.OrderType.String(), + OrderSide: s.OrderSide.String(), + Pair: s.Pair.String(), + Price: s.Price, + Volume: s.Amount, + }) + if err != nil { + return submitOrderResponse, err + } + submitOrderResponse.OrderID = resp submitOrderResponse.IsOrderPlaced = true + } else { + var response AddOrderResponse + response, err = k.AddOrder(s.Pair.String(), + s.OrderSide.String(), + s.OrderType.String(), + s.Amount, + s.Price, + 0, + 0, + &AddOrderOptions{}) + if err != nil { + return submitOrderResponse, err + } + if len(response.TransactionIds) > 0 { + submitOrderResponse.OrderID = strings.Join(response.TransactionIds, ", ") + } } - return submitOrderResponse, err + if s.OrderType == order.Market { + submitOrderResponse.FullyMatched = true + } + submitOrderResponse.IsOrderPlaced = true + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -426,6 +462,9 @@ func (k *Kraken) ModifyOrder(action *order.Modify) (string, error) { // CancelOrder cancels an order by its corresponding ID number func (k *Kraken) CancelOrder(order *order.Cancel) error { + if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + return k.wsCancelOrders([]string{order.OrderID}) + } _, err := k.CancelExistingOrder(order.OrderID) return err @@ -436,26 +475,78 @@ func (k *Kraken) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, erro cancelAllOrdersResponse := order.CancelAllResponse{ Status: make(map[string]string), } + var emptyOrderOptions OrderInfoOptions openOrders, err := k.GetOpenOrders(emptyOrderOptions) if err != nil { return cancelAllOrdersResponse, err } - for orderID := range openOrders.Open { - _, err = k.CancelExistingOrder(orderID) + var err error + if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + err = k.wsCancelOrders([]string{orderID}) + } else { + _, err = k.CancelExistingOrder(orderID) + } if err != nil { cancelAllOrdersResponse.Status[orderID] = err.Error() } } - return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order func (k *Kraken) GetOrderInfo(orderID string) (order.Detail, error) { var orderDetail order.Detail - return orderDetail, common.ErrNotYetImplemented + var emptyOrderOptions OrderInfoOptions + openOrders, err := k.GetOpenOrders(emptyOrderOptions) + if err != nil { + return orderDetail, err + } + if orderInfo, ok := openOrders.Open[orderID]; ok { + var trades []order.TradeHistory + for i := range orderInfo.Trades { + trades = append(trades, order.TradeHistory{ + TID: orderInfo.Trades[i], + }) + } + firstNum, decNum, err := convert.SplitFloatDecimals(orderInfo.StartTime) + if err != nil { + return orderDetail, err + } + side, err := order.StringToOrderSide(orderInfo.Description.Type) + if err != nil { + return orderDetail, err + } + status, err := order.StringToOrderStatus(orderInfo.Status) + if err != nil { + return orderDetail, err + } + oType, err := order.StringToOrderType(orderInfo.Description.OrderType) + if err != nil { + return orderDetail, err + } + + orderDetail = order.Detail{ + Exchange: k.Name, + ID: orderID, + CurrencyPair: currency.NewPairFromString(orderInfo.Description.Pair), + OrderSide: side, + OrderType: oType, + OrderDate: time.Unix(firstNum, decNum), + Status: status, + Price: orderInfo.Price, + Amount: orderInfo.Volume, + ExecutedAmount: orderInfo.VolumeExecuted, + RemainingAmount: orderInfo.Volume - orderInfo.VolumeExecuted, + Fee: orderInfo.Fee, + Trades: trades, + } + } else { + return orderDetail, errors.New(k.Name + " - Order ID not found: " + orderID) + } + + return orderDetail, nil } // GetDepositAddress returns a deposit address for a specified currency @@ -606,5 +697,9 @@ func (k *Kraken) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, e // AuthenticateWebsocket sends an authentication message to the websocket func (k *Kraken) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported + resp, err := k.GetWebsocketToken() + if resp != "" { + authToken = resp + } + return err } diff --git a/exchanges/lakebtc/lakebtc_test.go b/exchanges/lakebtc/lakebtc_test.go index 48b8e33c..fd2c87f1 100644 --- a/exchanges/lakebtc/lakebtc_test.go +++ b/exchanges/lakebtc/lakebtc_test.go @@ -3,6 +3,7 @@ package lakebtc import ( "fmt" "log" + "os" "testing" "github.com/thrasher-corp/gocryptotrader/common" @@ -16,7 +17,6 @@ import ( ) var l LakeBTC -var setupRan bool // Please add your own APIkeys to do correct due diligence testing. const ( @@ -25,34 +25,28 @@ const ( canManipulateRealOrders = false ) -func TestSetDefaults(t *testing.T) { - if !setupRan { - l.SetDefaults() +func TestMain(m *testing.M) { + l.SetDefaults() + cfg := config.GetConfig() + err := cfg.LoadConfig("../../testdata/configtest.json", true) + if err != nil { + log.Fatal("LakeBTC load config error", err) } -} + lakebtcConfig, err := cfg.GetExchangeConfig("LakeBTC") + if err != nil { + log.Fatal("LakeBTC Setup() init error") + } + lakebtcConfig.API.AuthenticatedSupport = true + lakebtcConfig.API.Credentials.Key = apiKey + lakebtcConfig.API.Credentials.Secret = apiSecret + lakebtcConfig.Features.Enabled.Websocket = true + err = l.Setup(lakebtcConfig) + if err != nil { + log.Fatal("LakeBTC setup error", err) + } + l.API.Endpoints.WebsocketURL = lakeBTCWSURL -func TestSetup(t *testing.T) { - if !setupRan { - cfg := config.GetConfig() - err := cfg.LoadConfig("../../testdata/configtest.json", true) - if err != nil { - log.Fatal("LakeBTC load config error", err) - } - lakebtcConfig, err := cfg.GetExchangeConfig("LakeBTC") - if err != nil { - t.Error("LakeBTC Setup() init error") - } - lakebtcConfig.API.AuthenticatedSupport = true - lakebtcConfig.API.Credentials.Key = apiKey - lakebtcConfig.API.Credentials.Secret = apiSecret - lakebtcConfig.Features.Enabled.Websocket = true - err = l.Setup(lakebtcConfig) - if err != nil { - t.Fatal("LakeBTC setup error", err) - } - l.API.Endpoints.WebsocketURL = lakeBTCWSURL - setupRan = true - } + os.Exit(m.Run()) } func TestFetchTradablePairs(t *testing.T) { @@ -171,7 +165,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() l.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -259,20 +253,14 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - l.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - withdrawPermissions := l.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - l.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -286,9 +274,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - l.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, } @@ -308,9 +293,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - l.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -335,15 +317,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - l.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -361,15 +339,11 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - l.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -392,6 +366,9 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := l.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -399,8 +376,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - l.SetDefaults() - TestSetup(t) withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -424,9 +399,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - l.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -439,9 +411,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - l.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -469,8 +438,6 @@ func TestGetDepositAddress(t *testing.T) { // TestWsConn websocket connection test func TestWsConn(t *testing.T) { - TestSetDefaults(t) - TestSetup(t) if !l.Websocket.IsEnabled() { t.Skip(wshandler.WebsocketNotEnabled) } @@ -484,8 +451,6 @@ func TestWsConn(t *testing.T) { // TestWsTradeProcessing logic test func TestWsTradeProcessing(t *testing.T) { - TestSetDefaults(t) - TestSetup(t) l.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() l.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() json := `{"trades":[{"type":"sell","date":1564985787,"price":"11913.02","amount":"0.49"}]}` @@ -497,8 +462,6 @@ func TestWsTradeProcessing(t *testing.T) { // TestWsTickerProcessing logic test func TestWsTickerProcessing(t *testing.T) { - TestSetDefaults(t) - TestSetup(t) const testChanSize = 26 l.Websocket.DataHandler = make(chan interface{}, testChanSize) l.Websocket.TrafficAlert = make(chan struct{}, testChanSize) @@ -519,8 +482,6 @@ func TestGetCurrencyFromChannel(t *testing.T) { // TestWsOrderbookProcessing logic test func TestWsOrderbookProcessing(t *testing.T) { - TestSetDefaults(t) - TestSetup(t) l.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() l.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() json := `{"asks":[["11905.66","0.0019"],["11905.73","0.0015"],["11906.43","0.0013"],["11906.62","0.0019"],["11907.25","11.087"],["11907.66","0.0006"],["11907.73","0.3113"],["11907.84","0.0006"],["11908.37","0.0016"],["11908.86","10.3786"],["11909.54","4.2955"],["11910.15","0.0012"],["11910.56","13.5505"],["11911.06","0.0011"],["11911.37","0.0023"]],"bids":[["11905.55","0.0171"],["11904.43","0.0225"],["11903.31","0.0223"],["11902.2","0.0027"],["11901.92","1.002"],["11901.6","0.0015"],["11901.49","0.0012"],["11901.08","0.0227"],["11900.93","0.0009"],["11900.53","1.662"],["11900.08","0.001"],["11900.01","3.6745"],["11899.96","0.003"],["11899.91","0.0006"],["11899.44","0.0013"]]}` diff --git a/exchanges/lakebtc/lakebtc_wrapper.go b/exchanges/lakebtc/lakebtc_wrapper.go index 3be7eb82..0e15e043 100644 --- a/exchanges/lakebtc/lakebtc_wrapper.go +++ b/exchanges/lakebtc/lakebtc_wrapper.go @@ -334,13 +334,18 @@ func (l *LakeBTC) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { isBuyOrder := s.OrderSide == order.Buy response, err := l.Trade(isBuyOrder, s.Amount, s.Price, s.Pair.Lower().String()) + if err != nil { + return submitOrderResponse, err + } if response.ID > 0 { submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10) } - if err == nil { - submitOrderResponse.IsOrderPlaced = true + + submitOrderResponse.IsOrderPlaced = true + if s.OrderType == order.Market { + submitOrderResponse.FullyMatched = true } - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -369,8 +374,8 @@ func (l *LakeBTC) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, err } var ordersToCancel []string - for _, order := range openOrders { - ordersToCancel = append(ordersToCancel, strconv.FormatInt(order.ID, 10)) + for i := range openOrders { + ordersToCancel = append(ordersToCancel, strconv.FormatInt(openOrders[i].ID, 10)) } return cancelAllOrdersResponse, l.CancelExistingOrders(ordersToCancel) diff --git a/exchanges/lbank/lbank_test.go b/exchanges/lbank/lbank_test.go index 05309537..4aa2c4a7 100644 --- a/exchanges/lbank/lbank_test.go +++ b/exchanges/lbank/lbank_test.go @@ -1,9 +1,9 @@ package lbank import ( - "fmt" "log" "os" + "strconv" "testing" "time" @@ -91,7 +91,7 @@ func TestGetMarketDepths(t *testing.T) { func TestGetTrades(t *testing.T) { t.Parallel() _, err := l.GetTrades(testCurrencyPair, "600", - fmt.Sprintf("%v", time.Now().Unix())) + strconv.FormatInt(time.Now().Unix(), 10)) if err != nil { t.Error(err) } @@ -104,7 +104,7 @@ func TestGetTrades(t *testing.T) { func TestGetKlines(t *testing.T) { t.Parallel() _, err := l.GetKlines(testCurrencyPair, "600", "minute1", - fmt.Sprintf("%v", time.Now().Unix())) + strconv.FormatInt(time.Now().Unix(), 10)) if err != nil { t.Error(err) } diff --git a/exchanges/lbank/lbank_wrapper.go b/exchanges/lbank/lbank_wrapper.go index e9fbd8b3..d5eab666 100644 --- a/exchanges/lbank/lbank_wrapper.go +++ b/exchanges/lbank/lbank_wrapper.go @@ -315,6 +315,9 @@ func (l *Lbank) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { } resp.IsOrderPlaced = true resp.OrderID = tempResp.OrderID + if s.OrderType == order.Market { + resp.FullyMatched = true + } return resp, nil } diff --git a/exchanges/localbitcoins/localbitcoins.go b/exchanges/localbitcoins/localbitcoins.go index dd37c207..4e97dc89 100644 --- a/exchanges/localbitcoins/localbitcoins.go +++ b/exchanges/localbitcoins/localbitcoins.go @@ -579,7 +579,7 @@ func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int64) er path := localbitcoinsAPIWalletSend if pin > 0 { - values.Set("pincode", fmt.Sprintf("%v", pin)) + values.Set("pincode", strconv.FormatInt(pin, 10)) path = localbitcoinsAPIWalletSendPin } diff --git a/exchanges/localbitcoins/localbitcoins_test.go b/exchanges/localbitcoins/localbitcoins_test.go index 8962afe1..d1c6bc0e 100644 --- a/exchanges/localbitcoins/localbitcoins_test.go +++ b/exchanges/localbitcoins/localbitcoins_test.go @@ -96,7 +96,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { t.Parallel() var feeBuilder = setFeeBuilder() l.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -189,7 +189,6 @@ func TestFormatWithdrawPermissions(t *testing.T) { expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - withdrawPermissions := l.FormatWithdrawPermissions() if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", @@ -275,7 +274,6 @@ func TestCancelExchangeOrder(t *testing.T) { if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -300,7 +298,6 @@ func TestCancelAllExchangeOrders(t *testing.T) { if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", diff --git a/exchanges/okcoin/okcoin_test.go b/exchanges/okcoin/okcoin_test.go index 8b7fa490..2d212f0e 100644 --- a/exchanges/okcoin/okcoin_test.go +++ b/exchanges/okcoin/okcoin_test.go @@ -2,7 +2,9 @@ package okcoin import ( "encoding/json" + "log" "net/http" + "os" "strings" "sync" "testing" @@ -34,43 +36,25 @@ var testSetupRan bool var spotCurrency = currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-").Lower().String() var websocketEnabled bool -// TestSetDefaults Sets standard default settings for running a test -func TestSetDefaults(t *testing.T) { - if o.Name != OKGroupExchange { - o.SetDefaults() - } - if o.Name != OKGroupExchange { - t.Errorf("%v - SetDefaults() error", OKGroupExchange) - } - TestSetup(t) -} - // TestSetRealOrderDefaults Sets test defaults when test can impact real money/orders func TestSetRealOrderDefaults(t *testing.T) { - TestSetDefaults(t) if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("Ensure canManipulateRealOrders is true and your API keys are set") } } // TestSetup Sets defaults for test environment -func TestSetup(t *testing.T) { - if testSetupRan { - return - } - if o.API.Credentials.Key == apiKey && o.API.Credentials.Secret == apiSecret && - o.API.Credentials.ClientID == passphrase && apiKey != "" && apiSecret != "" && passphrase != "" { - return - } +func TestMain(m *testing.M) { + o.SetDefaults() o.ExchangeName = OKGroupExchange cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("Okcoin load config error", err) + log.Fatal("Okcoin load config error", err) } okcoinConfig, err := cfg.GetExchangeConfig(OKGroupExchange) if err != nil { - t.Fatalf("%v Setup() init error", OKGroupExchange) + log.Fatalf("%v Setup() init error", OKGroupExchange) } if okcoinConfig.Features.Enabled.Websocket { websocketEnabled = true @@ -84,11 +68,13 @@ func TestSetup(t *testing.T) { okcoinConfig.API.Endpoints.WebsocketURL = o.API.Endpoints.WebsocketURL err = o.Setup(okcoinConfig) if err != nil { - t.Fatal("OKCoin setup error", err) + log.Fatal("OKCoin setup error", err) } testSetupRan = true o.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() o.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() + + os.Exit(m.Run()) } func areTestAPIKeysSet() bool { @@ -106,14 +92,12 @@ func testStandardErrorHandling(t *testing.T, err error) { // TestGetAccountCurrencies API endpoint test func TestGetAccountCurrencies(t *testing.T) { - TestSetDefaults(t) _, err := o.GetAccountCurrencies() testStandardErrorHandling(t, err) } // TestGetAccountWalletInformation API endpoint test func TestGetAccountWalletInformation(t *testing.T) { - TestSetDefaults(t) resp, err := o.GetAccountWalletInformation("") if areTestAPIKeysSet() { if err != nil { @@ -129,7 +113,6 @@ func TestGetAccountWalletInformation(t *testing.T) { // TestGetAccountWalletInformationForCurrency API endpoint test func TestGetAccountWalletInformationForCurrency(t *testing.T) { - TestSetDefaults(t) resp, err := o.GetAccountWalletInformation(currency.BTC.String()) if areTestAPIKeysSet() { if err != nil { @@ -173,7 +156,6 @@ func TestAccountWithdrawRequest(t *testing.T) { // TestGetAccountWithdrawalFee API endpoint test func TestGetAccountWithdrawalFee(t *testing.T) { - TestSetDefaults(t) resp, err := o.GetAccountWithdrawalFee("") if areTestAPIKeysSet() { if err != nil { @@ -189,7 +171,6 @@ func TestGetAccountWithdrawalFee(t *testing.T) { // TestGetWithdrawalFeeForCurrency API endpoint test func TestGetAccountWithdrawalFeeForCurrency(t *testing.T) { - TestSetDefaults(t) resp, err := o.GetAccountWithdrawalFee(currency.BTC.String()) if areTestAPIKeysSet() { if err != nil { @@ -205,63 +186,54 @@ func TestGetAccountWithdrawalFeeForCurrency(t *testing.T) { // TestGetAccountWithdrawalHistory API endpoint test func TestGetAccountWithdrawalHistory(t *testing.T) { - TestSetDefaults(t) _, err := o.GetAccountWithdrawalHistory("") testStandardErrorHandling(t, err) } // TestGetAccountWithdrawalHistoryForCurrency API endpoint test func TestGetAccountWithdrawalHistoryForCurrency(t *testing.T) { - TestSetDefaults(t) _, err := o.GetAccountWithdrawalHistory(currency.BTC.String()) testStandardErrorHandling(t, err) } // TestGetAccountBillDetails API endpoint test func TestGetAccountBillDetails(t *testing.T) { - TestSetDefaults(t) _, err := o.GetAccountBillDetails(okgroup.GetAccountBillDetailsRequest{}) testStandardErrorHandling(t, err) } // TestGetAccountDepositAddressForCurrency API endpoint test func TestGetAccountDepositAddressForCurrency(t *testing.T) { - TestSetDefaults(t) _, err := o.GetAccountDepositAddressForCurrency(currency.BTC.String()) testStandardErrorHandling(t, err) } // TestGetAccountDepositHistory API endpoint test func TestGetAccountDepositHistory(t *testing.T) { - TestSetDefaults(t) _, err := o.GetAccountDepositHistory("") testStandardErrorHandling(t, err) } // TestGetAccountDepositHistoryForCurrency API endpoint test func TestGetAccountDepositHistoryForCurrency(t *testing.T) { - TestSetDefaults(t) _, err := o.GetAccountDepositHistory(currency.BTC.String()) testStandardErrorHandling(t, err) } // TestGetSpotTradingAccounts API endpoint test func TestGetSpotTradingAccounts(t *testing.T) { - TestSetDefaults(t) _, err := o.GetSpotTradingAccounts() testStandardErrorHandling(t, err) } // TestGetSpotTradingAccountsForCurrency API endpoint test func TestGetSpotTradingAccountsForCurrency(t *testing.T) { - TestSetDefaults(t) _, err := o.GetSpotTradingAccountForCurrency(currency.BTC.String()) testStandardErrorHandling(t, err) } // TestGetSpotBillDetailsForCurrency API endpoint test func TestGetSpotBillDetailsForCurrency(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotBillDetailsForCurrencyRequest{ Currency: currency.BTC.String(), Limit: 100, @@ -272,7 +244,6 @@ func TestGetSpotBillDetailsForCurrency(t *testing.T) { // TestGetSpotBillDetailsForCurrencyBadLimit API logic test func TestGetSpotBillDetailsForCurrencyBadLimit(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotBillDetailsForCurrencyRequest{ Currency: currency.BTC.String(), Limit: -1, @@ -316,7 +287,7 @@ func TestPlaceSpotOrderMarket(t *testing.T) { // TestPlaceMultipleSpotOrders API endpoint test func TestPlaceMultipleSpotOrders(t *testing.T) { TestSetRealOrderDefaults(t) - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -325,7 +296,7 @@ func TestPlaceMultipleSpotOrders(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, + ord, } _, errs := o.PlaceMultipleSpotOrders(request) @@ -336,8 +307,7 @@ func TestPlaceMultipleSpotOrders(t *testing.T) { // TestPlaceMultipleSpotOrdersOverCurrencyLimits API logic test func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) { - TestSetDefaults(t) - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -346,11 +316,11 @@ func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, - order, - order, - order, - order, + ord, + ord, + ord, + ord, + ord, } _, errs := o.PlaceMultipleSpotOrders(request) @@ -361,8 +331,7 @@ func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) { // TestPlaceMultipleSpotOrdersOverPairLimits API logic test func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) { - TestSetDefaults(t) - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -371,7 +340,7 @@ func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, + ord, } pairs := currency.Pairs{ @@ -382,8 +351,8 @@ func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) { } for x := range pairs { - order.InstrumentID = pairs[x].Format("-", false).String() - request = append(request, order) + ord.InstrumentID = pairs[x].Format("-", false).String() + request = append(request, ord) } _, errs := o.PlaceMultipleSpotOrders(request) @@ -439,7 +408,6 @@ func TestCancelMultipleSpotOrdersOverCurrencyLimits(t *testing.T) { // TestGetSpotOrders API endpoint test func TestGetSpotOrders(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotOrdersRequest{ InstrumentID: spotCurrency, Status: "all", @@ -450,7 +418,6 @@ func TestGetSpotOrders(t *testing.T) { // TestGetSpotOpenOrders API endpoint test func TestGetSpotOpenOrders(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotOpenOrdersRequest{} _, err := o.GetSpotOpenOrders(request) testStandardErrorHandling(t, err) @@ -458,7 +425,6 @@ func TestGetSpotOpenOrders(t *testing.T) { // TestGetSpotOrder API endpoint test func TestGetSpotOrder(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotOrderRequest{ OrderID: "-1234", InstrumentID: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-").Upper().String(), @@ -469,7 +435,6 @@ func TestGetSpotOrder(t *testing.T) { // TestGetSpotTransactionDetails API endpoint test func TestGetSpotTransactionDetails(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotTransactionDetailsRequest{ OrderID: 1234, InstrumentID: spotCurrency, @@ -480,7 +445,6 @@ func TestGetSpotTransactionDetails(t *testing.T) { // TestGetSpotTokenPairDetails API endpoint test func TestGetSpotTokenPairDetails(t *testing.T) { - TestSetDefaults(t) _, err := o.GetSpotTokenPairDetails() if err != nil { t.Error(err) @@ -489,7 +453,6 @@ func TestGetSpotTokenPairDetails(t *testing.T) { // TestGetSpotAllTokenPairsInformation API endpoint test func TestGetSpotAllTokenPairsInformation(t *testing.T) { - TestSetDefaults(t) _, err := o.GetSpotAllTokenPairsInformation() if err != nil { t.Error(err) @@ -498,7 +461,6 @@ func TestGetSpotAllTokenPairsInformation(t *testing.T) { // TestGetSpotAllTokenPairsInformationForCurrency API endpoint test func TestGetSpotAllTokenPairsInformationForCurrency(t *testing.T) { - TestSetDefaults(t) _, err := o.GetSpotAllTokenPairsInformationForCurrency(spotCurrency) if err != nil { t.Error(err) @@ -507,7 +469,6 @@ func TestGetSpotAllTokenPairsInformationForCurrency(t *testing.T) { // TestGetSpotFilledOrdersInformation API endpoint test func TestGetSpotFilledOrdersInformation(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotFilledOrdersInformationRequest{ InstrumentID: spotCurrency, } @@ -519,7 +480,6 @@ func TestGetSpotFilledOrdersInformation(t *testing.T) { // TestGetSpotMarketData API endpoint test func TestGetSpotMarketData(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotMarketDataRequest{ InstrumentID: spotCurrency, Granularity: 604800, @@ -532,21 +492,18 @@ func TestGetSpotMarketData(t *testing.T) { // TestGetMarginTradingAccounts API endpoint test func TestGetMarginTradingAccounts(t *testing.T) { - TestSetDefaults(t) _, err := o.GetMarginTradingAccounts() testStandardErrorHandling(t, err) } // TestGetMarginTradingAccountsForCurrency API endpoint test func TestGetMarginTradingAccountsForCurrency(t *testing.T) { - TestSetDefaults(t) _, err := o.GetMarginTradingAccountsForCurrency(spotCurrency) testStandardErrorHandling(t, err) } // TestGetMarginBillDetails API endpoint test func TestGetMarginBillDetails(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetMarginBillDetailsRequest{ InstrumentID: spotCurrency, Limit: 100, @@ -557,14 +514,12 @@ func TestGetMarginBillDetails(t *testing.T) { // TestGetMarginAccountSettings API endpoint test func TestGetMarginAccountSettings(t *testing.T) { - TestSetDefaults(t) _, err := o.GetMarginAccountSettings("") testStandardErrorHandling(t, err) } // TestGetMarginAccountSettingsForCurrency API endpoint test func TestGetMarginAccountSettingsForCurrency(t *testing.T) { - TestSetDefaults(t) _, err := o.GetMarginAccountSettings(spotCurrency) testStandardErrorHandling(t, err) } @@ -631,7 +586,7 @@ func TestPlaceMarginOrderMarket(t *testing.T) { // TestPlaceMultipleMarginOrders API endpoint test func TestPlaceMultipleMarginOrders(t *testing.T) { TestSetRealOrderDefaults(t) - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -641,7 +596,7 @@ func TestPlaceMultipleMarginOrders(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, + ord, } _, errs := o.PlaceMultipleMarginOrders(request) @@ -652,8 +607,7 @@ func TestPlaceMultipleMarginOrders(t *testing.T) { // TestPlaceMultipleMarginOrdersOverCurrencyLimits API logic test func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) { - TestSetDefaults(t) - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -663,11 +617,11 @@ func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, - order, - order, - order, - order, + ord, + ord, + ord, + ord, + ord, } _, errs := o.PlaceMultipleMarginOrders(request) @@ -678,8 +632,7 @@ func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) { // TestPlaceMultipleMarginOrdersOverPairLimits API logic test func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) { - TestSetDefaults(t) - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -689,7 +642,7 @@ func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, + ord, } pairs := currency.Pairs{ @@ -700,8 +653,8 @@ func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) { } for x := range pairs { - order.InstrumentID = pairs[x].Format("-", false).String() - request = append(request, order) + ord.InstrumentID = pairs[x].Format("-", false).String() + request = append(request, ord) } _, errs := o.PlaceMultipleMarginOrders(request) @@ -752,7 +705,6 @@ func TestCancelMultipleMarginOrdersOverCurrencyLimits(t *testing.T) { // TestGetMarginOrders API endpoint test func TestGetMarginOrders(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotOrdersRequest{ InstrumentID: spotCurrency, Status: "all", @@ -763,7 +715,6 @@ func TestGetMarginOrders(t *testing.T) { // TestGetMarginOpenOrders API endpoint test func TestGetMarginOpenOrders(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotOpenOrdersRequest{} _, err := o.GetMarginOpenOrders(request) testStandardErrorHandling(t, err) @@ -771,7 +722,6 @@ func TestGetMarginOpenOrders(t *testing.T) { // TestGetMarginOrder API endpoint test func TestGetMarginOrder(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotOrderRequest{ OrderID: "1234", InstrumentID: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-").Upper().String(), @@ -782,7 +732,6 @@ func TestGetMarginOrder(t *testing.T) { // TestGetMarginTransactionDetails API endpoint test func TestGetMarginTransactionDetails(t *testing.T) { - TestSetDefaults(t) request := okgroup.GetSpotTransactionDetailsRequest{ OrderID: 1234, InstrumentID: spotCurrency, @@ -797,7 +746,6 @@ func TestGetMarginTransactionDetails(t *testing.T) { // Attempts to subscribe to a channel that doesn't exist // Will log in if credentials are present func TestSendWsMessages(t *testing.T) { - TestSetDefaults(t) if !o.Websocket.IsEnabled() && !o.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(wshandler.WebsocketNotEnabled) } @@ -850,7 +798,6 @@ func TestGetAssetTypeFromTableName(t *testing.T) { // TestGetWsChannelWithoutOrderType logic test func TestGetWsChannelWithoutOrderType(t *testing.T) { - TestSetDefaults(t) str := "spot/depth5:BTC-USDT" expected := "depth5" resp := o.GetWsChannelWithoutOrderType(str) @@ -872,7 +819,6 @@ func TestGetWsChannelWithoutOrderType(t *testing.T) { // TestOrderBookUpdateChecksumCalculator logic test func TestOrderBookUpdateChecksumCalculator(t *testing.T) { - TestSetDefaults(t) if !websocketEnabled { t.Skip("Websocket not enabled, skipping") } @@ -902,7 +848,6 @@ func TestOrderBookUpdateChecksumCalculator(t *testing.T) { // TestOrderBookUpdateChecksumCalculatorWithDash logic test func TestOrderBookUpdateChecksumCalculatorWith8DecimalPlaces(t *testing.T) { - TestSetDefaults(t) if !websocketEnabled { t.Skip("Websocket not enabled, skipping") } @@ -964,7 +909,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() o.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -976,7 +921,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - TestSetDefaults(t) var feeBuilder = setFeeBuilder() // CryptocurrencyTradeFee Basic if resp, err := o.GetFee(feeBuilder); resp != float64(0.0015) || err != nil { @@ -1031,7 +975,6 @@ func TestGetFee(t *testing.T) { // TestFormatWithdrawPermissions helper test func TestFormatWithdrawPermissions(t *testing.T) { - TestSetDefaults(t) expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText withdrawPermissions := o.FormatWithdrawPermissions() if withdrawPermissions != expectedResult { @@ -1149,3 +1092,25 @@ func TestWithdrawInternationalBank(t *testing.T) { t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err) } } + +// TestGetOrderbook logic test +func TestGetOrderbook(t *testing.T) { + t.Parallel() + _, err := o.GetOrderBook(okgroup.GetOrderBookRequest{InstrumentID: "BTC-USDT"}, + asset.Spot) + if err != nil { + t.Error(err) + } + + _, err = o.GetOrderBook(okgroup.GetOrderBookRequest{InstrumentID: "Payload"}, + asset.Futures) + if err == nil { + t.Error("error cannot be nil") + } + + _, err = o.GetOrderBook(okgroup.GetOrderBookRequest{InstrumentID: "BTC-USD-SWAP"}, + asset.PerpetualSwap) + if err == nil { + t.Error("error cannot be nil") + } +} diff --git a/exchanges/okex/okex_test.go b/exchanges/okex/okex_test.go index fa9d6e2a..70ae6246 100644 --- a/exchanges/okex/okex_test.go +++ b/exchanges/okex/okex_test.go @@ -3,7 +3,9 @@ package okex import ( "encoding/json" "fmt" + "log" "net/http" + "os" "strconv" "strings" "sync" @@ -31,49 +33,30 @@ const ( canManipulateRealOrders = false ) -var testSetupRan bool -var o = OKEX{} +var o OKEX var spotCurrency = currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "-").Lower().String() var websocketEnabled bool -// TestSetDefaults Sets standard default settings for running a test -func TestSetDefaults(t *testing.T) { - if o.Name != OKGroupExchange { - o.SetDefaults() - } - if o.Name != OKGroupExchange { - t.Errorf("%v - SetDefaults() error", OKGroupExchange) - } - TestSetup(t) -} - // TestSetRealOrderDefaults Sets test defaults when test can impact real money/orders func TestSetRealOrderDefaults(t *testing.T) { - TestSetDefaults(t) if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("Ensure canManipulateRealOrders is true and your API keys are set") } } // TestSetup Sets defaults for test environment -func TestSetup(t *testing.T) { - if testSetupRan { - return - } - if o.API.Credentials.Key == apiKey && o.API.Credentials.Secret == apiSecret && - o.API.Credentials.ClientID == passphrase && apiKey != "" && apiSecret != "" && passphrase != "" { - return - } +func TestMain(m *testing.M) { + o.SetDefaults() o.ExchangeName = OKGroupExchange cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("Okex load config error", err) + log.Fatal("Okex load config error", err) } okexConfig, err := cfg.GetExchangeConfig(OKGroupExchange) if err != nil { - t.Fatalf("%v Setup() init error", OKGroupExchange) + log.Fatalf("%v Setup() init error", OKGroupExchange) } if okexConfig.Features.Enabled.Websocket { websocketEnabled = true @@ -86,11 +69,12 @@ func TestSetup(t *testing.T) { okexConfig.API.Endpoints.WebsocketURL = o.API.Endpoints.WebsocketURL err = o.Setup(okexConfig) if err != nil { - t.Fatal("Okex setup error", err) + log.Fatal("Okex setup error", err) } - testSetupRan = true o.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() o.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() + + os.Exit(m.Run()) } func areTestAPIKeysSet() bool { @@ -108,7 +92,6 @@ func testStandardErrorHandling(t *testing.T, err error) { // TestGetAccountCurrencies API endpoint test func TestGetAccountCurrencies(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetAccountCurrencies() testStandardErrorHandling(t, err) @@ -116,7 +99,6 @@ func TestGetAccountCurrencies(t *testing.T) { // TestGetAccountWalletInformation API endpoint test func TestGetAccountWalletInformation(t *testing.T) { - TestSetDefaults(t) t.Parallel() resp, err := o.GetAccountWalletInformation("") if areTestAPIKeysSet() { @@ -133,7 +115,6 @@ func TestGetAccountWalletInformation(t *testing.T) { // TestGetAccountWalletInformationForCurrency API endpoint test func TestGetAccountWalletInformationForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() resp, err := o.GetAccountWalletInformation(currency.BTC.String()) if areTestAPIKeysSet() { @@ -182,7 +163,6 @@ func TestAccountWithdrawRequest(t *testing.T) { // TestGetAccountWithdrawalFee API endpoint test func TestGetAccountWithdrawalFee(t *testing.T) { - TestSetDefaults(t) t.Parallel() resp, err := o.GetAccountWithdrawalFee("") if areTestAPIKeysSet() { @@ -199,7 +179,6 @@ func TestGetAccountWithdrawalFee(t *testing.T) { // TestGetWithdrawalFeeForCurrency API endpoint test func TestGetAccountWithdrawalFeeForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() resp, err := o.GetAccountWithdrawalFee(currency.BTC.String()) if areTestAPIKeysSet() { @@ -216,7 +195,6 @@ func TestGetAccountWithdrawalFeeForCurrency(t *testing.T) { // TestGetAccountWithdrawalHistory API endpoint test func TestGetAccountWithdrawalHistory(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetAccountWithdrawalHistory("") testStandardErrorHandling(t, err) @@ -224,7 +202,6 @@ func TestGetAccountWithdrawalHistory(t *testing.T) { // TestGetAccountWithdrawalHistoryForCurrency API endpoint test func TestGetAccountWithdrawalHistoryForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetAccountWithdrawalHistory(currency.BTC.String()) testStandardErrorHandling(t, err) @@ -232,7 +209,6 @@ func TestGetAccountWithdrawalHistoryForCurrency(t *testing.T) { // TestGetAccountBillDetails API endpoint test func TestGetAccountBillDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetAccountBillDetails(okgroup.GetAccountBillDetailsRequest{}) testStandardErrorHandling(t, err) @@ -240,7 +216,6 @@ func TestGetAccountBillDetails(t *testing.T) { // TestGetAccountDepositAddressForCurrency API endpoint test func TestGetAccountDepositAddressForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetAccountDepositAddressForCurrency(currency.BTC.String()) testStandardErrorHandling(t, err) @@ -248,7 +223,6 @@ func TestGetAccountDepositAddressForCurrency(t *testing.T) { // TestGetAccountDepositHistory API endpoint test func TestGetAccountDepositHistory(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetAccountDepositHistory("") testStandardErrorHandling(t, err) @@ -256,7 +230,6 @@ func TestGetAccountDepositHistory(t *testing.T) { // TestGetAccountDepositHistoryForCurrency API endpoint test func TestGetAccountDepositHistoryForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetAccountDepositHistory(currency.BTC.String()) testStandardErrorHandling(t, err) @@ -264,7 +237,6 @@ func TestGetAccountDepositHistoryForCurrency(t *testing.T) { // TestGetSpotTradingAccounts API endpoint test func TestGetSpotTradingAccounts(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSpotTradingAccounts() testStandardErrorHandling(t, err) @@ -272,7 +244,6 @@ func TestGetSpotTradingAccounts(t *testing.T) { // TestGetSpotTradingAccountsForCurrency API endpoint test func TestGetSpotTradingAccountsForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSpotTradingAccountForCurrency(currency.BTC.String()) testStandardErrorHandling(t, err) @@ -280,7 +251,6 @@ func TestGetSpotTradingAccountsForCurrency(t *testing.T) { // TestGetSpotBillDetailsForCurrency API endpoint test func TestGetSpotBillDetailsForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotBillDetailsForCurrencyRequest{ Currency: currency.BTC.String(), @@ -292,7 +262,6 @@ func TestGetSpotBillDetailsForCurrency(t *testing.T) { // TestGetSpotBillDetailsForCurrencyBadLimit API logic test func TestGetSpotBillDetailsForCurrencyBadLimit(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotBillDetailsForCurrencyRequest{ Currency: currency.BTC.String(), @@ -340,7 +309,7 @@ func TestPlaceSpotOrderMarket(t *testing.T) { func TestPlaceMultipleSpotOrders(t *testing.T) { TestSetRealOrderDefaults(t) t.Parallel() - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -349,7 +318,7 @@ func TestPlaceMultipleSpotOrders(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, + ord, } _, errs := o.PlaceMultipleSpotOrders(request) @@ -360,9 +329,8 @@ func TestPlaceMultipleSpotOrders(t *testing.T) { // TestPlaceMultipleSpotOrdersOverCurrencyLimits API logic test func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) { - TestSetDefaults(t) t.Parallel() - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -371,11 +339,11 @@ func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, - order, - order, - order, - order, + ord, + ord, + ord, + ord, + ord, } _, errs := o.PlaceMultipleSpotOrders(request) @@ -386,9 +354,8 @@ func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) { // TestPlaceMultipleSpotOrdersOverPairLimits API logic test func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) { - TestSetDefaults(t) t.Parallel() - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -397,7 +364,7 @@ func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, + ord, } pairs := currency.Pairs{ @@ -408,8 +375,8 @@ func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) { } for x := range pairs { - order.InstrumentID = pairs[x].Format("-", false).String() - request = append(request, order) + ord.InstrumentID = pairs[x].Format("-", false).String() + request = append(request, ord) } _, errs := o.PlaceMultipleSpotOrders(request) @@ -468,7 +435,6 @@ func TestCancelMultipleSpotOrdersOverCurrencyLimits(t *testing.T) { // TestGetSpotOrders API endpoint test func TestGetSpotOrders(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotOrdersRequest{ InstrumentID: spotCurrency, @@ -481,7 +447,6 @@ func TestGetSpotOrders(t *testing.T) { // TestGetSpotOpenOrders API endpoint test func TestGetSpotOpenOrders(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotOpenOrdersRequest{} _, err := o.GetSpotOpenOrders(request) @@ -490,7 +455,6 @@ func TestGetSpotOpenOrders(t *testing.T) { // TestGetSpotOrder API endpoint test func TestGetSpotOrder(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotOrderRequest{ OrderID: "-1234", @@ -502,7 +466,6 @@ func TestGetSpotOrder(t *testing.T) { // TestGetSpotTransactionDetails API endpoint test func TestGetSpotTransactionDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotTransactionDetailsRequest{ OrderID: 1234, @@ -514,7 +477,6 @@ func TestGetSpotTransactionDetails(t *testing.T) { // TestGetSpotTokenPairDetails API endpoint test func TestGetSpotTokenPairDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSpotTokenPairDetails() if err != nil { @@ -524,7 +486,6 @@ func TestGetSpotTokenPairDetails(t *testing.T) { // TestGetSpotAllTokenPairsInformation API endpoint test func TestGetSpotAllTokenPairsInformation(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSpotAllTokenPairsInformation() if err != nil { @@ -534,7 +495,6 @@ func TestGetSpotAllTokenPairsInformation(t *testing.T) { // TestGetSpotAllTokenPairsInformationForCurrency API endpoint test func TestGetSpotAllTokenPairsInformationForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSpotAllTokenPairsInformationForCurrency(spotCurrency) if err != nil { @@ -544,7 +504,6 @@ func TestGetSpotAllTokenPairsInformationForCurrency(t *testing.T) { // TestGetSpotFilledOrdersInformation API endpoint test func TestGetSpotFilledOrdersInformation(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotFilledOrdersInformationRequest{ InstrumentID: spotCurrency, @@ -557,7 +516,6 @@ func TestGetSpotFilledOrdersInformation(t *testing.T) { // TestGetSpotMarketData API endpoint test func TestGetSpotMarketData(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotMarketDataRequest{ InstrumentID: spotCurrency, @@ -571,7 +529,6 @@ func TestGetSpotMarketData(t *testing.T) { // TestGetMarginTradingAccounts API endpoint test func TestGetMarginTradingAccounts(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetMarginTradingAccounts() testStandardErrorHandling(t, err) @@ -579,7 +536,6 @@ func TestGetMarginTradingAccounts(t *testing.T) { // TestGetMarginTradingAccountsForCurrency API endpoint test func TestGetMarginTradingAccountsForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetMarginTradingAccountsForCurrency(spotCurrency) testStandardErrorHandling(t, err) @@ -587,7 +543,6 @@ func TestGetMarginTradingAccountsForCurrency(t *testing.T) { // TestGetMarginBillDetails API endpoint test func TestGetMarginBillDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetMarginBillDetailsRequest{ InstrumentID: spotCurrency, @@ -600,7 +555,6 @@ func TestGetMarginBillDetails(t *testing.T) { // TestGetMarginAccountSettings API endpoint test func TestGetMarginAccountSettings(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetMarginAccountSettings("") testStandardErrorHandling(t, err) @@ -608,7 +562,6 @@ func TestGetMarginAccountSettings(t *testing.T) { // TestGetMarginAccountSettingsForCurrency API endpoint test func TestGetMarginAccountSettingsForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetMarginAccountSettings(spotCurrency) testStandardErrorHandling(t, err) @@ -681,7 +634,7 @@ func TestPlaceMarginOrderMarket(t *testing.T) { func TestPlaceMultipleMarginOrders(t *testing.T) { TestSetRealOrderDefaults(t) t.Parallel() - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -691,7 +644,7 @@ func TestPlaceMultipleMarginOrders(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, + ord, } _, errs := o.PlaceMultipleMarginOrders(request) @@ -702,9 +655,8 @@ func TestPlaceMultipleMarginOrders(t *testing.T) { // TestPlaceMultipleMarginOrdersOverCurrencyLimits API logic test func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) { - TestSetDefaults(t) t.Parallel() - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -714,11 +666,11 @@ func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, - order, - order, - order, - order, + ord, + ord, + ord, + ord, + ord, } _, errs := o.PlaceMultipleMarginOrders(request) @@ -729,9 +681,8 @@ func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) { // TestPlaceMultipleMarginOrdersOverPairLimits API logic test func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) { - TestSetDefaults(t) t.Parallel() - order := okgroup.PlaceOrderRequest{ + ord := okgroup.PlaceOrderRequest{ InstrumentID: spotCurrency, Type: order.Limit.Lower(), Side: order.Buy.Lower(), @@ -741,7 +692,7 @@ func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) { } request := []okgroup.PlaceOrderRequest{ - order, + ord, } pairs := currency.Pairs{ @@ -752,8 +703,8 @@ func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) { } for x := range pairs { - order.InstrumentID = pairs[x].Format("-", false).String() - request = append(request, order) + ord.InstrumentID = pairs[x].Format("-", false).String() + request = append(request, ord) } _, errs := o.PlaceMultipleMarginOrders(request) @@ -807,7 +758,6 @@ func TestCancelMultipleMarginOrdersOverCurrencyLimits(t *testing.T) { // TestGetMarginOrders API endpoint test func TestGetMarginOrders(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotOrdersRequest{ InstrumentID: spotCurrency, @@ -819,7 +769,6 @@ func TestGetMarginOrders(t *testing.T) { // TestGetMarginOpenOrders API endpoint test func TestGetMarginOpenOrders(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotOpenOrdersRequest{} _, err := o.GetMarginOpenOrders(request) @@ -828,7 +777,6 @@ func TestGetMarginOpenOrders(t *testing.T) { // TestGetMarginOrder API endpoint test func TestGetMarginOrder(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotOrderRequest{ OrderID: "1234", @@ -840,7 +788,6 @@ func TestGetMarginOrder(t *testing.T) { // TestGetMarginTransactionDetails API endpoint test func TestGetMarginTransactionDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSpotTransactionDetailsRequest{ OrderID: 1234, @@ -869,7 +816,6 @@ func getFutureInstrumentID() string { // TestGetFuturesPostions API endpoint test func TestGetFuturesPostions(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetFuturesPostions() testStandardErrorHandling(t, err) @@ -877,7 +823,6 @@ func TestGetFuturesPostions(t *testing.T) { // TestGetFuturesPostionsForCurrency API endpoint test func TestGetFuturesPostionsForCurrency(t *testing.T) { - TestSetDefaults(t) currencyContract := getFutureInstrumentID() _, err := o.GetFuturesPostionsForCurrency(currencyContract) testStandardErrorHandling(t, err) @@ -885,7 +830,6 @@ func TestGetFuturesPostionsForCurrency(t *testing.T) { // TestGetFuturesAccountOfAllCurrencies API endpoint test func TestGetFuturesAccountOfAllCurrencies(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetFuturesAccountOfAllCurrencies() testStandardErrorHandling(t, err) @@ -893,7 +837,6 @@ func TestGetFuturesAccountOfAllCurrencies(t *testing.T) { // TestGetFuturesAccountOfACurrency API endpoint test func TestGetFuturesAccountOfACurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetFuturesAccountOfACurrency(currency.BTC.String()) testStandardErrorHandling(t, err) @@ -901,7 +844,6 @@ func TestGetFuturesAccountOfACurrency(t *testing.T) { // TestGetFuturesLeverage API endpoint test func TestGetFuturesLeverage(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetFuturesLeverage(currency.BTC.String()) testStandardErrorHandling(t, err) @@ -922,7 +864,6 @@ func TestSetFuturesLeverage(t *testing.T) { // TestGetFuturesBillDetails API endpoint test func TestGetFuturesBillDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetFuturesBillDetails(okgroup.GetSpotBillDetailsForCurrencyRequest{ Currency: currency.BTC.String(), @@ -987,7 +928,6 @@ func TestCancelMultipleFuturesOrders(t *testing.T) { // TestGetFuturesOrderList API endpoint test func TestGetFuturesOrderList(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesOrderList(okgroup.GetFuturesOrdersListRequest{ InstrumentID: getFutureInstrumentID(), Status: 6, @@ -997,7 +937,6 @@ func TestGetFuturesOrderList(t *testing.T) { // TestGetFuturesOrderDetails API endpoint test func TestGetFuturesOrderDetails(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesOrderDetails(okgroup.GetFuturesOrderDetailsRequest{ InstrumentID: getFutureInstrumentID(), OrderID: 1, @@ -1007,7 +946,6 @@ func TestGetFuturesOrderDetails(t *testing.T) { // TestGetFuturesTransactionDetails API endpoint test func TestGetFuturesTransactionDetails(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesTransactionDetails(okgroup.GetFuturesTransactionDetailsRequest{ InstrumentID: getFutureInstrumentID(), OrderID: 1, @@ -1017,7 +955,6 @@ func TestGetFuturesTransactionDetails(t *testing.T) { // TestGetFuturesContractInformation API endpoint test func TestGetFuturesContractInformation(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetFuturesContractInformation() if err != nil { @@ -1027,7 +964,6 @@ func TestGetFuturesContractInformation(t *testing.T) { // TestGetAllFuturesTokenInfo API endpoint test func TestGetAllFuturesTokenInfo(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetAllFuturesTokenInfo() if err != nil { @@ -1037,7 +973,6 @@ func TestGetAllFuturesTokenInfo(t *testing.T) { // TestGetAllFuturesTokenInfo API endpoint test func TestGetFuturesTokenInfoForCurrency(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesTokenInfoForCurrency(getFutureInstrumentID()) if err != nil { t.Error(err) @@ -1046,7 +981,6 @@ func TestGetFuturesTokenInfoForCurrency(t *testing.T) { // TestGetFuturesFilledOrder API endpoint test func TestGetFuturesFilledOrder(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesFilledOrder(okgroup.GetFuturesFilledOrderRequest{ InstrumentID: getFutureInstrumentID(), }) @@ -1055,14 +989,12 @@ func TestGetFuturesFilledOrder(t *testing.T) { // TestGetFuturesHoldAmount API endpoint test func TestGetFuturesHoldAmount(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesHoldAmount(getFutureInstrumentID()) testStandardErrorHandling(t, err) } // TestGetFuturesHoldAmount API endpoint test func TestGetFuturesIndices(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesIndices(getFutureInstrumentID()) if err != nil { t.Error(err) @@ -1071,7 +1003,6 @@ func TestGetFuturesIndices(t *testing.T) { // TestGetFuturesHoldAmount API endpoint test func TestGetFuturesExchangeRates(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetFuturesExchangeRates() if err != nil { @@ -1081,7 +1012,6 @@ func TestGetFuturesExchangeRates(t *testing.T) { // TestGetFuturesHoldAmount API endpoint test func TestGetFuturesEstimatedDeliveryPrice(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesEstimatedDeliveryPrice(getFutureInstrumentID()) if err != nil { t.Error(err) @@ -1090,7 +1020,6 @@ func TestGetFuturesEstimatedDeliveryPrice(t *testing.T) { // TestGetFuturesOpenInterests API endpoint test func TestGetFuturesOpenInterests(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesOpenInterests(getFutureInstrumentID()) if err != nil { t.Error(err) @@ -1099,7 +1028,6 @@ func TestGetFuturesOpenInterests(t *testing.T) { // TestGetFuturesOpenInterests API endpoint test func TestGetFuturesCurrentPriceLimit(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesCurrentPriceLimit(getFutureInstrumentID()) if err != nil { t.Error(err) @@ -1108,7 +1036,6 @@ func TestGetFuturesCurrentPriceLimit(t *testing.T) { // TestGetFuturesCurrentMarkPrice API endpoint test func TestGetFuturesCurrentMarkPrice(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesCurrentMarkPrice(getFutureInstrumentID()) if err != nil { t.Error(err) @@ -1117,7 +1044,6 @@ func TestGetFuturesCurrentMarkPrice(t *testing.T) { // TestGetFuturesForceLiquidatedOrders API endpoint test func TestGetFuturesForceLiquidatedOrders(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesForceLiquidatedOrders(okgroup.GetFuturesForceLiquidatedOrdersRequest{ InstrumentID: getFutureInstrumentID(), Status: "1", @@ -1129,14 +1055,12 @@ func TestGetFuturesForceLiquidatedOrders(t *testing.T) { // TestGetFuturesTagPrice API endpoint test func TestGetFuturesTagPrice(t *testing.T) { - TestSetDefaults(t) _, err := o.GetFuturesTagPrice(getFutureInstrumentID()) testStandardErrorHandling(t, err) } // TestGetSwapPostions API endpoint test func TestGetSwapPostions(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapPostions() testStandardErrorHandling(t, err) @@ -1144,7 +1068,6 @@ func TestGetSwapPostions(t *testing.T) { // TestGetSwapPostionsForContract API endpoint test func TestGetSwapPostionsForContract(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapPostionsForContract(fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD)) testStandardErrorHandling(t, err) @@ -1152,7 +1075,6 @@ func TestGetSwapPostionsForContract(t *testing.T) { // TestGetSwapAccountOfAllCurrency API endpoint test func TestGetSwapAccountOfAllCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapAccountOfAllCurrency() testStandardErrorHandling(t, err) @@ -1160,7 +1082,6 @@ func TestGetSwapAccountOfAllCurrency(t *testing.T) { // TestGetSwapAccountSettingsOfAContract API endpoint test func TestGetSwapAccountSettingsOfAContract(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapAccountSettingsOfAContract(fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD)) testStandardErrorHandling(t, err) @@ -1168,7 +1089,6 @@ func TestGetSwapAccountSettingsOfAContract(t *testing.T) { // TestSetSwapLeverageLevelOfAContract API endpoint test func TestSetSwapLeverageLevelOfAContract(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.SetSwapLeverageLevelOfAContract(okgroup.SetSwapLeverageLevelOfAContractRequest{ InstrumentID: fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD), @@ -1181,7 +1101,6 @@ func TestSetSwapLeverageLevelOfAContract(t *testing.T) { // TestGetSwapAccountSettingsOfAContract API endpoint test func TestGetSwapBillDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapBillDetails(okgroup.GetSpotBillDetailsForCurrencyRequest{ Currency: fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD), @@ -1252,7 +1171,6 @@ func TestCancelMultipleSwapOrders(t *testing.T) { // TestGetSwapOrderList API endpoint test func TestGetSwapOrderList(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapOrderList(okgroup.GetSwapOrderListRequest{ InstrumentID: fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD), @@ -1263,7 +1181,6 @@ func TestGetSwapOrderList(t *testing.T) { // TestGetSwapOrderDetails API endpoint test func TestGetSwapOrderDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapOrderDetails(okgroup.GetSwapOrderDetailsRequest{ InstrumentID: fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD), @@ -1274,7 +1191,6 @@ func TestGetSwapOrderDetails(t *testing.T) { // TestGetSwapTransactionDetails API endpoint test func TestGetSwapTransactionDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapTransactionDetails(okgroup.GetSwapTransactionDetailsRequest{ InstrumentID: fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD), @@ -1285,7 +1201,6 @@ func TestGetSwapTransactionDetails(t *testing.T) { // TestGetSwapContractInformation API endpoint test func TestGetSwapContractInformation(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapContractInformation() if err != nil { @@ -1295,7 +1210,6 @@ func TestGetSwapContractInformation(t *testing.T) { // TestGetAllSwapTokensInformation API endpoint test func TestGetAllSwapTokensInformation(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetAllSwapTokensInformation() if err != nil { @@ -1305,7 +1219,6 @@ func TestGetAllSwapTokensInformation(t *testing.T) { // TestGetSwapTokensInformationForCurrency API endpoint test func TestGetSwapTokensInformationForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapTokensInformationForCurrency(fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD)) if err != nil { @@ -1315,7 +1228,6 @@ func TestGetSwapTokensInformationForCurrency(t *testing.T) { // TestGetSwapFilledOrdersData API endpoint test func TestGetSwapFilledOrdersData(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapFilledOrdersData(&okgroup.GetSwapFilledOrdersDataRequest{ InstrumentID: fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD), @@ -1328,7 +1240,6 @@ func TestGetSwapFilledOrdersData(t *testing.T) { // TestGetSwapMarketData API endpoint test func TestGetSwapMarketData(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetSwapMarketDataRequest{ InstrumentID: fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD), @@ -1342,7 +1253,6 @@ func TestGetSwapMarketData(t *testing.T) { // TestGetSwapIndeces API endpoint test func TestGetSwapIndeces(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapIndices(fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD)) if err != nil { @@ -1352,7 +1262,6 @@ func TestGetSwapIndeces(t *testing.T) { // TestGetSwapExchangeRates API endpoint test func TestGetSwapExchangeRates(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapExchangeRates() if err != nil { @@ -1362,7 +1271,6 @@ func TestGetSwapExchangeRates(t *testing.T) { // TestGetSwapOpenInterest API endpoint test func TestGetSwapOpenInterest(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapOpenInterest(fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD)) if err != nil { @@ -1372,7 +1280,6 @@ func TestGetSwapOpenInterest(t *testing.T) { // TestGetSwapCurrentPriceLimits API endpoint test func TestGetSwapCurrentPriceLimits(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapCurrentPriceLimits(fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD)) if err != nil { @@ -1382,7 +1289,6 @@ func TestGetSwapCurrentPriceLimits(t *testing.T) { // TestGetSwapForceLiquidatedOrders API endpoint test func TestGetSwapForceLiquidatedOrders(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapForceLiquidatedOrders(okgroup.GetSwapForceLiquidatedOrdersRequest{ InstrumentID: fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD), @@ -1395,7 +1301,6 @@ func TestGetSwapForceLiquidatedOrders(t *testing.T) { // TestGetSwapOnHoldAmountForOpenOrders API endpoint test func TestGetSwapOnHoldAmountForOpenOrders(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapOnHoldAmountForOpenOrders(fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD)) testStandardErrorHandling(t, err) @@ -1403,7 +1308,6 @@ func TestGetSwapOnHoldAmountForOpenOrders(t *testing.T) { // TestGetSwapNextSettlementTime API endpoint test func TestGetSwapNextSettlementTime(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapNextSettlementTime(fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD)) if err != nil { @@ -1413,7 +1317,6 @@ func TestGetSwapNextSettlementTime(t *testing.T) { // TestGetSwapMarkPrice API endpoint test func TestGetSwapMarkPrice(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapMarkPrice(fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD)) if err != nil { @@ -1423,7 +1326,6 @@ func TestGetSwapMarkPrice(t *testing.T) { // TestGetSwapFundingRateHistory API endpoint test func TestGetSwapFundingRateHistory(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetSwapFundingRateHistory(okgroup.GetSwapFundingRateHistoryRequest{ InstrumentID: fmt.Sprintf("%v-%v-SWAP", currency.BTC, currency.USD), @@ -1436,7 +1338,6 @@ func TestGetSwapFundingRateHistory(t *testing.T) { // TestGetETT API endpoint test func TestGetETT(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetETT() testStandardErrorHandling(t, err) @@ -1444,7 +1345,6 @@ func TestGetETT(t *testing.T) { // TestGetETTAccountInformationForCurrency API endpoint test func TestGetETTAccountInformationForCurrency(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetETTBillsDetails(currency.BTC.String()) testStandardErrorHandling(t, err) @@ -1452,7 +1352,6 @@ func TestGetETTAccountInformationForCurrency(t *testing.T) { // TestGetETTBillsDetails API endpoint test func TestGetETTBillsDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetETTBillsDetails(currency.BTC.String()) testStandardErrorHandling(t, err) @@ -1487,7 +1386,6 @@ func TestCancelETTOrder(t *testing.T) { // Or when it is submitted as URL params // Unsure how to fix func TestGetETTOrderList(t *testing.T) { - TestSetDefaults(t) t.Parallel() request := okgroup.GetETTOrderListRequest{ Type: 1, @@ -1501,7 +1399,6 @@ func TestGetETTOrderList(t *testing.T) { // TestGetETTOrderDetails API endpoint test func TestGetETTOrderDetails(t *testing.T) { - TestSetDefaults(t) t.Parallel() _, err := o.GetETTOrderDetails("888845020785408") testStandardErrorHandling(t, err) @@ -1510,7 +1407,6 @@ func TestGetETTOrderDetails(t *testing.T) { // TestGetETTConstituents API endpoint test func TestGetETTConstituents(t *testing.T) { t.Skip("ETT currently unavailable") - TestSetDefaults(t) t.Parallel() _, err := o.GetETTConstituents("OK06ETT") if err != nil { @@ -1521,7 +1417,6 @@ func TestGetETTConstituents(t *testing.T) { // TestGetETTSettlementPriceHistory API endpoint test func TestGetETTSettlementPriceHistory(t *testing.T) { t.Skip("ETT currently unavailable") - TestSetDefaults(t) t.Parallel() _, err := o.GetETTSettlementPriceHistory("OK06ETT") if err != nil { @@ -1535,7 +1430,6 @@ func TestGetETTSettlementPriceHistory(t *testing.T) { // Attempts to subscribe to a channel that doesn't exist // Will log in if credentials are present func TestSendWsMessages(t *testing.T) { - TestSetDefaults(t) if !o.Websocket.IsEnabled() && !o.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(wshandler.WebsocketNotEnabled) } @@ -1588,7 +1482,6 @@ func TestGetAssetTypeFromTableName(t *testing.T) { // TestGetWsChannelWithoutOrderType logic test func TestGetWsChannelWithoutOrderType(t *testing.T) { - TestSetDefaults(t) t.Parallel() str := "spot/depth5:BTC-USDT" expected := "depth5" @@ -1611,7 +1504,6 @@ func TestGetWsChannelWithoutOrderType(t *testing.T) { // TestOrderBookUpdateChecksumCalculator logic test func TestOrderBookUpdateChecksumCalculator(t *testing.T) { - TestSetDefaults(t) original := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"BTC-USDT","asks":[["3864.6786","0.145",1],["3864.7682","0.005",1],["3864.9851","0.57",1],["3864.9852","0.30137754",1],["3864.9986","2.81818419",1],["3864.9995","0.002",1],["3865","0.0597",1],["3865.0309","0.4",1],["3865.1995","0.004",1],["3865.3995","0.004",1],["3865.5995","0.004",1],["3865.7995","0.004",1],["3865.9995","0.004",1],["3866.0961","0.25865886",1],["3866.1995","0.004",1],["3866.3995","0.004",1],["3866.4004","0.3243",2],["3866.5995","0.004",1],["3866.7633","0.44247086",1],["3866.7995","0.004",1],["3866.9197","0.511",1],["3867.256","0.51716256",1],["3867.3951","0.02588112",1],["3867.4014","0.025",1],["3867.4566","0.02499999",1],["3867.4675","4.01155057",5],["3867.5515","1.1",1],["3867.6113","0.009",1],["3867.7349","0.026",1],["3867.7781","0.03738652",1],["3867.9163","0.0521",1],["3868.0381","0.34354941",1],["3868.0436","0.051",1],["3868.0657","0.90552172",3],["3868.1819","0.03863346",1],["3868.2013","0.194",1],["3868.346","0.051",1],["3868.3863","0.01155",1],["3868.7716","0.009",1],["3868.947","0.025",1],["3868.98","0.001",1],["3869.0764","1.03487931",1],["3869.2773","0.07724578",1],["3869.4039","0.025",1],["3869.4068","1.03",1],["3869.7068","2.06976398",1],["3870","0.5",1],["3870.0465","0.01",1],["3870.7042","0.02099651",1],["3870.9451","2.07047375",1],["3871.5254","1.2",1],["3871.5596","0.001",1],["3871.6605","0.01035032",1],["3871.7179","2.07047375",1],["3871.8816","0.51751625",1],["3872.1","0.75",1],["3872.2464","0.0646",1],["3872.3747","0.283",1],["3872.4039","0.2",1],["3872.7655","0.23179307",1],["3872.8005","2.06976398",1],["3873.1509","2",1],["3873.3215","0.26",1],["3874.1392","0.001",1],["3874.1487","3.88224364",4],["3874.1685","1.8",1],["3874.5571","0.08974762",1],["3874.734","2.06976398",1],["3874.99","0.3",1],["3875","1.001",2],["3875.0041","1.03505051",1],["3875.45","0.3",1],["3875.4766","0.15",1],["3875.7057","0.51751625",1],["3876","0.001",1],["3876.68","0.3",1],["3876.7188","0.001",1],["3877","0.75",1],["3877.31","0.035",1],["3877.38","0.3",1],["3877.7","0.3",1],["3877.88","0.3",1],["3878.0364","0.34770122",1],["3878.4525","0.48579748",1],["3878.4955","0.02812511",1],["3878.8855","0.00258579",1],["3878.9605","0.895",1],["3879","0.001",1],["3879.2984","0.002",2],["3879.432","0.001",1],["3879.6313","6",1],["3879.9999","0.002",2],["3880","1.25132834",5],["3880.2526","0.04075162",1],["3880.7145","0.0647",1],["3881.2469","1.883",1],["3881.878","0.002",2],["3884.4576","0.002",2],["3885","0.002",2],["3885.2233","0.28304103",1],["3885.7416","18",1],["3886","0.001",1],["3886.1554","5.4",1],["3887","0.001",1],["3887.0372","0.002",2],["3887.2559","0.05214011",1],["3887.9238","0.0019",1],["3888","0.15810538",4],["3889","0.001",1],["3889.5175","0.50510653",1],["3889.6168","0.002",2],["3889.9999","0.001",1],["3890","2.34968109",4],["3890.5222","0.00257806",1],["3891.2659","5",1],["3891.9999","0.00893897",1],["3892.1964","0.002",2],["3892.4358","0.0176",1],["3893.1388","1.4279",1],["3894","0.0026321",1],["3894.776","0.001",1],["3895","1.501",2],["3895.379","0.25881288",1],["3897","0.05",1],["3897.3556","0.001",1],["3897.8432","0.73708079",1],["3898","3.31353018",7],["3898.4462","4.757",1],["3898.6","0.47159638",1],["3898.8769","0.0129",1],["3899","6",2],["3899.6516","0.025",1],["3899.9352","0.001",1],["3899.9999","0.013",2],["3900","22.37447743",24],["3900.9999","0.07763916",1],["3901","0.10192487",1],["3902.1937","0.00257034",1],["3902.3991","1.5532141",1],["3902.5148","0.001",1],["3904","1.49331984",1],["3904.9999","0.95905447",1],["3905","0.501",2],["3905.0944","0.001",1],["3905.61","0.099",1],["3905.6801","0.54343686",1],["3906.2901","0.0258",1],["3907.674","0.001",1],["3907.85","1.35778084",1],["3908","0.03846153",1],["3908.23","1.95189531",1],["3908.906","0.03148978",1],["3909","0.001",1],["3909.9999","0.01398721",2],["3910","0.016",2],["3910.2536","0.001",1],["3912.5406","0.88270517",1],["3912.8332","0.001",1],["3913","1.2640608",1],["3913.87","1.69114184",1],["3913.9003","0.00256266",1],["3914","1.21766411",1],["3915","0.001",1],["3915.4128","0.001",1],["3915.7425","6.848",1],["3916","0.0050949",1],["3917.36","1.28658296",1],["3917.9924","0.001",1],["3919","0.001",1],["3919.9999","0.001",1],["3920","1.21171832",3],["3920.0002","0.20217038",1],["3920.572","0.001",1],["3921","0.128",1],["3923.0756","0.00148064",1],["3923.1516","0.001",1],["3923.86","1.38831714",1],["3925","0.01867801",2],["3925.642","0.00255499",1],["3925.7312","0.001",1],["3926","0.04290757",1],["3927","0.023",1],["3927.3175","0.01212865",1],["3927.65","1.51375612",1],["3928","0.5",1],["3928.3108","0.001",1],["3929","0.001",1],["3929.9999","0.01519338",2],["3930","0.0174985",3],["3930.21","1.49335799",1],["3930.8904","0.001",1],["3932.2999","0.01953",1],["3932.8962","7.96",1],["3933.0387","11.808",1],["3933.47","0.001",1],["3934","1.40839932",1],["3935","0.001",1],["3936.8","0.62879518",1],["3937.23","1.56977841",1],["3937.4189","0.00254735",1]],"bids":[["3864.5217","0.00540709",1],["3864.5216","0.14068758",2],["3864.2275","0.01033576",1],["3864.0989","0.00825047",1],["3864.0273","0.38",1],["3864.0272","0.4",1],["3863.9957","0.01083539",1],["3863.9184","0.01653723",1],["3863.8282","0.25588165",1],["3863.8153","0.154",1],["3863.7791","1.14122492",1],["3863.6866","0.01733662",1],["3863.6093","0.02645958",1],["3863.3775","0.02773862",1],["3863.0297","0.513",1],["3863.0286","1.1028564",2],["3862.8489","0.01",1],["3862.5972","0.01890179",1],["3862.3431","0.01152944",1],["3862.313","0.009",1],["3862.2445","0.90551002",3],["3862.0734","0.014",1],["3862.0539","0.64976067",1],["3861.8586","0.025",1],["3861.7888","0.025",1],["3861.7673","0.008",1],["3861.5785","0.01",1],["3861.3895","0.005",1],["3861.3338","0.25875855",1],["3861.161","0.01",1],["3861.1111","0.03863352",1],["3861.0732","0.51703882",1],["3860.9116","0.17754895",1],["3860.75","0.19",1],["3860.6554","0.015",1],["3860.6172","0.005",1],["3860.6088","0.008",1],["3860.4724","0.12940042",1],["3860.4424","0.25880084",1],["3860.42","0.01",1],["3860.3725","0.51760102",1],["3859.8449","0.005",1],["3859.8285","0.03738652",1],["3859.7638","0.07726703",1],["3859.4502","0.008",1],["3859.3772","0.05173471",1],["3859.3409","0.194",1],["3859","5",1],["3858.827","0.0521",1],["3858.8208","0.001",1],["3858.679","0.26",1],["3858.4814","0.07477305",1],["3858.1669","1.03503422",1],["3857.6005","0.006",1],["3857.4005","0.004",1],["3857.2005","0.004",1],["3857.1871","1.218",1],["3857.0005","0.004",1],["3856.8135","0.0646",1],["3856.8005","0.004",1],["3856.2412","0.001",1],["3856.2349","1.03503422",1],["3856.0197","0.01037339",1],["3855.8781","0.23178117",1],["3855.8005","0.004",1],["3855.7165","0.00259355",1],["3855.4858","0.25875855",1],["3854.4584","0.01",1],["3853.6616","0.001",1],["3853.1373","0.92",1],["3852.5072","0.48599702",1],["3851.3926","0.13008333",1],["3851.082","0.001",1],["3850.9317","2",1],["3850.6359","0.34770165",1],["3850.2058","0.51751624",1],["3850.0823","0.15",1],["3850.0042","0.5175171",1],["3850","0.001",1],["3849.6325","1.8",1],["3849.41","0.3",1],["3848.9686","1.85",1],["3848.7426","0.18511466",1],["3848.52","0.3",1],["3848.5024","0.001",1],["3848.42","0.3",1],["3848.1618","2.204",1],["3847.77","0.3",1],["3847.48","0.3",1],["3847.3581","2.05",1],["3846.8259","0.0646",1],["3846.59","0.3",1],["3846.49","0.3",1],["3845.9228","0.001",1],["3844.184","0.00260133",1],["3844.0092","6.3",1],["3843.3432","0.001",1],["3841","0.06300963",1],["3840.7636","0.001",1],["3840","0.201",3],["3839.7681","18",1],["3839.5328","0.05214011",1],["3838.184","0.001",1],["3837.2344","0.27589557",1],["3836.6479","5.2",1],["3836","2.37196773",3],["3835.6044","0.001",1],["3833.6053","0.25873556",1],["3833.0248","0.001",1],["3833","0.8726502",1],["3832.6859","0.00260913",1],["3832","0.007",1],["3831.637","6",1],["3831.0602","0.001",1],["3830.4452","0.001",1],["3830","0.20375718",4],["3829.7125","0.07833486",1],["3829.6283","0.3519681",1],["3829","0.0039261",1],["3827.8656","0.001",1],["3826.0001","0.53251232",1],["3826","0.0509",1],["3825.7834","0.00698562",1],["3825.286","0.001",1],["3823.0001","0.03010127",1],["3822.8014","0.00261588",1],["3822.7064","0.001",1],["3822.2","1",1],["3822.1121","0.35994101",1],["3821.2222","0.00261696",1],["3821","0.001",1],["3820.1268","0.001",1],["3820","1.12992803",4],["3819","0.01331195",2],["3817.5472","0.001",1],["3816","1.13807184",2],["3815.8343","0.32463428",1],["3815.7834","0.00525295",1],["3815","28.99386799",4],["3814.9676","0.001",1],["3813","0.91303023",4],["3812.388","0.002",2],["3811.2257","0.07",1],["3810","0.32573997",2],["3809.8084","0.001",1],["3809.7928","0.00262481",1],["3807.2288","0.001",1],["3806.8421","0.07003461",1],["3806","0.19",1],["3805.8041","0.05678805",1],["3805","1.01",2],["3804.6492","0.001",1],["3804.3551","0.1",1],["3803","0.005",1],["3802.22","2.05042631",1],["3802.0696","0.001",1],["3802","1.63290092",1],["3801.2257","0.07",1],["3801","57.4",3],["3800.9853","0.02492278",1],["3800.8421","0.06503533",1],["3800.7844","0.02812628",1],["3800.0001","0.00409473",1],["3800","17.91401074",15],["3799.49","0.001",1],["3799","0.1",1],["3796.9104","0.001",1],["3796","9.00128053",2],["3795.5441","0.0028",1],["3794.3308","0.001",1],["3791","55",1],["3790.7777","0.07",1],["3790","12.03238184",7],["3789","1",1],["3788","0.21110454",2],["3787.2959","9",1],["3786.592","0.001",1],["3786","9.01916822",2],["3785","12.87914268",5],["3784.0124","0.001",1],["3781.4328","0.002",2],["3781","56.3",2],["3780.7777","0.07",1],["3780","23.41537654",10],["3778.8532","0.002",2],["3776","9",1],["3774","0.003",1],["3772.2481","0.06901672",1],["3771","55.1",2],["3770.7777","0.07",1],["3770","7.30268416",5],["3769","0.25",1],["3768","1.3725",3],["3766.66","0.02",1],["3766","7.64837924",2],["3765.58","1.22775492",1],["3762.58","1.22873383",1],["3761","51.68262164",1],["3760.8031","0.0399",1],["3760.7777","0.07",1]],"timestamp":"2019-03-06T23:19:17.705Z","checksum":-1785549915}]}` update := `{"table":"spot/depth","action":"update","data":[{"instrument_id":"BTC-USDT","asks":[["3864.6786","0",0],["3864.9852","0",0],["3865.9994","0.48402971",1],["3866.4004","0.001",1],["3866.7995","0.3273",2],["3867.4566","0",0],["3867.7031","0.025",1],["3868.0436","0",0],["3868.346","0",0],["3868.3695","0.051",1],["3870.9243","0.642",1],["3874.9942","0.51751796",1],["3875.7057","0",0],["3939","0.001",1]],"bids":[["3864.55","0.0565449",1],["3863.8282","0",0],["3863.8153","0",0],["3863.7898","0.01320077",1],["3863.4807","0.02112123",1],["3863.3002","0.04233533",1],["3863.1717","0.03379397",1],["3863.0685","0.04438179",1],["3863.0286","0.7362564",1],["3862.9912","0.06773651",1],["3862.8626","0.05407035",1],["3862.7595","0.07101087",1],["3862.313","0.3756",2],["3862.1848","0.012",1],["3862.0734","0",0],["3861.8391","0.025",1],["3861.7888","0",0],["3856.6716","0.38893641",1],["3768","0",0],["3766.66","0",0],["3766","0",0],["3765.58","0",0],["3762.58","0",0],["3761","0",0],["3760.8031","0",0],["3760.7777","0",0]],"timestamp":"2019-03-06T23:19:18.239Z","checksum":-1587788848}]}` var dataResponse okgroup.WebsocketDataResponse @@ -1638,7 +1530,6 @@ func TestOrderBookUpdateChecksumCalculator(t *testing.T) { // TestOrderBookUpdateChecksumCalculatorWithDash logic test func TestOrderBookUpdateChecksumCalculatorWith8DecimalPlaces(t *testing.T) { - TestSetDefaults(t) original := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"WAVES-BTC","asks":[["0.000714","1.15414979",1],["0.000715","3.3",2],["0.000717","426.71348",2],["0.000719","140.84507042",1],["0.00072","590.77",1],["0.000721","991.77",1],["0.000724","0.3532032",1],["0.000725","58.82698567",1],["0.000726","1033.15469748",2],["0.000729","0.35320321",1],["0.00073","352.77",1],["0.000735","0.38469748",1],["0.000736","625.77",1],["0.00075191","152.44796961",1],["0.00075192","114.3359772",1],["0.00075193","85.7519829",1],["0.00075194","64.31398718",1],["0.00075195","48.23549038",1],["0.00075196","36.17661779",1],["0.00075199","61.04804253",1],["0.0007591","70.71318474",1],["0.0007621","53.03488855",1],["0.00076211","39.77616642",1],["0.00076212","29.83212481",1],["0.0007635","22.37409361",1],["0.00076351","29.36599786",2],["0.00076352","9.43907074",1],["0.00076353","7.07930306",1],["0.00076354","14.15860612",1],["0.00076355","3.53965153",1],["0.00076369","3.53965153",1],["0.0008","34.36841101",1],["0.00082858","1.69936503",1],["0.00083232","2.8",1],["0.00084","15.69220129",1],["0.00085","4.42785042",1],["0.00088","0.1",1],["0.000891","0.1",1],["0.0009","12.41486491",2],["0.00093","5",1],["0.0012","12.31486492",1],["0.00531314","6.91803114",1],["0.00799999","0.02",1],["0.0084","0.05989",1],["0.00931314","5.18852336",1],["0.0799999","0.02",1],["0.499","6.00423396",1],["0.5","0.4995",1],["0.799999","0.02",1],["4.99","2",1],["5","3.98583144",1],["7.99999999","0.02",1],["79.99999999","0.02",1],["799.99999999","0.02986704",1]],"bids":[["0.000709","222.91679881",3],["0.000703","0.47161952",1],["0.000701","140.73015789",2],["0.0007","0.3",1],["0.000699","401",1],["0.000698","232.61801667",2],["0.000689","0.71396896",1],["0.000688","0.69910125",1],["0.000613","227.54771052",1],["0.0005","0.01",1],["0.00026789","3.69905341",1],["0.000238","2.4",1],["0.00022","0.53",1],["0.0000055","374.09871696",1],["0.00000056","222",1],["0.00000055","736.84761363",1],["0.0000002","999",1],["0.00000009","1222.22222417",1],["0.00000008","20868.64520447",1],["0.00000002","110000",1],["0.00000001","10000",1]],"timestamp":"2019-03-12T22:22:42.274Z","checksum":1319037905}]}` update := `{"table":"spot/depth","action":"update","data":[{"instrument_id":"WAVES-BTC","asks":[["0.000715","100.48199596",3],["0.000716","62.21679881",1]],"bids":[["0.000713","38.95772168",1]],"timestamp":"2019-03-12T22:22:42.938Z","checksum":-131160897}]}` var dataResponse okgroup.WebsocketDataResponse @@ -1696,7 +1587,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() o.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -1708,7 +1599,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - TestSetDefaults(t) t.Parallel() var feeBuilder = setFeeBuilder() // CryptocurrencyTradeFee Basic @@ -1770,7 +1660,6 @@ func TestGetFee(t *testing.T) { // TestFormatWithdrawPermissions helper test func TestFormatWithdrawPermissions(t *testing.T) { - TestSetDefaults(t) t.Parallel() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText withdrawPermissions := o.FormatWithdrawPermissions() @@ -1901,3 +1790,25 @@ func TestWithdrawInternationalBank(t *testing.T) { err) } } + +// TestGetOrderbook logic test +func TestGetOrderbook(t *testing.T) { + t.Parallel() + _, err := o.GetOrderBook(okgroup.GetOrderBookRequest{InstrumentID: "BTC-USDT"}, + asset.Spot) + if err != nil { + t.Error(err) + } + contract := getFutureInstrumentID() + _, err = o.GetOrderBook(okgroup.GetOrderBookRequest{InstrumentID: contract}, + asset.Futures) + if err != nil { + t.Error(err) + } + + _, err = o.GetOrderBook(okgroup.GetOrderBookRequest{InstrumentID: "BTC-USD-SWAP"}, + asset.PerpetualSwap) + if err != nil { + t.Error(err) + } +} diff --git a/exchanges/okgroup/okgroup.go b/exchanges/okgroup/okgroup.go index afd95cf5..0b6b7c93 100644 --- a/exchanges/okgroup/okgroup.go +++ b/exchanges/okgroup/okgroup.go @@ -233,8 +233,8 @@ func (o *OKGroup) PlaceMultipleSpotOrders(request []PlaceOrderRequest) (map[stri var orderErrors []error for currency, orderResponse := range resp { - for _, order := range orderResponse { - if !order.Result { + for i := range orderResponse { + if !orderResponse[i].Result { orderErrors = append(orderErrors, fmt.Errorf("order for currency %v failed to be placed", currency)) } } @@ -262,15 +262,15 @@ func (o *OKGroup) CancelMultipleSpotOrders(request CancelMultipleSpotOrdersReque } for currency, orderResponse := range resp { - for _, order := range orderResponse { + for i := range orderResponse { cancellationResponse := CancelMultipleSpotOrdersResponse{ - OrderID: order.OrderID, - Result: order.Result, - ClientOID: order.ClientOID, + OrderID: orderResponse[i].OrderID, + Result: orderResponse[i].Result, + ClientOID: orderResponse[i].ClientOID, } - if !order.Result { - cancellationResponse.Error = fmt.Errorf("order %v for currency %v failed to be cancelled", order.OrderID, currency) + if !orderResponse[i].Result { + cancellationResponse.Error = fmt.Errorf("order %v for currency %v failed to be cancelled", orderResponse[i].OrderID, currency) } resp[currency] = append(resp[currency], cancellationResponse) @@ -454,8 +454,8 @@ func (o *OKGroup) PlaceMultipleMarginOrders(request []PlaceOrderRequest) (map[st var orderErrors []error for currency, orderResponse := range resp { - for _, order := range orderResponse { - if !order.Result { + for i := range orderResponse { + if !orderResponse[i].Result { orderErrors = append(orderErrors, fmt.Errorf("order for currency %v failed to be placed", currency)) } } @@ -484,9 +484,9 @@ func (o *OKGroup) CancelMultipleMarginOrders(request CancelMultipleSpotOrdersReq var orderErrors []error for currency, orderResponse := range resp { - for _, order := range orderResponse { - if !order.Result { - orderErrors = append(orderErrors, fmt.Errorf("order %v for currency %v failed to be cancelled", order.OrderID, currency)) + for i := range orderResponse { + if !orderResponse[i].Result { + orderErrors = append(orderErrors, fmt.Errorf("order %v for currency %v failed to be cancelled", orderResponse[i].OrderID, currency)) } } } diff --git a/exchanges/okgroup/okgroup_test.go b/exchanges/okgroup/okgroup_test.go deleted file mode 100644 index edcbf618..00000000 --- a/exchanges/okgroup/okgroup_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package okgroup - -import ( - "log" - "os" - "testing" - "time" - - "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/config" - exchange "github.com/thrasher-corp/gocryptotrader/exchanges" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" -) - -const ( - apiKey = "" - apiSecret = "" - - testAPIURL = "https://www.okex.com/api/" - testAPIVersion = "/v3/" -) - -var o OKGroup - -func TestMain(m *testing.M) { - cfg := config.GetConfig() - err := cfg.LoadConfig("../../testdata/configtest.json", true) - if err != nil { - log.Fatal("okgroup load config error", err) - } - okgroup, err := cfg.GetExchangeConfig("Okex") - if err != nil { - log.Fatal("okgroup Setup() init error", err) - } - - okgroup.API.AuthenticatedSupport = true - okgroup.API.Credentials.Key = apiKey - okgroup.API.Credentials.Secret = apiSecret - o.API.Endpoints.URL = testAPIURL - o.APIVersion = testAPIVersion - - o.Requester = request.New("okgroup_test_things", - request.NewRateLimit(time.Second, 10), - request.NewRateLimit(time.Second, 10), - common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), - ) - o.Websocket = wshandler.New() - - err = o.Setup(okgroup) - if err != nil { - log.Fatal("okgroup setup error", err) - } - os.Exit(m.Run()) -} - -func TestGetOrderbook(t *testing.T) { - t.Parallel() - _, err := o.GetOrderBook(GetOrderBookRequest{InstrumentID: "BTC-USDT"}, - asset.Spot) - if err != nil { - t.Error(err) - } - - // futures expire and break test, will need to mock this in the future - _, err = o.GetOrderBook(GetOrderBookRequest{InstrumentID: "Payload"}, - asset.Futures) - if err == nil { - t.Error("error cannot be nil") - } - - _, err = o.GetOrderBook(GetOrderBookRequest{InstrumentID: "BTC-USD-SWAP"}, - asset.PerpetualSwap) - if err != nil { - t.Error(err) - } -} diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go index 91658ba0..c3dcca42 100644 --- a/exchanges/okgroup/okgroup_wrapper.go +++ b/exchanges/okgroup/okgroup_wrapper.go @@ -273,6 +273,9 @@ func (o *OKGroup) SubmitOrder(s *order.Submit) (resp order.SubmitResponse, err e resp.IsOrderPlaced = orderResponse.Result resp.OrderID = orderResponse.OrderID + if s.OrderType == order.Market { + resp.FullyMatched = true + } return } diff --git a/exchanges/order/order_test.go b/exchanges/order/order_test.go index 2d7b7cd3..ed019e9d 100644 --- a/exchanges/order/order_test.go +++ b/exchanges/order/order_test.go @@ -1,6 +1,7 @@ package order import ( + "errors" "strings" "testing" "time" @@ -398,3 +399,138 @@ func TestSortOrdersByOrderType(t *testing.T) { orders[0].OrderType) } } + +var stringsToOrderSide = []struct { + in string + out Side + err error +}{ + {"buy", Buy, nil}, + {"BUY", Buy, nil}, + {"bUy", Buy, nil}, + {"sell", Sell, nil}, + {"SELL", Sell, nil}, + {"sElL", Sell, nil}, + {"bid", Bid, nil}, + {"BID", Bid, nil}, + {"bId", Bid, nil}, + {"ask", Ask, nil}, + {"ASK", Ask, nil}, + {"aSk", Ask, nil}, + {"any", AnySide, nil}, + {"ANY", AnySide, nil}, + {"aNy", AnySide, nil}, + {"woahMan", Buy, errors.New("woahMan not recognised as side type")}, +} + +func TestStringToOrderSide(t *testing.T) { + for i := 0; i < len(stringsToOrderSide); i++ { + testData := &stringsToOrderSide[i] + t.Run(testData.in, func(t *testing.T) { + out, err := StringToOrderSide(testData.in) + if err != nil { + if err.Error() != testData.err.Error() { + t.Error("Unexpected error", err) + } + } else if out != testData.out { + t.Errorf("Unexpected output %v. Expected %v", out, testData.out) + } + }) + } +} + +var stringsToOrderType = []struct { + in string + out Type + err error +}{ + {"limit", Limit, nil}, + {"LIMIT", Limit, nil}, + {"lImIt", Limit, nil}, + {"market", Market, nil}, + {"MARKET", Market, nil}, + {"mArKeT", Market, nil}, + {"immediate_or_cancel", ImmediateOrCancel, nil}, + {"IMMEDIATE_OR_CANCEL", ImmediateOrCancel, nil}, + {"iMmEdIaTe_Or_CaNcEl", ImmediateOrCancel, nil}, + {"stop", Stop, nil}, + {"STOP", Stop, nil}, + {"sToP", Stop, nil}, + {"trailingstop", TrailingStop, nil}, + {"TRAILINGSTOP", TrailingStop, nil}, + {"tRaIlInGsToP", TrailingStop, nil}, + {"any", AnyType, nil}, + {"ANY", AnyType, nil}, + {"aNy", AnyType, nil}, + {"woahMan", Unknown, errors.New("woahMan not recognised as order type")}, +} + +func TestStringToOrderType(t *testing.T) { + for i := 0; i < len(stringsToOrderType); i++ { + testData := &stringsToOrderType[i] + t.Run(testData.in, func(t *testing.T) { + out, err := StringToOrderType(testData.in) + if err != nil { + if err.Error() != testData.err.Error() { + t.Error("Unexpected error", err) + } + } else if out != testData.out { + t.Errorf("Unexpected output %v. Expected %v", out, testData.out) + } + }) + } +} + +var stringsToOrderStatus = []struct { + in string + out Status + err error +}{ + {"any", AnyStatus, nil}, + {"ANY", AnyStatus, nil}, + {"aNy", AnyStatus, nil}, + {"new", New, nil}, + {"NEW", New, nil}, + {"nEw", New, nil}, + {"active", Active, nil}, + {"ACTIVE", Active, nil}, + {"aCtIvE", Active, nil}, + {"partially_filled", PartiallyFilled, nil}, + {"PARTIALLY_FILLED", PartiallyFilled, nil}, + {"pArTiAlLy_FiLlEd", PartiallyFilled, nil}, + {"filled", Filled, nil}, + {"FILLED", Filled, nil}, + {"fIlLeD", Filled, nil}, + {"canceled", Cancelled, nil}, + {"CANCELED", Cancelled, nil}, + {"cAnCelEd", Cancelled, nil}, + {"pending_cancel", PendingCancel, nil}, + {"PENDING_CANCEL", PendingCancel, nil}, + {"pENdInG_cAnCeL", PendingCancel, nil}, + {"rejected", Rejected, nil}, + {"REJECTED", Rejected, nil}, + {"rEjEcTeD", Rejected, nil}, + {"expired", Expired, nil}, + {"EXPIRED", Expired, nil}, + {"eXpIrEd", Expired, nil}, + {"hidden", Hidden, nil}, + {"HIDDEN", Hidden, nil}, + {"hIdDeN", Hidden, nil}, + {"woahMan", UnknownStatus, errors.New("woahMan not recognised as order STATUS")}, +} + +func TestStringToOrderStatus(t *testing.T) { + for i := 0; i < len(stringsToOrderStatus); i++ { + testData := &stringsToOrderStatus[i] + t.Run(testData.in, func(t *testing.T) { + out, err := StringToOrderStatus(testData.in) + if err != nil { + if err.Error() != testData.err.Error() { + t.Error("Unexpected error", err) + } + } else if out != testData.out { + t.Errorf("Unexpected output %v. Expected %v", out, testData.out) + } + }) + } +} diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index 0a02e9ed..ef869e02 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -50,6 +50,7 @@ type Submit struct { // SubmitResponse is what is returned after submitting an order to an exchange type SubmitResponse struct { IsOrderPlaced bool + FullyMatched bool OrderID string } @@ -127,7 +128,7 @@ type Detail struct { // TradeHistory holds exchange history data type TradeHistory struct { Timestamp time.Time - TID int64 + TID string Price float64 Amount float64 Exchange string diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index 85452186..1fb1d2c7 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -1,6 +1,7 @@ package order import ( + "fmt" "sort" "strings" "time" @@ -10,18 +11,18 @@ import ( // NewOrder creates a new order and returns a an orderID func NewOrder(exchangeName string, amount, price float64) int { - order := &Order{} + ord := &Order{} if len(Orders) == 0 { - order.OrderID = 0 + ord.OrderID = 0 } else { - order.OrderID = len(Orders) + ord.OrderID = len(Orders) } - order.Exchange = exchangeName - order.Amount = amount - order.Price = price - Orders = append(Orders, order) - return order.OrderID + ord.Exchange = exchangeName + ord.Amount = amount + ord.Price = price + Orders = append(Orders, ord) + return ord.OrderID } // DeleteOrder deletes orders by ID and returns state @@ -300,3 +301,72 @@ func SortOrdersBySide(orders *[]Detail, reverse bool) { sort.Sort(ByOrderSide(*orders)) } } + +// StringToOrderSide for converting case insensitive order side +// and returning a real Side +func StringToOrderSide(side string) (Side, error) { + switch { + case strings.EqualFold(side, Buy.String()): + return Buy, nil + case strings.EqualFold(side, Sell.String()): + return Sell, nil + case strings.EqualFold(side, Bid.String()): + return Bid, nil + case strings.EqualFold(side, Ask.String()): + return Ask, nil + case strings.EqualFold(side, AnySide.String()): + return AnySide, nil + default: + return Side(""), fmt.Errorf("%s not recognised as side type", side) + } +} + +// StringToOrderType for converting case insensitive order type +// and returning a real Type +func StringToOrderType(oType string) (Type, error) { + switch { + case strings.EqualFold(oType, Limit.String()): + return Limit, nil + case strings.EqualFold(oType, Market.String()): + return Market, nil + case strings.EqualFold(oType, ImmediateOrCancel.String()): + return ImmediateOrCancel, nil + case strings.EqualFold(oType, Stop.String()): + return Stop, nil + case strings.EqualFold(oType, TrailingStop.String()): + return TrailingStop, nil + case strings.EqualFold(oType, AnyType.String()): + return AnyType, nil + default: + return Unknown, fmt.Errorf("%s not recognised as order type", oType) + } +} + +// StringToOrderStatus for converting case insensitive order status +// and returning a real Status +func StringToOrderStatus(status string) (Status, error) { + switch { + case strings.EqualFold(status, AnyStatus.String()): + return AnyStatus, nil + case strings.EqualFold(status, New.String()): + return New, nil + case strings.EqualFold(status, Active.String()): + return Active, nil + case strings.EqualFold(status, PartiallyFilled.String()): + return PartiallyFilled, nil + case strings.EqualFold(status, Filled.String()): + return Filled, nil + case strings.EqualFold(status, Cancelled.String()): + return Cancelled, nil + case strings.EqualFold(status, PendingCancel.String()): + return PendingCancel, nil + case strings.EqualFold(status, Rejected.String()): + return Rejected, nil + case strings.EqualFold(status, Expired.String()): + return Expired, nil + case strings.EqualFold(status, Hidden.String()): + return Hidden, nil + default: + return UnknownStatus, fmt.Errorf("%s not recognised as order STATUS", status) + } +} diff --git a/exchanges/orderbook/orderbook_test.go b/exchanges/orderbook/orderbook_test.go index 9c750478..8c767abf 100644 --- a/exchanges/orderbook/orderbook_test.go +++ b/exchanges/orderbook/orderbook_test.go @@ -158,9 +158,9 @@ func TestVerify(t *testing.T) { func TestCalculateTotalBids(t *testing.T) { t.Parallel() - currency := currency.NewPairFromStrings("BTC", "USD") + curr := currency.NewPairFromStrings("BTC", "USD") base := Base{ - Pair: currency, + Pair: curr, Bids: []Item{{Price: 100, Amount: 10}}, LastUpdated: time.Now(), } @@ -173,9 +173,9 @@ func TestCalculateTotalBids(t *testing.T) { func TestCalculateTotaAsks(t *testing.T) { t.Parallel() - currency := currency.NewPairFromStrings("BTC", "USD") + curr := currency.NewPairFromStrings("BTC", "USD") base := Base{ - Pair: currency, + Pair: curr, Asks: []Item{{Price: 100, Amount: 10}}, } @@ -187,10 +187,10 @@ func TestCalculateTotaAsks(t *testing.T) { func TestUpdate(t *testing.T) { t.Parallel() - currency := currency.NewPairFromStrings("BTC", "USD") + curr := currency.NewPairFromStrings("BTC", "USD") timeNow := time.Now() base := Base{ - Pair: currency, + Pair: curr, Asks: []Item{{Price: 100, Amount: 10}}, Bids: []Item{{Price: 200, Amount: 10}}, LastUpdated: timeNow, diff --git a/exchanges/poloniex/poloniex_test.go b/exchanges/poloniex/poloniex_test.go index a89aca41..cc939aca 100644 --- a/exchanges/poloniex/poloniex_test.go +++ b/exchanges/poloniex/poloniex_test.go @@ -103,7 +103,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() p.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, @@ -202,9 +202,7 @@ func TestFormatWithdrawPermissions(t *testing.T) { expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.NoFiatWithdrawalsText - withdrawPermissions := p.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, @@ -284,7 +282,6 @@ func TestCancelExchangeOrder(t *testing.T) { if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -310,7 +307,6 @@ func TestCancelAllExchangeOrders(t *testing.T) { } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", diff --git a/exchanges/poloniex/poloniex_websocket.go b/exchanges/poloniex/poloniex_websocket.go index b5078c45..18b91525 100644 --- a/exchanges/poloniex/poloniex_websocket.go +++ b/exchanges/poloniex/poloniex_websocket.go @@ -469,12 +469,12 @@ func (p *Poloniex) WsProcessOrderbookUpdate(sequenceNumber int64, target []inter func (p *Poloniex) GenerateDefaultSubscriptions() { var subscriptions []wshandler.WebsocketChannelSubscription subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ - Channel: fmt.Sprintf("%v", wsTickerDataID), + Channel: strconv.FormatInt(wsTickerDataID, 10), }) if p.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ - Channel: fmt.Sprintf("%v", wsAccountNotificationID), + Channel: strconv.FormatInt(wsAccountNotificationID, 10), }) } @@ -495,9 +495,9 @@ func (p *Poloniex) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscr Command: "subscribe", } switch { - case strings.EqualFold(fmt.Sprintf("%v", wsAccountNotificationID), channelToSubscribe.Channel): + case strings.EqualFold(strconv.FormatInt(wsAccountNotificationID, 10), channelToSubscribe.Channel): return p.wsSendAuthorisedCommand("subscribe") - case strings.EqualFold(fmt.Sprintf("%v", wsTickerDataID), channelToSubscribe.Channel): + case strings.EqualFold(strconv.FormatInt(wsTickerDataID, 10), channelToSubscribe.Channel): subscriptionRequest.Channel = wsTickerDataID default: subscriptionRequest.Channel = channelToSubscribe.Currency.String() @@ -511,9 +511,9 @@ func (p *Poloniex) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubs Command: "unsubscribe", } switch { - case strings.EqualFold(fmt.Sprintf("%v", wsAccountNotificationID), channelToSubscribe.Channel): + case strings.EqualFold(strconv.FormatInt(wsAccountNotificationID, 10), channelToSubscribe.Channel): return p.wsSendAuthorisedCommand("unsubscribe") - case strings.EqualFold(fmt.Sprintf("%v", wsTickerDataID), channelToSubscribe.Channel): + case strings.EqualFold(strconv.FormatInt(wsTickerDataID, 10), channelToSubscribe.Channel): unsubscriptionRequest.Channel = wsTickerDataID default: unsubscriptionRequest.Channel = channelToSubscribe.Currency.String() diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index 02ed3818..496e1e63 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -238,13 +238,14 @@ func (p *Poloniex) UpdateTicker(currencyPair currency.Pair, assetType asset.Item return tickerPrice, err } - for _, x := range p.GetEnabledPairs(assetType) { + enabledPairs := p.GetEnabledPairs(assetType) + for i := range enabledPairs { var tp ticker.Price - curr := p.FormatExchangeCurrency(x, assetType).String() + curr := p.FormatExchangeCurrency(enabledPairs[i], assetType).String() if _, ok := tick[curr]; !ok { continue } - tp.Pair = x + tp.Pair = enabledPairs[i] tp.Ask = tick[curr].LowestAsk tp.Bid = tick[curr].HighestBid tp.High = tick[curr].High24Hr @@ -287,9 +288,9 @@ func (p *Poloniex) UpdateOrderbook(currencyPair currency.Pair, assetType asset.I return orderBook, err } - for _, x := range p.GetEnabledPairs(assetType) { - currency := p.FormatExchangeCurrency(x, assetType).String() - data, ok := orderbookNew.Data[currency] + enabledPairs := p.GetEnabledPairs(assetType) + for i := range enabledPairs { + data, ok := orderbookNew.Data[p.FormatExchangeCurrency(enabledPairs[i], assetType).String()] if !ok { continue } @@ -307,7 +308,7 @@ func (p *Poloniex) UpdateOrderbook(currencyPair currency.Pair, assetType asset.I Amount: data.Asks[y].Amount, Price: data.Asks[y].Price}) } orderBook.Asks = obItems - orderBook.Pair = x + orderBook.Pair = enabledPairs[i] orderBook.ExchangeName = p.Name orderBook.AssetType = assetType @@ -370,13 +371,18 @@ func (p *Poloniex) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { false, fillOrKill, isBuyOrder) + if err != nil { + return submitOrderResponse, err + } if response.OrderNumber > 0 { submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10) } - if err == nil { - submitOrderResponse.IsOrderPlaced = true + + submitOrderResponse.IsOrderPlaced = true + if s.OrderType == order.Market { + submitOrderResponse.FullyMatched = true } - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/websocket/wshandler/wshandler.go b/exchanges/websocket/wshandler/wshandler.go index 27895cbd..be0ee3c2 100644 --- a/exchanges/websocket/wshandler/wshandler.go +++ b/exchanges/websocket/wshandler/wshandler.go @@ -327,6 +327,17 @@ func (w *Websocket) IsConnectionMonitorRunning() bool { return w.connectionMonitorRunning } +// CanUseAuthenticatedWebsocketForWrapper Handles a common check to +// verify whether a wrapper can use an authenticated websocket endpoint +func (w *Websocket) CanUseAuthenticatedWebsocketForWrapper() bool { + if w.IsConnected() && w.CanUseAuthenticatedEndpoints() { + return true + } else if w.IsConnected() && !w.CanUseAuthenticatedEndpoints() { + log.Infof(log.WebsocketMgr, WebsocketNotAuthenticatedUsingRest, w.exchangeName) + } + return false +} + // SetWebsocketURL sets websocket URL func (w *Websocket) SetWebsocketURL(websocketURL string) { if websocketURL == "" || websocketURL == config.WebsocketURLNonDefaultMessage { @@ -629,7 +640,6 @@ func (w *WebsocketConnection) Dial(dialer *websocket.Dialer, headers http.Header } dialer.Proxy = http.ProxyURL(proxy) } - var err error var conStatus *http.Response w.Connection, conStatus, err = dialer.Dial(w.URL, headers) @@ -640,7 +650,7 @@ func (w *WebsocketConnection) Dial(dialer *websocket.Dialer, headers http.Header return fmt.Errorf("%v Error: %v", w.URL, err) } if w.Verbose { - log.Infof(log.WebsocketMgr, "%v Websocket connected", w.ExchangeName) + log.Infof(log.WebsocketMgr, "%v Websocket connected to %s", w.ExchangeName, w.URL) } w.setConnectedStatus(true) return nil @@ -703,6 +713,12 @@ func (w *WebsocketConnection) WaitForResult(id int64, wg *sync.WaitGroup) { for k := range w.IDResponses { if k == id { w.Unlock() + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } return } } diff --git a/exchanges/websocket/wshandler/wshandler_test.go b/exchanges/websocket/wshandler/wshandler_test.go index 25ac23f6..bfb20816 100644 --- a/exchanges/websocket/wshandler/wshandler_test.go +++ b/exchanges/websocket/wshandler/wshandler_test.go @@ -678,3 +678,22 @@ func readMessages(wc *WebsocketConnection, t *testing.T) { } } } + +// TestCanUseAuthenticatedWebsocketForWrapper logic test +func TestCanUseAuthenticatedWebsocketForWrapper(t *testing.T) { + ws := &Websocket{} + resp := ws.CanUseAuthenticatedWebsocketForWrapper() + if resp { + t.Error("Expected false, `connected` is false") + } + ws.setConnectedStatus(true) + resp = ws.CanUseAuthenticatedWebsocketForWrapper() + if resp { + t.Error("Expected false, `connected` is true and `CanUseAuthenticatedEndpoints` is false") + } + ws.canUseAuthenticatedEndpoints = true + resp = ws.CanUseAuthenticatedWebsocketForWrapper() + if !resp { + t.Error("Expected true, `connected` and `CanUseAuthenticatedEndpoints` is true") + } +} diff --git a/exchanges/websocket/wshandler/wshandler_types.go b/exchanges/websocket/wshandler/wshandler_types.go index c4e9b341..22e47266 100644 --- a/exchanges/websocket/wshandler/wshandler_types.go +++ b/exchanges/websocket/wshandler/wshandler_types.go @@ -17,7 +17,8 @@ const ( WebsocketNotEnabled = "exchange_websocket_not_enabled" manageSubscriptionsDelay = 5 * time.Second // connection monitor time delays and limits - connectionMonitorDelay = 2 * time.Second + connectionMonitorDelay = 2 * time.Second + WebsocketNotAuthenticatedUsingRest = "%v - Websocket not authenticated, using REST" ) // Websocket defines a return type for websocket connections via the interface diff --git a/exchanges/yobit/yobit_test.go b/exchanges/yobit/yobit_test.go index 3307cb71..4a2dafab 100644 --- a/exchanges/yobit/yobit_test.go +++ b/exchanges/yobit/yobit_test.go @@ -1,7 +1,9 @@ package yobit import ( + "log" "math" + "os" "testing" "time" @@ -22,19 +24,16 @@ const ( canManipulateRealOrders = false ) -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { y.SetDefaults() -} - -func TestSetup(t *testing.T) { yobitConfig := config.GetConfig() err := yobitConfig.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("Yobit load config error", err) + log.Fatal("Yobit load config error", err) } conf, err := yobitConfig.GetExchangeConfig("Yobit") if err != nil { - t.Fatal("Yobit init error", err) + log.Fatal("Yobit init error", err) } conf.API.Credentials.Key = apiKey conf.API.Credentials.Secret = apiSecret @@ -42,8 +41,9 @@ func TestSetup(t *testing.T) { err = y.Setup(conf) if err != nil { - t.Fatal("Yobit setup error", err) + log.Fatal("Yobit setup error", err) } + os.Exit(m.Run()) } func TestFetchTradablePairs(t *testing.T) { @@ -167,7 +167,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() y.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -179,8 +179,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - y.SetDefaults() - TestSetup(t) var feeBuilder = setFeeBuilder() // CryptocurrencyTradeFee Basic @@ -317,20 +315,14 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - y.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - withdrawPermissions := y.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - y.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.LTC, @@ -346,9 +338,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - y.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.LTC, @@ -372,9 +361,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - y.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -400,15 +386,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - y.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -426,15 +408,11 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - y.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.LTC, currency.BTC) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -457,6 +435,9 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := y.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -464,8 +445,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - y.SetDefaults() - TestSetup(t) withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -489,9 +468,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - y.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -506,9 +482,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - y.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -523,7 +496,7 @@ func TestWithdrawInternationalBank(t *testing.T) { } func TestGetDepositAddress(t *testing.T) { - if apiKey != "" || apiSecret != "" { + if areTestAPIKeysSet() { _, err := y.GetDepositAddress(currency.BTC, "") if err != nil { t.Error("GetDepositAddress() Expected error") diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index 400383a4..363f8208 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -187,20 +187,22 @@ func (y *Yobit) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pric return tickerPrice, err } - for _, x := range y.GetEnabledPairs(assetType) { - curr := y.FormatExchangeCurrency(x, assetType).Lower().String() + enabledPairs := y.GetEnabledPairs(assetType) + for i := range enabledPairs { + curr := y.FormatExchangeCurrency(enabledPairs[i], assetType).Lower().String() if _, ok := result[curr]; !ok { continue } + resultCurr := result[curr] var tickerPrice ticker.Price - tickerPrice.Pair = x - tickerPrice.Last = result[curr].Last - tickerPrice.Ask = result[curr].Sell - tickerPrice.Bid = result[curr].Buy - tickerPrice.Last = result[curr].Last - tickerPrice.Low = result[curr].Low - tickerPrice.QuoteVolume = result[curr].VolumeCurrent - tickerPrice.Volume = result[curr].Vol + tickerPrice.Pair = enabledPairs[i] + tickerPrice.Last = resultCurr.Last + tickerPrice.Ask = resultCurr.Sell + tickerPrice.Bid = resultCurr.Buy + tickerPrice.Last = resultCurr.Last + tickerPrice.Low = resultCurr.Low + tickerPrice.QuoteVolume = resultCurr.VolumeCurrent + tickerPrice.Volume = resultCurr.Vol err = ticker.ProcessTicker(y.Name, &tickerPrice, assetType) if err != nil { @@ -323,13 +325,15 @@ func (y *Yobit) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { s.OrderSide.String(), s.Amount, s.Price) + if err != nil { + return submitOrderResponse, err + } if response > 0 { submitOrderResponse.OrderID = strconv.FormatInt(response, 10) } - if err == nil { - submitOrderResponse.IsOrderPlaced = true - } - return submitOrderResponse, err + + submitOrderResponse.IsOrderPlaced = true + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/zb/zb.go b/exchanges/zb/zb.go index 998d4016..dfb5d457 100644 --- a/exchanges/zb/zb.go +++ b/exchanges/zb/zb.go @@ -423,10 +423,10 @@ func (z *ZB) Withdraw(currency, address, safepassword string, amount, fees float vals := url.Values{} vals.Set("accesskey", z.API.Credentials.Key) - vals.Set("amount", fmt.Sprintf("%v", amount)) + vals.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64)) vals.Set("currency", currency) - vals.Set("fees", fmt.Sprintf("%v", fees)) - vals.Set("itransfer", fmt.Sprintf("%v", itransfer)) + vals.Set("fees", strconv.FormatFloat(fees, 'f', -1, 64)) + vals.Set("itransfer", strconv.FormatBool(itransfer)) vals.Set("method", "withdraw") vals.Set("recieveAddr", address) vals.Set("safePwd", safepassword) diff --git a/exchanges/zb/zb_test.go b/exchanges/zb/zb_test.go index c0bf48d4..3182e16e 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -3,7 +3,10 @@ package zb import ( "encoding/json" "fmt" + "log" "net/http" + "os" + "strconv" "testing" "github.com/gorilla/websocket" @@ -25,19 +28,16 @@ const ( var z ZB var wsSetupRan bool -func TestSetDefaults(t *testing.T) { +func TestMain(m *testing.M) { z.SetDefaults() -} - -func TestSetup(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig("../../testdata/configtest.json", true) if err != nil { - t.Fatal("ZB load config error", err) + log.Fatal("ZB load config error", err) } zbConfig, err := cfg.GetExchangeConfig("ZB") if err != nil { - t.Fatal("ZB Setup() init error", err) + log.Fatal("ZB Setup() init error", err) } zbConfig.API.AuthenticatedSupport = true zbConfig.API.AuthenticatedWebsocketSupport = true @@ -46,16 +46,16 @@ func TestSetup(t *testing.T) { err = z.Setup(zbConfig) if err != nil { - t.Fatal("ZB setup error", err) + log.Fatal("ZB setup error", err) } + + os.Exit(m.Run()) } func setupWsAuth(t *testing.T) { if wsSetupRan { return } - z.SetDefaults() - TestSetup(t) if !z.Websocket.IsEnabled() && !z.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip(wshandler.WebsocketNotEnabled) } @@ -90,11 +90,9 @@ func TestSpotNewOrder(t *testing.T) { Amount: 0.01, Price: 10246.1, } - orderid, err := z.SpotNewOrder(arg) + _, err := z.SpotNewOrder(arg) if err != nil { t.Errorf("ZB SpotNewOrder: %s", err) - } else { - t.Log(orderid) } } @@ -182,7 +180,7 @@ func setFeeBuilder() *exchange.FeeBuilder { func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() z.GetFeeByType(feeBuilder) - if apiKey == "" || apiSecret == "" { + if !areTestAPIKeysSet() { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -194,8 +192,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestGetFee(t *testing.T) { - z.SetDefaults() - TestSetup(t) var feeBuilder = setFeeBuilder() // CryptocurrencyTradeFee Basic @@ -272,20 +268,14 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - z.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText - withdrawPermissions := z.FormatWithdrawPermissions() - if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } func TestGetActiveOrders(t *testing.T) { - z.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, Currencies: []currency.Pair{currency.NewPair(currency.XRP, @@ -301,9 +291,6 @@ func TestGetActiveOrders(t *testing.T) { } func TestGetOrderHistory(t *testing.T) { - z.SetDefaults() - TestSetup(t) - var getOrdersRequest = order.GetOrdersRequest{ OrderType: order.AnyType, OrderSide: order.Buy, @@ -326,9 +313,6 @@ func areTestAPIKeysSet() bool { } func TestSubmitOrder(t *testing.T) { - z.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip(fmt.Sprintf("ApiKey: %s. Can place orders: %v", z.API.Credentials.Key, @@ -356,15 +340,11 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - z.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.XRP, currency.USDT) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -382,15 +362,11 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - z.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } currencyPair := currency.NewPair(currency.XRP, currency.USDT) - var orderCancellation = &order.Cancel{ OrderID: "1", WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", @@ -427,6 +403,9 @@ func TestGetAccountInfo(t *testing.T) { } func TestModifyOrder(t *testing.T) { + if areTestAPIKeysSet() && !canManipulateRealOrders { + t.Skip("API keys set, canManipulateRealOrders false, skipping test") + } _, err := z.ModifyOrder(&order.Modify{}) if err == nil { t.Error("ModifyOrder() Expected error") @@ -434,8 +413,6 @@ func TestModifyOrder(t *testing.T) { } func TestWithdraw(t *testing.T) { - z.SetDefaults() - TestSetup(t) withdrawCryptoRequest := exchange.CryptoWithdrawRequest{ GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{ Amount: -1, @@ -460,9 +437,6 @@ func TestWithdraw(t *testing.T) { } func TestWithdrawFiat(t *testing.T) { - z.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -475,9 +449,6 @@ func TestWithdrawFiat(t *testing.T) { } func TestWithdrawInternationalBank(t *testing.T) { - z.SetDefaults() - TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } @@ -490,7 +461,7 @@ func TestWithdrawInternationalBank(t *testing.T) { } func TestGetDepositAddress(t *testing.T) { - if apiKey != "" || apiSecret != "" { + if areTestAPIKeysSet() { _, err := z.GetDepositAddress(currency.BTC, "") if err != nil { t.Error("GetDepositAddress() error PLEASE MAKE SURE YOU CREATE DEPOSIT ADDRESSES VIA ZB.COM", @@ -548,7 +519,7 @@ func TestWsCreateSuUserKey(t *testing.T) { t.Fatal(err) } userID := subUsers.Message[0].UserID - _, err = z.wsCreateSubUserKey(true, true, true, true, "subu", fmt.Sprintf("%v", userID)) + _, err = z.wsCreateSubUserKey(true, true, true, true, "subu", strconv.FormatInt(userID, 10)) if err != nil { t.Fatal(err) } diff --git a/exchanges/zb/zb_types.go b/exchanges/zb/zb_types.go index 6953e841..2dc9308a 100644 --- a/exchanges/zb/zb_types.go +++ b/exchanges/zb/zb_types.go @@ -16,7 +16,7 @@ type OrderbookResponse struct { // AccountsResponseCoin holds the accounts coin details type AccountsResponseCoin struct { - Freez string `json:"freez"` // 冻结资产 + Freeze string `json:"freez"` // 冻结资产 EnName string `json:"enName"` // 币种英文名 UnitDecimal int `json:"unitDecimal"` // 保留小数位 UnName string `json:"cnName"` // 币种中文名 @@ -44,6 +44,9 @@ type Order struct { TradeDate int `json:"trade_date"` TradeMoney float64 `json:"trade_money"` Type int64 `json:"type"` + Fees float64 `json:"fees,omitempty"` + TradePrice float64 `json:"trade_price,omitempty"` + No int64 `json:"no,string,omitempty"` } // AccountsResponse 用户基本信息 diff --git a/exchanges/zb/zb_websocket.go b/exchanges/zb/zb_websocket.go index 4560f582..b4d31804 100644 --- a/exchanges/zb/zb_websocket.go +++ b/exchanges/zb/zb_websocket.go @@ -187,40 +187,6 @@ func (z *ZB) WsHandleData() { } } -var wsErrCodes = map[int64]string{ - 1000: "Successful call", - 1001: "General error message", - 1002: "internal error", - 1003: "Verification failed", - 1004: "Financial security password lock", - 1005: "The fund security password is incorrect. Please confirm and re-enter.", - 1006: "Real-name certification is awaiting review or review", - 1007: "Channel is empty", - 1008: "Event is empty", - 1009: "This interface is being maintained", - 1011: "Not open yet", - 1012: "Insufficient permissions", - 1013: "Can not trade, if you have any questions, please contact online customer service", - 1014: "Cannot be sold during the pre-sale period", - 2002: "Insufficient balance in Bitcoin account", - 2003: "Insufficient balance of Litecoin account", - 2005: "Insufficient balance in Ethereum account", - 2006: "Insufficient balance in ETC currency account", - 2007: "Insufficient balance of BTS currency account", - 2008: "Insufficient balance in EOS currency account", - 2009: "Insufficient account balance", - 3001: "Pending order not found", - 3002: "Invalid amount", - 3003: "Invalid quantity", - 3004: "User does not exist", - 3005: "Invalid parameter", - 3006: "Invalid IP or inconsistent with the bound IP", - 3007: "Request time has expired", - 3008: "Transaction history not found", - 4001: "API interface is locked", - 4002: "Request too frequently", -} - // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() func (z *ZB) GenerateDefaultSubscriptions() { var subscriptions []wshandler.WebsocketChannelSubscription diff --git a/exchanges/zb/zb_websocket_types.go b/exchanges/zb/zb_websocket_types.go index 983c92aa..29275f48 100644 --- a/exchanges/zb/zb_websocket_types.go +++ b/exchanges/zb/zb_websocket_types.go @@ -191,28 +191,12 @@ type WsGetOrderRequest struct { // WsGetOrderResponse contains order data type WsGetOrderResponse struct { - Message string `json:"message"` - No int64 `json:"no,string"` - Code int64 `json:"code"` - Channel string `json:"channel"` - Success bool `json:"success"` - Data WsGetOrderResponseData `json:"data"` -} - -// WsGetOrderResponseData Detailed order data -type WsGetOrderResponseData struct { - Currency string `json:"currency"` - Fees float64 `json:"fees"` - ID string `json:"id"` - Price float64 `json:"price"` - Status int64 `json:"status"` - TotalAmount float64 `json:"total_amount"` - TradeAmount float64 `json:"trade_amount"` - TradePrice float64 `json:"trade_price"` - TradeDate int64 `json:"trade_date"` - TradeMoney float64 `json:"trade_money"` - Type int64 `json:"type"` - No int64 `json:"no,string"` + Message string `json:"message"` + No int64 `json:"no,string"` + Code int64 `json:"code"` + Channel string `json:"channel"` + Success bool `json:"success"` + Data []Order `json:"data"` } // WsGetOrdersRequest get more orders, with no orderID filtering @@ -228,12 +212,12 @@ type WsGetOrdersRequest struct { // WsGetOrdersResponse contains orders data type WsGetOrdersResponse struct { - Message string `json:"message"` - No int64 `json:"no,string"` - Code int64 `json:"code"` - Channel string `json:"channel"` - Success bool `json:"success"` - Data []WsGetOrderResponseData `json:"data"` + Message string `json:"message"` + No int64 `json:"no,string"` + Code int64 `json:"code"` + Channel string `json:"channel"` + Success bool `json:"success"` + Data []Order `json:"data"` } // WsGetOrdersIgnoreTradeTypeRequest ws request @@ -249,12 +233,12 @@ type WsGetOrdersIgnoreTradeTypeRequest struct { // WsGetOrdersIgnoreTradeTypeResponse contains orders data type WsGetOrdersIgnoreTradeTypeResponse struct { - Message string `json:"message"` - No int64 `json:"no,string"` - Code int64 `json:"code"` - Channel string `json:"channel"` - Success bool `json:"success"` - Data []WsGetOrderResponseData `json:"data"` + Message string `json:"message"` + No int64 `json:"no,string"` + Code int64 `json:"code"` + Channel string `json:"channel"` + Success bool `json:"success"` + Data []Order `json:"data"` } // WsGetAccountInfoResponse contains account data @@ -262,23 +246,44 @@ type WsGetAccountInfoResponse struct { Message string `json:"message"` No int64 `json:"no,string"` Data struct { - Coins []struct { - Freez float64 `json:"freez,string"` - EnName string `json:"enName"` - UnitDecimal int64 `json:"unitDecimal"` - CnName string `json:"cnName"` - UnitTag string `json:"unitTag"` - Available float64 `json:"available,string"` - Key string `json:"key"` - } `json:"coins"` - Base struct { - Username string `json:"username"` - TradePasswordEnabled bool `json:"trade_password_enabled"` - AuthGoogleEnabled bool `json:"auth_google_enabled"` - AuthMobileEnabled bool `json:"auth_mobile_enabled"` - } `json:"base"` + Coins []AccountsResponseCoin `json:"coins"` + Base AccountsBaseResponse `json:"base"` } `json:"data"` Code int64 `json:"code"` Channel string `json:"channel"` Success bool `json:"success"` } + +var wsErrCodes = map[int64]string{ + 1000: "Successful call", + 1001: "General error message", + 1002: "internal error", + 1003: "Verification failed", + 1004: "Financial security password lock", + 1005: "The fund security password is incorrect. Please confirm and re-enter.", + 1006: "Real-name certification is awaiting review or review", + 1007: "Channel is empty", + 1008: "Event is empty", + 1009: "This interface is being maintained", + 1011: "Not open yet", + 1012: "Insufficient permissions", + 1013: "Can not trade, if you have any questions, please contact online customer service", + 1014: "Cannot be sold during the pre-sale period", + 2002: "Insufficient balance in Bitcoin account", + 2003: "Insufficient balance of Litecoin account", + 2005: "Insufficient balance in Ethereum account", + 2006: "Insufficient balance in ETC currency account", + 2007: "Insufficient balance of BTS currency account", + 2008: "Insufficient balance in EOS currency account", + 2009: "Insufficient account balance", + 3001: "Pending order not found", + 3002: "Invalid amount", + 3003: "Invalid quantity", + 3004: "User does not exist", + 3005: "Invalid parameter", + 3006: "Invalid IP or inconsistent with the bound IP", + 3007: "Request time has expired", + 3008: "Transaction history not found", + 4001: "API interface is locked", + 4002: "Request too frequently", +} diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index 2f9608e9..eac00ae5 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -2,6 +2,7 @@ package zb import ( "errors" + "fmt" "strconv" "strings" "sync" @@ -268,8 +269,9 @@ func (z *ZB) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Ba // UpdateOrderbook updates and returns the orderbook for a currency pair func (z *ZB) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) { var orderBook orderbook.Base - orderbookNew, err := z.GetOrderbook(z.FormatExchangeCurrency(p, - assetType).String()) + curr := z.FormatExchangeCurrency(p, assetType).String() + + orderbookNew, err := z.GetOrderbook(curr) if err != nil { return orderBook, err } @@ -304,25 +306,35 @@ func (z *ZB) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.B // ZB exchange func (z *ZB) GetAccountInfo() (exchange.AccountInfo, error) { var info exchange.AccountInfo - bal, err := z.GetAccountInformation() - if err != nil { - return info, err + var balances []exchange.AccountCurrencyInfo + var coins []AccountsResponseCoin + if z.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + resp, err := z.wsGetAccountInfoRequest() + if err != nil { + return info, err + } + coins = resp.Data.Coins + } else { + bal, err := z.GetAccountInformation() + if err != nil { + return info, err + } + coins = bal.Result.Coins } - var balances []exchange.AccountCurrencyInfo - for _, data := range bal.Result.Coins { - hold, err := strconv.ParseFloat(data.Freez, 64) + for i := range coins { + hold, err := strconv.ParseFloat(coins[i].Freeze, 64) if err != nil { return info, err } - avail, err := strconv.ParseFloat(data.Available, 64) + avail, err := strconv.ParseFloat(coins[i].Available, 64) if err != nil { return info, err } balances = append(balances, exchange.AccountCurrencyInfo{ - CurrencyName: currency.NewCode(data.EnName), + CurrencyName: currency.NewCode(coins[i].EnName), TotalValue: hold + avail, Hold: hold, }) @@ -348,33 +360,53 @@ func (z *ZB) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchan } // SubmitOrder submits a new order -func (z *ZB) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { +func (z *ZB) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) { var submitOrderResponse order.SubmitResponse - if err := s.Validate(); err != nil { + err := o.Validate() + if err != nil { return submitOrderResponse, err } - - var oT SpotNewOrderRequestParamsType - if s.OrderSide == order.Buy { - oT = SpotNewOrderRequestParamsTypeBuy + if z.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + var isBuyOrder int64 + if o.OrderSide == order.Buy { + isBuyOrder = 1 + } else { + isBuyOrder = 0 + } + var response *WsSubmitOrderResponse + response, err = z.wsSubmitOrder(o.Pair, o.Amount, o.Price, isBuyOrder) + if err != nil { + return submitOrderResponse, err + } + submitOrderResponse.OrderID = strconv.FormatInt(response.Data.EntrustID, 10) } else { - oT = SpotNewOrderRequestParamsTypeSell - } + var oT SpotNewOrderRequestParamsType + if o.OrderSide == order.Buy { + oT = SpotNewOrderRequestParamsTypeBuy + } else { + oT = SpotNewOrderRequestParamsTypeSell + } - var params = SpotNewOrderRequestParams{ - Amount: s.Amount, - Price: s.Price, - Symbol: s.Pair.Lower().String(), - Type: oT, + var params = SpotNewOrderRequestParams{ + Amount: o.Amount, + Price: o.Price, + Symbol: o.Pair.Lower().String(), + Type: oT, + } + var response int64 + response, err = z.SpotNewOrder(params) + if err != nil { + return submitOrderResponse, err + } + if response > 0 { + submitOrderResponse.OrderID = strconv.FormatInt(response, 10) + } } - response, err := z.SpotNewOrder(params) - if response > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response, 10) + submitOrderResponse.IsOrderPlaced = true + if o.OrderType == order.Market { + submitOrderResponse.FullyMatched = true } - if err == nil { - submitOrderResponse.IsOrderPlaced = true - } - return submitOrderResponse, err + return submitOrderResponse, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -389,8 +421,20 @@ func (z *ZB) CancelOrder(o *order.Cancel) error { if err != nil { return err } - curr := z.FormatExchangeCurrency(o.CurrencyPair, o.AssetType).String() - return z.CancelExistingOrder(orderIDInt, curr) + + if z.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + var response *WsCancelOrderResponse + response, err = z.wsCancelOrder(o.CurrencyPair, orderIDInt) + if err != nil { + return err + } + if !response.Success { + return fmt.Errorf("%v - Could not cancel order %v", z.Name, o.OrderID) + } + return nil + } + return z.CancelExistingOrder(orderIDInt, z.FormatExchangeCurrency(o.CurrencyPair, + o.AssetType).String()) } // CancelAllOrders cancels all orders associated with a currency pair @@ -424,11 +468,12 @@ func (z *ZB) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) { } for i := range allOpenOrders { - err := z.CancelExistingOrder(allOpenOrders[i].ID, - allOpenOrders[i].Currency) + err := z.CancelOrder(&order.Cancel{ + OrderID: strconv.FormatInt(allOpenOrders[i].ID, 10), + CurrencyPair: currency.NewPairFromString(allOpenOrders[i].Currency), + }) if err != nil { - ID := strconv.FormatInt(allOpenOrders[i].ID, 10) - cancelAllOrdersResponse.Status[ID] = err.Error() + cancelAllOrdersResponse.Status[strconv.FormatInt(allOpenOrders[i].ID, 10)] = err.Error() } } @@ -539,35 +584,45 @@ func (z *ZB) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error if req.OrderSide == order.AnySide || req.OrderSide == "" { return nil, errors.New("specific order side is required") } - var allOrders []Order - + var orders []order.Detail var side int64 - if req.OrderSide == order.Buy { - side = 1 - } - for x := range req.Currencies { - for y := int64(1); ; y++ { - fPair := z.FormatExchangeCurrency(req.Currencies[x], asset.Spot).String() - resp, err := z.GetOrders(fPair, y, side) - if err != nil { - return nil, err + if z.Websocket.CanUseAuthenticatedWebsocketForWrapper() { + for x := range req.Currencies { + for y := int64(1); ; y++ { + resp, err := z.wsGetOrdersIgnoreTradeType(req.Currencies[x], y, 10) + if err != nil { + return nil, err + } + allOrders = append(allOrders, resp.Data...) + if len(resp.Data) != 10 { + break + } } - - if len(resp) == 0 { - break - } - - allOrders = append(allOrders, resp...) - - if len(resp) != 10 { - break + } + } else { + if req.OrderSide == order.Buy { + side = 1 + } + for x := range req.Currencies { + for y := int64(1); ; y++ { + fPair := z.FormatExchangeCurrency(req.Currencies[x], asset.Spot).String() + resp, err := z.GetOrders(fPair, y, side) + if err != nil { + return nil, err + } + if len(resp) == 0 { + break + } + allOrders = append(allOrders, resp...) + if len(resp) != 10 { + break + } } } } - var orders []order.Detail for i := range allOrders { symbol := currency.NewPairDelimiter(allOrders[i].Currency, z.GetPairFormat(asset.Spot, false).Delimiter)