From ff6a84f0f1494415628f792a69b60731694906bd Mon Sep 17 00:00:00 2001 From: Scott Date: Fri, 14 Dec 2018 15:53:26 +1100 Subject: [PATCH] Cancel all orders wrapper implementation (#217) * Changes method signature for cancelling all orders (experitmental). Implements cancelAllOrders wrapper for alphapoint, anx, binance * Implements cancel all wrapper for bitfinex, bitmex, bitstamp, bittrex, btcmarkets, coinbasepro and hilariously coinut * Changes method signature to only use one OrderCancellation type. Adds support for Exmo, gateio, gemini, itbit, lakebtc * Adds/updates support for hitbtc, huobi, hadax, itbit and kraken * Adds support for liqui, localbitcoins, okcoin, poloniex, wex and yobit. Splits up open order methods for poloniex * Adds bithumb, okex and zb support. BTCC for another PR * Updates bitflyer, bithumb, bitmex, coinut, okex and zb cancelAllOrders method to cancel via enabled currency pairs rather than a singular currency * Adds tests to all exchanges to test wrapper function CancelAllOrders * Fixes OKEX and huobi, btcmarkets, kraken, okCoin cancel order implementations * Fixes coinut, hitbtc and okex api for authenticated requests * Fixes comment and spacing * Changes the CancelAllOrders signature to return orderids and errors along with a generic error. * Fixes OKEX delimiter * Removes spacing and test verbosity * Removes more spacing * Removes space * Fixes okex rebasing issue. Also makes the maps instead of assuming they just work --- exchanges/alphapoint/alphapoint.go | 4 +- exchanges/alphapoint/alphapoint_test.go | 31 +++++- exchanges/alphapoint/alphapoint_wrapper.go | 8 +- exchanges/anx/anx.go | 43 ++++++-- exchanges/anx/anx_test.go | 31 +++++- exchanges/anx/anx_types.go | 21 ++++ exchanges/anx/anx_wrapper.go | 31 +++++- exchanges/binance/binance.go | 4 +- exchanges/binance/binance_test.go | 36 +++++- exchanges/binance/binance_wrapper.go | 19 +++- exchanges/bitfinex/bitfinex.go | 2 +- exchanges/bitfinex/bitfinex_test.go | 33 +++++- exchanges/bitfinex/bitfinex_wrapper.go | 5 +- exchanges/bitflyer/bitflyer_test.go | 32 +++++- exchanges/bitflyer/bitflyer_wrapper.go | 6 +- exchanges/bithumb/bithumb_test.go | 32 +++++- exchanges/bithumb/bithumb_types.go | 35 +++--- exchanges/bithumb/bithumb_wrapper.go | 27 ++++- exchanges/bitmex/bitmex_test.go | 34 +++++- exchanges/bitmex/bitmex_wrapper.go | 17 ++- exchanges/bitstamp/bitstamp_test.go | 33 +++++- exchanges/bitstamp/bitstamp_wrapper.go | 10 +- exchanges/bittrex/bittrex_test.go | 32 +++++- exchanges/bittrex/bittrex_wrapper.go | 19 +++- exchanges/btcc/btcc_test.go | 32 +++++- exchanges/btcc/btcc_wrapper.go | 4 +- exchanges/btcmarkets/btcmarkets.go | 44 +++++--- exchanges/btcmarkets/btcmarkets_test.go | 39 +++++-- exchanges/btcmarkets/btcmarkets_types.go | 23 ++-- exchanges/btcmarkets/btcmarkets_wrapper.go | 35 +++--- exchanges/coinbasepro/coinbasepro.go | 2 +- exchanges/coinbasepro/coinbasepro_test.go | 32 +++++- exchanges/coinbasepro/coinbasepro_wrapper.go | 6 +- exchanges/coinut/coinut.go | 18 ++- exchanges/coinut/coinut_test.go | 41 ++++++- exchanges/coinut/coinut_types.go | 11 +- exchanges/coinut/coinut_wrapper.go | 53 ++++++++- exchanges/exchange.go | 7 +- exchanges/exmo/exmo_test.go | 33 +++++- exchanges/exmo/exmo_wrapper.go | 19 +++- exchanges/gateio/gateio.go | 68 ++++++++++-- exchanges/gateio/gateio_test.go | 33 +++++- exchanges/gateio/gateio_types.go | 25 +++++ exchanges/gateio/gateio_wrapper.go | 24 +++- exchanges/gemini/gemini_test.go | 33 +++++- exchanges/gemini/gemini_wrapper.go | 18 ++- exchanges/hitbtc/hitbtc.go | 13 +++ exchanges/hitbtc/hitbtc_test.go | 33 +++++- exchanges/hitbtc/hitbtc_wrapper.go | 16 ++- exchanges/huobi/huobi.go | 101 ++++++++++++----- exchanges/huobi/huobi_test.go | 33 +++++- exchanges/huobi/huobi_types.go | 10 ++ exchanges/huobi/huobi_wrapper.go | 18 ++- exchanges/huobihadax/huobihadax.go | 104 +++++++++++++----- exchanges/huobihadax/huobihadax_test.go | 33 +++++- exchanges/huobihadax/huobihadax_types.go | 10 ++ exchanges/huobihadax/huobihadax_wrapper.go | 18 ++- exchanges/itbit/itbit.go | 28 +++++ exchanges/itbit/itbit_test.go | 33 +++++- exchanges/itbit/itbit_wrapper.go | 19 +++- exchanges/kraken/kraken_test.go | 33 +++++- exchanges/kraken/kraken_wrapper.go | 22 +++- exchanges/lakebtc/lakebtc.go | 19 ++++ exchanges/lakebtc/lakebtc_test.go | 33 +++++- exchanges/lakebtc/lakebtc_wrapper.go | 19 +++- exchanges/liqui/liqui.go | 10 +- exchanges/liqui/liqui_test.go | 35 +++++- exchanges/liqui/liqui_wrapper.go | 27 ++++- exchanges/localbitcoins/localbitcoins_test.go | 34 +++++- .../localbitcoins/localbitcoins_wrapper.go | 21 +++- exchanges/okcoin/okcoin_test.go | 33 +++++- exchanges/okcoin/okcoin_wrapper.go | 30 ++++- exchanges/okex/okex.go | 16 ++- exchanges/okex/okex_test.go | 31 ++++++ exchanges/okex/okex_types.go | 18 +++ exchanges/okex/okex_wrapper.go | 33 +++++- exchanges/poloniex/poloniex.go | 23 ++-- exchanges/poloniex/poloniex_test.go | 33 +++++- exchanges/poloniex/poloniex_wrapper.go | 21 +++- exchanges/wex/wex_test.go | 38 ++++++- exchanges/wex/wex_wrapper.go | 33 +++++- exchanges/yobit/yobit_test.go | 33 +++++- exchanges/yobit/yobit_wrapper.go | 34 +++++- exchanges/zb/zb.go | 35 ++++-- exchanges/zb/zb_test.go | 33 +++++- exchanges/zb/zb_type.go | 13 +++ exchanges/zb/zb_wrapper.go | 26 ++++- testdata/configtest.json | 2 +- tools/exchange_template/wrapper_file.tmpl | 2 +- 89 files changed, 2050 insertions(+), 304 deletions(-) diff --git a/exchanges/alphapoint/alphapoint.go b/exchanges/alphapoint/alphapoint.go index 759cfae6..5b21d04a 100644 --- a/exchanges/alphapoint/alphapoint.go +++ b/exchanges/alphapoint/alphapoint.go @@ -454,9 +454,9 @@ func (a *Alphapoint) CancelExistingOrder(OrderID int64, OMSID string) (int64, er // CancelAllExistingOrders cancels all open orders by symbol // symbol - Instrument code (ex: “BTCUSD”) -func (a *Alphapoint) CancelAllExistingOrders(symbol string) error { +func (a *Alphapoint) CancelAllExistingOrders(OMSID string) error { request := make(map[string]interface{}) - request["ins"] = symbol + request["OMSId"] = OMSID response := Response{} err := a.SendAuthenticatedHTTPRequest( diff --git a/exchanges/alphapoint/alphapoint_test.go b/exchanges/alphapoint/alphapoint_test.go index 617e40b6..a69ef9cd 100644 --- a/exchanges/alphapoint/alphapoint_test.go +++ b/exchanges/alphapoint/alphapoint_test.go @@ -531,7 +531,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - a.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.BTC, symbol.LTC) var orderCancellation = exchange.OrderCancellation{ @@ -548,3 +547,33 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Test Failed - ANX CancelOrder() error: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + a := &Alphapoint{} + a.SetDefaults() + + if !isRealOrderTestEnabled(a) { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.BTC, symbol.LTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + // Act + resp, err := a.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index 7aa1b836..eaff8af9 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -133,7 +133,6 @@ func (a *Alphapoint) ModifyOrder(orderID int64, action exchange.ModifyOrder) (in // CancelOrder cancels an order by its corresponding ID number func (a *Alphapoint) CancelOrder(order exchange.OrderCancellation) error { orderIDInt, err := strconv.ParseInt(order.OrderID, 10, 64) - if err != nil { return err } @@ -143,10 +142,9 @@ func (a *Alphapoint) CancelOrder(order exchange.OrderCancellation) error { return err } -// CancelAllOrders cancels all orders associated with a currency pair -func (a *Alphapoint) CancelAllOrders() error { - // return a.CancelAllExistingOrders(p.Pair().String()) - return common.ErrNotYetImplemented +// CancelAllOrders cancels all orders for a given account +func (a *Alphapoint) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + return exchange.CancelAllOrdersResponse{}, a.CancelAllExistingOrders(orderCancellation.AccountID) } // GetOrderInfo returns information on a current open order diff --git a/exchanges/anx/anx.go b/exchanges/anx/anx.go index 975a7896..8f8c60ff 100644 --- a/exchanges/anx/anx.go +++ b/exchanges/anx/anx.go @@ -24,7 +24,8 @@ const ( anxCurrencies = "currencyStatic" anxDataToken = "dataToken" anxOrderNew = "order/new" - anxCancel = "order/cancel" + anxOrderCancel = "order/cancel" + anxOrderList = "order/list" anxOrderInfo = "order/info" anxSend = "send" anxSubaccountNew = "subaccount/new" @@ -242,22 +243,42 @@ func (a *ANX) NewOrder(orderType string, buy bool, tradedCurrency string, traded // CancelOrderByIDs cancels orders, requires already knowing order IDs // There is no existing API call to retrieve orderIds -func (a *ANX) CancelOrderByIDs(orderIds []string) (err error) { +func (a *ANX) CancelOrderByIDs(orderIds []string) (OrderCancelResponse, error) { request := make(map[string]interface{}) request["orderIds"] = orderIds - type OrderCancelResponse struct { - Order OrderResponse `json:"order"` - ResultCode string `json:"resultCode"` - UUID int64 `json:"uuid"` - ErrorCode int64 `json:"errorCode"` - } var response OrderCancelResponse - err = a.SendAuthenticatedHTTPRequest(anxCancel, request, &response) + + err := a.SendAuthenticatedHTTPRequest(anxOrderCancel, request, &response) + if response.ResultCode != "OK" { + return response, errors.New(response.ResultCode) + } + + return response, err +} + +// GetOrderList retrieves orders from the exchange +func (a *ANX) GetOrderList(isActiveOrdersOnly bool) ([]OrderResponse, error) { + request := make(map[string]interface{}) + request["activeOnly"] = isActiveOrdersOnly + + type OrderListResponse struct { + Timestamp int64 `json:"timestamp"` + ResultCode string `json:"resultCode"` + Count int64 `json:"count"` + OrderResponses []OrderResponse `json:"orders"` + } + var response OrderListResponse + err := a.SendAuthenticatedHTTPRequest(anxOrderList, request, &response) + if err != nil { + return nil, err + } + if response.ResultCode != "OK" { log.Printf("Response code is not OK: %s\n", response.ResultCode) - return errors.New(response.ResultCode) + return nil, errors.New(response.ResultCode) } - return err + + return response.OrderResponses, err } // OrderInfo returns information about a specific order diff --git a/exchanges/anx/anx_test.go b/exchanges/anx/anx_test.go index 66c19378..b373d5d0 100644 --- a/exchanges/anx/anx_test.go +++ b/exchanges/anx/anx_test.go @@ -266,7 +266,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - a.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.BTC, symbol.LTC) var orderCancellation = exchange.OrderCancellation{ @@ -284,6 +283,36 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + a.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.BTC, symbol.LTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + // Act + resp, err := a.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if testAPIKey != "" || testAPISecret != "" { _, err := a.GetAccountInfo() diff --git a/exchanges/anx/anx_types.go b/exchanges/anx/anx_types.go index 43bb12c4..76d087e3 100644 --- a/exchanges/anx/anx_types.go +++ b/exchanges/anx/anx_types.go @@ -2,6 +2,13 @@ package anx import "github.com/thrasher-/gocryptotrader/currency/symbol" +// List of strings +const ( + CancelOrderNotFound string = "ORDER_NOT_FOUND" + CancelRequestSubmitted string = "CANCEL_REQUEST_SUBMITTED" + CancelOrderWrongState string = "ORDER_CANCEL_WRONG_STATE" +) + // Currency holds the currency information type Currency struct { Decimals int `json:"decimals"` @@ -128,6 +135,20 @@ type OrderResponse struct { TradedCurrencyOutstanding string `json:"tradedCurrencyOutstanding"` } +// OrderCancelResponse returned when cancelling multiple orders +type OrderCancelResponse struct { + OrderCancellationResponses []OrderCancellationResponse `json:"orderIds"` + ResultCode string `json:"resultCode"` + UUID int64 `json:"uuid"` + ErrorCode int64 `json:"errorCode"` +} + +// OrderCancellationResponse contians the orderID and error when cancelling multiple orders +type OrderCancellationResponse struct { + UUID string `json:"uuid"` + Error string `json:"errorCode"` +} + // TickerComponent is a sub-type for ticker type TickerComponent struct { Currency string `json:"currency"` diff --git a/exchanges/anx/anx_wrapper.go b/exchanges/anx/anx_wrapper.go index 68cafabc..7ed42668 100644 --- a/exchanges/anx/anx_wrapper.go +++ b/exchanges/anx/anx_wrapper.go @@ -264,12 +264,37 @@ func (a *ANX) ModifyOrder(orderID int64, action exchange.ModifyOrder) (int64, er // CancelOrder cancels an order by its corresponding ID number func (a *ANX) CancelOrder(order exchange.OrderCancellation) error { orderIDs := []string{order.OrderID} - return a.CancelOrderByIDs(orderIDs) + _, err := a.CancelOrderByIDs(orderIDs) + return err } // CancelAllOrders cancels all orders associated with a currency pair -func (a *ANX) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (a *ANX) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + placedOrders, err := a.GetOrderList(true) + if err != nil { + return cancelAllOrdersResponse, err + } + + var orderIDs []string + for _, order := range placedOrders { + orderIDs = append(orderIDs, order.OrderID) + } + + resp, err := a.CancelOrderByIDs(orderIDs) + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range resp.OrderCancellationResponses { + if order.Error != CancelRequestSubmitted { + cancelAllOrdersResponse.OrderStatus[order.UUID] = order.Error + } + } + + return cancelAllOrdersResponse, err } // GetOrderInfo returns information on a current open order diff --git a/exchanges/binance/binance.go b/exchanges/binance/binance.go index 6d0c2b27..92a50727 100644 --- a/exchanges/binance/binance.go +++ b/exchanges/binance/binance.go @@ -487,13 +487,13 @@ func (b *Binance) CancelExistingOrder(symbol string, orderID int64, origClientOr // Get all open orders on a symbol. Careful when accessing this with no symbol. func (b *Binance) OpenOrders(symbol string) ([]QueryOrderData, error) { var resp []QueryOrderData - path := fmt.Sprintf("%s%s", b.APIUrl, openOrders) - params := url.Values{} + if symbol != "" { params.Set("symbol", common.StringToUpper(symbol)) } + if err := b.SendAuthHTTPRequest("GET", path, params, &resp); err != nil { return resp, err } diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index 17c62314..d93d75c7 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -367,13 +367,10 @@ func TestCancelExchangeOrder(t *testing.T) { b.SetDefaults() TestSetup(t) - if b.APIKey == "" || b.APISecret == "" || - b.APIKey == "Key" || b.APISecret == "Secret" || - !canManipulateRealOrders { + if !isRealOrderTestEnabled() { t.Skip() } - b.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -392,6 +389,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + b.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := b.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if testAPIKey == "" || testAPISecret == "" { t.Skip() diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index 6d357014..592cf0ab 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -234,8 +234,23 @@ func (b *Binance) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (b *Binance) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (b *Binance) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + openOrders, err := b.OpenOrders("") + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range openOrders { + _, err = b.CancelExistingOrder(order.Symbol, order.OrderID, "") + if err != nil { + cancelAllOrdersResponse.OrderStatus[strconv.FormatInt(order.OrderID, 10)] = err.Error() + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/bitfinex/bitfinex.go b/exchanges/bitfinex/bitfinex.go index 761740a6..5e5f4e42 100644 --- a/exchanges/bitfinex/bitfinex.go +++ b/exchanges/bitfinex/bitfinex.go @@ -653,7 +653,7 @@ func (b *Bitfinex) CancelAllExistingOrders() (string, error) { response := GenericResponse{} return response.Result, - b.SendAuthenticatedHTTPRequest("GET", bitfinexOrderCancelAll, nil, nil) + b.SendAuthenticatedHTTPRequest("POST", bitfinexOrderCancelAll, nil, nil) } // ReplaceOrder replaces an older order with a new order diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index 67e15ced..1fd6ae19 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -637,7 +637,6 @@ func setFeeBuilder() exchange.FeeBuilder { func TestGetFee(t *testing.T) { b.SetDefaults() - b.Verbose = true TestSetup(t) var feeBuilder = setFeeBuilder() @@ -759,7 +758,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - b.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -777,3 +775,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrdera(t *testing.T) { + // Arrange + b.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := b.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index 94232b8b..771a4390 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -216,8 +216,9 @@ func (b *Bitfinex) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (b *Bitfinex) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (b *Bitfinex) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + _, err := b.CancelAllExistingOrders() + return exchange.CancelAllOrdersResponse{}, err } // GetOrderInfo returns information on a current open order diff --git a/exchanges/bitflyer/bitflyer_test.go b/exchanges/bitflyer/bitflyer_test.go index 43e82acc..90b93a8e 100644 --- a/exchanges/bitflyer/bitflyer_test.go +++ b/exchanges/bitflyer/bitflyer_test.go @@ -286,7 +286,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - b.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -304,3 +303,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + b.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := b.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/bitflyer/bitflyer_wrapper.go b/exchanges/bitflyer/bitflyer_wrapper.go index 513a36c2..a9660b30 100644 --- a/exchanges/bitflyer/bitflyer_wrapper.go +++ b/exchanges/bitflyer/bitflyer_wrapper.go @@ -171,8 +171,10 @@ func (b *Bitflyer) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (b *Bitflyer) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (b *Bitflyer) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + // TODO, implement BitFlyer API + b.CancelAllExistingOrders() + return exchange.CancelAllOrdersResponse{}, common.ErrNotYetImplemented } // GetOrderInfo returns information on a current open order diff --git a/exchanges/bithumb/bithumb_test.go b/exchanges/bithumb/bithumb_test.go index a78eee01..62877a93 100644 --- a/exchanges/bithumb/bithumb_test.go +++ b/exchanges/bithumb/bithumb_test.go @@ -326,7 +326,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - b.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -345,6 +344,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + b.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := b.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { t.Parallel() if testAPIKey != "" || testAPISecret != "" { diff --git a/exchanges/bithumb/bithumb_types.go b/exchanges/bithumb/bithumb_types.go index 625823c5..2fdc5ab6 100644 --- a/exchanges/bithumb/bithumb_types.go +++ b/exchanges/bithumb/bithumb_types.go @@ -115,22 +115,25 @@ type LastTransactionTicker struct { // Orders contains information about your current orders type Orders struct { - Status string `json:"status"` - Data []struct { - OrderID string `json:"order_id"` - OrderCurrency string `json:"order_currency"` - OrderDate int64 `json:"order_date"` - PaymentCurrency string `json:"payment_currency"` - Type string `json:"type"` - Status string `json:"status"` - Units float64 `json:"units,string"` - UnitsRemaining float64 `json:"units_remaining,string"` - Price float64 `json:"price,string"` - Fee float64 `json:"fee,string"` - Total float64 `json:"total,string"` - DateCompleted int64 `json:"date_completed"` - } `json:"data"` - Message string `json:"message"` + Status string `json:"status"` + Data []OrderData `json:"data"` + Message string `json:"message"` +} + +// OrderData contains all individual order details +type OrderData struct { + OrderID string `json:"order_id"` + OrderCurrency string `json:"order_currency"` + OrderDate int64 `json:"order_date"` + PaymentCurrency string `json:"payment_currency"` + Type string `json:"type"` + Status string `json:"status"` + Units float64 `json:"units,string"` + UnitsRemaining float64 `json:"units_remaining,string"` + Price float64 `json:"price,string"` + Fee float64 `json:"fee,string"` + Total float64 `json:"total,string"` + DateCompleted int64 `json:"date_completed"` } // UserTransactions holds users full transaction list diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index b2f68892..546f1c58 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -200,8 +200,31 @@ func (b *Bithumb) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (b *Bithumb) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (b *Bithumb) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + var allOrders []OrderData + + for _, currency := range b.GetEnabledCurrencies() { + orders, err := b.GetOrders("", orderCancellation.Side.ToString(), "100", "", currency.FirstCurrency.String()) + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range orders.Data { + allOrders = append(allOrders, order) + } + } + + for _, order := range allOrders { + _, err := b.CancelTrade(orderCancellation.Side.ToString(), order.OrderID, orderCancellation.CurrencyPair.FirstCurrency.String()) + if err != nil { + cancelAllOrdersResponse.OrderStatus[order.OrderID] = err.Error() + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/bitmex/bitmex_test.go b/exchanges/bitmex/bitmex_test.go index ed8535ab..b973879d 100644 --- a/exchanges/bitmex/bitmex_test.go +++ b/exchanges/bitmex/bitmex_test.go @@ -242,7 +242,7 @@ func TestCancelOrders(t *testing.T) { func TestCancelAllOrders(t *testing.T) { _, err := b.CancelAllExistingOrders(OrderCancelAllParams{}) if err == nil { - t.Error("test failed - CancelAllOrders() error", err) + t.Error("test failed - CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error)", err) } } @@ -503,7 +503,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - b.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -522,6 +521,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + b.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "123456789012345678901234567890123456", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := b.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if testAPIKey != "" || testAPISecret != "" { _, err := b.GetAccountInfo() diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index e22e2a10..ec25699c 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -204,8 +204,21 @@ func (b *Bitmex) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (b *Bitmex) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (b *Bitmex) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + var emptyParams OrderCancelAllParams + orders, err := b.CancelAllExistingOrders(emptyParams) + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range orders { + cancelAllOrdersResponse.OrderStatus[order.OrderID] = order.OrdRejReason + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/bitstamp/bitstamp_test.go b/exchanges/bitstamp/bitstamp_test.go index eeb1e9b6..df3e0baa 100644 --- a/exchanges/bitstamp/bitstamp_test.go +++ b/exchanges/bitstamp/bitstamp_test.go @@ -54,6 +54,7 @@ func TestSetup(t *testing.T) { bConfig.ClientID = customerID b.Setup(bConfig) + b.ClientID = customerID if !b.IsEnabled() || b.RESTPollingDelay != time.Duration(10) || b.Verbose || b.Websocket.IsEnabled() || len(b.BaseCurrencies) < 1 || @@ -406,7 +407,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - b.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -424,3 +424,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + b.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := b.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index 9e50aca7..fe2b81c6 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -1,6 +1,7 @@ package bitstamp import ( + "errors" "fmt" "log" "strconv" @@ -202,8 +203,13 @@ func (b *Bitstamp) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (b *Bitstamp) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (b *Bitstamp) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + isCancelAllSuccessful, err := b.CancelAllExistingOrders() + if !isCancelAllSuccessful { + err = errors.New("Cancel all failed. Bitstamp provides no further information. Check order status to verify") + } + + return exchange.CancelAllOrdersResponse{}, err } // GetOrderInfo returns information on a current open order diff --git a/exchanges/bittrex/bittrex_test.go b/exchanges/bittrex/bittrex_test.go index 9abf0162..7a1c0c4d 100644 --- a/exchanges/bittrex/bittrex_test.go +++ b/exchanges/bittrex/bittrex_test.go @@ -372,7 +372,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - b.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -390,3 +389,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + b.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := b.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index 20c9b20d..a07271c7 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -210,8 +210,23 @@ func (b *Bittrex) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (b *Bittrex) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (b *Bittrex) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + openOrders, err := b.GetOpenOrders("") + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range openOrders.Result { + _, err := b.CancelExistingOrder(order.OrderUUID) + if err != nil { + cancelAllOrdersResponse.OrderStatus[order.OrderUUID] = err.Error() + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/btcc/btcc_test.go b/exchanges/btcc/btcc_test.go index cac2391a..a8c49c94 100644 --- a/exchanges/btcc/btcc_test.go +++ b/exchanges/btcc/btcc_test.go @@ -210,7 +210,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - b.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -228,3 +227,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + b.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := b.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/btcc/btcc_wrapper.go b/exchanges/btcc/btcc_wrapper.go index 8648e600..fa532ae4 100644 --- a/exchanges/btcc/btcc_wrapper.go +++ b/exchanges/btcc/btcc_wrapper.go @@ -169,8 +169,8 @@ func (b *BTCC) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (b *BTCC) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (b *BTCC) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + return exchange.CancelAllOrdersResponse{}, common.ErrNotYetImplemented } // GetOrderInfo returns information on a current open order diff --git a/exchanges/btcmarkets/btcmarkets.go b/exchanges/btcmarkets/btcmarkets.go index b5ca154a..ac16793f 100644 --- a/exchanges/btcmarkets/btcmarkets.go +++ b/exchanges/btcmarkets/btcmarkets.go @@ -208,7 +208,7 @@ func (b *BTCMarkets) NewOrder(currency, instrument string, price, amount float64 // CancelExistingOrder cancels an order by its ID // orderID - id for order example "1337" -func (b *BTCMarkets) CancelExistingOrder(orderID []int64) (bool, error) { +func (b *BTCMarkets) CancelExistingOrder(orderID []int64) ([]ResponseDetails, error) { resp := Response{} type CancelOrder struct { OrderIDs []int64 `json:"orderIds"` @@ -218,28 +218,14 @@ func (b *BTCMarkets) CancelExistingOrder(orderID []int64) (bool, error) { err := b.SendAuthenticatedRequest("POST", btcMarketsOrderCancel, orders, &resp) if err != nil { - return false, err + return resp.Responses, err } if !resp.Success { - return false, fmt.Errorf("%s Unable to cancel order. Error message: %s", b.GetName(), resp.ErrorMessage) + return resp.Responses, fmt.Errorf("%s Unable to cancel order. Error message: %s", b.GetName(), resp.ErrorMessage) } - ordersToBeCancelled := len(orderID) - ordersCancelled := 0 - for _, y := range resp.Responses { - if y.Success { - ordersCancelled++ - log.Printf("%s Cancelled order %d.\n", b.GetName(), y.ID) - } else { - log.Printf("%s Unable to cancel order %d. Error message: %s", b.GetName(), y.ID, y.ErrorMessage) - } - } - - if ordersCancelled == ordersToBeCancelled { - return true, nil - } - return false, fmt.Errorf("%s Unable to cancel order(s)", b.GetName()) + return resp.Responses, nil } // GetOrders returns current order information on the exchange @@ -294,6 +280,28 @@ func (b *BTCMarkets) GetOrders(currency, instrument string, limit, since int64, return resp.Orders, nil } +// GetOpenOrders returns all open orders +func (b *BTCMarkets) GetOpenOrders() ([]Order, error) { + type marketsResp struct { + Response + Orders []Order `json:"orders"` + } + request := make(map[string]interface{}) + var resp marketsResp + path := fmt.Sprintf("/v2/order/open") + + err := b.SendAuthenticatedRequest("GET", path, request, &resp) + if err != nil { + return nil, err + } + + if !resp.Success { + return nil, errors.New(resp.ErrorMessage) + } + + return resp.Orders, nil +} + // GetOrderDetail returns order information an a specific order // orderID - example "1337" func (b *BTCMarkets) GetOrderDetail(orderID []int64) ([]Order, error) { diff --git a/exchanges/btcmarkets/btcmarkets_test.go b/exchanges/btcmarkets/btcmarkets_test.go index acbec6f0..305c0179 100644 --- a/exchanges/btcmarkets/btcmarkets_test.go +++ b/exchanges/btcmarkets/btcmarkets_test.go @@ -169,13 +169,6 @@ func TestCancelOrder(t *testing.T) { } } -func TestCancelAllOrders(t *testing.T) { - err := b.CancelAllOrders() - if err == nil { - t.Error("Test failed - CancelAllOrders() error", err) - } -} - func TestGetOrderInfo(t *testing.T) { _, err := b.GetOrderInfo(1337) if err == nil { @@ -343,7 +336,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - b.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -361,3 +353,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + b.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := b.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/btcmarkets/btcmarkets_types.go b/exchanges/btcmarkets/btcmarkets_types.go index bd43b364..5b08d3f8 100644 --- a/exchanges/btcmarkets/btcmarkets_types.go +++ b/exchanges/btcmarkets/btcmarkets_types.go @@ -4,19 +4,22 @@ import "github.com/thrasher-/gocryptotrader/currency/symbol" // Response is the genralized response type type Response struct { + Success bool `json:"success"` + ErrorCode int `json:"errorCode"` + ErrorMessage string `json:"errorMessage"` + ID int `json:"id"` + Responses []ResponseDetails `json:"responses"` + ClientRequestID string `json:"clientRequestId"` + Orders []Order `json:"orders"` + Status string `json:"status"` +} + +// ResponseDetails holds order status details +type ResponseDetails struct { Success bool `json:"success"` ErrorCode int `json:"errorCode"` ErrorMessage string `json:"errorMessage"` - ID int `json:"id"` - Responses []struct { - Success bool `json:"success"` - ErrorCode int `json:"errorCode"` - ErrorMessage string `json:"errorMessage"` - ID int64 `json:"id"` - } - ClientRequestID string `json:"clientRequestId"` - Orders []Order `json:"orders"` - Status string `json:"status"` + ID int64 `json:"id"` } // Market holds a tradable market instrument diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 2fb7a54d..77517525 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -186,28 +186,37 @@ func (b *BTCMarkets) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (b *BTCMarkets) CancelAllOrders() error { - orders, err := b.GetOrders("", "", 0, 0, true) +func (b *BTCMarkets) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + openOrders, err := b.GetOpenOrders() if err != nil { - return err + return cancelAllOrdersResponse, err } var orderList []int64 - for _, order := range orders { - orderIDInt, strconvErr := strconv.ParseInt(order.ID, 10, 64) - - if strconvErr != nil { - return strconvErr + for _, order := range openOrders { + orderIDInt, err := strconv.ParseInt(order.ID, 10, 64) + if err != nil { + cancelAllOrdersResponse.OrderStatus[order.ID] = err.Error() } - orderList = append(orderList, orderIDInt) } - _, err = b.CancelExistingOrder(orderList) - if err != nil { - return err + if len(orderList) > 0 { + orders, err := b.CancelExistingOrder(orderList) + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range orders { + if err != nil { + cancelAllOrdersResponse.OrderStatus[strconv.FormatInt(order.ID, 10)] = err.Error() + } + } } - return nil + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/coinbasepro/coinbasepro.go b/exchanges/coinbasepro/coinbasepro.go index dc772424..2ebfc507 100644 --- a/exchanges/coinbasepro/coinbasepro.go +++ b/exchanges/coinbasepro/coinbasepro.go @@ -499,7 +499,7 @@ func (c *CoinbasePro) CancelAllExistingOrders(currencyPair string) ([]string, er var resp []string request := make(map[string]interface{}) - if len(currencyPair) != 0 { + if len(currencyPair) > 0 { request["product_id"] = currencyPair } return resp, c.SendAuthenticatedHTTPRequest("DELETE", coinbaseproOrders, request, &resp) diff --git a/exchanges/coinbasepro/coinbasepro_test.go b/exchanges/coinbasepro/coinbasepro_test.go index 1cc728cb..ce6af061 100644 --- a/exchanges/coinbasepro/coinbasepro_test.go +++ b/exchanges/coinbasepro/coinbasepro_test.go @@ -446,7 +446,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - c.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -464,3 +463,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + c.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := c.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index 76a11507..eaf2dee6 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -180,8 +180,10 @@ func (c *CoinbasePro) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (c *CoinbasePro) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (c *CoinbasePro) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + // CancellAllExisting orders returns a list of successful cancellations, we're only interested in failures + _, err := c.CancelAllExistingOrders("") + return exchange.CancelAllOrdersResponse{}, err } // GetOrderInfo returns information on a current open order diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index 24a0c2d6..4128fa8e 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -204,8 +204,8 @@ func (c *COINUT) NewOrders(orders []Order) ([]OrdersBase, error) { } // GetOpenOrders returns a list of open order and relevant information -func (c *COINUT) GetOpenOrders(instrumentID int) ([]OrdersResponse, error) { - var result []OrdersResponse +func (c *COINUT) GetOpenOrders(instrumentID int) (GetOpenOrdersResponse, error) { + var result GetOpenOrdersResponse params := make(map[string]interface{}) params["inst_id"] = instrumentID @@ -240,7 +240,17 @@ func (c *COINUT) CancelExistingOrder(instrumentID, orderID int) (bool, error) { func (c *COINUT) CancelOrders(orders []CancelOrders) (CancelOrdersResponse, error) { var result CancelOrdersResponse params := make(map[string]interface{}) - params["entries"] = orders + type Request struct { + InstrumentID int `json:"inst_id"` + OrderID int `json:"order_id"` + } + + entries := []CancelOrders{} + for _, order := range orders { + entries = append(entries, order) + } + + params["entries"] = entries return result, c.SendHTTPRequest(coinutOrdersCancel, params, true, &result) } @@ -348,7 +358,7 @@ func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{ headers := make(map[string]string) if authenticated { headers["X-USER"] = c.ClientID - hmac := common.GetHMAC(common.HashSHA256, []byte(payload), []byte(c.APIKey)) + hmac := common.GetHMAC(common.HashSHA256, []byte(payload), []byte(c.APISecret)) headers["X-SIGNATURE"] = common.HexEncodeToString(hmac) } headers["Content-Type"] = "application/json" diff --git a/exchanges/coinut/coinut_test.go b/exchanges/coinut/coinut_test.go index 3a49bb27..d5db3755 100644 --- a/exchanges/coinut/coinut_test.go +++ b/exchanges/coinut/coinut_test.go @@ -32,9 +32,8 @@ func TestSetup(t *testing.T) { } bConfig.AuthenticatedAPISupport = true bConfig.APIKey = apiKey - bConfig.ClientID = clientID - bConfig.Verbose = true c.Setup(bConfig) + c.ClientID = clientID if !c.IsEnabled() || c.RESTPollingDelay != time.Duration(10) || @@ -196,8 +195,9 @@ func TestFormatWithdrawPermissions(t *testing.T) { // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func isRealOrderTestEnabled() bool { - if c.APISecret == "" || - c.APISecret == "Secret" || + + if c.APIKey == "" || + c.APIKey == "Key" || !canManipulateRealOrders { return false } @@ -207,7 +207,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { c.SetDefaults() TestSetup(t) - c.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -233,7 +232,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - c.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -252,6 +250,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + c.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := c.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if apiKey != "" || clientID != "" { _, err := c.GetAccountInfo() diff --git a/exchanges/coinut/coinut_types.go b/exchanges/coinut/coinut_types.go index f42b198e..050a6b9c 100644 --- a/exchanges/coinut/coinut_types.go +++ b/exchanges/coinut/coinut_types.go @@ -137,6 +137,15 @@ type OrdersBase struct { OrderResponse } +// GetOpenOrdersResponse holds all order data from GetOpenOrders request +type GetOpenOrdersResponse struct { + Nonce int `json:"nonce"` + Orders []OrderResponse `json:"orders"` + Reply string `json:"reply"` + Status []string `json:"status"` + TransID int `json:"trans_id"` +} + // OrdersResponse holds the full data range on orders type OrdersResponse struct { Data []OrdersBase @@ -144,7 +153,7 @@ type OrdersResponse struct { // CancelOrders holds information about a cancelled order type CancelOrders struct { - InstrumentID int `json:"int"` + InstrumentID int64 `json:"inst_id"` OrderID int64 `json:"order_id"` } diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index afbe0588..5e8f246d 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -286,8 +286,57 @@ func (c *COINUT) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (c *COINUT) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (c *COINUT) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, 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 := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + instruments, err := c.GetInstruments() + if err != nil { + return cancelAllOrdersResponse, err + } + + var allTheOrders []OrderResponse + for _, allInstrumentData := range instruments.Instruments { + for _, instrumentData := range allInstrumentData { + + openOrders, err := c.GetOpenOrders(instrumentData.InstID) + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, openOrder := range openOrders.Orders { + allTheOrders = append(allTheOrders, openOrder) + } + } + } + + var allTheOrdersToCancel []CancelOrders + for _, orderToCancel := range allTheOrders { + cancelOrder := CancelOrders{ + InstrumentID: orderToCancel.InstrumentID, + OrderID: orderToCancel.OrderID, + } + allTheOrdersToCancel = append(allTheOrdersToCancel, cancelOrder) + } + + if len(allTheOrdersToCancel) > 0 { + resp, err := c.CancelOrders(allTheOrdersToCancel) + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range resp.Results { + if order.Status != "OK" { + cancelAllOrdersResponse.OrderStatus[strconv.FormatInt(order.OrderID, 10)] = order.Status + } + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 5d968c77..1a61bf3e 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -264,7 +264,7 @@ type IBotExchange interface { SubmitOrder(p pair.CurrencyPair, side OrderSide, orderType OrderType, amount, price float64, clientID string) (SubmitOrderResponse, error) ModifyOrder(orderID int64, modify ModifyOrder) (int64, error) CancelOrder(order OrderCancellation) error - CancelAllOrders() error + CancelAllOrders(orders OrderCancellation) (CancelAllOrdersResponse, error) GetOrderInfo(orderID int64) (OrderDetail, error) GetDepositAddress(cryptocurrency pair.CurrencyItem) (string, error) @@ -741,6 +741,11 @@ type Format struct { OrderSide map[string]string } +// CancelAllOrdersResponse returns the status from attempting to cancel all orders on an exchagne +type CancelAllOrdersResponse struct { + OrderStatus map[string]string +} + // Formatting contain a range of exchanges formatting type Formatting []Format diff --git a/exchanges/exmo/exmo_test.go b/exchanges/exmo/exmo_test.go index d94aeb7e..82aa5c59 100644 --- a/exchanges/exmo/exmo_test.go +++ b/exchanges/exmo/exmo_test.go @@ -262,7 +262,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { e.SetDefaults() TestSetup(t) - e.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -288,7 +287,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - e.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -306,3 +304,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + e.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := e.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index 9e329785..a480a53c 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -223,8 +223,23 @@ func (e *EXMO) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (e *EXMO) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (e *EXMO) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + openOrders, err := e.GetOpenOrders() + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range openOrders { + err = e.CancelExistingOrder(order.OrderID) + if err != nil { + cancelAllOrdersResponse.OrderStatus[strconv.FormatInt(order.OrderID, 10)] = err.Error() + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/gateio/gateio.go b/exchanges/gateio/gateio.go index b23761c7..e69b600c 100644 --- a/exchanges/gateio/gateio.go +++ b/exchanges/gateio/gateio.go @@ -21,15 +21,17 @@ const ( gateioMarketURL = "https://data.gateio.io" gateioAPIVersion = "api2/1" - gateioSymbol = "pairs" - gateioMarketInfo = "marketinfo" - gateioKline = "candlestick2" - gateioOrder = "private" - gateioBalances = "private/balances" - gateioCancelOrder = "private/cancelOrder" - gateioTicker = "ticker" - gateioTickers = "tickers" - gateioOrderbook = "orderBook" + gateioSymbol = "pairs" + gateioMarketInfo = "marketinfo" + gateioKline = "candlestick2" + gateioOrder = "private" + gateioBalances = "private/balances" + gateioCancelOrder = "private/cancelOrder" + gateioCancelAllOrders = "private/cancelAllOrders" + gateioOpenOrders = "private/openOrders" + gateioTicker = "ticker" + gateioTickers = "tickers" + gateioOrderbook = "orderBook" gateioAuthRate = 100 gateioUnauthRate = 100 @@ -381,6 +383,54 @@ func (g *Gateio) SendHTTPRequest(path string, result interface{}) error { return g.SendPayload("GET", path, nil, nil, result, false, g.Verbose) } +// CancelAllExistingOrders all orders for a given symbol and side +// orderType (0: sell,1: buy,-1: unlimited) +func (g *Gateio) CancelAllExistingOrders(orderType int64, symbol string) error { + type response struct { + Result bool `json:"result"` + Code int `json:"code"` + Message string `json:"message"` + } + + var result response + params := fmt.Sprintf("type=%d¤cyPair=%s", + orderType, + symbol, + ) + err := g.SendAuthenticatedHTTPRequest("POST", gateioCancelAllOrders, params, &result) + if err != nil { + return err + } + + if !result.Result { + return fmt.Errorf("code:%d message:%s", result.Code, result.Message) + } + + return nil +} + +// +// GetOpenOrders retrieves all orders with an optional symbol filter +func (g *Gateio) GetOpenOrders(symbol string) (OpenOrdersResponse, error) { + var params string + var result OpenOrdersResponse + + if symbol != "" { + params = fmt.Sprintf("currencyPair=%s", symbol) + } + + err := g.SendAuthenticatedHTTPRequest("POST", gateioOpenOrders, params, &result) + if err != nil { + return result, err + } + + if result.Code > 0 { + return result, fmt.Errorf("code:%d message:%s", result.Code, result.Message) + } + + return result, nil +} + // SendAuthenticatedHTTPRequest sends authenticated requests to the Gateio API // To use this you must setup an APIKey and APISecret from the exchange func (g *Gateio) SendAuthenticatedHTTPRequest(method, endpoint, param string, result interface{}) error { diff --git a/exchanges/gateio/gateio_test.go b/exchanges/gateio/gateio_test.go index 5ea4da34..75352e7a 100644 --- a/exchanges/gateio/gateio_test.go +++ b/exchanges/gateio/gateio_test.go @@ -264,7 +264,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { g.SetDefaults() TestSetup(t) - g.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -290,7 +289,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - g.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -309,6 +307,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + g.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := g.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if apiSecret == "" || apiKey == "" { _, err := g.GetAccountInfo() diff --git a/exchanges/gateio/gateio_types.go b/exchanges/gateio/gateio_types.go index 81ea3215..7a30e975 100644 --- a/exchanges/gateio/gateio_types.go +++ b/exchanges/gateio/gateio_types.go @@ -130,6 +130,31 @@ type SpotNewOrderResponse struct { Filledrate float64 `json:"filledRate,string"` // FilledPrice } +// OpenOrdersResponse the main response from GetOpenOrders +type OpenOrdersResponse struct { + Code int `json:"code"` + Elapsed string `json:"elapsed"` + Message string `json:"message"` + Orders []OpenOrder `json:"orders"` + Result string `json:"result"` +} + +// OpenOrder details each open order +type OpenOrder struct { + Amount string `json:"amount"` + CurrencyPair string `json:"currencyPair"` + FilledAmount int `json:"filledAmount"` + FilledRate int `json:"filledRate"` + InitialAmount string `json:"initialAmount"` + InitialRate float64 `json:"initialRate"` + OrderNumber string `json:"orderNumber"` + Rate float64 `json:"rate"` + Status string `json:"status"` + Timestamp string `json:"timestamp"` + Total string `json:"total"` + Type string `json:"type"` +} + // WithdrawalFees the large list of predefined withdrawal fees // Prone to change var WithdrawalFees = map[string]float64{ diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index 0f3a7efc..a4eb391e 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -231,8 +231,28 @@ func (g *Gateio) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (g *Gateio) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (g *Gateio) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + openOrders, err := g.GetOpenOrders("") + if err != nil { + return cancelAllOrdersResponse, err + } + + var uniqueSymbols map[string]string + for _, openOrder := range openOrders.Orders { + uniqueSymbols[openOrder.CurrencyPair] = openOrder.CurrencyPair + } + + for _, uniqueSymbol := range uniqueSymbols { + err = g.CancelAllExistingOrders(-1, uniqueSymbol) + if err != nil { + return cancelAllOrdersResponse, err + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/gemini/gemini_test.go b/exchanges/gemini/gemini_test.go index f4f90161..9a4c81ac 100644 --- a/exchanges/gemini/gemini_test.go +++ b/exchanges/gemini/gemini_test.go @@ -338,7 +338,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { Session[1].SetDefaults() TestSetup(t) - Session[1].Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -364,7 +363,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - Session[1].Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -382,3 +380,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + Session[1].SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := Session[1].CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 48ae004e..9b2e0425 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -153,19 +153,29 @@ func (g *Gemini) ModifyOrder(orderID int64, action exchange.ModifyOrder) (int64, // CancelOrder cancels an order by its corresponding ID number func (g *Gemini) CancelOrder(order exchange.OrderCancellation) error { orderIDInt, err := strconv.ParseInt(order.OrderID, 10, 64) - if err != nil { return err } _, err = g.CancelExistingOrder(orderIDInt) - return err } // CancelAllOrders cancels all orders associated with a currency pair -func (g *Gemini) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (g *Gemini) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + resp, err := g.CancelExistingOrders(false) + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range resp.Details.CancelRejects { + cancelAllOrdersResponse.OrderStatus[order] = "Could not cancel order" + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/hitbtc/hitbtc.go b/exchanges/hitbtc/hitbtc.go index 0174fed4..490f55f3 100644 --- a/exchanges/hitbtc/hitbtc.go +++ b/exchanges/hitbtc/hitbtc.go @@ -449,6 +449,19 @@ func (h *HitBTC) CancelExistingOrder(orderID int64) (bool, error) { return true, nil } +// CancelAllExistingOrders cancels all open orders +func (h *HitBTC) CancelAllExistingOrders() ([]Order, error) { + var result []Order + values := url.Values{} + + err := h.SendAuthenticatedHTTPRequest("DELETE", orderBuy, values, &result) + if err != nil { + return result, err + } + + return result, nil +} + // MoveOrder generates a new move order func (h *HitBTC) MoveOrder(orderID int64, rate, amount float64) (MoveOrderResponse, error) { result := MoveOrderResponse{} diff --git a/exchanges/hitbtc/hitbtc_test.go b/exchanges/hitbtc/hitbtc_test.go index eb80a064..036ddc13 100644 --- a/exchanges/hitbtc/hitbtc_test.go +++ b/exchanges/hitbtc/hitbtc_test.go @@ -189,7 +189,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { h.SetDefaults() TestSetup(t) - h.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -215,7 +214,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - h.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -233,3 +231,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + h.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := h.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index 1ed0fc5d..a075a3e7 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -191,8 +191,20 @@ func (h *HitBTC) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (h *HitBTC) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (h *HitBTC) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + resp, err := h.CancelAllExistingOrders() + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range resp { + cancelAllOrdersResponse.OrderStatus[strconv.FormatInt(order.ID, 10)] = fmt.Sprintf("Could not cancel order %v. Status: %v", order.ID, order.Status) + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/huobi/huobi.go b/exchanges/huobi/huobi.go index 7d08101d..c2a6671b 100644 --- a/exchanges/huobi/huobi.go +++ b/exchanges/huobi/huobi.go @@ -29,32 +29,34 @@ const ( huobiAPIURL = "https://api.huobi.pro" huobiAPIVersion = "1" - huobiMarketHistoryKline = "market/history/kline" - huobiMarketDetail = "market/detail" - huobiMarketDetailMerged = "market/detail/merged" - huobiMarketDepth = "market/depth" - huobiMarketTrade = "market/trade" - huobiMarketTradeHistory = "market/history/trade" - huobiSymbols = "common/symbols" - huobiCurrencies = "common/currencys" - huobiTimestamp = "common/timestamp" - huobiAccounts = "account/accounts" - huobiAccountBalance = "account/accounts/%s/balance" - huobiOrderPlace = "order/orders/place" - huobiOrderCancel = "order/orders/%s/submitcancel" - huobiOrderCancelBatch = "order/orders/batchcancel" - huobiGetOrder = "order/orders/%s" - huobiGetOrderMatch = "order/orders/%s/matchresults" - huobiGetOrders = "order/orders" - huobiGetOrdersMatch = "orders/matchresults" - huobiMarginTransferIn = "dw/transfer-in/margin" - huobiMarginTransferOut = "dw/transfer-out/margin" - huobiMarginOrders = "margin/orders" - huobiMarginRepay = "margin/orders/%s/repay" - huobiMarginLoanOrders = "margin/loan-orders" - huobiMarginAccountBalance = "margin/accounts/balance" - huobiWithdrawCreate = "dw/withdraw/api/create" - huobiWithdrawCancel = "dw/withdraw-virtual/%s/cancel" + huobiMarketHistoryKline = "market/history/kline" + huobiMarketDetail = "market/detail" + huobiMarketDetailMerged = "market/detail/merged" + huobiMarketDepth = "market/depth" + huobiMarketTrade = "market/trade" + huobiMarketTradeHistory = "market/history/trade" + huobiSymbols = "common/symbols" + huobiCurrencies = "common/currencys" + huobiTimestamp = "common/timestamp" + huobiAccounts = "account/accounts" + huobiAccountBalance = "account/accounts/%s/balance" + huobiOrderPlace = "order/orders/place" + huobiOrderCancel = "order/orders/%s/submitcancel" + huobiOrderCancelBatch = "order/orders/batchcancel" + huobiBatchCancelOpenOrders = "order/orders/batchCancelOpenOrders" + huobiGetOrder = "order/orders/%s" + huobiGetOrderMatch = "order/orders/%s/matchresults" + huobiGetOrders = "order/orders" + huobiGetOpenOrders = "order/order/openOrders" + huobiGetOrdersMatch = "orders/matchresults" + huobiMarginTransferIn = "dw/transfer-in/margin" + huobiMarginTransferOut = "dw/transfer-out/margin" + huobiMarginOrders = "margin/orders" + huobiMarginRepay = "margin/orders/%s/repay" + huobiMarginLoanOrders = "margin/loan-orders" + huobiMarginAccountBalance = "margin/accounts/balance" + huobiWithdrawCreate = "dw/withdraw/api/create" + huobiWithdrawCancel = "dw/withdraw-virtual/%s/cancel" huobiAuthRate = 100 huobiUnauthRate = 100 @@ -129,6 +131,7 @@ func (h *HUOBI) Setup(exch config.ExchangeConfig) { if err != nil { log.Fatal(err) } + err = h.WebsocketSetup(h.WsConnect, exch.Name, exch.Websocket, @@ -452,6 +455,29 @@ func (h *HUOBI) CancelOrderBatch(orderIDs []int64) ([]CancelOrderBatch, error) { return result.Data, err } +// CancelOpenOrdersBatch cancels a batch of orders -- to-do +func (h *HUOBI) CancelOpenOrdersBatch(accountID, symbol string) (CancelOpenOrdersBatch, error) { + params := url.Values{} + + params.Set("account-id", accountID) + var result CancelOpenOrdersBatch + + data := struct { + AccountID string `json:"account-id"` + Symbol string `json:"symbol"` + }{ + AccountID: accountID, + Symbol: symbol, + } + + err := h.SendAuthenticatedHTTPRequest("POST", huobiBatchCancelOpenOrders, url.Values{}, data, &result) + if result.Data.FailedCount > 0 { + return result, fmt.Errorf("There were %v failed order cancellations", result.Data.FailedCount) + } + + return result, err +} + // GetOrder returns order information for the specified order func (h *HUOBI) GetOrder(orderID int64) (OrderInfo, error) { type response struct { @@ -530,6 +556,29 @@ func (h *HUOBI) GetOrders(symbol, types, start, end, states, from, direct, size return result.Orders, err } +// GetOpenOrders returns a list of orders +func (h *HUOBI) GetOpenOrders(accountID, symbol, side string, size int) ([]OrderInfo, error) { + type response struct { + Response + Orders []OrderInfo `json:"data"` + } + + vals := url.Values{} + vals.Set("symbol", symbol) + vals.Set("accountID", accountID) + vals.Set("side", side) + vals.Set("size", fmt.Sprintf("%v", size)) + + var result response + err := h.SendAuthenticatedHTTPRequest("GET", huobiGetOpenOrders, vals, nil, &result) + + if result.ErrorMessage != "" { + return nil, errors.New(result.ErrorMessage) + } + + return result.Orders, err +} + // GetOrdersMatch returns a list of matched orders func (h *HUOBI) GetOrdersMatch(symbol, types, start, end, from, direct, size string) ([]OrderMatchInfo, error) { type response struct { diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index 5ba5e19a..80f9ec67 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -410,7 +410,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { h.SetDefaults() TestSetup(t) - h.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -438,7 +437,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - h.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -457,6 +455,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + h.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := h.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if apiKey == "" || apiSecret == "" { _, err := h.GetAccountInfo() diff --git a/exchanges/huobi/huobi_types.go b/exchanges/huobi/huobi_types.go index 880f928c..714cc2d1 100644 --- a/exchanges/huobi/huobi_types.go +++ b/exchanges/huobi/huobi_types.go @@ -21,6 +21,16 @@ type KlineItem struct { Count int `json:"count"` } +// CancelOpenOrdersBatch stores open order batch response data +type CancelOpenOrdersBatch struct { + Data struct { + FailedCount int `json:"failed-count"` + NextID int `json:"next-id"` + SuccessCount int `json:"success-count"` + } `json:"data"` + Status string `json:"status"` +} + // DetailMerged stores the ticker detail merged data type DetailMerged struct { Detail diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index d7a4f0d7..a5f2eea1 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -297,8 +297,22 @@ func (h *HUOBI) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (h *HUOBI) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (h *HUOBI) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + for _, currency := range h.GetEnabledCurrencies() { + resp, err := h.CancelOpenOrdersBatch(orderCancellation.AccountID, exchange.FormatExchangeCurrency(h.Name, currency).String()) + if err != nil { + return cancelAllOrdersResponse, err + } + + if resp.Data.FailedCount > 0 { + return cancelAllOrdersResponse, fmt.Errorf("%v orders failed to cancel", resp.Data.FailedCount) + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/huobihadax/huobihadax.go b/exchanges/huobihadax/huobihadax.go index 6cb54d22..bb941dda 100644 --- a/exchanges/huobihadax/huobihadax.go +++ b/exchanges/huobihadax/huobihadax.go @@ -22,32 +22,34 @@ const ( huobihadaxAPIVersion = "1" huobihadaxAPIName = "hadax" - huobihadaxMarketHistoryKline = "market/history/kline" - huobihadaxMarketDetail = "market/detail" - huobihadaxMarketDetailMerged = "market/detail/merged" - huobihadaxMarketDepth = "market/depth" - huobihadaxMarketTrade = "market/trade" - huobihadaxMarketTradeHistory = "market/history/trade" - huobihadaxSymbols = "common/symbols" - huobihadaxCurrencies = "common/currencys" - huobihadaxTimestamp = "common/timestamp" - huobihadaxAccounts = "account/accounts" - huobihadaxAccountBalance = "account/accounts/%s/balance" - huobihadaxOrderPlace = "order/orders/place" - huobihadaxOrderCancel = "order/orders/%s/submitcancel" - huobihadaxOrderCancelBatch = "order/orders/batchcancel" - huobihadaxGetOrder = "order/orders/%s" - huobihadaxGetOrderMatch = "order/orders/%s/matchresults" - huobihadaxGetOrders = "order/orders" - huobihadaxGetOrdersMatch = "orders/matchresults" - huobihadaxMarginTransferIn = "dw/transfer-in/margin" - huobihadaxMarginTransferOut = "dw/transfer-out/margin" - huobihadaxMarginOrders = "margin/orders" - huobihadaxMarginRepay = "margin/orders/%s/repay" - huobihadaxMarginLoanOrders = "margin/loan-orders" - huobihadaxMarginAccountBalance = "margin/accounts/balance" - huobihadaxWithdrawCreate = "dw/withdraw/api/create" - huobihadaxWithdrawCancel = "dw/withdraw-virtual/%s/cancel" + huobihadaxMarketHistoryKline = "market/history/kline" + huobihadaxMarketDetail = "market/detail" + huobihadaxMarketDetailMerged = "market/detail/merged" + huobihadaxMarketDepth = "market/depth" + huobihadaxMarketTrade = "market/trade" + huobihadaxMarketTradeHistory = "market/history/trade" + huobihadaxSymbols = "common/symbols" + huobihadaxCurrencies = "common/currencys" + huobihadaxTimestamp = "common/timestamp" + huobihadaxAccounts = "account/accounts" + huobihadaxAccountBalance = "account/accounts/%s/balance" + huobihadaxOrderPlace = "order/orders/place" + huobihadaxOrderCancel = "order/orders/%s/submitcancel" + huobihadaxGetOpenOrders = "order/order/openOrders" + huobihadaxOrderCancelBatch = "order/orders/batchcancel" + huobiHadaxBatchCancelOpenOrders = "order/orders/batchCancelOpenOrders" + huobihadaxGetOrder = "order/orders/%s" + huobihadaxGetOrderMatch = "order/orders/%s/matchresults" + huobihadaxGetOrders = "order/orders" + huobihadaxGetOrdersMatch = "orders/matchresults" + huobihadaxMarginTransferIn = "dw/transfer-in/margin" + huobihadaxMarginTransferOut = "dw/transfer-out/margin" + huobihadaxMarginOrders = "margin/orders" + huobihadaxMarginRepay = "margin/orders/%s/repay" + huobihadaxMarginLoanOrders = "margin/loan-orders" + huobihadaxMarginAccountBalance = "margin/accounts/balance" + huobihadaxWithdrawCreate = "dw/withdraw/api/create" + huobihadaxWithdrawCancel = "dw/withdraw-virtual/%s/cancel" huobihadaxAuthRate = 100 huobihadaxUnauthRate = 100 @@ -443,6 +445,56 @@ func (h *HUOBIHADAX) CancelOrderBatch(orderIDs []int64) (CancelOrderBatch, error return result.Data, err } +// CancelOpenOrdersBatch cancels a batch of orders -- to-do +func (h *HUOBIHADAX) CancelOpenOrdersBatch(accountID, symbol string) (CancelOpenOrdersBatch, error) { + params := url.Values{} + + params.Set("account-id", accountID) + var result CancelOpenOrdersBatch + + data := struct { + AccountID string `json:"account-id"` + Symbol string `json:"symbol"` + }{ + AccountID: accountID, + Symbol: symbol, + } + + bytesParams, _ := common.JSONEncode(data) + postBodyParams := string(bytesParams) + + err := h.SendAuthenticatedHTTPPostRequest("POST", huobiHadaxBatchCancelOpenOrders, postBodyParams, &result) + + if result.Data.FailedCount > 0 { + return result, fmt.Errorf("There were %v failed order cancellations", result.Data.FailedCount) + } + + return result, err +} + +// GetOpenOrders returns a list of orders +func (h *HUOBIHADAX) GetOpenOrders(accountID, symbol, side string, size int) ([]OrderInfo, error) { + type response struct { + Response + Orders []OrderInfo `json:"data"` + } + + vals := url.Values{} + vals.Set("symbol", symbol) + vals.Set("accountID", accountID) + vals.Set("side", side) + vals.Set("size", fmt.Sprintf("%v", size)) + + var result response + err := h.SendAuthenticatedHTTPRequest("GET", huobihadaxGetOpenOrders, vals, &result) + + if result.ErrorMessage != "" { + return nil, errors.New(result.ErrorMessage) + } + + return result.Orders, err +} + // GetOrder returns order information for the specified order func (h *HUOBIHADAX) GetOrder(orderID int64) (OrderInfo, error) { type response struct { diff --git a/exchanges/huobihadax/huobihadax_test.go b/exchanges/huobihadax/huobihadax_test.go index 4d8fc213..89e3cb52 100644 --- a/exchanges/huobihadax/huobihadax_test.go +++ b/exchanges/huobihadax/huobihadax_test.go @@ -389,7 +389,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { h.SetDefaults() TestSetup(t) - h.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -417,7 +416,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - h.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -436,6 +434,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + h.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := h.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if apiKey == "" || apiSecret == "" { _, err := h.GetAccountInfo() diff --git a/exchanges/huobihadax/huobihadax_types.go b/exchanges/huobihadax/huobihadax_types.go index 8ee867f6..f8c3df88 100644 --- a/exchanges/huobihadax/huobihadax_types.go +++ b/exchanges/huobihadax/huobihadax_types.go @@ -46,6 +46,16 @@ type Trade struct { Timestamp int64 `json:"ts"` } +// CancelOpenOrdersBatch stores open order batch response data +type CancelOpenOrdersBatch struct { + Data struct { + FailedCount int `json:"failed-count"` + NextID int `json:"next-id"` + SuccessCount int `json:"success-count"` + } `json:"data"` + Status string `json:"status"` +} + // TradeHistory stores the the trade history data type TradeHistory struct { ID int64 `json:"id"` diff --git a/exchanges/huobihadax/huobihadax_wrapper.go b/exchanges/huobihadax/huobihadax_wrapper.go index 8c8551d8..2e4c24c8 100644 --- a/exchanges/huobihadax/huobihadax_wrapper.go +++ b/exchanges/huobihadax/huobihadax_wrapper.go @@ -262,8 +262,22 @@ func (h *HUOBIHADAX) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (h *HUOBIHADAX) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (h *HUOBIHADAX) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + for _, currency := range h.GetEnabledCurrencies() { + resp, err := h.CancelOpenOrdersBatch(orderCancellation.AccountID, exchange.FormatExchangeCurrency(h.Name, currency).String()) + if err != nil { + return cancelAllOrdersResponse, err + } + + if resp.Data.FailedCount > 0 { + return cancelAllOrdersResponse, fmt.Errorf("%v orders failed to cancel", resp.Data.FailedCount) + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/itbit/itbit.go b/exchanges/itbit/itbit.go index 962387f7..916bc3a9 100644 --- a/exchanges/itbit/itbit.go +++ b/exchanges/itbit/itbit.go @@ -195,6 +195,34 @@ func (i *ItBit) GetWalletBalance(walletID, currency string) (Balance, error) { return resp, nil } +// GetOrders returns active orders for itBit +// perPage defaults to & has a limit of 50 +func (i *ItBit) GetOrders(walletID, symbol, status string, page, perPage int64) ([]Order, error) { + var resp []Order + params := make(map[string]interface{}) + params["walletID"] = walletID + + if symbol != "" { + params["instrument"] = symbol + } + if status != "" { + params["status"] = status + } + if page > 0 { + params["page"] = strconv.FormatInt(page, 10) + } + if perPage > 0 { + params["perPage"] = strconv.FormatInt(perPage, 10) + } + + err := i.SendAuthenticatedHTTPRequest("GET", itbitOrders, params, &resp) + if err != nil { + return resp, err + } + + return resp, nil +} + // GetWalletTrades returns all trades for a specified wallet. func (i *ItBit) GetWalletTrades(walletID string, params url.Values) (Records, error) { resp := Records{} diff --git a/exchanges/itbit/itbit_test.go b/exchanges/itbit/itbit_test.go index 4cc890cf..d69c5580 100644 --- a/exchanges/itbit/itbit_test.go +++ b/exchanges/itbit/itbit_test.go @@ -258,7 +258,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { i.SetDefaults() TestSetup(t) - i.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -284,7 +283,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - i.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -303,6 +301,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + i.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := i.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if apiKey != "" || apiSecret != "" || clientID != "" { _, err := i.GetAccountInfo() diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index 6ccddd55..9e21601f 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -211,8 +211,23 @@ func (i *ItBit) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (i *ItBit) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (i *ItBit) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + openOrders, err := i.GetOrders(orderCancellation.WalletAddress, "", "open", 0, 0) + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, openOrder := range openOrders { + err = i.CancelExistingOrder(orderCancellation.WalletAddress, openOrder.ID) + if err != nil { + cancelAllOrdersResponse.OrderStatus[openOrder.ID] = err.Error() + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index ca6697f8..5633ffe9 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -344,7 +344,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { k.SetDefaults() TestSetup(t) - k.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -370,7 +369,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - k.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -389,6 +387,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + k.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := k.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if apiKey != "" || apiSecret != "" || clientID != "" { _, err := k.GetAccountInfo() diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 7c1b0d8e..a99368f3 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -207,8 +207,26 @@ func (k *Kraken) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (k *Kraken) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (k *Kraken) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + var emptyOrderOptions OrderInfoOptions + openOrders, err := k.GetOpenOrders(emptyOrderOptions) + if err != nil { + return cancelAllOrdersResponse, err + } + + if openOrders.Count > 0 { + for orderID := range openOrders.Open { + _, err = k.CancelExistingOrder(orderID) + if err != nil { + cancelAllOrdersResponse.OrderStatus[orderID] = err.Error() + } + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/lakebtc/lakebtc.go b/exchanges/lakebtc/lakebtc.go index c5577f55..cea4b98e 100644 --- a/exchanges/lakebtc/lakebtc.go +++ b/exchanges/lakebtc/lakebtc.go @@ -278,6 +278,25 @@ func (l *LakeBTC) CancelExistingOrder(orderID int64) error { return nil } +// CancelExistingOrders cancels an order by ID number and returns an error +func (l *LakeBTC) CancelExistingOrders(orderIDs []string) error { + type Response struct { + Result bool `json:"Result"` + } + + resp := Response{} + params := common.JoinStrings(orderIDs, ",") + err := l.SendAuthenticatedHTTPRequest(lakeBTCCancelOrder, params, &resp) + if err != nil { + return err + } + + if resp.Result != true { + return fmt.Errorf("unable to cancel order(s): %v", orderIDs) + } + return nil +} + // GetTrades returns trades associated with your account by timestamp func (l *LakeBTC) GetTrades(timestamp int64) ([]AuthenticatedTradeHistory, error) { params := "" diff --git a/exchanges/lakebtc/lakebtc_test.go b/exchanges/lakebtc/lakebtc_test.go index 64eae0a3..3c85afa5 100644 --- a/exchanges/lakebtc/lakebtc_test.go +++ b/exchanges/lakebtc/lakebtc_test.go @@ -262,7 +262,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { l.SetDefaults() TestSetup(t) - l.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -288,7 +287,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - l.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -306,3 +304,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + l.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := l.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/lakebtc/lakebtc_wrapper.go b/exchanges/lakebtc/lakebtc_wrapper.go index dd6b5708..7773c827 100644 --- a/exchanges/lakebtc/lakebtc_wrapper.go +++ b/exchanges/lakebtc/lakebtc_wrapper.go @@ -173,8 +173,23 @@ func (l *LakeBTC) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (l *LakeBTC) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (l *LakeBTC) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + openOrders, err := l.GetOpenOrders() + if err != nil { + return cancelAllOrdersResponse, err + } + + var ordersToCancel []string + for _, order := range openOrders { + orderIDString := strconv.FormatInt(order.ID, 10) + ordersToCancel = append(ordersToCancel, orderIDString) + } + + return cancelAllOrdersResponse, l.CancelExistingOrders(ordersToCancel) + } // GetOrderInfo returns information on a current open order diff --git a/exchanges/liqui/liqui.go b/exchanges/liqui/liqui.go index 9e967379..3cac5ee5 100644 --- a/exchanges/liqui/liqui.go +++ b/exchanges/liqui/liqui.go @@ -228,18 +228,12 @@ func (l *Liqui) GetOrderInfoByID(OrderID int64) (map[string]OrderInfo, error) { } // CancelExistingOrder method is used for order cancelation. -func (l *Liqui) CancelExistingOrder(OrderID int64) (bool, error) { +func (l *Liqui) CancelExistingOrder(OrderID int64) error { req := url.Values{} req.Add("order_id", strconv.FormatInt(OrderID, 10)) - var result CancelOrder - err := l.SendAuthenticatedHTTPRequest(liquiCancelOrder, req, &result) - if err != nil { - return false, err - } - - return true, nil + return l.SendAuthenticatedHTTPRequest(liquiCancelOrder, req, &result) } // GetTradeHistory returns trade history diff --git a/exchanges/liqui/liqui_test.go b/exchanges/liqui/liqui_test.go index c0f80999..7c2dfc64 100644 --- a/exchanges/liqui/liqui_test.go +++ b/exchanges/liqui/liqui_test.go @@ -99,7 +99,7 @@ func TestAuthRequests(t *testing.T) { t.Error("Test Failed - liqui GetOrderInfo() error", err) } - _, err = l.CancelExistingOrder(1337) + err = l.CancelExistingOrder(1337) if err == nil { t.Error("Test Failed - liqui CancelExistingOrder() error", err) } @@ -247,7 +247,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { l.SetDefaults() TestSetup(t) - l.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -273,7 +272,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - l.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -291,3 +289,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + l.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := l.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/liqui/liqui_wrapper.go b/exchanges/liqui/liqui_wrapper.go index 78fef956..3518ef33 100644 --- a/exchanges/liqui/liqui_wrapper.go +++ b/exchanges/liqui/liqui_wrapper.go @@ -178,14 +178,33 @@ func (l *Liqui) CancelOrder(order exchange.OrderCancellation) error { return err } - _, err = l.CancelExistingOrder(orderIDInt) + return l.CancelExistingOrder(orderIDInt) - return err } // CancelAllOrders cancels all orders associated with a currency pair -func (l *Liqui) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (l *Liqui) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + activeOrders, err := l.GetActiveOrders("") + if err != nil { + return cancelAllOrdersResponse, err + } + + for activeOrder := range activeOrders { + orderIDInt, err := strconv.ParseInt(activeOrder, 10, 64) + if err != nil { + return cancelAllOrdersResponse, err + } + + err = l.CancelExistingOrder(orderIDInt) + if err != nil { + cancelAllOrdersResponse.OrderStatus[activeOrder] = err.Error() + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/localbitcoins/localbitcoins_test.go b/exchanges/localbitcoins/localbitcoins_test.go index 7a62d6e7..11d4b44a 100644 --- a/exchanges/localbitcoins/localbitcoins_test.go +++ b/exchanges/localbitcoins/localbitcoins_test.go @@ -212,7 +212,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { l.SetDefaults() TestSetup(t) - l.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -228,6 +227,7 @@ func TestSubmitOrder(t *testing.T) { t.Errorf("Order failed to be placed: %v", err) } } + func TestCancelExchangeOrder(t *testing.T) { // Arrange l.SetDefaults() @@ -237,7 +237,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - l.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -255,3 +254,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + l.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := l.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 74cce3ab..5c46af8e 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "math" + "strconv" "sync" "github.com/thrasher-/gocryptotrader/common" @@ -218,8 +219,24 @@ func (l *LocalBitcoins) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (l *LocalBitcoins) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (l *LocalBitcoins) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + ads, err := l.Getads() + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, ad := range ads.AdList { + adIDString := strconv.FormatInt(ad.Data.AdID, 10) + err = l.DeleteAd(adIDString) + if err != nil { + cancelAllOrdersResponse.OrderStatus[strconv.FormatInt(ad.Data.AdID, 10)] = err.Error() + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/okcoin/okcoin_test.go b/exchanges/okcoin/okcoin_test.go index 942e9341..bb1d4bc0 100644 --- a/exchanges/okcoin/okcoin_test.go +++ b/exchanges/okcoin/okcoin_test.go @@ -162,7 +162,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { o.SetDefaults() TestSetup(t) - o.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -188,7 +187,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - o.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -206,3 +204,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + o.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := o.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go index 7d4deb09..5f936b16 100644 --- a/exchanges/okcoin/okcoin_wrapper.go +++ b/exchanges/okcoin/okcoin_wrapper.go @@ -246,8 +246,34 @@ func (o *OKCoin) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (o *OKCoin) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (o *OKCoin) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + orderInfo, err := o.GetOrderInformation(-1, exchange.FormatExchangeCurrency(o.Name, orderCancellation.CurrencyPair).String()) + if err != nil { + return cancelAllOrdersResponse, err + } + + var ordersToCancel []int64 + for _, order := range orderInfo { + ordersToCancel = append(ordersToCancel, order.OrderID) + } + + if len(ordersToCancel) > 0 { + resp, err := o.CancelExistingOrder(ordersToCancel, exchange.FormatExchangeCurrency(o.Name, orderCancellation.CurrencyPair).String()) + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, order := range common.SplitStrings(resp.Error, ",") { + if err != nil { + cancelAllOrdersResponse.OrderStatus[order] = "Order could not be cancelled" + } + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/okex/okex.go b/exchanges/okex/okex.go index c97938b2..ce7c3760 100644 --- a/exchanges/okex/okex.go +++ b/exchanges/okex/okex.go @@ -650,6 +650,20 @@ func (o *OKEX) GetContractFuturesTradeHistory(symbol, date string, since int) er return nil } +// GetTokenOrders returns details for a single orderID or all open orders when orderID == -1 +func (o *OKEX) GetTokenOrders(symbol string, orderID int64) (TokenOrdersResponse, error) { + var resp TokenOrdersResponse + values := url.Values{} + values.Set("symbol", symbol) + values.Set("order_id", strconv.FormatInt(orderID, 10)) + + if err := o.SendAuthenticatedHTTPRequest(contractFutureTradeHistory, values, &resp); err != nil { + return resp, err + } + + return resp, nil +} + // GetUserInfo returns the user info func (o *OKEX) GetUserInfo() (SpotUserInfo, error) { var resp SpotUserInfo @@ -696,8 +710,8 @@ func (o *OKEX) SpotCancelOrder(symbol string, argOrderID int64) (int64, error) { params := url.Values{} params.Set("symbol", symbol) params.Set("order_id", strconv.FormatInt(argOrderID, 10)) - var returnOrderID int64 + err := o.SendAuthenticatedHTTPRequest(spotCancelTrade+".do", params, &res) if err != nil { return returnOrderID, err diff --git a/exchanges/okex/okex_test.go b/exchanges/okex/okex_test.go index 47e0b166..dd8c3f13 100644 --- a/exchanges/okex/okex_test.go +++ b/exchanges/okex/okex_test.go @@ -454,6 +454,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + o.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := o.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if apiKey != "" || apiSecret != "" { _, err := o.GetAccountInfo() diff --git a/exchanges/okex/okex_types.go b/exchanges/okex/okex_types.go index 40a91ea2..1d750509 100644 --- a/exchanges/okex/okex_types.go +++ b/exchanges/okex/okex_types.go @@ -40,6 +40,24 @@ type MultiStreamData struct { Data json.RawMessage `json:"data"` } +// TokenOrdersResponse is returned after a request for all Token Orders +type TokenOrdersResponse struct { + Result bool `json:"result"` + Orders []TokenOrder `json:"orders"` +} + +// TokenOrder is the individual order details returned from TokenOrderResponse +type TokenOrder struct { + Amount float64 `json:"amount"` + AvgPrice int64 `json:"avg_price"` + DealAmount int64 `json:"deal_amount"` + OrderID int64 `json:"order_id"` + Price int64 `json:"price"` + Status int64 `json:"status"` + Symbol string `json:"symbol"` + Type string `json:"type"` +} + // TickerStreamData contains ticker stream data from okex type TickerStreamData struct { Buy string `json:"buy"` diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index 6c5d84b8..985f6258 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -243,9 +243,36 @@ func (o *OKEX) CancelOrder(order exchange.OrderCancellation) error { return err } -// CancelAllOrders cancels all orders associated with a currency pair -func (o *OKEX) CancelAllOrders() error { - return common.ErrNotYetImplemented +// CancelAllOrders cancels all orders for all enabled currencies +func (o *OKEX) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + var allOpenOrders []TokenOrder + for _, currency := range o.GetEnabledCurrencies() { + formattedCurrency := exchange.FormatExchangeCurrency(o.Name, currency).String() + openOrders, err := o.GetTokenOrders(formattedCurrency, -1) + if err != nil { + return cancelAllOrdersResponse, err + } + + if !openOrders.Result { + return cancelAllOrdersResponse, fmt.Errorf("Something went wrong for currency %s", formattedCurrency) + } + + for _, openOrder := range openOrders.Orders { + allOpenOrders = append(allOpenOrders, openOrder) + } + } + + for _, openOrder := range allOpenOrders { + _, err := o.SpotCancelOrder(openOrder.Symbol, openOrder.OrderID) + if err != nil { + cancelAllOrdersResponse.OrderStatus[strconv.FormatInt(openOrder.OrderID, 10)] = err.Error() + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/poloniex/poloniex.go b/exchanges/poloniex/poloniex.go index e8526480..c22dd45e 100644 --- a/exchanges/poloniex/poloniex.go +++ b/exchanges/poloniex/poloniex.go @@ -425,20 +425,23 @@ func (p *Poloniex) GetDepositsWithdrawals(start, end string) (DepositsWithdrawal } // GetOpenOrders returns current unfilled opened orders -func (p *Poloniex) GetOpenOrders(currency string) (interface{}, error) { +func (p *Poloniex) GetOpenOrders(currency string) (OpenOrdersResponse, error) { values := url.Values{} - if currency != "" { - values.Set("currencyPair", currency) - result := OpenOrdersResponse{} + values.Set("currencyPair", currency) + result := OpenOrdersResponse{} - err := p.SendAuthenticatedHTTPRequest("POST", poloniexOrders, values, &result.Data) - if err != nil { - return result, err - } - - return result, nil + err := p.SendAuthenticatedHTTPRequest("POST", poloniexOrders, values, &result.Data) + if err != nil { + return result, err } + + return result, nil +} + +// GetOpenOrdersForAllCurrencies returns all open orders +func (p *Poloniex) GetOpenOrdersForAllCurrencies() (OpenOrdersResponseAll, error) { + values := url.Values{} values.Set("currencyPair", "all") result := OpenOrdersResponseAll{} diff --git a/exchanges/poloniex/poloniex_test.go b/exchanges/poloniex/poloniex_test.go index 00ccceb7..9606850a 100644 --- a/exchanges/poloniex/poloniex_test.go +++ b/exchanges/poloniex/poloniex_test.go @@ -207,7 +207,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { p.SetDefaults() TestSetup(t) - p.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -233,7 +232,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - p.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -251,3 +249,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + p.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := p.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index e43c5e17..05ebd63c 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -193,8 +193,25 @@ func (p *Poloniex) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (p *Poloniex) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (p *Poloniex) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + openOrders, err := p.GetOpenOrdersForAllCurrencies() + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, openOrderPerCurrency := range openOrders.Data { + for _, openOrder := range openOrderPerCurrency { + _, err = p.CancelExistingOrder(openOrder.OrderNumber) + if err != nil { + cancelAllOrdersResponse.OrderStatus[strconv.FormatInt(openOrder.OrderNumber, 10)] = err.Error() + } + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/wex/wex_test.go b/exchanges/wex/wex_test.go index 0f644974..62bd1034 100644 --- a/exchanges/wex/wex_test.go +++ b/exchanges/wex/wex_test.go @@ -16,7 +16,7 @@ const ( apiKey = "" apiSecret = "" canManipulateRealOrders = false - isWexEncounteringIssues = false + isWexEncounteringIssues = true ) func TestSetDefaults(t *testing.T) { @@ -340,7 +340,6 @@ func TestSubmitOrder(t *testing.T) { } w.SetDefaults() TestSetup(t) - w.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -369,7 +368,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - w.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -387,3 +385,37 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + if isWexEncounteringIssues { + t.Skip() + } + // Arrange + w.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := w.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/wex/wex_wrapper.go b/exchanges/wex/wex_wrapper.go index 3c8ef1dd..ecc1f716 100644 --- a/exchanges/wex/wex_wrapper.go +++ b/exchanges/wex/wex_wrapper.go @@ -183,7 +183,6 @@ func (w *WEX) ModifyOrder(orderID int64, action exchange.ModifyOrder) (int64, er // CancelOrder cancels an order by its corresponding ID number func (w *WEX) CancelOrder(order exchange.OrderCancellation) error { orderIDInt, err := strconv.ParseInt(order.OrderID, 10, 64) - if err != nil { return err } @@ -194,8 +193,36 @@ func (w *WEX) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (w *WEX) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (w *WEX) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + var allActiveOrders map[string]ActiveOrders + + for _, pair := range w.EnabledPairs { + activeOrders, err := w.GetActiveOrders(pair) + if err != nil { + return cancelAllOrdersResponse, err + } + + for k, v := range activeOrders { + allActiveOrders[k] = v + } + } + + for k := range allActiveOrders { + orderIDInt, err := strconv.ParseInt(k, 10, 64) + if err != nil { + return cancelAllOrdersResponse, err + } + + _, err = w.CancelExistingOrder(orderIDInt) + if err != nil { + cancelAllOrdersResponse.OrderStatus[k] = err.Error() + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/yobit/yobit_test.go b/exchanges/yobit/yobit_test.go index 501a0c09..de908b68 100644 --- a/exchanges/yobit/yobit_test.go +++ b/exchanges/yobit/yobit_test.go @@ -326,7 +326,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { y.SetDefaults() TestSetup(t) - y.Verbose = true if !isRealOrderTestEnabled() { t.Skip() @@ -352,7 +351,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - y.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -370,3 +368,34 @@ func TestCancelExchangeOrder(t *testing.T) { t.Errorf("Could not cancel order: %s", err) } } + +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + y.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := y.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index 2ba65341..3f04bb30 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -165,19 +165,45 @@ func (y *Yobit) ModifyOrder(orderID int64, action exchange.ModifyOrder) (int64, // CancelOrder cancels an order by its corresponding ID number func (y *Yobit) CancelOrder(order exchange.OrderCancellation) error { orderIDInt, err := strconv.ParseInt(order.OrderID, 10, 64) - if err != nil { return err } _, err = y.CancelExistingOrder(orderIDInt) - return err } // CancelAllOrders cancels all orders associated with a currency pair -func (y *Yobit) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (y *Yobit) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + var allActiveOrders []map[string]ActiveOrders + + for _, pair := range y.EnabledPairs { + activeOrdersForPair, err := y.GetActiveOrders(pair) + if err != nil { + return cancelAllOrdersResponse, err + } + + allActiveOrders = append(allActiveOrders, activeOrdersForPair) + } + + for _, activeOrders := range allActiveOrders { + for key := range activeOrders { + orderIDInt, err := strconv.ParseInt(key, 10, 64) + if err != nil { + return cancelAllOrdersResponse, err + } + + _, err = y.CancelExistingOrder(orderIDInt) + if err != nil { + cancelAllOrdersResponse.OrderStatus[key] = err.Error() + } + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/exchanges/zb/zb.go b/exchanges/zb/zb.go index 861a0e5d..ecccfba8 100644 --- a/exchanges/zb/zb.go +++ b/exchanges/zb/zb.go @@ -22,14 +22,15 @@ const ( zbMarketURL = "https://trade.zb.com/api" zbAPIVersion = "v1" - zbAccountInfo = "getAccountInfo" - zbMarkets = "markets" - zbKline = "kline" - zbOrder = "order" - zbCancelOrder = "cancelOrder" - zbTicker = "ticker" - zbTickers = "allTicker" - zbDepth = "depth" + zbAccountInfo = "getAccountInfo" + zbMarkets = "markets" + zbKline = "kline" + zbOrder = "order" + zbCancelOrder = "cancelOrder" + zbTicker = "ticker" + zbTickers = "allTicker" + zbDepth = "depth" + zbUnfinishedOrdersIgnoreTradeType = "getUnfinishedOrdersIgnoreTradeType" zbAuthRate = 100 zbUnauthRate = 100 @@ -175,6 +176,24 @@ func (z *ZB) GetAccountInformation() (AccountsResponse, error) { return result, nil } +// GetUnfinishedOrdersIgnoreTradeType returns unfinished orders +func (z *ZB) GetUnfinishedOrdersIgnoreTradeType(currency, pageindex, pagesize string) ([]UnfinishedOpenOrder, error) { + var result []UnfinishedOpenOrder + vals := url.Values{} + vals.Set("accesskey", z.APIKey) + vals.Set("method", zbUnfinishedOrdersIgnoreTradeType) + vals.Set("currency", currency) + vals.Set("pageIndex", pageindex) + vals.Set("pageSize", pagesize) + + err := z.SendAuthenticatedHTTPRequest("GET", zbUnfinishedOrdersIgnoreTradeType, vals, &result) + if err != nil { + return result, err + } + + return result, nil +} + // GetMarkets returns market information including pricing, symbols and // each symbols decimal precision func (z *ZB) GetMarkets() (map[string]MarketResponseItem, error) { diff --git a/exchanges/zb/zb_test.go b/exchanges/zb/zb_test.go index b2e82534..c935ebac 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -244,7 +244,6 @@ func isRealOrderTestEnabled() bool { func TestSubmitOrder(t *testing.T) { z.SetDefaults() TestSetup(t) - z.Verbose = true if !isRealOrderTestEnabled() { t.Skip(fmt.Sprintf("ApiKey: %s. Can place orders: %v", z.APIKey, canManipulateRealOrders)) @@ -269,7 +268,6 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip() } - z.Verbose = true currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) var orderCancellation = exchange.OrderCancellation{ @@ -288,6 +286,37 @@ func TestCancelExchangeOrder(t *testing.T) { } } +func TestCancelAllExchangeOrders(t *testing.T) { + // Arrange + z.SetDefaults() + TestSetup(t) + + if !isRealOrderTestEnabled() { + t.Skip() + } + + currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + + var orderCancellation = exchange.OrderCancellation{ + OrderID: "1", + WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB", + AccountID: "1", + CurrencyPair: currencyPair, + } + + // Act + resp, err := z.CancelAllOrders(orderCancellation) + + // Assert + if err != nil { + t.Errorf("Could not cancel order: %s", err) + } + + if len(resp.OrderStatus) > 0 { + t.Errorf("%v orders failed to cancel", len(resp.OrderStatus)) + } +} + func TestGetAccountInfo(t *testing.T) { if apiKey != "" || apiSecret != "" { _, err := z.GetAccountInfo() diff --git a/exchanges/zb/zb_type.go b/exchanges/zb/zb_type.go index 4f7648e1..954afb57 100644 --- a/exchanges/zb/zb_type.go +++ b/exchanges/zb/zb_type.go @@ -29,6 +29,19 @@ type AccountsBaseResponse struct { AuthMobileEnabled bool `json:"auth_mobile_enabled"` //是否开通手机验证 } +// UnfinishedOpenOrder is the order details for retrieving all open orders +type UnfinishedOpenOrder struct { + Currency string `json:"currency"` + ID int64 `json:"id"` + Price int `json:"price"` + Status int `json:"status"` + TotalAmount float64 `json:"total_amount"` + TradeAmount int `json:"trade_amount"` + TradeDate int `json:"trade_date"` + TradeMoney int `json:"trade_money"` + Type int `json:"type"` +} + // AccountsResponse 用户基本信息 type AccountsResponse struct { Result struct { diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index b4e4a9e6..5023bf08 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -210,8 +210,30 @@ func (z *ZB) CancelOrder(order exchange.OrderCancellation) error { } // CancelAllOrders cancels all orders associated with a currency pair -func (z *ZB) CancelAllOrders() error { - return common.ErrNotYetImplemented +func (z *ZB) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { + cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ + OrderStatus: make(map[string]string), + } + var allOpenOrders []UnfinishedOpenOrder + for _, currency := range z.GetEnabledCurrencies() { + openOrders, err := z.GetUnfinishedOrdersIgnoreTradeType(exchange.FormatExchangeCurrency(z.Name, currency).String(), "1", "10") + if err != nil { + return cancelAllOrdersResponse, err + } + + for _, openOrder := range openOrders { + allOpenOrders = append(allOpenOrders, openOrder) + } + } + + for _, openOrder := range allOpenOrders { + err := z.CancelExistingOrder(openOrder.ID, openOrder.Currency) + if err != nil { + cancelAllOrdersResponse.OrderStatus[strconv.FormatInt(openOrder.ID, 10)] = err.Error() + } + } + + return cancelAllOrdersResponse, nil } // GetOrderInfo returns information on a current open order diff --git a/testdata/configtest.json b/testdata/configtest.json index 45728a1f..bf321527 100644 --- a/testdata/configtest.json +++ b/testdata/configtest.json @@ -1164,7 +1164,7 @@ "proxyAddress": "", "websocketUrl": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API", "availablePairs": "DASH_BTC,CTXC_BTC,ZIL_BTC,YOU_BTC,LBA_BTC,LSK_BTC,CAI_BTC,AE_BTC,SC_BTC,KAN_BTC,WIN_BTC,DCR_BTC,WAVES_BTC,ORS_BTC,MVP_BTC,NXT_BTC,ARDR_BTC,XAS_BTC,CVT_BTC,EGT_BTC,ZCO_BTC,LET_BTC,CIT_BTC,HPB_BTC,ADA_BTC,HYC_BTC,VITE_BTC,HIT_BTC,ABL_BTC,PAX_BTC,TUSD_BTC,USDC_BTC,GUSD_BTC,BCHABC_BTC,BCHSV_BTC,XRP_BTC,LRC_BTC,NULS_BTC,MCO_BTC,ELF_BTC,ZEC_BTC,CMT_BTC,ITC_BTC,SBTC_BTC,EDO_BTC,AVT_BTC,BCX_BTC,NEO_BTC,GAS_BTC,HSR_BTC,QTUM_BTC,IOTA_BTC,XUC_BTC,EOS_BTC,STORJ_BTC,SNT_BTC,OMG_BTC,LTC_BTC,ETH_BTC,ETC_BTC,BCD_BTC,BTG_BTC,ACT_BTC,PAY_BTC,BTM_BTC,DGD_BTC,GNT_BTC,LINK_BTC,SALT_BTC,WTC_BTC,SNGLS_BTC,ZRX_BTC,BNT_BTC,CVC_BTC,MANA_BTC,RCN_BTC,TNB_BTC,KNC_BTC,DAT_BTC,GNX_BTC,ICX_BTC,XEM_BTC,ARK_BTC,YOYO_BTC,SUB_BTC,FUN_BTC,ACE_BTC,TRX_BTC,MDA_BTC,MTL_BTC,DGB_BTC,PPT_BTC,ENG_BTC,SWFTC_BTC,XMR_BTC,XLM_BTC,RDN_BTC,KCASH_BTC,MDT_BTC,NAS_BTC,RNT_BTC,UGC_BTC,DPY_BTC,SSC_BTC,AAC_BTC,LEND_BTC,SHOW_BTC,VIB_BTC,QUN_BTC,OST_BTC,INT_BTC,NGC_BTC,IOST_BTC,POE_BTC,INS_BTC,YEE_BTC,MOF_BTC,TCT_BTC,LEV_BTC,SPF_BTC,STC_BTC,THETA_BTC,HOT_BTC,PST_BTC,SNC_BTC,MKR_BTC,KEY_BTC,LIGHT_BTC,TRUE_BTC,OF_BTC,SOC_BTC,DENT_BTC,ZEN_BTC,HMC_BTC,ZIP_BTC,NANO_BTC,CIC_BTC,GTO_BTC,CHAT_BTC,INSUR_BTC,CBT_BTC,R_BTC,BEC_BTC,MITH_BTC,ABT_BTC,BKX_BTC,RFR_BTC,TRIO_BTC,REN_BTC,DADI_BTC,ENJ_BTC,ONT_BTC,OKB_BTC,CTXC_ETH,ZIL_ETH,YOU_ETH,LBA_ETH,LSK_ETH,CAI_ETH,SC_ETH,AE_ETH,KAN_ETH,WIN_ETH,DCR_ETH,WAVES_ETH,ORS_ETH,MVP_ETH,CVT_ETH,EGT_ETH,ZCO_ETH,LET_ETH,CIT_ETH,HPB_ETH,SDA_ETH,ADA_ETH,HYC_ETH,VITE_ETH,HIT_ETH,ABL_ETH,ELF_ETH,LTC_ETH,CMT_ETH,ITC_ETH,PRA_ETH,EDO_ETH,LRC_ETH,NULS_ETH,MCO_ETH,STORJ_ETH,SNT_ETH,PAY_ETH,DGD_ETH,GNT_ETH,ACT_ETH,BTM_ETH,EOS_ETH,OMG_ETH,DASH_ETH,XRP_ETH,ZEC_ETH,NEO_ETH,GAS_ETH,HSR_ETH,QTUM_ETH,IOTA_ETH,XUC_ETH,ETC_ETH,LINK_ETH,SALT_ETH,WTC_ETH,SNGLS_ETH,SNM_ETH,ZRX_ETH,BNT_ETH,CVC_ETH,MANA_ETH,VEE_ETH,TNB_ETH,KNC_ETH,DAT_ETH,GNX_ETH,ICX_ETH,XEM_ETH,ARK_ETH,YOYO_ETH,SUB_ETH,FUN_ETH,TRX_ETH,EVX_ETH,MDA_ETH,MTH_ETH,MTL_ETH,DGB_ETH,PPT_ETH,REQ_ETH,ENG_ETH,SWFTC_ETH,XMR_ETH,XLM_ETH,RDN_ETH,KCASH_ETH,MDT_ETH,NAS_ETH,RNT_ETH,UKG_ETH,UGC_ETH,DPY_ETH,SSC_ETH,AAC_ETH,FAIR_ETH,LEND_ETH,RCT_ETH,SHOW_ETH,VIB_ETH,TOPC_ETH,QUN_ETH,BRD_ETH,OST_ETH,AIDOC_ETH,INT_ETH,LA_ETH,IOST_ETH,POE_ETH,INS_ETH,YEE_ETH,MOF_ETH,TCT_ETH,ATL_ETH,LEV_ETH,REF_ETH,THETA_ETH,CAN_ETH,HOT_ETH,PST_ETH,SNC_ETH,MKR_ETH,KEY_ETH,LIGHT_ETH,TRUE_ETH,OF_ETH,SOC_ETH,DENT_ETH,ZEN_ETH,HMC_ETH,ZIP_ETH,NANO_ETH,CIC_ETH,GTO_ETH,INSUR_ETH,R_ETH,UCT_ETH,BEC_ETH,MITH_ETH,ABT_ETH,BKX_ETH,AUTO_ETH,GSC_ETH,RFR_ETH,TRIO_ETH,TRA_ETH,REN_ETH,DADI_ETH,ENJ_ETH,ONT_ETH,OKB_ETH,CTXC_USDT,ZIL_USDT,YOU_OKB,YOU_USDT,LBA_OKB,LBA_USDT,OK06ETT_USDT,CAI_OKB,LSK_USDT,CAI_USDT,AE_OKB,SC_OKB,KAN_OKB,WIN_OKB,SC_USDT,AE_USDT,KAN_USDT,WIN_USDT,ORS_OKB,MVP_OKB,DCR_OKB,DCR_USDT,WAVES_OKB,WAVES_USDT,ORS_USDT,MVP_USDT,NAS_OKB,XAS_OKB,CVT_OKB,ZCO_OKB,EGT_OKB,XAS_USDT,CVT_USDT,EGT_USDT,LET_OKB,LET_USDT,CIT_OKB,HPB_OKB,HPB_USDT,SDA_OKB,ADA_OKB,ADA_USDT,HYC_USDT,VITE_OKB,TRX_OKB,PAX_USDT,TUSD_USDT,USDC_USDT,GUSD_USDT,BCHABC_USDT,BCHSV_USDT,ELF_USDT,DASH_USDT,LRC_USDT,NULS_USDT,MCO_USDT,BTG_USDT,DASH_OKB,XRP_USDT,ZEC_USDT,NEO_USDT,GAS_USDT,HSR_USDT,QTUM_USDT,IOTA_USDT,BTC_USDT,BCD_USDT,XUC_USDT,CMT_USDT,ITC_USDT,PRA_USDT,SAN_USDT,EDO_USDT,ETH_USDT,LTC_USDT,ETC_USDT,EOS_USDT,OMG_USDT,ACT_USDT,BTM_USDT,STORJ_USDT,PAY_USDT,DGD_USDT,GNT_USDT,SNT_USDT,LINK_USDT,SALT_USDT,1ST_USDT,WTC_USDT,SNGLS_USDT,ZRX_USDT,BNT_USDT,CVC_USDT,MANA_USDT,TNB_USDT,AMM_USDT,KNC_USDT,DAT_USDT,GNX_USDT,ICX_USDT,XEM_USDT,ARK_USDT,YOYO_USDT,QVT_USDT,AST_USDT,DNT_USDT,FUN_USDT,ACE_USDT,TRX_USDT,EVX_USDT,MDA_USDT,DGB_USDT,PPT_USDT,OAX_USDT,REQ_USDT,ENG_USDT,ICN_USDT,RCN_USDT,SWFTC_USDT,XMR_USDT,XLM_USDT,RDN_USDT,KCASH_USDT,MDT_USDT,NAS_USDT,RNT_USDT,WRC_USDT,UGC_USDT,DPY_USDT,SSC_USDT,AAC_USDT,FAIR_USDT,UBTC_USDT,CAG_USDT,DNA_USDT,LEND_USDT,SHOW_USDT,VIB_USDT,MOT_USDT,UTK_USDT,MAG_USDT,TOPC_USDT,QUN_USDT,OST_USDT,AIDOC_USDT,INT_USDT,IPC_USDT,IOST_USDT,POE_USDT,INS_USDT,YEE_USDT,MOF_USDT,TCT_USDT,LEV_USDT,SPF_USDT,STC_USDT,THETA_USDT,CAN_USDT,HOT_USDT,PST_USDT,SNC_USDT,MKR_USDT,KEY_USDT,LIGHT_USDT,TRUE_USDT,OF_USDT,SOC_USDT,DENT_USDT,ZEN_USDT,HMC_USDT,ZIP_USDT,NANO_USDT,CIC_USDT,GTO_USDT,CHAT_USDT,INSUR_USDT,R_USDT,UCT_USDT,BEC_USDT,MITH_USDT,ABT_USDT,BKX_USDT,GSC_USDT,RFR_USDT,TRIO_USDT,TRA_USDT,REN_USDT,DADI_USDT,ENJ_USDT,ONT_USDT,OKB_USDT,NEO_OKB,LTC_OKB,ETC_OKB,XRP_OKB,ZEC_OKB,QTUM_OKB,IOTA_OKB,EOS_OKB", - "enabledPairs": "tct_eth", + "enabledPairs": "ltc_btc", "baseCurrencies": "USD", "assetTypes": "SPOT", "supportsAutoPairUpdates": true, diff --git a/tools/exchange_template/wrapper_file.tmpl b/tools/exchange_template/wrapper_file.tmpl index 2f8d0e47..0ea78ab5 100644 --- a/tools/exchange_template/wrapper_file.tmpl +++ b/tools/exchange_template/wrapper_file.tmpl @@ -137,7 +137,7 @@ func ({{.Variable}} *{{.CapitalName}}) CancelOrder(order exchange.OrderCancellat } // CancelAllOrders cancels all orders associated with a currency pair -func ({{.Variable}} *{{.CapitalName}}) CancelAllOrders() error { +func ({{.Variable}} *{{.CapitalName}}) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) { return common.ErrNotYetImplemented }