diff --git a/communications/slack/slack_test.go b/communications/slack/slack_test.go index aa2381b7..6a92da4f 100644 --- a/communications/slack/slack_test.go +++ b/communications/slack/slack_test.go @@ -71,7 +71,6 @@ func TestBuildURL(t *testing.T) { } func TestGetChannelsString(t *testing.T) { - s.Details.Channels = append(s.Details.Channels, struct { Created int `json:"created"` Creator string `json:"creator"` @@ -315,7 +314,6 @@ func TestHandleHelloResponse(t *testing.T) { } func TestHandleReconnectResponse(t *testing.T) { - err := s.handleReconnectResponse([]byte(`{"malformedjson}`)) if err == nil { t.Error("test failed - slack handleReconnectResponse(), unmarshalled malformed json") diff --git a/currency/pair/pair.go b/currency/pair/pair.go index dd989c95..36f55363 100644 --- a/currency/pair/pair.go +++ b/currency/pair/pair.go @@ -108,6 +108,15 @@ func NewCurrencyPair(firstCurrency, secondCurrency string) CurrencyPair { } } +// NewCurrencyPairWithDelimiter returns a CurrencyPair with a delimiter +func NewCurrencyPairWithDelimiter(firstCurrency, secondCurrency, delimiter string) CurrencyPair { + return CurrencyPair{ + FirstCurrency: CurrencyItem(firstCurrency), + SecondCurrency: CurrencyItem(secondCurrency), + Delimiter: delimiter, + } +} + // NewCurrencyPairFromIndex returns a CurrencyPair via a currency string and // specific index func NewCurrencyPairFromIndex(currency, index string) CurrencyPair { diff --git a/currency/pair/pair_test.go b/currency/pair/pair_test.go index b74b65e5..3d738e38 100644 --- a/currency/pair/pair_test.go +++ b/currency/pair/pair_test.go @@ -180,6 +180,29 @@ func TestNewCurrencyPair(t *testing.T) { } } +func TestNewCurrencyPairWithDelimiter(t *testing.T) { + t.Parallel() + pair := NewCurrencyPairWithDelimiter("BTC", "USD", "-test-") + actual := pair.Pair() + expected := CurrencyItem("BTC-test-USD") + if actual != expected { + t.Errorf( + "Test failed. Pair(): %s was not equal to expected value: %s", + actual, expected, + ) + } + + pair = NewCurrencyPairWithDelimiter("BTC", "USD", "") + actual = pair.Pair() + expected = CurrencyItem("BTCUSD") + if actual != expected { + t.Errorf( + "Test failed. Pair(): %s was not equal to expected value: %s", + actual, expected, + ) + } +} + func TestNewCurrencyPairDelimiter(t *testing.T) { t.Parallel() pair := NewCurrencyPairDelimiter("BTC-USD", "-") diff --git a/exchanges/alphapoint/alphapoint.go b/exchanges/alphapoint/alphapoint.go index 8a949d71..e8c909e8 100644 --- a/exchanges/alphapoint/alphapoint.go +++ b/exchanges/alphapoint/alphapoint.go @@ -361,7 +361,7 @@ func (a *Alphapoint) WithdrawCoins(symbol, product, address string, amount float } func (a *Alphapoint) convertOrderTypeToOrderTypeNumber(orderType string) (orderTypeNumber int64) { - if orderType == exchange.Market.ToString() { + if orderType == exchange.MarketOrderType.ToString() { orderTypeNumber = 1 } diff --git a/exchanges/alphapoint/alphapoint_test.go b/exchanges/alphapoint/alphapoint_test.go index 9a40ef08..c3abce41 100644 --- a/exchanges/alphapoint/alphapoint_test.go +++ b/exchanges/alphapoint/alphapoint_test.go @@ -413,7 +413,7 @@ func TestCreateOrder(t *testing.T) { return } - _, err := a.CreateOrder("", "", exchange.Market.ToString(), 0.01, 0) + _, err := a.CreateOrder("", "", exchange.MarketOrderType.ToString(), 0.01, 0) if err == nil { t.Error("Test Failed - GetUserInfo() error") } @@ -480,18 +480,49 @@ func TestGetOrderFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange a := &Alphapoint{} a.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.WithdrawCryptoWith2FAText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := a.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + a := &Alphapoint{} + a.SetDefaults() + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := a.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet(a) && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet(a) && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + a := &Alphapoint{} + a.SetDefaults() + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := a.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet(a) && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet(a) && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- @@ -515,9 +546,9 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.USD, } - response, err := a.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 1, "clientId") + response, err := a.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId") if !areTestAPIKeysSet(a) && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet(a) && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -529,7 +560,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange a := &Alphapoint{} a.SetDefaults() @@ -545,12 +575,10 @@ func TestCancelExchangeOrder(t *testing.T) { AccountID: "1", CurrencyPair: currencyPair, } - // Act - err := a.CancelOrder(orderCancellation) - // Assert + err := a.CancelOrder(orderCancellation) if !areTestAPIKeysSet(a) && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet(a) && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -558,7 +586,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange a := &Alphapoint{} a.SetDefaults() @@ -574,12 +601,11 @@ func TestCancelAllExchangeOrders(t *testing.T) { AccountID: "1", CurrencyPair: currencyPair, } - // Act + resp, err := a.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet(a) && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet(a) && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/alphapoint/alphapoint_types.go b/exchanges/alphapoint/alphapoint_types.go index f00cedf2..37a2db17 100644 --- a/exchanges/alphapoint/alphapoint_types.go +++ b/exchanges/alphapoint/alphapoint_types.go @@ -1,5 +1,7 @@ package alphapoint +import exchange "github.com/thrasher-/gocryptotrader/exchanges" + // Response contains general responses from the exchange type Response struct { IsAccepted bool `json:"isAccepted"` @@ -144,19 +146,21 @@ type AccountInfo struct { // Order is a generalised order type type Order struct { - Serverorderid int `json:"ServerOrderId"` - AccountID int `json:"AccountId"` - Price int `json:"Price"` - QtyTotal int `json:"QtyTotal"` - QtyRemaining int `json:"QtyRemaining"` - ReceiveTime int64 `json:"ReceiveTime"` - Side int `json:"Side"` + ServerOrderID int `json:"ServerOrderId"` + AccountID int `json:"AccountId"` + Price float64 `json:"Price"` + QtyTotal float64 `json:"QtyTotal"` + QtyRemaining float64 `json:"QtyRemaining"` + ReceiveTime int64 `json:"ReceiveTime"` + Side int64 `json:"Side"` + State int `json:"orderState"` + OrderType int `json:"orderType"` } // OpenOrders holds the full range of orders by instrument type OpenOrders struct { Instrument string `json:"ins"` - Openorders []Order `json:"openOrders"` + OpenOrders []Order `json:"openOrders"` } // OrderInfo holds all open orders across the entire range of all instruments @@ -192,3 +196,17 @@ type WebsocketTicker struct { BuyOrderCount int `json:"buyOrderCount"` SellOrderCount int `json:"sellOrderCount"` } + +// orderSideMap holds order type info based on Alphapoint data +var orderSideMap = map[int64]exchange.OrderSide{ + 1: exchange.BuyOrderSide, + 2: exchange.SellOrderSide, +} + +// orderTypeMap holds order type info based on Alphapoint data +var orderTypeMap = map[int]exchange.OrderType{ + 1: exchange.MarketOrderType, + 2: exchange.LimitOrderType, + 3: exchange.StopOrderType, + 6: exchange.TrailingStopOrderType, +} diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index 7bb38bab..0d9c9492 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "strconv" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -161,9 +162,9 @@ func (a *Alphapoint) GetOrderInfo(orderID int64) (float64, error) { } for x := range orders { - for y := range orders[x].Openorders { - if int64(orders[x].Openorders[y].Serverorderid) == orderID { - return float64(orders[x].Openorders[y].QtyRemaining), nil + for y := range orders[x].OpenOrders { + if int64(orders[x].OpenOrders[y].ServerOrderID) == orderID { + return float64(orders[x].OpenOrders[y].QtyRemaining), nil } } } @@ -216,3 +217,88 @@ func (a *Alphapoint) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, erro func (a *Alphapoint) GetWithdrawCapabilities() uint32 { return a.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +// This function is not concurrency safe due to orderSide/orderType maps +func (a *Alphapoint) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := a.GetOrders() + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for x := range resp { + for _, order := range resp[x].OpenOrders { + if order.State != 1 { + continue + } + + orderDetail := exchange.OrderDetail{ + Amount: float64(order.QtyTotal), + Exchange: a.Name, + AccountID: fmt.Sprintf("%v", order.AccountID), + ID: fmt.Sprintf("%v", order.ServerOrderID), + Price: float64(order.Price), + RemainingAmount: float64(order.QtyRemaining), + } + + orderDetail.OrderSide = orderSideMap[order.Side] + orderDetail.OrderDate = time.Unix(order.ReceiveTime, 0) + orderDetail.OrderType = orderTypeMap[order.OrderType] + if orderDetail.OrderType == "" { + orderDetail.OrderType = exchange.UnknownOrderType + } + + orders = append(orders, orderDetail) + } + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +// This function is not concurrency safe due to orderSide/orderType maps +func (a *Alphapoint) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := a.GetOrders() + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for x := range resp { + for _, order := range resp[x].OpenOrders { + if order.State == 1 { + continue + } + + orderDetail := exchange.OrderDetail{ + Amount: float64(order.QtyTotal), + AccountID: fmt.Sprintf("%v", order.AccountID), + Exchange: a.Name, + ID: fmt.Sprintf("%v", order.ServerOrderID), + Price: float64(order.Price), + RemainingAmount: float64(order.QtyRemaining), + } + + orderDetail.OrderSide = orderSideMap[order.Side] + orderDetail.OrderDate = time.Unix(order.ReceiveTime, 0) + orderDetail.OrderType = orderTypeMap[order.OrderType] + if orderDetail.OrderType == "" { + orderDetail.OrderType = exchange.UnknownOrderType + } + + orders = append(orders, orderDetail) + } + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + + return orders, nil +} diff --git a/exchanges/anx/anx_test.go b/exchanges/anx/anx_test.go index 886c28b3..637c1205 100644 --- a/exchanges/anx/anx_test.go +++ b/exchanges/anx/anx_test.go @@ -55,11 +55,9 @@ func TestSetup(t *testing.T) { t.Error("Test Failed - ANX Setup() init error") } a.Setup(anxConfig) - if testAPIKey != "" && testAPISecret != "" { - a.APIKey = testAPIKey - a.APISecret = testAPISecret - a.AuthenticatedAPISupport = true - } + a.APIKey = testAPIKey + a.APISecret = testAPISecret + a.AuthenticatedAPISupport = true if a.Enabled != true { t.Error("Test Failed - ANX Setup() incorrect values set") @@ -216,18 +214,49 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange a.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawCryptoWith2FAText + " & " + exchange.WithdrawCryptoWithEmailText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - // Act + withdrawPermissions := a.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + a.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := a.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + a.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := a.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- @@ -251,7 +280,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.USD, } - response, err := a.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 1, "clientId") + response, err := a.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -260,7 +289,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange a.SetDefaults() TestSetup(t) @@ -276,19 +304,16 @@ func TestCancelExchangeOrder(t *testing.T) { AccountID: "1", CurrencyPair: currencyPair, } - // Act - err := a.CancelOrder(orderCancellation) - // Assert + err := a.CancelOrder(orderCancellation) if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel order: %s", err) } else if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange a.SetDefaults() TestSetup(t) @@ -304,14 +329,13 @@ func TestCancelAllExchangeOrders(t *testing.T) { AccountID: "1", CurrencyPair: currencyPair, } - // Act + resp, err := a.CancelAllOrders(orderCancellation) - // Assert if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel order: %s", err) } else if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if len(resp.OrderStatus) > 0 { diff --git a/exchanges/anx/anx_types.go b/exchanges/anx/anx_types.go index 49b1d498..5deca029 100644 --- a/exchanges/anx/anx_types.go +++ b/exchanges/anx/anx_types.go @@ -119,20 +119,20 @@ type Order struct { // OrderResponse holds order response data type OrderResponse struct { - BuyTradedCurrency bool `json:"buyTradedCurrency"` - ExecutedAverageRate string `json:"executedAverageRate"` - LimitPriceInSettlementCurrency string `json:"limitPriceInSettlementCurrency"` - OrderID string `json:"orderId"` - OrderStatus string `json:"orderStatus"` - OrderType string `json:"orderType"` - ReplaceExistingOrderUUID string `json:"replaceExistingOrderId"` - SettlementCurrency string `json:"settlementCurrency"` - SettlementCurrencyAmount string `json:"settlementCurrencyAmount"` - SettlementCurrencyOutstanding string `json:"settlementCurrencyOutstanding"` - Timestamp int64 `json:"timestamp"` - TradedCurrency string `json:"tradedCurrency"` - TradedCurrencyAmount string `json:"tradedCurrencyAmount"` - TradedCurrencyOutstanding string `json:"tradedCurrencyOutstanding"` + BuyTradedCurrency bool `json:"buyTradedCurrency"` + ExecutedAverageRate string `json:"executedAverageRate"` + LimitPriceInSettlementCurrency string `json:"limitPriceInSettlementCurrency"` + OrderID string `json:"orderId"` + OrderStatus string `json:"orderStatus"` + OrderType string `json:"orderType"` + ReplaceExistingOrderUUID string `json:"replaceExistingOrderId"` + SettlementCurrency string `json:"settlementCurrency"` + SettlementCurrencyAmount float64 `json:"settlementCurrencyAmount,string"` + SettlementCurrencyOutstanding string `json:"settlementCurrencyOutstanding"` + Timestamp int64 `json:"timestamp"` + TradedCurrency string `json:"tradedCurrency"` + TradedCurrencyAmount float64 `json:"tradedCurrencyAmount,string"` + TradedCurrencyOutstanding string `json:"tradedCurrencyOutstanding"` } // OrderCancelResponse returned when cancelling multiple orders diff --git a/exchanges/anx/anx_wrapper.go b/exchanges/anx/anx_wrapper.go index 089eabb9..5a93f92b 100644 --- a/exchanges/anx/anx_wrapper.go +++ b/exchanges/anx/anx_wrapper.go @@ -3,7 +3,9 @@ package anx import ( "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -228,11 +230,11 @@ func (a *ANX) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderTyp var isBuying bool var limitPriceInSettlementCurrency float64 - if side == exchange.Buy { + if side == exchange.BuyOrderSide { isBuying = true } - if orderType == exchange.Limit { + if orderType == exchange.LimitOrderType { limitPriceInSettlementCurrency = price } @@ -345,3 +347,70 @@ func (a *ANX) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (a *ANX) GetWithdrawCapabilities() uint32 { return a.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (a *ANX) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := a.GetOrderList(true) + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, order := range resp { + orderDate := time.Unix(order.Timestamp, 0) + orderType := exchange.OrderType(strings.ToUpper(order.OrderType)) + + orderDetail := exchange.OrderDetail{ + Amount: order.TradedCurrencyAmount, + CurrencyPair: pair.NewCurrencyPairWithDelimiter(order.TradedCurrency, order.SettlementCurrency, a.ConfigCurrencyPairFormat.Delimiter), + OrderDate: orderDate, + Exchange: a.Name, + ID: order.OrderID, + OrderType: orderType, + Price: order.SettlementCurrencyAmount, + Status: order.OrderStatus, + } + + orders = append(orders, orderDetail) + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (a *ANX) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := a.GetOrderList(false) + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, order := range resp { + orderDate := time.Unix(order.Timestamp, 0) + orderType := exchange.OrderType(strings.ToUpper(order.OrderType)) + + orderDetail := exchange.OrderDetail{ + Amount: order.TradedCurrencyAmount, + OrderDate: orderDate, + Exchange: a.Name, + ID: order.OrderID, + OrderType: orderType, + Price: order.SettlementCurrencyAmount, + Status: order.OrderStatus, + CurrencyPair: pair.NewCurrencyPairWithDelimiter(order.TradedCurrency, order.SettlementCurrency, a.ConfigCurrencyPairFormat.Delimiter), + } + + orders = append(orders, orderDetail) + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} diff --git a/exchanges/binance/binance.go b/exchanges/binance/binance.go index a3d4c582..278bb4b5 100644 --- a/exchanges/binance/binance.go +++ b/exchanges/binance/binance.go @@ -498,8 +498,9 @@ func (b *Binance) CancelExistingOrder(symbol string, orderID int64, origClientOr return resp, b.SendAuthHTTPRequest("DELETE", path, params, &resp) } -// OpenOrders Current open orders -// Get all open orders on a symbol. Careful when accessing this with no symbol. +// OpenOrders Current open orders. Get all open orders on a symbol. +// Careful when accessing this with no symbol: The number of requests counted against the rate limiter +// is equal to the number of symbols currently trading on the exchange. func (b *Binance) OpenOrders(symbol string) ([]QueryOrderData, error) { var resp []QueryOrderData path := fmt.Sprintf("%s%s", b.APIUrl, openOrders) diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index b3d23866..c563c317 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -320,17 +320,61 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange b.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := b.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + _, err := b.GetActiveOrders(getOrdersRequest) + if err == nil { + t.Error("Expected: 'At least one currency is required to fetch order history'. received nil") + } + + getOrdersRequest.Currencies = []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)} + + _, err = b.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetOrderHistory(getOrdersRequest) + if err == nil { + t.Error("Expected: 'At least one currency is required to fetch order history'. received nil") + } + + getOrdersRequest.Currencies = []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)} + + _, err = b.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ----------------------------------------------------------------------------------------------------------------------------- @@ -351,7 +395,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.LTC, SecondCurrency: symbol.BTC, } - response, err := b.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 1, "clientId") + response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -360,7 +404,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -373,12 +416,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := b.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -386,7 +426,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -399,12 +438,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := b.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel order: %v", err) @@ -452,7 +489,7 @@ func TestWithdraw(t *testing.T) { _, err := b.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index fdf28e02..fc9d8075 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -183,16 +185,16 @@ func (b *Binance) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orde var submitOrderResponse exchange.SubmitOrderResponse var sideType RequestParamsSideType - if side == exchange.Buy { + if side == exchange.BuyOrderSide { sideType = BinanceRequestParamsSideBuy } else { sideType = BinanceRequestParamsSideSell } var requestParamsOrderType RequestParamsOrderType - if orderType == exchange.Market { + if orderType == exchange.MarketOrderType { requestParamsOrderType = BinanceRequestParamsOrderMarket - } else if orderType == exchange.Limit { + } else if orderType == exchange.LimitOrderType { requestParamsOrderType = BinanceRequestParamsOrderLimit } else { submitOrderResponse.IsOrderPlaced = false @@ -306,3 +308,86 @@ func (b *Binance) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) func (b *Binance) GetWithdrawCapabilities() uint32 { return b.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (b *Binance) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("At least one currency is required to fetch order history") + } + + var orders []exchange.OrderDetail + for _, currency := range getOrdersRequest.Currencies { + resp, err := b.OpenOrders(exchange.FormatExchangeCurrency(b.Name, currency).String()) + if err != nil { + return nil, err + } + + for _, order := range resp { + orderSide := exchange.OrderSide(strings.ToUpper(order.Side)) + orderType := exchange.OrderType(strings.ToUpper(order.Type)) + orderDate := time.Unix(int64(order.Time), 0) + + orders = append(orders, exchange.OrderDetail{ + Amount: order.OrigQty, + OrderDate: orderDate, + Exchange: b.Name, + ID: fmt.Sprintf("%v", order.OrderID), + OrderSide: orderSide, + OrderType: orderType, + Price: order.Price, + Status: order.Status, + CurrencyPair: pair.NewCurrencyPairFromString(order.Symbol), + }) + } + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (b *Binance) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("At least one currency is required to fetch order history") + } + + var orders []exchange.OrderDetail + for _, currency := range getOrdersRequest.Currencies { + resp, err := b.AllOrders(exchange.FormatExchangeCurrency(b.Name, currency).String(), "", "1000") + if err != nil { + return nil, err + } + + for _, order := range resp { + orderSide := exchange.OrderSide(strings.ToUpper(order.Side)) + orderType := exchange.OrderType(strings.ToUpper(order.Type)) + orderDate := time.Unix(int64(order.Time), 0) + // New orders are covered in GetOpenOrders + if order.Status == "NEW" { + continue + } + + orders = append(orders, exchange.OrderDetail{ + Amount: order.OrigQty, + OrderDate: orderDate, + Exchange: b.Name, + ID: fmt.Sprintf("%v", order.OrderID), + OrderSide: orderSide, + OrderType: orderType, + Price: order.Price, + CurrencyPair: pair.NewCurrencyPairFromString(order.Symbol), + Status: order.Status, + }) + } + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + + return orders, nil +} diff --git a/exchanges/bitfinex/bitfinex.go b/exchanges/bitfinex/bitfinex.go index eb48bc98..ba3d4dda 100644 --- a/exchanges/bitfinex/bitfinex.go +++ b/exchanges/bitfinex/bitfinex.go @@ -46,6 +46,7 @@ const ( bitfinexOrderCancelReplace = "order/cancel/replace" bitfinexOrderStatus = "order/status" bitfinexOrders = "orders" + bitfinexInactiveOrders = "orders/hist" bitfinexPositions = "positions" bitfinexClaimPosition = "position/claim" bitfinexHistory = "history" @@ -732,9 +733,19 @@ func (b *Bitfinex) GetOrderStatus(OrderID int64) (Order, error) { b.SendAuthenticatedHTTPRequest("POST", bitfinexOrderStatus, request, &orderStatus) } -// GetActiveOrders returns all active orders and statuses -func (b *Bitfinex) GetActiveOrders() ([]Order, error) { - response := []Order{} +// GetInactiveOrders returns order status information +func (b *Bitfinex) GetInactiveOrders() ([]Order, error) { + var response []Order + request := make(map[string]interface{}) + request["limit"] = "100" + + return response, + b.SendAuthenticatedHTTPRequest("POST", bitfinexInactiveOrders, request, &response) +} + +// GetOpenOrders returns all active orders and statuses +func (b *Bitfinex) GetOpenOrders() ([]Order, error) { + var response []Order return response, b.SendAuthenticatedHTTPRequest("POST", bitfinexOrders, nil, &response) diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index 454eb07a..18ea00e6 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -431,15 +431,15 @@ func TestGetOrderStatus(t *testing.T) { } } -func TestGetActiveOrders(t *testing.T) { +func TestGetOpenOrders(t *testing.T) { if b.APIKey == "" || b.APISecret == "" { t.SkipNow() } t.Parallel() - _, err := b.GetActiveOrders() + _, err := b.GetOpenOrders() if err == nil { - t.Error("Test Failed - GetActiveOrders() error") + t.Error("Test Failed - GetOpenOrders() error") } } @@ -697,17 +697,48 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange b.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.AutoWithdrawFiatWithAPIPermissionText - // Act + withdrawPermissions := b.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -730,7 +761,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.LTC, SecondCurrency: symbol.BTC, } - response, err := b.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 1, "clientId") + response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -739,7 +770,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -756,12 +786,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := b.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -769,7 +796,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrdera(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -786,12 +812,10 @@ func TestCancelAllExchangeOrdera(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := b.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -825,7 +849,7 @@ func TestWithdraw(t *testing.T) { _, err := b.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -858,7 +882,7 @@ func TestWithdrawFiat(t *testing.T) { _, err := b.WithdrawFiatFunds(withdrawFiatRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -897,7 +921,7 @@ func TestWithdrawInternationalBank(t *testing.T) { _, err := b.WithdrawFiatFundsToInternationalBank(withdrawFiatRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/bitfinex/bitfinex_types.go b/exchanges/bitfinex/bitfinex_types.go index 4ab0e68e..3eea281c 100644 --- a/exchanges/bitfinex/bitfinex_types.go +++ b/exchanges/bitfinex/bitfinex_types.go @@ -257,7 +257,7 @@ type Order struct { OriginalAmount float64 `json:"original_amount,string"` RemainingAmount float64 `json:"remaining_amount,string"` ExecutedAmount float64 `json:"executed_amount,string"` - OrderID int64 `json:"order_id"` + OrderID int64 `json:"order_id,omitempty"` } // OrderMultiResponse holds order information on the executed orders diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index 127afb46..89d97700 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -5,7 +5,9 @@ import ( "fmt" "net/url" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -166,7 +168,7 @@ func (b *Bitfinex) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, ord var submitOrderResponse exchange.SubmitOrderResponse var isBuying bool - if side == exchange.Buy { + if side == exchange.BuyOrderSide { isBuying = true } @@ -306,3 +308,120 @@ func (b *Bitfinex) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) func (b *Bitfinex) GetWithdrawCapabilities() uint32 { return b.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (b *Bitfinex) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var orders []exchange.OrderDetail + resp, err := b.GetOpenOrders() + if err != nil { + return nil, err + } + + for _, order := range resp { + orderSide := exchange.OrderSide(strings.ToUpper(order.Side)) + timestamp, err := strconv.ParseInt(order.Timestamp, 10, 64) + if err != nil { + log.Warnf("Unable to convert timestamp '%v', leaving blank", order.Timestamp) + } + orderDate := time.Unix(timestamp, 0) + + orderDetail := exchange.OrderDetail{ + Amount: order.OriginalAmount, + OrderDate: orderDate, + Exchange: b.Name, + ID: fmt.Sprintf("%v", order.OrderID), + OrderSide: orderSide, + Price: order.Price, + RemainingAmount: order.RemainingAmount, + CurrencyPair: pair.NewCurrencyPairFromString(order.Symbol), + ExecutedAmount: order.ExecutedAmount, + } + + if order.IsLive { + orderDetail.Status = string(exchange.ActiveOrderStatus) + } else if order.IsCancelled { + orderDetail.Status = string(exchange.CancelledOrderStatus) + } else if order.IsHidden { + orderDetail.Status = string(exchange.HiddenOrderStatus) + } else { + orderDetail.Status = string(exchange.UnknownOrderStatus) + } + + // API docs discrepency. Example contains prefixed "exchange " + // Return type suggests “market” / “limit” / “stop” / “trailing-stop” + orderType := strings.Replace(order.Type, "exchange ", "", 1) + if orderType == "trailing-stop" { + orderDetail.OrderType = exchange.TrailingStopOrderType + } else { + orderDetail.OrderType = exchange.OrderType(strings.ToUpper(orderType)) + } + + orders = append(orders, orderDetail) + } + + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (b *Bitfinex) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var orders []exchange.OrderDetail + resp, err := b.GetInactiveOrders() + if err != nil { + return nil, err + } + + for _, order := range resp { + orderSide := exchange.OrderSide(strings.ToUpper(order.Side)) + timestamp, err := strconv.ParseInt(order.Timestamp, 10, 64) + if err != nil { + log.Warnf("Unable to convert timestamp '%v', leaving blank", order.Timestamp) + } + orderDate := time.Unix(timestamp, 0) + + orderDetail := exchange.OrderDetail{ + Amount: order.OriginalAmount, + OrderDate: orderDate, + Exchange: b.Name, + ID: fmt.Sprintf("%v", order.OrderID), + OrderSide: orderSide, + Price: order.Price, + RemainingAmount: order.RemainingAmount, + ExecutedAmount: order.ExecutedAmount, + CurrencyPair: pair.NewCurrencyPairFromString(order.Symbol), + } + + if order.IsLive { + orderDetail.Status = string(exchange.ActiveOrderStatus) + } else if order.IsCancelled { + orderDetail.Status = string(exchange.CancelledOrderStatus) + } else if order.IsHidden { + orderDetail.Status = string(exchange.HiddenOrderStatus) + } else { + orderDetail.Status = string(exchange.UnknownOrderStatus) + } + + // API docs discrepency. Example contains prefixed "exchange " + // Return type suggests “market” / “limit” / “stop” / “trailing-stop” + orderType := strings.Replace(order.Type, "exchange ", "", 1) + if orderType == "trailing-stop" { + orderDetail.OrderType = exchange.TrailingStopOrderType + } else { + orderDetail.OrderType = exchange.OrderType(strings.ToUpper(orderType)) + } + + orders = append(orders, orderDetail) + } + + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} diff --git a/exchanges/bitflyer/bitflyer_test.go b/exchanges/bitflyer/bitflyer_test.go index de5cd7c4..4d3dd9d4 100644 --- a/exchanges/bitflyer/bitflyer_test.go +++ b/exchanges/bitflyer/bitflyer_test.go @@ -238,17 +238,46 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange b.SetDefaults() expectedResult := exchange.AutoWithdrawFiatText + " & " + exchange.WithdrawCryptoViaWebsiteOnlyText - // Act + withdrawPermissions := b.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetOrderHistory(getOrdersRequest) + if err != common.ErrNotYetImplemented { + t.Errorf("Expected '%v', received '%v'", common.ErrNotYetImplemented, err) + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -272,14 +301,13 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.LTC, SecondCurrency: symbol.BTC, } - _, err := b.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 1, "clientId") + _, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId") if err != common.ErrNotYetImplemented { t.Errorf("Expected 'Not Yet Implemented', received %v", err) } } func TestCancelExchangeOrder(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -295,16 +323,14 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := b.CancelOrder(orderCancellation) - // Assert + if err != common.ErrNotYetImplemented { t.Errorf("Expected 'Not Yet Implemented', received %v", err) } } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -320,9 +346,8 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act _, err := b.CancelAllOrders(orderCancellation) - // Assert + if err != common.ErrNotYetImplemented { t.Errorf("Expected 'Not Yet Implemented', received %v", err) } diff --git a/exchanges/bitflyer/bitflyer_wrapper.go b/exchanges/bitflyer/bitflyer_wrapper.go index bfab32b7..24ab5476 100644 --- a/exchanges/bitflyer/bitflyer_wrapper.go +++ b/exchanges/bitflyer/bitflyer_wrapper.go @@ -215,3 +215,14 @@ func (b *Bitflyer) GetWebsocket() (*exchange.Websocket, error) { func (b *Bitflyer) GetWithdrawCapabilities() uint32 { return b.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (b *Bitflyer) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + return nil, common.ErrNotYetImplemented +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (b *Bitflyer) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + return nil, common.ErrNotYetImplemented +} diff --git a/exchanges/bithumb/bithumb.go b/exchanges/bithumb/bithumb.go index 5d25e138..0f4a72c5 100644 --- a/exchanges/bithumb/bithumb.go +++ b/exchanges/bithumb/bithumb.go @@ -355,11 +355,25 @@ func (b *Bithumb) GetOrders(orderID, transactionType, count, after, currency str response := Orders{} params := url.Values{} - params.Set("order_id", orderID) - params.Set("type", transactionType) - params.Set("count", count) - params.Set("after", after) - params.Set("currency", common.StringToUpper(currency)) + if len(orderID) > 0 { + params.Set("order_id", orderID) + } + + if len(transactionType) > 0 { + params.Set("type", transactionType) + } + + if len(count) > 0 { + params.Set("count", count) + } + + if len(after) > 0 { + params.Set("after", after) + } + + if len(currency) > 0 { + params.Set("currency", common.StringToUpper(currency)) + } return response, b.SendAuthenticatedHTTPRequest(privateOrders, params, &response) @@ -421,7 +435,7 @@ func (b *Bithumb) GetOrderDetails(orderID, transactionType, currency string) (Or params := url.Values{} params.Set("order_id", common.StringToUpper(orderID)) - params.Set("type", common.StringToUpper(transactionType)) + params.Set("type", transactionType) params.Set("currency", common.StringToUpper(currency)) return response, diff --git a/exchanges/bithumb/bithumb_test.go b/exchanges/bithumb/bithumb_test.go index 2c52eeea..70afa0d5 100644 --- a/exchanges/bithumb/bithumb_test.go +++ b/exchanges/bithumb/bithumb_test.go @@ -277,17 +277,49 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange b.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.AutoWithdrawFiatText - // Act + withdrawPermissions := b.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + OrderSide: exchange.SellOrderSide, + } + + _, err := b.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -311,7 +343,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.LTC, } - response, err := b.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 1, "clientId") + response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -320,7 +352,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -337,12 +368,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := b.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel order: %v", err) @@ -350,7 +378,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -367,12 +394,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := b.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel order: %v", err) @@ -403,7 +428,7 @@ func TestModifyOrder(t *testing.T) { _, err := b.ModifyOrder(exchange.ModifyOrder{OrderID: "1337", Price: 100, Amount: 1000, - OrderSide: exchange.Sell, + OrderSide: exchange.SellOrderSide, Currency: curr}) if err == nil { t.Error("Test Failed - ModifyOrder() error") @@ -426,7 +451,7 @@ func TestWithdraw(t *testing.T) { _, err := b.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -460,7 +485,7 @@ func TestWithdrawFiat(t *testing.T) { _, err := b.WithdrawFiatFunds(withdrawFiatRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 7f310ac1..18ee971c 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -6,6 +6,7 @@ import ( "math" "strconv" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -173,11 +174,11 @@ func (b *Bithumb) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orde var submitOrderResponse exchange.SubmitOrderResponse var err error var orderID string - if side == exchange.Buy { + if side == exchange.BuyOrderSide { var result MarketBuy result, err = b.MarketBuyOrder(p.FirstCurrency.String(), amount) orderID = result.OrderID - } else if side == exchange.Sell { + } else if side == exchange.SellOrderSide { var result MarketSell result, err = b.MarketSellOrder(p.FirstCurrency.String(), amount) orderID = result.OrderID @@ -306,3 +307,85 @@ func (b *Bithumb) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) func (b *Bithumb) GetWithdrawCapabilities() uint32 { return b.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (b *Bithumb) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var orders []exchange.OrderDetail + resp, err := b.GetOrders("", "", "1000", "", "") + if err != nil { + return nil, err + } + + for _, order := range resp.Data { + if order.Status != "placed" { + continue + } + + orderDate := time.Unix(order.OrderDate, 0) + orderDetail := exchange.OrderDetail{ + Amount: order.Units, + Exchange: b.Name, + ID: order.OrderID, + OrderDate: orderDate, + Price: order.Price, + RemainingAmount: order.UnitsRemaining, + Status: string(exchange.ActiveOrderStatus), + CurrencyPair: pair.NewCurrencyPairWithDelimiter(order.OrderCurrency, order.PaymentCurrency, b.ConfigCurrencyPairFormat.Delimiter), + } + + if order.Type == "bid" { + orderDetail.OrderSide = exchange.BuyOrderSide + } else if order.Type == "ask" { + orderDetail.OrderSide = exchange.SellOrderSide + } + + orders = append(orders, orderDetail) + } + + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (b *Bithumb) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var orders []exchange.OrderDetail + resp, err := b.GetOrders("", "", "1000", "", "") + if err != nil { + return nil, err + } + + for _, order := range resp.Data { + if order.Status == "placed" { + continue + } + + orderDate := time.Unix(order.OrderDate, 0) + orderDetail := exchange.OrderDetail{ + Amount: order.Units, + Exchange: b.Name, + ID: order.OrderID, + OrderDate: orderDate, + Price: order.Price, + RemainingAmount: order.UnitsRemaining, + CurrencyPair: pair.NewCurrencyPairWithDelimiter(order.OrderCurrency, order.PaymentCurrency, b.ConfigCurrencyPairFormat.Delimiter), + } + + if order.Type == "bid" { + orderDetail.OrderSide = exchange.BuyOrderSide + } else if order.Type == "ask" { + orderDetail.OrderSide = exchange.SellOrderSide + } + + orders = append(orders, orderDetail) + } + + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} diff --git a/exchanges/bitmex/bitmex.go b/exchanges/bitmex/bitmex.go index 70cb7042..aa42216f 100644 --- a/exchanges/bitmex/bitmex.go +++ b/exchanges/bitmex/bitmex.go @@ -400,11 +400,11 @@ func (b *Bitmex) GetCurrentNotifications() ([]Notification, error) { } // GetOrders returns all the orders, open and closed -func (b *Bitmex) GetOrders(params GenericRequestParams) ([]Order, error) { +func (b *Bitmex) GetOrders(params OrdersRequest) ([]Order, error) { var orders []Order return orders, b.SendAuthenticatedHTTPRequest("GET", - bitmexEndpointOrder, + fmt.Sprintf("%v%v", bitmexEndpointOrder, ""), params, &orders) } diff --git a/exchanges/bitmex/bitmex_parameters.go b/exchanges/bitmex/bitmex_parameters.go index ea40bb34..60f736c9 100644 --- a/exchanges/bitmex/bitmex_parameters.go +++ b/exchanges/bitmex/bitmex_parameters.go @@ -1037,3 +1037,31 @@ func (p UserRequestWithdrawalParams) ToURLVals(path string) (string, error) { func (p UserRequestWithdrawalParams) IsNil() bool { return p == (UserRequestWithdrawalParams{}) } + +// OrdersRequest used for GetOrderHistory +type OrdersRequest struct { + Symbol string `json:"symbol,omitempty"` + Filter string `json:"filter,omitempty"` + Columns string `json:"columns,omitempty"` + Count float64 `json:"count,omitempty"` + Start float64 `json:"start,omitempty"` + Reverse bool `json:"reverse,omitempty"` + StartTime string `json:"startTime,omitempty"` + EndTime string `json:"endTime,omitempty"` +} + +// VerifyData verifies parameter data during SendAuthenticatedHTTPRequest +func (p OrdersRequest) VerifyData() error { + return nil +} + +// ToURLVals converts struct values to url.values and encodes it on the supplied +// path +func (p OrdersRequest) ToURLVals(path string) (string, error) { + return "", nil +} + +// IsNil checks to see if any values has been set for the paramater +func (p OrdersRequest) IsNil() bool { + return p == (OrdersRequest{}) +} diff --git a/exchanges/bitmex/bitmex_test.go b/exchanges/bitmex/bitmex_test.go index 0efc06a6..ecfc3189 100644 --- a/exchanges/bitmex/bitmex_test.go +++ b/exchanges/bitmex/bitmex_test.go @@ -32,7 +32,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - Bitmex Setup() init error") } - bitmexConfig.AuthenticatedAPISupport = true bitmexConfig.APIKey = testAPIKey bitmexConfig.APISecret = testAPISecret @@ -209,13 +208,6 @@ func TestGetCurrentNotifications(t *testing.T) { } } -func TestGetOrders(t *testing.T) { - _, err := b.GetOrders(GenericRequestParams{}) - if err == nil { - t.Error("test failed - GetOrders() error", err) - } -} - func TestAmendOrder(t *testing.T) { _, err := b.AmendOrder(OrderAmendParams{}) if err == nil { @@ -454,18 +446,50 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange b.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.WithdrawCryptoWith2FAText + " & " + exchange.WithdrawCryptoWithEmailText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := b.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := b.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -489,7 +513,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.XBT, SecondCurrency: symbol.USD, } - response, err := b.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 1, "clientId") + response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -498,7 +522,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -515,12 +538,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := b.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -528,7 +548,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -545,12 +564,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := b.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -599,7 +616,7 @@ func TestWithdraw(t *testing.T) { _, err := b.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/bitmex/bitmex_types.go b/exchanges/bitmex/bitmex_types.go index d6d4030b..2535e80d 100644 --- a/exchanges/bitmex/bitmex_types.go +++ b/exchanges/bitmex/bitmex_types.go @@ -1,5 +1,7 @@ package bitmex +import exchange "github.com/thrasher-/gocryptotrader/exchanges" + // RequestError allows for a general error capture from requests type RequestError struct { Error struct { @@ -293,14 +295,14 @@ type Order struct { MultiLegReportingType string `json:"multiLegReportingType"` OrdRejReason string `json:"ordRejReason"` OrdStatus string `json:"ordStatus"` - OrdType string `json:"ordType"` + OrdType int64 `json:"ordType,string"` OrderID string `json:"orderID"` OrderQty int64 `json:"orderQty"` PegOffsetValue float64 `json:"pegOffsetValue"` PegPriceType string `json:"pegPriceType"` Price float64 `json:"price"` SettlCurrency string `json:"settlCurrency"` - Side string `json:"side"` + Side int64 `json:"side,string"` SimpleCumQty float64 `json:"simpleCumQty"` SimpleLeavesQty float64 `json:"simpleLeavesQty"` SimpleOrderQty float64 `json:"simpleOrderQty"` @@ -666,3 +668,17 @@ type WalletInfo struct { WithdrawalLock []string `json:"withdrawalLock"` Withdrawn int64 `json:"withdrawn"` } + +// orderTypeMap holds order type info based on Bitmex data +var orderTypeMap = map[int64]exchange.OrderType{ + 1: exchange.MarketOrderType, + 2: exchange.LimitOrderType, + 3: exchange.StopOrderType, + 7: exchange.TrailingStopOrderType, +} + +// orderSideMap holds order type info based on Bitmex data +var orderSideMap = map[int64]exchange.OrderSide{ + 1: exchange.BuyOrderSide, + 2: exchange.SellOrderSide, +} diff --git a/exchanges/bitmex/bitmex_websocket.go b/exchanges/bitmex/bitmex_websocket.go index c3e63032..d1503403 100644 --- a/exchanges/bitmex/bitmex_websocket.go +++ b/exchanges/bitmex/bitmex_websocket.go @@ -322,7 +322,7 @@ func (b *Bitmex) processOrderbook(data []OrderBookL2, action string, currencyPai var bids, asks []orderbook.Item for _, orderbookItem := range data { - if orderbookItem.Side == exchange.Sell.ToString() { + if orderbookItem.Side == exchange.SellOrderSide.ToString() { asks = append(asks, orderbook.Item{ Price: orderbookItem.Price, Amount: float64(orderbookItem.Size), diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index 6888b980..3fb46520 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -107,12 +107,12 @@ func (b *Bitmex) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbo } for _, ob := range orderbookNew { - if ob.Side == exchange.Sell.ToString() { + if ob.Side == exchange.SellOrderSide.ToString() { orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: float64(ob.Size), Price: ob.Price}) continue } - if ob.Side == exchange.Buy.ToString() { + if ob.Side == exchange.BuyOrderSide.ToString() { orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: float64(ob.Size), Price: ob.Price}) continue @@ -181,7 +181,7 @@ func (b *Bitmex) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, order Side: side.ToString(), } - if orderType == exchange.Limit { + if orderType == exchange.LimitOrderType { orderNewParams.Price = price } @@ -304,3 +304,84 @@ func (b *Bitmex) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (b *Bitmex) GetWithdrawCapabilities() uint32 { return b.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +// This function is not concurrency safe due to orderSide/orderType maps +func (b *Bitmex) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var orders []exchange.OrderDetail + params := OrdersRequest{} + params.Filter = "{\"open\":true}" + + resp, err := b.GetOrders(params) + if err != nil { + return nil, err + } + + for _, order := range resp { + orderSide := orderSideMap[order.Side] + orderType := orderTypeMap[order.OrdType] + if orderType == "" { + orderType = exchange.UnknownOrderType + } + + orderDetail := exchange.OrderDetail{ + Price: order.Price, + Amount: float64(order.OrderQty), + Exchange: b.Name, + ID: order.OrderID, + OrderSide: orderSide, + OrderType: orderType, + Status: order.OrdStatus, + CurrencyPair: pair.NewCurrencyPairWithDelimiter(order.Symbol, order.SettlCurrency, b.ConfigCurrencyPairFormat.Delimiter), + } + + orders = append(orders, orderDetail) + } + + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +// This function is not concurrency safe due to orderSide/orderType maps +func (b *Bitmex) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var orders []exchange.OrderDetail + params := OrdersRequest{} + resp, err := b.GetOrders(params) + if err != nil { + return nil, err + } + + for _, order := range resp { + orderSide := orderSideMap[order.Side] + orderType := orderTypeMap[order.OrdType] + if orderType == "" { + orderType = exchange.UnknownOrderType + } + + orderDetail := exchange.OrderDetail{ + Price: order.Price, + Amount: float64(order.OrderQty), + Exchange: b.Name, + ID: order.OrderID, + OrderSide: orderSide, + OrderType: orderType, + Status: order.OrdStatus, + CurrencyPair: pair.NewCurrencyPairWithDelimiter(order.Symbol, order.SettlCurrency, b.ConfigCurrencyPairFormat.Delimiter), + } + + orders = append(orders, orderDetail) + } + + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} diff --git a/exchanges/bitstamp/bitstamp.go b/exchanges/bitstamp/bitstamp.go index 33822160..9d64ec3b 100644 --- a/exchanges/bitstamp/bitstamp.go +++ b/exchanges/bitstamp/bitstamp.go @@ -339,7 +339,7 @@ func (b *Bitstamp) GetBalance() (Balances, error) { // GetUserTransactions returns an array of transactions func (b *Bitstamp) GetUserTransactions(currencyPair string) ([]UserTransactions, error) { type Response struct { - Date string `json:"datetime"` + Date int64 `json:"datetime"` TransID int64 `json:"id"` Type int `json:"type,string"` USD interface{} `json:"usd"` diff --git a/exchanges/bitstamp/bitstamp_test.go b/exchanges/bitstamp/bitstamp_test.go index 3cdabfbb..0528869f 100644 --- a/exchanges/bitstamp/bitstamp_test.go +++ b/exchanges/bitstamp/bitstamp_test.go @@ -339,7 +339,6 @@ func TestGetUnconfirmedBitcoinDeposits(t *testing.T) { } func TestTransferAccountBalance(t *testing.T) { - t.Parallel() if b.APIKey == "" || b.APISecret == "" || b.APIKey == "Key" || b.APISecret == "Secret" { @@ -356,17 +355,48 @@ func TestTransferAccountBalance(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange b.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.AutoWithdrawFiatText - // Act + withdrawPermissions := b.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -389,7 +419,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.USD, } - response, err := b.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 1, "clientId") + response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -398,7 +428,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -415,12 +444,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := b.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -428,7 +454,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -445,12 +470,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := b.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -484,7 +507,7 @@ func TestWithdraw(t *testing.T) { _, err := b.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -519,7 +542,7 @@ func TestWithdrawFiat(t *testing.T) { _, err := b.WithdrawFiatFunds(withdrawFiatRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -560,7 +583,7 @@ func TestWithdrawInternationalBank(t *testing.T) { _, err := b.WithdrawFiatFundsToInternationalBank(withdrawFiatRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/bitstamp/bitstamp_types.go b/exchanges/bitstamp/bitstamp_types.go index 0b5b2c15..0a51dbbd 100644 --- a/exchanges/bitstamp/bitstamp_types.go +++ b/exchanges/bitstamp/bitstamp_types.go @@ -77,7 +77,7 @@ type Balances struct { // UserTransactions holds user transaction information type UserTransactions struct { - Date string `json:"datetime"` + Date int64 `json:"datetime"` TransID int64 `json:"id"` Type int `json:"type,string"` USD float64 `json:"usd"` @@ -91,11 +91,12 @@ type UserTransactions struct { // Order holds current open order data type Order struct { - ID int64 `json:"id"` - Date string `json:"datetime"` - Type int `json:"type"` - Price float64 `json:"price"` - Amount float64 `json:"amount"` + ID int64 `json:"id"` + Date int64 `json:"datetime"` + Type int `json:"type"` + Price float64 `json:"price"` + Amount float64 `json:"amount"` + Currency string `json:"currency_pair"` } // OrderStatus holds order status information diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index 2e4e43f8..21cc5af4 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -6,9 +6,11 @@ import ( "strconv" "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" + "github.com/thrasher-/gocryptotrader/currency/symbol" exchange "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/orderbook" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -176,8 +178,8 @@ func (b *Bitstamp) GetExchangeHistory(p pair.CurrencyPair, assetType string) ([] // SubmitOrder submits a new order func (b *Bitstamp) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - buy := side == exchange.Buy - market := orderType == exchange.Market + buy := side == exchange.BuyOrderSide + market := orderType == exchange.MarketOrderType response, err := b.PlaceOrder(p.Pair().String(), price, amount, buy, market) if response.ID > 0 { @@ -289,3 +291,94 @@ func (b *Bitstamp) GetWebsocket() (*exchange.Websocket, error) { func (b *Bitstamp) GetWithdrawCapabilities() uint32 { return b.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (b *Bitstamp) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var orders []exchange.OrderDetail + var currPair string + if len(getOrdersRequest.Currencies) != 1 { + currPair = "all" + } else { + currPair = getOrdersRequest.Currencies[0].Pair().String() + } + + resp, err := b.GetOpenOrders(currPair) + if err != nil { + return nil, err + } + + for _, order := range resp { + symbolOne := order.Currency[0:3] + symbolTwo := order.Currency[len(order.Currency)-3:] + orderDate := time.Unix(order.Date, 0) + + orders = append(orders, exchange.OrderDetail{ + Amount: order.Amount, + ID: fmt.Sprintf("%v", order.ID), + Price: order.Price, + OrderDate: orderDate, + CurrencyPair: pair.NewCurrencyPair(symbolOne, symbolTwo), + Exchange: b.Name, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (b *Bitstamp) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var currPair string + if len(getOrdersRequest.Currencies) == 1 { + currPair = getOrdersRequest.Currencies[0].Pair().String() + } + resp, err := b.GetUserTransactions(currPair) + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, order := range resp { + if order.Type == 2 { + quoteCurrency := "" + baseCurrency := "" + if order.BTC > 0 { + baseCurrency = symbol.BTC + } else if order.XRP > 0 { + baseCurrency = symbol.XRP + } else { + log.Warnf("No quote currency found for OrderID '%v'", order.OrderID) + } + + if order.USD > 0 { + quoteCurrency = symbol.USD + } else if order.EUR > 0 { + quoteCurrency = symbol.EUR + } else { + log.Warnf("No quote currency found for OrderID '%v'", order.OrderID) + } + + var currPair pair.CurrencyPair + if quoteCurrency != "" && baseCurrency != "" { + currPair = pair.NewCurrencyPairWithDelimiter(baseCurrency, quoteCurrency, b.ConfigCurrencyPairFormat.Delimiter) + } + orderDate := time.Unix(order.Date, 0) + + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.OrderID), + OrderDate: orderDate, + Exchange: b.Name, + CurrencyPair: currPair, + }) + + } + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} diff --git a/exchanges/bittrex/bittrex.go b/exchanges/bittrex/bittrex.go index eebd3af9..2069733d 100644 --- a/exchanges/bittrex/bittrex.go +++ b/exchanges/bittrex/bittrex.go @@ -422,9 +422,9 @@ func (b *Bittrex) GetOrder(uuid string) (Order, error) { return order, nil } -// GetOrderHistory is used to retrieve your order history. If currencyPair +// GetOrderHistoryForCurrency is used to retrieve your order history. If currencyPair // omitted it will return the entire order History. -func (b *Bittrex) GetOrderHistory(currencyPair string) (Order, error) { +func (b *Bittrex) GetOrderHistoryForCurrency(currencyPair string) (Order, error) { var orders Order values := url.Values{} diff --git a/exchanges/bittrex/bittrex_test.go b/exchanges/bittrex/bittrex_test.go index c17ddee6..6378bac0 100644 --- a/exchanges/bittrex/bittrex_test.go +++ b/exchanges/bittrex/bittrex_test.go @@ -181,14 +181,14 @@ func TestGetOrder(t *testing.T) { } } -func TestGetOrderHistory(t *testing.T) { +func TestGetOrderHistoryForCurrency(t *testing.T) { t.Parallel() - _, err := b.GetOrderHistory("") + _, err := b.GetOrderHistoryForCurrency("") if err == nil { t.Error("Test Failed - Bittrex - GetOrderHistory() error") } - _, err = b.GetOrderHistory("btc-ltc") + _, err = b.GetOrderHistoryForCurrency("btc-ltc") if err == nil { t.Error("Test Failed - Bittrex - GetOrderHistory() error") } @@ -305,17 +305,51 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange b.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := b.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.LTC)}, + } + + getOrdersRequest.Currencies[0].Delimiter = "-" + + _, err := b.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -339,7 +373,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.LTC, } - response, err := b.SubmitOrder(p, exchange.Buy, exchange.Limit, 1, 1, "clientId") + response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -348,7 +382,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -365,12 +398,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := b.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -378,7 +408,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -395,12 +424,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := b.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -434,7 +461,7 @@ func TestWithdraw(t *testing.T) { _, err := b.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/bittrex/bittrex_types.go b/exchanges/bittrex/bittrex_types.go index c1ea773f..854281a5 100644 --- a/exchanges/bittrex/bittrex_types.go +++ b/exchanges/bittrex/bittrex_types.go @@ -183,6 +183,9 @@ type Order struct { IsConditional bool `json:"IsConditional"` Condition string `json:"Condition"` ConditionTarget string `json:"ConditionTarget"` + // Below Used in OrderHistory + TimeStamp string `json:"TimeStamp"` + Commission float64 `json:"Commission"` } `json:"result"` } diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index 18774ffe..399971d2 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -3,7 +3,9 @@ package bittrex import ( "errors" "fmt" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -178,11 +180,11 @@ func (b *Bittrex) GetExchangeHistory(p pair.CurrencyPair, assetType string) ([]e // SubmitOrder submits a new order func (b *Bittrex) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - buy := side == exchange.Buy + buy := side == exchange.BuyOrderSide var response UUID var err error - if orderType != exchange.Limit { + if orderType != exchange.LimitOrderType { return submitOrderResponse, errors.New("not supported on exchange") } @@ -286,3 +288,89 @@ func (b *Bittrex) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) func (b *Bittrex) GetWithdrawCapabilities() uint32 { return b.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (b *Bittrex) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var currPair string + if len(getOrdersRequest.Currencies) == 1 { + currPair = getOrdersRequest.Currencies[0].Pair().String() + } + + resp, err := b.GetOpenOrders(currPair) + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, order := range resp.Result { + orderDate, err := time.Parse(time.RFC3339, order.Opened) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + b.Name, "GetActiveOrders", order.OrderUUID, order.Opened) + } + + currency := pair.NewCurrencyPairDelimiter(order.Exchange, b.ConfigCurrencyPairFormat.Delimiter) + orderType := exchange.OrderType(strings.ToUpper(order.Type)) + + orders = append(orders, exchange.OrderDetail{ + Amount: order.Quantity, + RemainingAmount: order.QuantityRemaining, + Price: order.Price, + OrderDate: orderDate, + ID: order.OrderUUID, + Exchange: b.Name, + OrderType: orderType, + CurrencyPair: currency, + }) + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (b *Bittrex) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var currPair string + if len(getOrdersRequest.Currencies) == 1 { + currPair = getOrdersRequest.Currencies[0].Pair().String() + } + + resp, err := b.GetOrderHistoryForCurrency(currPair) + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, order := range resp.Result { + orderDate, err := time.Parse(time.RFC3339, order.TimeStamp) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + b.Name, "GetActiveOrders", order.OrderUUID, order.Opened) + } + + currency := pair.NewCurrencyPairDelimiter(order.Exchange, b.ConfigCurrencyPairFormat.Delimiter) + orderType := exchange.OrderType(strings.ToUpper(order.Type)) + + orders = append(orders, exchange.OrderDetail{ + Amount: order.Quantity, + RemainingAmount: order.QuantityRemaining, + Price: order.Price, + OrderDate: orderDate, + ID: order.OrderUUID, + Exchange: b.Name, + OrderType: orderType, + Fee: order.Commission, + CurrencyPair: currency, + }) + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} diff --git a/exchanges/btcc/btcc_test.go b/exchanges/btcc/btcc_test.go index fdb1c670..ae8b3928 100644 --- a/exchanges/btcc/btcc_test.go +++ b/exchanges/btcc/btcc_test.go @@ -161,17 +161,48 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange b.SetDefaults() expectedResult := exchange.NoAPIWithdrawalMethodsText - // Act + withdrawPermissions := b.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -195,14 +226,13 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.LTC, } - _, err := b.SubmitOrder(p, exchange.Buy, exchange.Limit, 1, 1, "clientId") + _, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") if err != common.ErrNotYetImplemented { t.Errorf("Expected 'Not Yet Implemented', received %v", err) } } func TestCancelExchangeOrder(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -219,17 +249,13 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := b.CancelOrder(orderCancellation) - - // Assert if err != common.ErrNotYetImplemented { t.Errorf("Expected 'Not Yet Implemented', received %v", err) } } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -246,10 +272,8 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act _, err := b.CancelAllOrders(orderCancellation) - // Assert if err != common.ErrNotYetImplemented { t.Errorf("Expected 'Not Yet Implemented', received %v", err) } diff --git a/exchanges/btcc/btcc_wrapper.go b/exchanges/btcc/btcc_wrapper.go index 53feabcf..bcbc2ae1 100644 --- a/exchanges/btcc/btcc_wrapper.go +++ b/exchanges/btcc/btcc_wrapper.go @@ -216,3 +216,14 @@ func (b *BTCC) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (b *BTCC) GetWithdrawCapabilities() uint32 { return b.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (b *BTCC) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + return nil, common.ErrNotYetImplemented +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (b *BTCC) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + return nil, common.ErrNotYetImplemented +} diff --git a/exchanges/btcmarkets/btcmarkets.go b/exchanges/btcmarkets/btcmarkets.go index 0e416586..418f6a16 100644 --- a/exchanges/btcmarkets/btcmarkets.go +++ b/exchanges/btcmarkets/btcmarkets.go @@ -267,17 +267,18 @@ func (b *BTCMarkets) GetOrders(currency, instrument string, limit, since int64, return nil, errors.New(resp.ErrorMessage) } - for i := range resp.Orders { - resp.Orders[i].Price = resp.Orders[i].Price / common.SatoshisPerBTC - resp.Orders[i].OpenVolume = resp.Orders[i].OpenVolume / common.SatoshisPerBTC - resp.Orders[i].Volume = resp.Orders[i].Volume / common.SatoshisPerBTC + for _, order := range resp.Orders { + order.Price = order.Price / common.SatoshisPerBTC + order.OpenVolume = order.OpenVolume / common.SatoshisPerBTC + order.Volume = order.Volume / common.SatoshisPerBTC - for x := range resp.Orders[i].Trades { - resp.Orders[i].Trades[x].Fee = resp.Orders[i].Trades[x].Fee / common.SatoshisPerBTC - resp.Orders[i].Trades[x].Price = resp.Orders[i].Trades[x].Price / common.SatoshisPerBTC - resp.Orders[i].Trades[x].Volume = resp.Orders[i].Trades[x].Volume / common.SatoshisPerBTC + for _, trade := range order.Trades { + trade.Fee = trade.Fee / common.SatoshisPerBTC + trade.Price = trade.Price / common.SatoshisPerBTC + trade.Volume = trade.Volume / common.SatoshisPerBTC } } + return resp.Orders, nil } @@ -300,6 +301,18 @@ func (b *BTCMarkets) GetOpenOrders() ([]Order, error) { return nil, errors.New(resp.ErrorMessage) } + for _, order := range resp.Orders { + order.Price = order.Price / common.SatoshisPerBTC + order.OpenVolume = order.OpenVolume / common.SatoshisPerBTC + order.Volume = order.Volume / common.SatoshisPerBTC + + for _, trade := range order.Trades { + trade.Fee = trade.Fee / common.SatoshisPerBTC + trade.Price = trade.Price / common.SatoshisPerBTC + trade.Volume = trade.Volume / common.SatoshisPerBTC + } + } + return resp.Orders, nil } diff --git a/exchanges/btcmarkets/btcmarkets_test.go b/exchanges/btcmarkets/btcmarkets_test.go index 6b38c53b..7096b371 100644 --- a/exchanges/btcmarkets/btcmarkets_test.go +++ b/exchanges/btcmarkets/btcmarkets_test.go @@ -31,12 +31,9 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - BTC Markets Setup() init error") } - - if areTestAPIKeysSet() { - bConfig.APIKey = apiKey - bConfig.APISecret = apiSecret - bConfig.AuthenticatedAPISupport = true - } + bConfig.APIKey = apiKey + bConfig.APISecret = apiSecret + bConfig.AuthenticatedAPISupport = true b.Setup(bConfig) } @@ -155,7 +152,6 @@ func TestGetFundingHistory(t *testing.T) { } func TestCancelOrder(t *testing.T) { - _, err := b.CancelExistingOrder([]int64{1337}) if err == nil { @@ -266,17 +262,49 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange b.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.AutoWithdrawFiatText - // Act + withdrawPermissions := b.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := b.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.LTC)}, + } + + _, err := b.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -300,7 +328,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.LTC, } - response, err := b.SubmitOrder(p, exchange.Buy, exchange.Limit, 1, 1, "clientId") + response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -309,7 +337,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -326,12 +353,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := b.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -339,7 +363,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange b.SetDefaults() TestSetup(t) @@ -356,12 +379,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := b.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -395,7 +416,7 @@ func TestWithdraw(t *testing.T) { _, err := b.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -428,7 +449,7 @@ func TestWithdrawFiat(t *testing.T) { _, err := b.WithdrawFiatFunds(withdrawFiatRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index fb77e0ef..68f0b293 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -245,17 +247,25 @@ func (b *BTCMarkets) GetOrderInfo(orderID int64) (exchange.OrderDetail, error) { } for _, order := range orders { + var side exchange.OrderSide + if strings.EqualFold(order.OrderSide, exchange.AskOrderSide.ToString()) { + side = exchange.SellOrderSide + } else if strings.EqualFold(order.OrderSide, exchange.BidOrderSide.ToString()) { + side = exchange.BuyOrderSide + } + orderDate := time.Unix(int64(order.CreationTime), 0) + orderType := exchange.OrderType(strings.ToUpper(order.OrderType)) + OrderDetail.Amount = order.Volume - OrderDetail.BaseCurrency = order.Currency - OrderDetail.CreationTime = int64(order.CreationTime) + OrderDetail.OrderDate = orderDate OrderDetail.Exchange = b.GetName() OrderDetail.ID = order.ID - OrderDetail.OpenVolume = order.OpenVolume - OrderDetail.OrderSide = order.OrderSide - OrderDetail.OrderType = order.OrderType + OrderDetail.RemainingAmount = order.OpenVolume + OrderDetail.OrderSide = side + OrderDetail.OrderType = orderType OrderDetail.Price = order.Price - OrderDetail.QuoteCurrency = order.Instrument OrderDetail.Status = order.Status + OrderDetail.CurrencyPair = pair.NewCurrencyPairWithDelimiter(order.Instrument, order.Currency, b.ConfigCurrencyPairFormat.Delimiter) } return OrderDetail, nil @@ -300,3 +310,120 @@ func (b *BTCMarkets) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, erro func (b *BTCMarkets) GetWithdrawCapabilities() uint32 { return b.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (b *BTCMarkets) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := b.GetOpenOrders() + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, order := range resp { + var side exchange.OrderSide + if strings.EqualFold(order.OrderSide, exchange.AskOrderSide.ToString()) { + side = exchange.SellOrderSide + } else if strings.EqualFold(order.OrderSide, exchange.BidOrderSide.ToString()) { + side = exchange.BuyOrderSide + } + orderDate := time.Unix(int64(order.CreationTime), 0) + orderType := exchange.OrderType(strings.ToUpper(order.OrderType)) + + openOrder := exchange.OrderDetail{ + ID: order.ID, + Amount: order.Volume, + Exchange: b.Name, + RemainingAmount: order.OpenVolume, + OrderDate: orderDate, + OrderSide: side, + OrderType: orderType, + Price: order.Price, + Status: order.Status, + CurrencyPair: pair.NewCurrencyPairWithDelimiter(order.Instrument, order.Currency, b.ConfigCurrencyPairFormat.Delimiter), + } + + for _, trade := range order.Trades { + tradeDate := time.Unix(int64(trade.CreationTime), 0) + openOrder.Trades = append(openOrder.Trades, exchange.TradeHistory{ + Amount: trade.Volume, + Exchange: b.Name, + Price: trade.Price, + TID: trade.ID, + Timestamp: tradeDate, + Fee: trade.Fee, + Description: trade.Description, + }) + } + + orders = append(orders, openOrder) + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (b *BTCMarkets) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("Requires at least one currency pair to retrieve history") + } + + var respOrders []Order + for _, currency := range getOrdersRequest.Currencies { + resp, err := b.GetOrders(currency.FirstCurrency.String(), currency.SecondCurrency.String(), 200, 0, true) + if err != nil { + return nil, err + } + respOrders = append(respOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range respOrders { + var side exchange.OrderSide + if strings.EqualFold(order.OrderSide, exchange.AskOrderSide.ToString()) { + side = exchange.SellOrderSide + } else if strings.EqualFold(order.OrderSide, exchange.BidOrderSide.ToString()) { + side = exchange.BuyOrderSide + } + orderDate := time.Unix(int64(order.CreationTime), 0) + orderType := exchange.OrderType(strings.ToUpper(order.OrderType)) + + openOrder := exchange.OrderDetail{ + ID: order.ID, + Amount: order.Volume, + Exchange: b.Name, + RemainingAmount: order.OpenVolume, + OrderDate: orderDate, + OrderSide: side, + OrderType: orderType, + Price: order.Price, + Status: order.Status, + CurrencyPair: pair.NewCurrencyPairWithDelimiter(order.Instrument, order.Currency, b.ConfigCurrencyPairFormat.Delimiter), + } + + for _, trade := range order.Trades { + tradeDate := time.Unix(int64(trade.CreationTime), 0) + openOrder.Trades = append(openOrder.Trades, exchange.TradeHistory{ + Amount: trade.Volume, + Exchange: b.Name, + Price: trade.Price, + TID: trade.ID, + Timestamp: tradeDate, + Fee: trade.Fee, + Description: trade.Description, + }) + } + + orders = append(orders, openOrder) + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/coinbasepro/coinbasepro_test.go b/exchanges/coinbasepro/coinbasepro_test.go index 89027f5d..18adeee6 100644 --- a/exchanges/coinbasepro/coinbasepro_test.go +++ b/exchanges/coinbasepro/coinbasepro_test.go @@ -396,17 +396,51 @@ func TestCalculateTradingFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange + c.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.AutoWithdrawFiatWithAPIPermissionText - // Act + withdrawPermissions := c.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + c.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.LTC)}, + } + + _, err := c.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + c.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.LTC)}, + } + + _, err := c.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -430,7 +464,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.LTC, } - response, err := c.SubmitOrder(p, exchange.Buy, exchange.Limit, 1, 1, "clientId") + response, err := c.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -439,7 +473,7 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange + c.SetDefaults() TestSetup(t) @@ -456,12 +490,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := c.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -469,7 +500,7 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange + c.SetDefaults() TestSetup(t) @@ -486,12 +517,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := c.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -525,7 +554,7 @@ func TestWithdraw(t *testing.T) { _, err := c.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -548,7 +577,7 @@ func TestWithdrawFiat(t *testing.T) { _, err := c.WithdrawFiatFunds(withdrawFiatRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -571,7 +600,7 @@ func TestWithdrawInternationalBank(t *testing.T) { _, err := c.WithdrawFiatFundsToInternationalBank(withdrawFiatRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index 40c2ad8c..94cc2bd2 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -3,7 +3,9 @@ package coinbasepro import ( "errors" "fmt" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -156,10 +158,10 @@ func (c *CoinbasePro) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, var submitOrderResponse exchange.SubmitOrderResponse var response string var err error - if orderType == exchange.Market { + if orderType == exchange.MarketOrderType { response, err = c.PlaceMarginOrder("", amount, amount, side.ToString(), p.Pair().String(), "") - } else if orderType == exchange.Limit { + } else if orderType == exchange.LimitOrderType { response, err = c.PlaceLimitOrder("", price, amount, side.ToString(), "", "", p.Pair().String(), "", false) } else { err = errors.New("not supported") @@ -259,3 +261,86 @@ func (c *CoinbasePro) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, err func (c *CoinbasePro) GetWithdrawCapabilities() uint32 { return c.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (c *CoinbasePro) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var respOrders []GeneralizedOrderResponse + for _, currency := range getOrdersRequest.Currencies { + resp, err := c.GetOrders([]string{"open", "pending", "active"}, exchange.FormatExchangeCurrency(c.Name, currency).String()) + if err != nil { + return nil, err + } + respOrders = append(respOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range respOrders { + currency := pair.NewCurrencyPairDelimiter(order.ProductID, c.ConfigCurrencyPairFormat.Delimiter) + orderSide := exchange.OrderSide(strings.ToUpper(order.Side)) + orderType := exchange.OrderType(strings.ToUpper(order.Type)) + orderDate, err := time.Parse(time.RFC3339, order.CreatedAt) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + c.Name, "GetActiveOrders", order.ID, order.CreatedAt) + } + + orders = append(orders, exchange.OrderDetail{ + ID: order.ID, + Amount: order.Size, + ExecutedAmount: order.FilledSize, + OrderType: orderType, + OrderDate: orderDate, + OrderSide: orderSide, + CurrencyPair: currency, + Exchange: c.Name, + }) + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (c *CoinbasePro) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var respOrders []GeneralizedOrderResponse + for _, currency := range getOrdersRequest.Currencies { + resp, err := c.GetOrders([]string{"done", "settled"}, exchange.FormatExchangeCurrency(c.Name, currency).String()) + if err != nil { + return nil, err + } + respOrders = append(respOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range respOrders { + currency := pair.NewCurrencyPairDelimiter(order.ProductID, c.ConfigCurrencyPairFormat.Delimiter) + orderSide := exchange.OrderSide(strings.ToUpper(order.Side)) + orderType := exchange.OrderType(strings.ToUpper(order.Type)) + orderDate, err := time.Parse(time.RFC3339, order.CreatedAt) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + c.Name, "GetActiveOrders", order.ID, order.CreatedAt) + } + + orders = append(orders, exchange.OrderDetail{ + ID: order.ID, + Amount: order.Size, + ExecutedAmount: order.FilledSize, + OrderType: orderType, + OrderDate: orderDate, + OrderSide: orderSide, + CurrencyPair: currency, + Exchange: c.Name, + }) + } + + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/coinut/coinut_test.go b/exchanges/coinut/coinut_test.go index 4a92bed7..a19106cb 100644 --- a/exchanges/coinut/coinut_test.go +++ b/exchanges/coinut/coinut_test.go @@ -182,22 +182,50 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange + c.SetDefaults() expectedResult := exchange.WithdrawCryptoViaWebsiteOnlyText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - // Act + withdrawPermissions := c.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + c.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := c.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } +} + +func TestGetOrderHistory(t *testing.T) { + c.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.LTC)}, + } + + _, err := c.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { - if c.APIKey != "" && c.APIKey != "Key" && - c.APISecret != "" && c.APISecret != "Secret" { + if c.APIKey != "" && c.APIKey != "Key" { return true } return false @@ -216,7 +244,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.USD, } - response, err := c.SubmitOrder(p, exchange.Buy, exchange.Limit, 1, 10, "1234234") + response, err := c.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "1234234") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -225,7 +253,7 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange + c.SetDefaults() TestSetup(t) @@ -242,12 +270,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := c.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -255,7 +280,7 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange + c.SetDefaults() TestSetup(t) @@ -272,12 +297,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := c.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index 447fd60a..4f8ed508 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -215,7 +217,7 @@ func (c *COINUT) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, order var submitOrderResponse exchange.SubmitOrderResponse var err error var APIresponse interface{} - isBuyOrder := side == exchange.Buy + isBuyOrder := side == exchange.BuyOrderSide clientIDInt, err := strconv.ParseUint(clientID, 0, 32) clientIDUint := uint32(clientIDInt) @@ -231,9 +233,9 @@ func (c *COINUT) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, order currencyArray := instruments.Instruments[p.Pair().String()] currencyID := currencyArray[0].InstID - if orderType == exchange.Limit { + if orderType == exchange.LimitOrderType { APIresponse, err = c.NewOrder(currencyID, amount, price, isBuyOrder, clientIDUint) - } else if orderType == exchange.Market { + } else if orderType == exchange.MarketOrderType { APIresponse, err = c.NewOrder(currencyID, amount, 0, isBuyOrder, clientIDUint) } else { return submitOrderResponse, errors.New("unsupported order type") @@ -382,3 +384,110 @@ func (c *COINUT) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (c *COINUT) GetWithdrawCapabilities() uint32 { return c.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (c *COINUT) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + instruments, err := c.GetInstruments() + if err != nil { + return nil, err + } + + var allTheOrders []OrderResponse + for instrument, allInstrumentData := range instruments.Instruments { + for _, instrumentData := range allInstrumentData { + for _, currency := range getOrdersRequest.Currencies { + currStr := fmt.Sprintf("%v%v%v", currency.FirstCurrency.String(), c.ConfigCurrencyPairFormat.Delimiter, currency.SecondCurrency.String()) + if strings.EqualFold(currStr, instrument) { + openOrders, err := c.GetOpenOrders(instrumentData.InstID) + if err != nil { + return nil, err + } + allTheOrders = append(allTheOrders, openOrders.Orders...) + + continue + } + } + } + } + + var orders []exchange.OrderDetail + for _, order := range allTheOrders { + for instrument, allInstrumentData := range instruments.Instruments { + for _, instrumentData := range allInstrumentData { + if instrumentData.InstID == int(order.InstrumentID) { + currPair := pair.NewCurrencyPairDelimiter(instrument, "") + orderSide := exchange.OrderSide(strings.ToUpper(order.Side)) + orderDate := time.Unix(order.Timestamp, 0) + orders = append(orders, exchange.OrderDetail{ + ID: strconv.FormatInt(order.OrderID, 10), + Amount: order.Quantity, + Price: order.Price, + Exchange: c.Name, + OrderSide: orderSide, + OrderDate: orderDate, + CurrencyPair: currPair, + }) + } + } + } + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (c *COINUT) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + instruments, err := c.GetInstruments() + if err != nil { + return nil, err + } + + var allTheOrders []OrderFilledResponse + for instrument, allInstrumentData := range instruments.Instruments { + for _, instrumentData := range allInstrumentData { + for _, currency := range getOrdersRequest.Currencies { + currStr := fmt.Sprintf("%v%v%v", currency.FirstCurrency.String(), c.ConfigCurrencyPairFormat.Delimiter, currency.SecondCurrency.String()) + if strings.EqualFold(currStr, instrument) { + orders, err := c.GetTradeHistory(instrumentData.InstID, -1, -1) + if err != nil { + return nil, err + } + allTheOrders = append(allTheOrders, orders.Trades...) + + continue + } + } + } + } + + var orders []exchange.OrderDetail + for _, order := range allTheOrders { + for instrument, allInstrumentData := range instruments.Instruments { + for _, instrumentData := range allInstrumentData { + if instrumentData.InstID == int(order.Order.InstrumentID) { + currPair := pair.NewCurrencyPairDelimiter(instrument, "") + orderSide := exchange.OrderSide(strings.ToUpper(order.Order.Side)) + orderDate := time.Unix(order.Order.Timestamp, 0) + orders = append(orders, exchange.OrderDetail{ + ID: strconv.FormatInt(order.Order.OrderID, 10), + Amount: order.Order.Quantity, + Price: order.Order.Price, + Exchange: c.Name, + OrderSide: orderSide, + OrderDate: orderDate, + CurrencyPair: currPair, + }) + } + } + } + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 13d149ad..ec8e4ed6 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "net/url" + "sort" "strings" "sync" "time" @@ -207,27 +208,32 @@ type AccountCurrencyInfo struct { // TradeHistory holds exchange history data type TradeHistory struct { - Timestamp int64 - TID int64 - Price float64 - Amount float64 - Exchange string - Type string + Timestamp time.Time + TID int64 + Price float64 + Amount float64 + Exchange string + Type string + Fee float64 + Description string } // OrderDetail holds order detail data type OrderDetail struct { - Exchange string - ID string - BaseCurrency string - QuoteCurrency string - OrderSide string - OrderType string - CreationTime int64 - Status string - Price float64 - Amount float64 - OpenVolume float64 + Exchange string + AccountID string + ID string + CurrencyPair pair.CurrencyPair + OrderSide OrderSide + OrderType OrderType + OrderDate time.Time + Status string + Price float64 + Amount float64 + ExecutedAmount float64 + RemainingAmount float64 + Fee float64 + Trades []TradeHistory } // FundHistory holds exchange funding history data @@ -316,6 +322,9 @@ type IBotExchange interface { GetOrderInfo(orderID int64) (OrderDetail, error) GetDepositAddress(cryptocurrency pair.CurrencyItem, accountID string) (string, error) + GetOrderHistory(GetOrdersRequest GetOrdersRequest) ([]OrderDetail, error) + GetActiveOrders(getOrdersRequest GetOrdersRequest) ([]OrderDetail, error) + WithdrawCryptocurrencyFunds(wtihdrawRequest WithdrawRequest) (string, error) WithdrawFiatFunds(wtihdrawRequest WithdrawRequest) (string, error) WithdrawFiatFundsToInternationalBank(wtihdrawRequest WithdrawRequest) (string, error) @@ -817,9 +826,13 @@ type OrderType string // OrderType ...types const ( - Limit OrderType = "Limit" - Market OrderType = "Market" - ImmediateOrCancel OrderType = "IMMEDIATE_OR_CANCEL" + AnyOrderType OrderType = "ANY" + LimitOrderType OrderType = "LIMIT" + MarketOrderType OrderType = "MARKET" + ImmediateOrCancelOrderType OrderType = "IMMEDIATE_OR_CANCEL" + StopOrderType OrderType = "STOP" + TrailingStopOrderType OrderType = "TRAILINGSTOP" + UnknownOrderType OrderType = "UNKNOWN" ) // ToString changes the ordertype to the exchange standard and returns a string @@ -832,8 +845,11 @@ type OrderSide string // OrderSide types const ( - Buy OrderSide = "Buy" - Sell OrderSide = "Sell" + AnyOrderSide OrderSide = "ANY" + BuyOrderSide OrderSide = "BUY" + SellOrderSide OrderSide = "SELL" + BidOrderSide OrderSide = "BID" + AskOrderSide OrderSide = "ASK" ) // ToString changes the ordertype to the exchange standard and returns a string @@ -942,3 +958,224 @@ func (e *Base) FormatWithdrawPermissions() string { return NoAPIWithdrawalMethodsText } + +// GetOrdersRequest used for GetOrderHistory and GetOpenOrders wrapper functions +type GetOrdersRequest struct { + OrderType OrderType + OrderSide OrderSide + StartTicks time.Time + EndTicks time.Time + // Currencies Empty array = all currencies. Some endpoints only support singular currency enquiries + Currencies []pair.CurrencyPair +} + +// OrderStatus defines order status types +type OrderStatus string + +// All OrderStatus types +const ( + AnyOrderStatus OrderStatus = "ANY" + NewOrderStatus OrderStatus = "NEW" + ActiveOrderStatus OrderStatus = "ACTIVE" + PartiallyFilledOrderStatus OrderStatus = "PARTIALLY_FILLED" + FilledOrderStatus OrderStatus = "FILLED" + CancelledOrderStatus OrderStatus = "CANCELED" + PendingCancelOrderStatus OrderStatus = "PENDING_CANCEL" + RejectedOrderStatus OrderStatus = "REJECTED" + ExpiredOrderStatus OrderStatus = "EXPIRED" + HiddenOrderStatus OrderStatus = "HIDDEN" + UnknownOrderStatus OrderStatus = "UNKNOWN" +) + +// FilterOrdersBySide removes any OrderDetails that don't match the orderStatus provided +func FilterOrdersBySide(orders *[]OrderDetail, orderSide OrderSide) { + if orderSide == "" || orderSide == AnyOrderSide { + return + } + + var filteredOrders []OrderDetail + for _, orderDetail := range *orders { + if strings.EqualFold(string(orderDetail.OrderSide), string(orderSide)) { + filteredOrders = append(filteredOrders, orderDetail) + } + } + + *orders = filteredOrders +} + +// FilterOrdersByType removes any OrderDetails that don't match the orderType provided +func FilterOrdersByType(orders *[]OrderDetail, orderType OrderType) { + if orderType == "" || orderType == AnyOrderType { + return + } + + var filteredOrders []OrderDetail + for _, orderDetail := range *orders { + if strings.EqualFold(string(orderDetail.OrderType), string(orderType)) { + filteredOrders = append(filteredOrders, orderDetail) + } + } + + *orders = filteredOrders +} + +// FilterOrdersByTickRange removes any OrderDetails outside of the tick range +func FilterOrdersByTickRange(orders *[]OrderDetail, startTicks, endTicks time.Time) { + if startTicks.IsZero() || endTicks.IsZero() || + startTicks.Unix() == 0 || endTicks.Unix() == 0 || endTicks.Before(startTicks) { + return + } + + var filteredOrders []OrderDetail + for _, orderDetail := range *orders { + if orderDetail.OrderDate.Unix() >= startTicks.Unix() && orderDetail.OrderDate.Unix() <= endTicks.Unix() { + filteredOrders = append(filteredOrders, orderDetail) + } + } + + *orders = filteredOrders +} + +// FilterOrdersByCurrencies removes any OrderDetails that do not match the provided currency list +// It is forgiving in that the provided currencies can match quote or base currencies +func FilterOrdersByCurrencies(orders *[]OrderDetail, currencies []pair.CurrencyPair) { + if len(currencies) <= 0 { + return + } + + var filteredOrders []OrderDetail + for _, orderDetail := range *orders { + matchFound := false + for _, currency := range currencies { + if !matchFound && orderDetail.CurrencyPair.Equal(currency, false) { + matchFound = true + } + } + + if matchFound { + filteredOrders = append(filteredOrders, orderDetail) + } + } + + *orders = filteredOrders +} + +// ByPrice used for sorting orders by price +type ByPrice []OrderDetail + +func (b ByPrice) Len() int { + return len(b) +} + +func (b ByPrice) Less(i, j int) bool { + return b[i].Price < b[j].Price +} + +func (b ByPrice) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +// SortOrdersByPrice the caller function to sort orders +func SortOrdersByPrice(orders *[]OrderDetail, reverse bool) { + if reverse { + sort.Sort(sort.Reverse(ByPrice(*orders))) + } else { + sort.Sort(ByPrice(*orders)) + } +} + +// ByOrderType used for sorting orders by order type +type ByOrderType []OrderDetail + +func (b ByOrderType) Len() int { + return len(b) +} + +func (b ByOrderType) Less(i, j int) bool { + return b[i].OrderType.ToString() < b[j].OrderType.ToString() +} + +func (b ByOrderType) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +// SortOrdersByType the caller function to sort orders +func SortOrdersByType(orders *[]OrderDetail, reverse bool) { + if reverse { + sort.Sort(sort.Reverse(ByOrderType(*orders))) + } else { + sort.Sort(ByOrderType(*orders)) + } +} + +// ByCurrency used for sorting orders by order currency +type ByCurrency []OrderDetail + +func (b ByCurrency) Len() int { + return len(b) +} + +func (b ByCurrency) Less(i, j int) bool { + return b[i].CurrencyPair.Pair().String() < b[j].CurrencyPair.Pair().String() +} + +func (b ByCurrency) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +// SortOrdersByCurrency the caller function to sort orders +func SortOrdersByCurrency(orders *[]OrderDetail, reverse bool) { + if reverse { + sort.Sort(sort.Reverse(ByCurrency(*orders))) + } else { + sort.Sort(ByCurrency(*orders)) + } +} + +// ByDate used for sorting orders by order date +type ByDate []OrderDetail + +func (b ByDate) Len() int { + return len(b) +} + +func (b ByDate) Less(i, j int) bool { + return b[i].OrderDate.Unix() < b[j].OrderDate.Unix() +} + +func (b ByDate) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +// SortOrdersByDate the caller function to sort orders +func SortOrdersByDate(orders *[]OrderDetail, reverse bool) { + if reverse { + sort.Sort(sort.Reverse(ByDate(*orders))) + } else { + sort.Sort(ByDate(*orders)) + } +} + +// ByOrderSide used for sorting orders by order side (buy sell) +type ByOrderSide []OrderDetail + +func (b ByOrderSide) Len() int { + return len(b) +} + +func (b ByOrderSide) Less(i, j int) bool { + return b[i].OrderSide.ToString() < b[j].OrderSide.ToString() +} + +func (b ByOrderSide) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +// SortOrdersBySide the caller function to sort orders +func SortOrdersBySide(orders *[]OrderDetail, reverse bool) { + if reverse { + sort.Sort(sort.Reverse(ByOrderSide(*orders))) + } else { + sort.Sort(ByOrderSide(*orders)) + } +} diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index 8309b58a..60d38a48 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -2,12 +2,14 @@ package exchange import ( "net/http" + "strings" "testing" "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/currency/pair" + "github.com/thrasher-/gocryptotrader/currency/symbol" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) @@ -929,3 +931,248 @@ func TestOrderTypes(t *testing.T) { t.Errorf("test failed - unexpected string %s", os.ToString()) } } + +func TestFilterOrdersByType(t *testing.T) { + var orders []OrderDetail + + orders = append(orders, OrderDetail{ + OrderType: ImmediateOrCancelOrderType, + }) + orders = append(orders, OrderDetail{ + OrderType: LimitOrderType, + }) + + FilterOrdersByType(&orders, AnyOrderType) + if len(orders) != 2 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders)) + } + + FilterOrdersByType(&orders, LimitOrderType) + if len(orders) != 1 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) + } + + FilterOrdersByType(&orders, StopOrderType) + if len(orders) != 0 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 0, len(orders)) + } +} + +func TestFilterOrdersBySide(t *testing.T) { + var orders []OrderDetail + + orders = append(orders, OrderDetail{ + OrderSide: BuyOrderSide, + }) + orders = append(orders, OrderDetail{ + OrderSide: SellOrderSide, + }) + orders = append(orders, OrderDetail{}) + + FilterOrdersBySide(&orders, AnyOrderSide) + if len(orders) != 3 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders)) + } + + FilterOrdersBySide(&orders, BuyOrderSide) + if len(orders) != 1 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) + } + + FilterOrdersBySide(&orders, SellOrderSide) + if len(orders) != 0 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 0, len(orders)) + } +} + +func TestFilterOrdersByTickRange(t *testing.T) { + var orders []OrderDetail + + orders = append(orders, OrderDetail{ + OrderDate: time.Unix(100, 0), + }) + orders = append(orders, OrderDetail{ + OrderDate: time.Unix(110, 0), + }) + orders = append(orders, OrderDetail{ + OrderDate: time.Unix(111, 0), + }) + + FilterOrdersByTickRange(&orders, time.Unix(0, 0), time.Unix(0, 0)) + if len(orders) != 3 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders)) + } + + FilterOrdersByTickRange(&orders, time.Unix(100, 0), time.Unix(111, 0)) + if len(orders) != 3 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders)) + } + + FilterOrdersByTickRange(&orders, time.Unix(101, 0), time.Unix(111, 0)) + if len(orders) != 2 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders)) + } + + FilterOrdersByTickRange(&orders, time.Unix(200, 0), time.Unix(300, 0)) + if len(orders) != 0 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 0, len(orders)) + } +} + +func TestFilterOrdersByCurrencies(t *testing.T) { + var orders []OrderDetail + + orders = append(orders, OrderDetail{ + CurrencyPair: pair.NewCurrencyPair(symbol.BTC, symbol.USD), + }) + orders = append(orders, OrderDetail{ + CurrencyPair: pair.NewCurrencyPair(symbol.LTC, symbol.EUR), + }) + orders = append(orders, OrderDetail{ + CurrencyPair: pair.NewCurrencyPair(symbol.DOGE, symbol.RUB), + }) + + currencies := []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.USD), pair.NewCurrencyPair(symbol.LTC, symbol.EUR), pair.NewCurrencyPair(symbol.DOGE, symbol.RUB)} + FilterOrdersByCurrencies(&orders, currencies) + if len(orders) != 3 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders)) + } + + currencies = []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.USD), pair.NewCurrencyPair(symbol.LTC, symbol.EUR)} + FilterOrdersByCurrencies(&orders, currencies) + if len(orders) != 2 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders)) + } + + currencies = []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.USD)} + FilterOrdersByCurrencies(&orders, currencies) + if len(orders) != 1 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) + } + + currencies = []pair.CurrencyPair{} + FilterOrdersByCurrencies(&orders, currencies) + if len(orders) != 1 { + t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) + } +} + +func TestSortOrdersByPrice(t *testing.T) { + orders := []OrderDetail{ + { + Price: 100, + }, { + Price: 0, + }, { + Price: 50, + }, + } + + SortOrdersByPrice(&orders, false) + if orders[0].Price != 0 { + t.Errorf("Test failed. Expected: '%v', received: '%v'", 0, orders[0].Price) + } + + SortOrdersByPrice(&orders, true) + if orders[0].Price != 100 { + t.Errorf("Test failed. Expected: '%v', received: '%v'", 100, orders[0].Price) + } +} + +func TestSortOrdersByDate(t *testing.T) { + orders := []OrderDetail{ + { + OrderDate: time.Unix(0, 0), + }, { + OrderDate: time.Unix(1, 0), + }, { + OrderDate: time.Unix(2, 0), + }, + } + + SortOrdersByDate(&orders, false) + if orders[0].OrderDate.Unix() != time.Unix(0, 0).Unix() { + t.Errorf("Test failed. Expected: '%v', received: '%v'", time.Unix(0, 0).Unix(), orders[0].OrderDate.Unix()) + } + + SortOrdersByDate(&orders, true) + if orders[0].OrderDate.Unix() != time.Unix(2, 0).Unix() { + t.Errorf("Test failed. Expected: '%v', received: '%v'", time.Unix(2, 0).Unix(), orders[0].OrderDate.Unix()) + } +} + +func TestSortOrdersByCurrency(t *testing.T) { + orders := []OrderDetail{ + { + CurrencyPair: pair.NewCurrencyPairWithDelimiter(symbol.BTC, symbol.USD, "-"), + }, { + CurrencyPair: pair.NewCurrencyPairWithDelimiter(symbol.DOGE, symbol.USD, "-"), + }, { + CurrencyPair: pair.NewCurrencyPairWithDelimiter(symbol.BTC, symbol.RUB, "-"), + }, { + CurrencyPair: pair.NewCurrencyPairWithDelimiter(symbol.LTC, symbol.EUR, "-"), + }, { + CurrencyPair: pair.NewCurrencyPairWithDelimiter(symbol.LTC, symbol.AUD, "-"), + }, + } + + SortOrdersByCurrency(&orders, false) + if orders[0].CurrencyPair.Pair().String() != symbol.BTC+"-"+symbol.RUB { + t.Errorf("Test failed. Expected: '%v', received: '%v'", symbol.BTC+"-"+symbol.RUB, orders[0].CurrencyPair.Pair().String()) + } + + SortOrdersByCurrency(&orders, true) + if orders[0].CurrencyPair.Pair().String() != symbol.LTC+"-"+symbol.EUR { + t.Errorf("Test failed. Expected: '%v', received: '%v'", symbol.LTC+"-"+symbol.EUR, orders[0].CurrencyPair.Pair().String()) + } +} + +func TestSortOrdersByOrderSide(t *testing.T) { + orders := []OrderDetail{ + { + OrderSide: BuyOrderSide, + }, { + OrderSide: SellOrderSide, + }, { + OrderSide: SellOrderSide, + }, { + OrderSide: BuyOrderSide, + }, + } + + SortOrdersBySide(&orders, false) + if !strings.EqualFold(orders[0].OrderSide.ToString(), BuyOrderSide.ToString()) { + t.Errorf("Test failed. Expected: '%v', received: '%v'", BuyOrderSide, orders[0].OrderSide) + } + + t.Log(orders) + + SortOrdersBySide(&orders, true) + if !strings.EqualFold(orders[0].OrderSide.ToString(), SellOrderSide.ToString()) { + t.Errorf("Test failed. Expected: '%v', received: '%v'", SellOrderSide, orders[0].OrderSide) + } +} + +func TestSortOrdersByOrderType(t *testing.T) { + orders := []OrderDetail{ + { + OrderType: MarketOrderType, + }, { + OrderType: LimitOrderType, + }, { + OrderType: ImmediateOrCancelOrderType, + }, { + OrderType: TrailingStopOrderType, + }, + } + + SortOrdersByType(&orders, false) + if !strings.EqualFold(orders[0].OrderType.ToString(), ImmediateOrCancelOrderType.ToString()) { + t.Errorf("Test failed. Expected: '%v', received: '%v'", ImmediateOrCancelOrderType, orders[0].OrderType) + } + + SortOrdersByType(&orders, true) + if !strings.EqualFold(orders[0].OrderType.ToString(), TrailingStopOrderType.ToString()) { + t.Errorf("Test failed. Expected: '%v', received: '%v'", TrailingStopOrderType, orders[0].OrderType) + } +} diff --git a/exchanges/exmo/exmo_test.go b/exchanges/exmo/exmo_test.go index 76d9732b..b011de0e 100644 --- a/exchanges/exmo/exmo_test.go +++ b/exchanges/exmo/exmo_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/common" + "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/currency/pair" "github.com/thrasher-/gocryptotrader/currency/symbol" exchange "github.com/thrasher-/gocryptotrader/exchanges" @@ -24,6 +25,18 @@ func TestDefault(t *testing.T) { } func TestSetup(t *testing.T) { + cfg := config.GetConfig() + cfg.LoadConfig("../../testdata/configtest.json") + exmoConf, err := cfg.GetExchangeConfig("EXMO") + if err != nil { + t.Error("Test Failed - OKCoin Setup() init error") + } + exmoConf.AuthenticatedAPISupport = true + exmoConf.APIKey = APIKey + exmoConf.APISecret = APISecret + + e.Setup(exmoConf) + e.AuthenticatedAPISupport = true e.APIKey = APIKey e.APISecret = APISecret @@ -226,17 +239,52 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange + e.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := e.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + e.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := e.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + e.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + currPair := pair.NewCurrencyPair(symbol.BTC, symbol.USD) + currPair.Delimiter = "_" + getOrdersRequest.Currencies = []pair.CurrencyPair{currPair} + + _, err := e.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -259,7 +307,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.USD, } - response, err := e.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 10, "1234234") + response, err := e.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "1234234") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -268,7 +316,7 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange + e.SetDefaults() TestSetup(t) if areTestAPIKeysSet() && !canManipulateRealOrders { @@ -284,11 +332,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := e.CancelOrder(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -296,7 +342,7 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange + e.SetDefaults() TestSetup(t) @@ -312,11 +358,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := e.CancelAllOrders(orderCancellation) - // Assert + if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -350,7 +395,7 @@ func TestWithdraw(t *testing.T) { _, err := e.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/exmo/exmo_types.go b/exchanges/exmo/exmo_types.go index 4b59c7d2..91b1e38d 100644 --- a/exchanges/exmo/exmo_types.go +++ b/exchanges/exmo/exmo_types.go @@ -10,6 +10,7 @@ type Trades struct { Price float64 `json:"price,string"` Amount float64 `json:"amount,string"` Date int64 `json:"date"` + Pair string `json:"pair"` } // Orderbook holds the orderbook data diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index a0d0bd08..3bfba783 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -186,10 +188,10 @@ func (e *EXMO) GetExchangeHistory(p pair.CurrencyPair, assetType string) ([]exch func (e *EXMO) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse var oT string - if orderType == exchange.Limit { + if orderType == exchange.LimitOrderType { return submitOrderResponse, errors.New("Unsupported order type") - } else if orderType == exchange.Market { - if side == exchange.Buy { + } else if orderType == exchange.MarketOrderType { + if side == exchange.BuyOrderSide { oT = "market_buy" } else { oT = "market_sell" @@ -303,3 +305,71 @@ func (e *EXMO) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (e *EXMO) GetWithdrawCapabilities() uint32 { return e.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (e *EXMO) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := e.GetOpenOrders() + if err != nil { + return nil, err + } + var orders []exchange.OrderDetail + for _, order := range resp { + symbol := pair.NewCurrencyPairDelimiter(order.Pair, "_") + orderDate := time.Unix(order.Created, 0) + orderSide := exchange.OrderSide(strings.ToUpper(order.Type)) + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.OrderID), + Amount: order.Quantity, + OrderDate: orderDate, + Price: order.Price, + OrderSide: orderSide, + Exchange: e.Name, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (e *EXMO) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("Currency must be supplied") + } + + var allTrades []UserTrades + for _, currency := range getOrdersRequest.Currencies { + resp, err := e.GetUserTrades(exchange.FormatExchangeCurrency(e.Name, currency).String(), "", "10000") + if err != nil { + return nil, err + } + for _, order := range resp { + allTrades = append(allTrades, order...) + } + } + + var orders []exchange.OrderDetail + for _, order := range allTrades { + symbol := pair.NewCurrencyPairDelimiter(order.Pair, "_") + orderDate := time.Unix(order.Date, 0) + orderSide := exchange.OrderSide(strings.ToUpper(order.Type)) + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.TradeID), + Amount: order.Quantity, + OrderDate: orderDate, + Price: order.Price, + OrderSide: orderSide, + Exchange: e.Name, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/gateio/gateio.go b/exchanges/gateio/gateio.go index 15359180..298084bb 100644 --- a/exchanges/gateio/gateio.go +++ b/exchanges/gateio/gateio.go @@ -31,6 +31,7 @@ const ( gateioCancelAllOrders = "private/cancelAllOrders" gateioWithdraw = "private/withdraw" gateioOpenOrders = "private/openOrders" + gateioTradeHistory = "private/tradeHistory" gateioDepositAddress = "private/depositAddress" gateioTicker = "ticker" gateioTickers = "tickers" @@ -428,8 +429,7 @@ func (g *Gateio) CancelAllExistingOrders(orderType int64, symbol string) error { return nil } -// -// GetOpenOrders retrieves all orders with an optional symbol filter +// GetOpenOrders retrieves all open orders with an optional symbol filter func (g *Gateio) GetOpenOrders(symbol string) (OpenOrdersResponse, error) { var params string var result OpenOrdersResponse @@ -450,6 +450,24 @@ func (g *Gateio) GetOpenOrders(symbol string) (OpenOrdersResponse, error) { return result, nil } +// GetTradeHistory retrieves all orders with an optional symbol filter +func (g *Gateio) GetTradeHistory(symbol string) (TradHistoryResponse, error) { + var params string + var result TradHistoryResponse + params = fmt.Sprintf("currencyPair=%s", symbol) + + err := g.SendAuthenticatedHTTPRequest("POST", gateioTradeHistory, 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 a9a503a9..9de181d4 100644 --- a/exchanges/gateio/gateio_test.go +++ b/exchanges/gateio/gateio_test.go @@ -31,7 +31,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - GateIO Setup() init error") } - gateioConfig.AuthenticatedAPISupport = true gateioConfig.APIKey = apiKey gateioConfig.APISecret = apiSecret @@ -240,17 +239,52 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange g.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := g.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + g.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := g.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + g.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + currPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC) + currPair.Delimiter = "_" + getOrdersRequest.Currencies = []pair.CurrencyPair{currPair} + + _, err := g.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -274,7 +308,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.LTC, SecondCurrency: symbol.BTC, } - response, err := g.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 10, "1234234") + response, err := g.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "1234234") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -283,7 +317,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange g.SetDefaults() TestSetup(t) @@ -300,12 +333,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := g.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -313,7 +343,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange g.SetDefaults() TestSetup(t) @@ -330,12 +359,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := g.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -383,7 +410,7 @@ func TestWithdraw(t *testing.T) { _, err := g.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/gateio/gateio_types.go b/exchanges/gateio/gateio_types.go index 9f7a8207..84bc7375 100644 --- a/exchanges/gateio/gateio_types.go +++ b/exchanges/gateio/gateio_types.go @@ -142,20 +142,41 @@ type OpenOrdersResponse struct { // OpenOrder details each open order type OpenOrder struct { - Amount string `json:"amount"` + Amount float64 `json:"amount,string"` CurrencyPair string `json:"currencyPair"` - FilledAmount int `json:"filledAmount"` - FilledRate int `json:"filledRate"` - InitialAmount string `json:"initialAmount"` + FilledAmount float64 `json:"filledAmount,string"` + FilledRate float64 `json:"filledRate"` + InitialAmount float64 `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"` + Timestamp int64 `json:"timestamp"` + Total float64 `json:"total,string"` Type string `json:"type"` } +// TradHistoryResponse The full response for retrieving all user trade history +type TradHistoryResponse struct { + Code int `json:"code,omitempty"` + Elapsed string `json:"elapsed,omitempty"` + Message string `json:"message"` + Trades []TradesResponse `json:"trades"` + Result string `json:"result"` +} + +// TradesResponse details trade history +type TradesResponse struct { + ID string `json:"id"` + OrderID string `json:"orderid"` + Pair string `json:"pair"` + Type string `json:"type"` + Rate float64 `json:"rate,string"` + Amount float64 `json:"amount,string"` + Time string `json:"time"` + TimeUnix int64 `json:"time_unix,string"` +} + // 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 d64d16a7..04b3d835 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "strconv" + "strings" "sync" "time" @@ -191,7 +192,7 @@ func (g *Gateio) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, order var submitOrderResponse exchange.SubmitOrderResponse var orderTypeFormat SpotNewOrderRequestParamsType - if side == exchange.Buy { + if side == exchange.BuyOrderSide { orderTypeFormat = SpotNewOrderRequestParamsTypeBuy } else { orderTypeFormat = SpotNewOrderRequestParamsTypeSell @@ -319,3 +320,77 @@ func (g *Gateio) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (g *Gateio) GetWithdrawCapabilities() uint32 { return g.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (g *Gateio) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var currPair string + if len(getOrdersRequest.Currencies) == 1 { + currPair = getOrdersRequest.Currencies[0].Pair().String() + } + + resp, err := g.GetOpenOrders(currPair) + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, order := range resp.Orders { + if order.Status != "open" { + continue + } + + symbol := pair.NewCurrencyPairDelimiter(order.CurrencyPair, g.ConfigCurrencyPairFormat.Delimiter) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + orderDate := time.Unix(order.Timestamp, 0) + + orders = append(orders, exchange.OrderDetail{ + ID: order.OrderNumber, + Amount: order.Amount, + Price: order.Rate, + RemainingAmount: order.FilledAmount, + OrderDate: orderDate, + OrderSide: side, + Exchange: g.Name, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (g *Gateio) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var trades []TradesResponse + for _, currency := range getOrdersRequest.Currencies { + resp, err := g.GetTradeHistory(currency.Pair().String()) + if err != nil { + return nil, err + } + trades = append(trades, resp.Trades...) + } + + var orders []exchange.OrderDetail + for _, trade := range trades { + symbol := pair.NewCurrencyPairDelimiter(trade.Pair, g.ConfigCurrencyPairFormat.Delimiter) + side := exchange.OrderSide(strings.ToUpper(trade.Type)) + orderDate := time.Unix(trade.TimeUnix, 0) + orders = append(orders, exchange.OrderDetail{ + ID: trade.OrderID, + Amount: trade.Amount, + Price: trade.Rate, + OrderDate: orderDate, + OrderSide: side, + Exchange: g.Name, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/gemini/gemini.go b/exchanges/gemini/gemini.go index 257044ae..497811d6 100644 --- a/exchanges/gemini/gemini.go +++ b/exchanges/gemini/gemini.go @@ -373,19 +373,22 @@ func (g *Gemini) GetOrderStatus(orderID int64) (Order, error) { // GetOrders returns active orders in the market func (g *Gemini) GetOrders() ([]Order, error) { - var response struct { - orders []Order - Message string `json:"message"` + var response interface{} + + type orders struct { + orders []Order } err := g.SendAuthenticatedHTTPRequest("POST", geminiOrders, nil, &response) if err != nil { - return response.orders, err + return nil, err } - if response.Message != "" { - return response.orders, errors.New(response.Message) + switch r := response.(type) { + case orders: + return r.orders, nil + default: + return []Order{}, nil } - return response.orders, nil } // GetTradeHistory returns an array of trades that have been on the exchange @@ -512,9 +515,12 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st PayloadBase64 := common.Base64Encode(PayloadJSON) hmac := common.GetHMAC(common.HashSHA512_384, []byte(PayloadBase64), []byte(g.APISecret)) + headers["Content-Length"] = "0" + headers["Content-Type"] = "text/plain" headers["X-GEMINI-APIKEY"] = g.APIKey headers["X-GEMINI-PAYLOAD"] = PayloadBase64 headers["X-GEMINI-SIGNATURE"] = common.HexEncodeToString(hmac) + headers["Cache-Control"] = "no-cache" return g.SendPayload(method, g.APIUrl+"/v1/"+path, headers, strings.NewReader(""), result, true, g.Verbose) } diff --git a/exchanges/gemini/gemini_test.go b/exchanges/gemini/gemini_test.go index a19da251..a7874587 100644 --- a/exchanges/gemini/gemini_test.go +++ b/exchanges/gemini/gemini_test.go @@ -24,17 +24,17 @@ const ( apiKeyRole2 = "" sessionHeartBeat2 = false - canManipulateRealOrders = !false + canManipulateRealOrders = false ) func TestAddSession(t *testing.T) { var g1 Gemini if Session[1] == nil { - err := AddSession(&g1, 1, apiKey1, apiSecret1, apiKeyRole1, true, false) + err := AddSession(&g1, 1, apiKey1, apiSecret1, apiKeyRole1, true, true) if err != nil { t.Error("Test failed - AddSession() error", err) } - err = AddSession(&g1, 1, apiKey1, apiSecret1, apiKeyRole1, true, false) + err = AddSession(&g1, 1, apiKey1, apiSecret1, apiKeyRole1, true, true) if err == nil { t.Error("Test failed - AddSession() error", err) } @@ -55,7 +55,6 @@ func TestSetDefaults(t *testing.T) { } func TestSetup(t *testing.T) { - cfg := config.GetConfig() cfg.LoadConfig("../../testdata/configtest.json") geminiConfig, err := cfg.GetExchangeConfig("Gemini") @@ -74,6 +73,8 @@ func TestSetup(t *testing.T) { Session[2].APIKey = apiKey2 Session[2].APISecret = apiSecret2 + Session[1].APIUrl = geminiSandboxAPIURL + Session[2].APIUrl = geminiSandboxAPIURL } func TestGetSymbols(t *testing.T) { @@ -249,7 +250,6 @@ func setFeeBuilder() exchange.FeeBuilder { } func TestGetFee(t *testing.T) { - var feeBuilder = setFeeBuilder() if apiKey1 != "" && apiSecret1 != "" { // CryptocurrencyTradeFee Basic @@ -330,16 +330,54 @@ func TestFormatWithdrawPermissions(t *testing.T) { TestAddSession(t) TestSetDefaults(t) TestSetup(t) - // Arrange + expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - // Act + withdrawPermissions := Session[1].FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + TestAddSession(t) + TestSetDefaults(t) + TestSetup(t) + Session[1].Verbose = true + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := Session[1].GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + TestAddSession(t) + TestSetDefaults(t) + TestSetup(t) + Session[1].Verbose = true + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := Session[1].GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -364,7 +402,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.LTC, SecondCurrency: symbol.BTC, } - response, err := Session[1].SubmitOrder(p, exchange.Buy, exchange.Market, 1, 10, "1234234") + response, err := Session[1].SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "1234234") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -373,7 +411,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange TestAddSession(t) TestSetDefaults(t) TestSetup(t) @@ -391,12 +428,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := Session[1].CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -404,7 +438,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange TestAddSession(t) TestSetDefaults(t) TestSetup(t) @@ -422,12 +455,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := Session[1].CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -462,7 +493,7 @@ func TestWithdraw(t *testing.T) { _, err := Session[1].WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/gemini/gemini_types.go b/exchanges/gemini/gemini_types.go index 371b0a80..47f360bf 100644 --- a/exchanges/gemini/gemini_types.go +++ b/exchanges/gemini/gemini_types.go @@ -117,6 +117,9 @@ type TradeHistory struct { Exchange string `json:"exchange"` IsAuctionFilled bool `json:"is_auction_fill"` ClientOrderID string `json:"client_order_id"` + // Used to store values + BaseCurrency string + QuoteCurrency string } // TradeVolume holds Volume information diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 734d533e..f2a172ac 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -5,7 +5,9 @@ import ( "fmt" "net/url" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -241,3 +243,89 @@ func (g *Gemini) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (g *Gemini) GetWithdrawCapabilities() uint32 { return g.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (g *Gemini) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := g.GetOrders() + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, order := range resp { + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, g.ConfigCurrencyPairFormat.Delimiter) + var orderType exchange.OrderType + if order.Type == "exchange limit" { + orderType = exchange.LimitOrderType + } else if order.Type == "market buy" || order.Type == "market sell" { + orderType = exchange.MarketOrderType + } + + side := exchange.OrderSide(strings.ToUpper(order.Type)) + orderDate := time.Unix(order.Timestamp, 0) + + orders = append(orders, exchange.OrderDetail{ + Amount: order.OriginalAmount, + RemainingAmount: order.RemainingAmount, + ID: fmt.Sprintf("%v", order.OrderID), + ExecutedAmount: order.ExecutedAmount, + Exchange: g.Name, + OrderType: orderType, + OrderSide: side, + Price: order.Price, + CurrencyPair: symbol, + OrderDate: orderDate, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (g *Gemini) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("Currency must be supplied") + } + + var trades []TradeHistory + for _, currency := range getOrdersRequest.Currencies { + resp, err := g.GetTradeHistory(exchange.FormatExchangeCurrency(g.Name, currency).String(), getOrdersRequest.StartTicks.Unix()) + if err != nil { + return nil, err + } + + for _, trade := range resp { + trade.BaseCurrency = currency.FirstCurrency.String() + trade.QuoteCurrency = currency.SecondCurrency.String() + trades = append(trades, trade) + } + } + + var orders []exchange.OrderDetail + for _, trade := range trades { + side := exchange.OrderSide(strings.ToUpper(trade.Type)) + orderDate := time.Unix(trade.Timestamp, 0) + + orders = append(orders, exchange.OrderDetail{ + Amount: trade.Amount, + ID: fmt.Sprintf("%v", trade.OrderID), + Exchange: g.Name, + OrderDate: orderDate, + OrderSide: side, + Fee: trade.FeeAmount, + Price: trade.Price, + CurrencyPair: pair.NewCurrencyPairWithDelimiter(trade.BaseCurrency, trade.QuoteCurrency, g.ConfigCurrencyPairFormat.Delimiter), + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/hitbtc/hitbtc.go b/exchanges/hitbtc/hitbtc.go index 360cba4b..1961fd4c 100644 --- a/exchanges/hitbtc/hitbtc.go +++ b/exchanges/hitbtc/hitbtc.go @@ -35,6 +35,8 @@ const ( apiV2CryptoAddress = "api/2/account/crypto/address" apiV2CryptoWithdraw = "api/2/account/crypto/withdraw" apiV2TradeHistory = "api/2/history/trades" + apiV2OrderHistory = "api/2/history/order" + apiv2OpenOrders = "api/2/order" apiV2FeeInfo = "api/2/trading/fee" orders = "order" orderBuy = "api/2/order" @@ -387,8 +389,8 @@ func (h *HitBTC) GetActiveorders(currency string) ([]Order, error) { return resp, err } -// GetAuthenticatedTradeHistory returns your trade history -func (h *HitBTC) GetAuthenticatedTradeHistory(currency, start, end string) (interface{}, error) { +// GetTradeHistoryForCurrency returns your trade history +func (h *HitBTC) GetTradeHistoryForCurrency(currency, start, end string) (AuthenticatedTradeHistoryResponse, error) { values := url.Values{} if start != "" { @@ -399,11 +401,22 @@ func (h *HitBTC) GetAuthenticatedTradeHistory(currency, start, end string) (inte values.Set("end", end) } - if currency != "" && currency != "all" { - values.Set("currencyPair", currency) - result := AuthenticatedTradeHistoryResponse{} + values.Set("currencyPair", currency) + result := AuthenticatedTradeHistoryResponse{} - return result, h.SendAuthenticatedHTTPRequest("POST", apiV2TradeHistory, values, &result.Data) + return result, h.SendAuthenticatedHTTPRequest("POST", apiV2TradeHistory, values, &result.Data) +} + +// GetTradeHistoryForAllCurrencies returns your trade history +func (h *HitBTC) GetTradeHistoryForAllCurrencies(currency, start, end string) (AuthenticatedTradeHistoryAll, error) { + values := url.Values{} + + if start != "" { + values.Set("start", start) + } + + if end != "" { + values.Set("end", end) } values.Set("currencyPair", "all") @@ -412,6 +425,24 @@ func (h *HitBTC) GetAuthenticatedTradeHistory(currency, start, end string) (inte return result, h.SendAuthenticatedHTTPRequest("POST", apiV2TradeHistory, values, &result.Data) } +// GetOrders List of your order history. +func (h *HitBTC) GetOrders(currency string) ([]OrderHistoryResponse, error) { + values := url.Values{} + values.Set("symbol", currency) + var result []OrderHistoryResponse + + return result, h.SendAuthenticatedHTTPRequest("GET", apiV2OrderHistory, values, &result) +} + +// GetOpenOrders List of your currently open orders. +func (h *HitBTC) GetOpenOrders(currency string) ([]OrderHistoryResponse, error) { + values := url.Values{} + values.Set("symbol", currency) + var result []OrderHistoryResponse + + return result, h.SendAuthenticatedHTTPRequest("GET", apiv2OpenOrders, values, &result) +} + // PlaceOrder places an order on the exchange func (h *HitBTC) PlaceOrder(currency string, rate, amount float64, orderType, side string) (OrderResponse, error) { result := OrderResponse{} diff --git a/exchanges/hitbtc/hitbtc_test.go b/exchanges/hitbtc/hitbtc_test.go index 8776d265..d7af31b0 100644 --- a/exchanges/hitbtc/hitbtc_test.go +++ b/exchanges/hitbtc/hitbtc_test.go @@ -30,7 +30,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - HitBTC Setup() init error") } - hitbtcConfig.AuthenticatedAPISupport = true hitbtcConfig.APIKey = apiKey hitbtcConfig.APISecret = apiSecret @@ -164,17 +163,50 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange h.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := h.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + h.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.ETH, symbol.BTC)}, + } + + _, err := h.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + h.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.ETH, symbol.BTC)}, + } + + _, err := h.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -198,7 +230,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.DGD, SecondCurrency: symbol.BTC, } - response, err := h.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 10, "1234234") + response, err := h.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "1234234") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -207,7 +239,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange h.SetDefaults() TestSetup(t) @@ -224,12 +255,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := h.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -237,7 +265,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange h.SetDefaults() TestSetup(t) @@ -254,12 +281,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := h.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -293,7 +318,7 @@ func TestWithdraw(t *testing.T) { _, err := h.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/hitbtc/hitbtc_types.go b/exchanges/hitbtc/hitbtc_types.go index fb998b89..4833d6db 100644 --- a/exchanges/hitbtc/hitbtc_types.go +++ b/exchanges/hitbtc/hitbtc_types.go @@ -184,6 +184,23 @@ type AuthenticatedTradeHistoryResponse struct { Data []AuthentictedTradeHistory } +// OrderHistoryResponse used for GetOrderHistory +type OrderHistoryResponse struct { + ID string `json:"id"` + ClientOrderID string `json:"clientOrderId"` + Symbol string `json:"symbol"` + Side string `json:"side"` + Status string `json:"status"` + Type string `json:"type"` + TimeInForce string `json:"timeInForce"` + Price float64 `json:"price,string"` + Quantity float64 `json:"quantity,string"` + PostOnly bool `json:"postOnly"` + CumQuantity float64 `json:"cumQuantity,string"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} + // ResultingTrades holds resulting trade information type ResultingTrades struct { Amount float64 `json:"amount,string"` diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index 2ddbc2df..8b5a2211 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -1,9 +1,12 @@ package hitbtc import ( + "errors" "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -264,3 +267,88 @@ func (h *HitBTC) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (h *HitBTC) GetWithdrawCapabilities() uint32 { return h.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (h *HitBTC) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("Currency must be supplied") + } + + var allOrders []OrderHistoryResponse + for _, currency := range getOrdersRequest.Currencies { + resp, err := h.GetOpenOrders(currency.Pair().String()) + if err != nil { + return nil, err + } + allOrders = append(allOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, h.ConfigCurrencyPairFormat.Delimiter) + side := exchange.OrderSide(strings.ToUpper(order.Side)) + orderDate, err := time.Parse(time.RFC3339, order.CreatedAt) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + h.Name, "GetActiveOrders", order.ID, order.CreatedAt) + } + + orders = append(orders, exchange.OrderDetail{ + ID: order.ID, + Amount: order.Quantity, + Exchange: h.Name, + Price: order.Price, + OrderDate: orderDate, + OrderSide: side, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (h *HitBTC) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("Currency must be supplied") + } + + var allOrders []OrderHistoryResponse + for _, currency := range getOrdersRequest.Currencies { + resp, err := h.GetOrders(currency.Pair().String()) + if err != nil { + return nil, err + } + allOrders = append(allOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, h.ConfigCurrencyPairFormat.Delimiter) + side := exchange.OrderSide(strings.ToUpper(order.Side)) + orderDate, err := time.Parse(time.RFC3339, order.CreatedAt) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + h.Name, "GetOrderHistory", order.ID, order.CreatedAt) + } + + orders = append(orders, exchange.OrderDetail{ + ID: order.ID, + Amount: order.Quantity, + Exchange: h.Name, + Price: order.Price, + OrderDate: orderDate, + OrderSide: side, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/huobi/huobi.go b/exchanges/huobi/huobi.go index 178eccb1..bae84724 100644 --- a/exchanges/huobi/huobi.go +++ b/exchanges/huobi/huobi.go @@ -48,7 +48,7 @@ const ( huobiGetOrder = "order/orders/%s" huobiGetOrderMatch = "order/orders/%s/matchresults" huobiGetOrders = "order/orders" - huobiGetOpenOrders = "order/order/openOrders" + huobiGetOpenOrders = "order/openOrders" huobiGetOrdersMatch = "orders/matchresults" huobiMarginTransferIn = "dw/transfer-in/margin" huobiMarginTransferOut = "dw/transfer-out/margin" @@ -570,7 +570,9 @@ func (h *HUOBI) GetOpenOrders(accountID, symbol, side string, size int) ([]Order vals := url.Values{} vals.Set("symbol", symbol) vals.Set("accountID", accountID) - vals.Set("side", side) + if len(side) > 0 { + vals.Set("side", side) + } vals.Set("size", fmt.Sprintf("%v", size)) var result response diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index cc2bfe13..fdf4767f 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -66,7 +66,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - Huobi Setup() init error") } - hConfig.AuthenticatedAPISupport = true hConfig.APIKey = apiKey hConfig.APISecret = apiSecret @@ -385,17 +384,50 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange h.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := h.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + h.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.USDT)}, + } + + _, err := h.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + h.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.USDT)}, + } + + _, err := h.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -430,7 +462,7 @@ func TestSubmitOrder(t *testing.T) { t.Errorf("Failed to get accounts. Err: %s", err) } - response, err := h.SubmitOrder(p, exchange.Buy, exchange.Limit, 1, 10, strconv.FormatInt(accounts[0].ID, 10)) + response, err := h.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, strconv.FormatInt(accounts[0].ID, 10)) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -439,7 +471,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange h.SetDefaults() TestSetup(t) @@ -456,12 +487,10 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := h.CancelOrder(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -469,7 +498,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange h.SetDefaults() TestSetup(t) if areTestAPIKeysSet() && !canManipulateRealOrders { @@ -484,11 +512,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := h.CancelAllOrders(orderCancellation) - // Assert + if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -536,7 +563,7 @@ func TestWithdraw(t *testing.T) { _, err := h.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/huobi/huobi_types.go b/exchanges/huobi/huobi_types.go index b7dab784..19f49aaf 100644 --- a/exchanges/huobi/huobi_types.go +++ b/exchanges/huobi/huobi_types.go @@ -141,23 +141,26 @@ type CancelOrderBatch struct { // OrderInfo stores the order info type OrderInfo struct { - ID int `json:"id"` - Symbol string `json:"symbol"` - AccountID int `json:"account-id"` - Amount string `json:"amount"` - Price string `json:"price"` - CreatedAt int64 `json:"created-at"` - Type string `json:"type"` - FieldAmount string `json:"field-amount"` - FieldCashAmount string `json:"field-cash-amount"` - FieldFees string `json:"field-fees"` - FinishedAt int64 `json:"finished-at"` - UserID int `json:"user-id"` - Source string `json:"source"` - State string `json:"state"` - CanceledAt int `json:"canceled-at"` - Exchange string `json:"exchange"` - Batch string `json:"batch"` + ID int `json:"id"` + Symbol string `json:"symbol"` + AccountID float64 `json:"account-id"` + Amount float64 `json:"amount,string"` + Price float64 `json:"price,string"` + CreatedAt int64 `json:"created-at"` + Type string `json:"type"` + FieldAmount float64 `json:"field-amount,string"` + FieldCashAmount float64 `json:"field-cash-amount,string"` + Fieldees float64 `json:"field-fees,string"` + FilledAmount float64 `json:"filled-amount,string"` + FilledCashAmount float64 `json:"filled-cash-amount,string"` + FilledFees float64 `json:"filled-fees,string"` + FinishedAt int64 `json:"finished-at"` + UserID int `json:"user-id"` + Source string `json:"source"` + State string `json:"state"` + CanceledAt int `json:"canceled-at"` + Exchange string `json:"exchange"` + Batch string `json:"batch"` } // OrderMatchInfo stores the order match info diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index ce8d2cd7..49156952 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -259,14 +261,14 @@ func (h *HUOBI) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderT AccountID: int(accountID), } - if side == exchange.Buy && orderType == exchange.Market { + if side == exchange.BuyOrderSide && orderType == exchange.MarketOrderType { formattedType = SpotNewOrderRequestTypeBuyMarket - } else if side == exchange.Sell && orderType == exchange.Market { + } else if side == exchange.SellOrderSide && orderType == exchange.MarketOrderType { formattedType = SpotNewOrderRequestTypeSellMarket - } else if side == exchange.Buy && orderType == exchange.Limit { + } else if side == exchange.BuyOrderSide && orderType == exchange.LimitOrderType { formattedType = SpotNewOrderRequestTypeBuyLimit params.Price = price - } else if side == exchange.Sell && orderType == exchange.Limit { + } else if side == exchange.SellOrderSide && orderType == exchange.LimitOrderType { formattedType = SpotNewOrderRequestTypeSellLimit params.Price = price } else { @@ -374,3 +376,84 @@ func (h *HUOBI) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (h *HUOBI) GetWithdrawCapabilities() uint32 { return h.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (h *HUOBI) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("Currency must be supplied") + } + + side := "" + if getOrdersRequest.OrderSide == exchange.AnyOrderSide || getOrdersRequest.OrderSide == "" { + side = "" + } else if getOrdersRequest.OrderSide == exchange.SellOrderSide { + side = strings.ToLower(string(getOrdersRequest.OrderSide)) + } + + var allOrders []OrderInfo + for _, currency := range getOrdersRequest.Currencies { + resp, err := h.GetOpenOrders(h.ClientID, currency.Pair().Lower().String(), side, 500) + if err != nil { + return nil, err + } + allOrders = append(allOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, h.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(order.CreatedAt, 0) + + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.ID), + Exchange: h.Name, + Amount: order.Amount, + Price: order.Price, + OrderDate: orderDate, + ExecutedAmount: order.FilledAmount, + RemainingAmount: (order.Amount - order.FilledAmount), + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (h *HUOBI) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("Currency must be supplied") + } + + states := "partial-canceled,filled,canceled" + var allOrders []OrderInfo + for _, currency := range getOrdersRequest.Currencies { + resp, err := h.GetOrders(currency.Pair().Lower().String(), "", "", "", states, "", "", "") + if err != nil { + return nil, err + } + allOrders = append(allOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, h.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(order.CreatedAt, 0) + + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.ID), + Exchange: h.Name, + Amount: order.Amount, + Price: order.Price, + OrderDate: orderDate, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + + return orders, nil +} diff --git a/exchanges/huobihadax/huobihadax.go b/exchanges/huobihadax/huobihadax.go index cce2bfeb..8eae8c60 100644 --- a/exchanges/huobihadax/huobihadax.go +++ b/exchanges/huobihadax/huobihadax.go @@ -37,7 +37,7 @@ const ( huobihadaxAccountBalance = "account/accounts/%s/balance" huobihadaxOrderPlace = "order/orders/place" huobihadaxOrderCancel = "order/orders/%s/submitcancel" - huobihadaxGetOpenOrders = "order/order/openOrders" + huobihadaxGetOpenOrders = "order/openOrders" huobihadaxOrderCancelBatch = "order/orders/batchcancel" huobiHadaxBatchCancelOpenOrders = "order/orders/batchCancelOpenOrders" huobihadaxGetOrder = "order/orders/%s" @@ -497,7 +497,9 @@ func (h *HUOBIHADAX) GetOpenOrders(accountID, symbol, side string, size int) ([] vals := url.Values{} vals.Set("symbol", symbol) vals.Set("accountID", accountID) - vals.Set("side", side) + if len(side) > 0 { + vals.Set("side", side) + } vals.Set("size", fmt.Sprintf("%v", size)) var result response diff --git a/exchanges/huobihadax/huobihadax_test.go b/exchanges/huobihadax/huobihadax_test.go index a002c17b..a3ce08fb 100644 --- a/exchanges/huobihadax/huobihadax_test.go +++ b/exchanges/huobihadax/huobihadax_test.go @@ -62,7 +62,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - HuobiHadax Setup() init error") } - hadaxConfig.AuthenticatedAPISupport = true hadaxConfig.APIKey = apiKey hadaxConfig.APISecret = apiSecret @@ -365,17 +364,50 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange h.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := h.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + h.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.USDT)}, + } + + _, err := h.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + h.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.USDT)}, + } + + _, err := h.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -410,7 +442,7 @@ func TestSubmitOrder(t *testing.T) { t.Errorf("Failed to get accounts. Err: %s", err) } - response, err := h.SubmitOrder(p, exchange.Buy, exchange.Limit, 1, 10, strconv.FormatInt(accounts[0].ID, 10)) + response, err := h.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, strconv.FormatInt(accounts[0].ID, 10)) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -419,8 +451,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange - h.SetDefaults() TestSetup(t) @@ -437,12 +467,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := h.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -450,7 +477,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange h.SetDefaults() TestSetup(t) @@ -467,12 +493,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := h.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -520,7 +544,7 @@ func TestWithdraw(t *testing.T) { _, err := h.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/huobihadax/huobihadax_types.go b/exchanges/huobihadax/huobihadax_types.go index 85a24d6f..8bc591c5 100644 --- a/exchanges/huobihadax/huobihadax_types.go +++ b/exchanges/huobihadax/huobihadax_types.go @@ -123,23 +123,26 @@ type CancelOrderBatch struct { // OrderInfo stores the order info type OrderInfo struct { - ID int `json:"id"` - Symbol string `json:"symbol"` - AccountID int `json:"account-id"` - Amount string `json:"amount"` - Price string `json:"price"` - CreatedAt int64 `json:"created-at"` - Type string `json:"type"` - FieldAmount string `json:"field-amount"` - FieldCashAmount string `json:"field-cash-amount"` - FieldFees string `json:"field-fees"` - FinishedAt int64 `json:"finished-at"` - UserID int `json:"user-id"` - Source string `json:"source"` - State string `json:"state"` - CanceledAt int `json:"canceled-at"` - Exchange string `json:"exchange"` - Batch string `json:"batch"` + ID int `json:"id"` + Symbol string `json:"symbol"` + AccountID float64 `json:"account-id"` + Amount float64 `json:"amount,string"` + Price float64 `json:"price,string"` + CreatedAt int64 `json:"created-at"` + Type string `json:"type"` + FieldAmount float64 `json:"field-amount,string"` + FieldCashAmount float64 `json:"field-cash-amount,string"` + Fieldees float64 `json:"field-fees,string"` + FilledAmount float64 `json:"filled-amount,string"` + FilledCashAmount float64 `json:"filled-cash-amount,string"` + FilledFees float64 `json:"filled-fees,string"` + FinishedAt int64 `json:"finished-at"` + UserID int `json:"user-id"` + Source string `json:"source"` + State string `json:"state"` + CanceledAt int `json:"canceled-at"` + Exchange string `json:"exchange"` + Batch string `json:"batch"` } // OrderMatchInfo stores the order match info diff --git a/exchanges/huobihadax/huobihadax_wrapper.go b/exchanges/huobihadax/huobihadax_wrapper.go index 808cd00b..364b0140 100644 --- a/exchanges/huobihadax/huobihadax_wrapper.go +++ b/exchanges/huobihadax/huobihadax_wrapper.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -224,14 +226,14 @@ func (h *HUOBIHADAX) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, o AccountID: int(accountID), } - if side == exchange.Buy && orderType == exchange.Market { + if side == exchange.BuyOrderSide && orderType == exchange.MarketOrderType { formattedType = SpotNewOrderRequestTypeBuyMarket - } else if side == exchange.Sell && orderType == exchange.Market { + } else if side == exchange.SellOrderSide && orderType == exchange.MarketOrderType { formattedType = SpotNewOrderRequestTypeSellMarket - } else if side == exchange.Buy && orderType == exchange.Limit { + } else if side == exchange.BuyOrderSide && orderType == exchange.LimitOrderType { formattedType = SpotNewOrderRequestTypeBuyLimit params.Price = price - } else if side == exchange.Sell && orderType == exchange.Limit { + } else if side == exchange.SellOrderSide && orderType == exchange.LimitOrderType { formattedType = SpotNewOrderRequestTypeSellLimit params.Price = price } else { @@ -339,3 +341,84 @@ func (h *HUOBIHADAX) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, erro func (h *HUOBIHADAX) GetWithdrawCapabilities() uint32 { return h.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (h *HUOBIHADAX) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("Currency must be supplied") + } + + side := "" + if getOrdersRequest.OrderSide == exchange.AnyOrderSide || getOrdersRequest.OrderSide == "" { + side = "" + } else if getOrdersRequest.OrderSide == exchange.SellOrderSide { + side = strings.ToLower(string(getOrdersRequest.OrderSide)) + } + + var allOrders []OrderInfo + for _, currency := range getOrdersRequest.Currencies { + resp, err := h.GetOpenOrders(h.ClientID, currency.Pair().Lower().String(), side, 500) + if err != nil { + return nil, err + } + allOrders = append(allOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, h.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(order.CreatedAt, 0) + + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.ID), + Exchange: h.Name, + Amount: order.Amount, + Price: order.Price, + OrderDate: orderDate, + ExecutedAmount: order.FilledAmount, + RemainingAmount: (order.Amount - order.FilledAmount), + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (h *HUOBIHADAX) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("Currency must be supplied") + } + + states := "partial-canceled,filled,canceled" + var allOrders []OrderInfo + for _, currency := range getOrdersRequest.Currencies { + resp, err := h.GetOrders(currency.Pair().Lower().String(), "", "", "", states, "", "", "") + if err != nil { + return nil, err + } + allOrders = append(allOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, h.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(order.CreatedAt, 0) + + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.ID), + Exchange: h.Name, + Amount: order.Amount, + Price: order.Price, + OrderDate: orderDate, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + + return orders, nil +} diff --git a/exchanges/itbit/itbit_test.go b/exchanges/itbit/itbit_test.go index 8a01e239..828fa288 100644 --- a/exchanges/itbit/itbit_test.go +++ b/exchanges/itbit/itbit_test.go @@ -32,7 +32,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - Gemini Setup() init error") } - itbitConfig.AuthenticatedAPISupport = true itbitConfig.APIKey = apiKey itbitConfig.APISecret = apiSecret @@ -234,17 +233,48 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange i.SetDefaults() expectedResult := exchange.WithdrawCryptoViaWebsiteOnlyText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - // Act + withdrawPermissions := i.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + i.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := i.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + i.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := i.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -267,7 +297,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.USDT, } - response, err := i.SubmitOrder(p, exchange.Buy, exchange.Limit, 1, 10, "hi") + response, err := i.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -276,7 +306,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange i.SetDefaults() TestSetup(t) @@ -293,12 +322,10 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := i.CancelOrder(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -306,7 +333,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange i.SetDefaults() TestSetup(t) @@ -323,12 +349,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := i.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index 18ecdfbc..a2737977 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -4,7 +4,9 @@ import ( "fmt" "net/url" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -280,3 +282,98 @@ func (i *ItBit) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (i *ItBit) GetWithdrawCapabilities() uint32 { return i.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (i *ItBit) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + wallets, err := i.GetWallets(url.Values{}) + if err != nil { + return nil, err + } + + var allOrders []Order + for _, wallet := range wallets { + resp, err := i.GetOrders(wallet.ID, "", "open", 0, 0) + if err != nil { + return nil, err + } + allOrders = append(allOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Instrument, i.ConfigCurrencyPairFormat.Delimiter) + side := exchange.OrderSide(strings.ToUpper(order.Side)) + orderDate, err := time.Parse(time.RFC3339, order.CreatedTime) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + i.Name, "GetActiveOrders", order.ID, order.CreatedTime) + } + + orders = append(orders, exchange.OrderDetail{ + ID: order.ID, + OrderSide: side, + Amount: order.Amount, + ExecutedAmount: order.AmountFilled, + RemainingAmount: (order.Amount - order.AmountFilled), + Exchange: i.Name, + OrderDate: orderDate, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (i *ItBit) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + wallets, err := i.GetWallets(url.Values{}) + if err != nil { + return nil, err + } + + var allOrders []Order + for _, wallet := range wallets { + resp, err := i.GetOrders(wallet.ID, "", "", 0, 0) + if err != nil { + return nil, err + } + allOrders = append(allOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + if order.Type == "open" { + continue + } + + symbol := pair.NewCurrencyPairDelimiter(order.Instrument, i.ConfigCurrencyPairFormat.Delimiter) + side := exchange.OrderSide(strings.ToUpper(order.Side)) + orderDate, err := time.Parse(time.RFC3339, order.CreatedTime) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + i.Name, "GetActiveOrders", order.ID, order.CreatedTime) + } + + orders = append(orders, exchange.OrderDetail{ + ID: order.ID, + OrderSide: side, + Amount: order.Amount, + ExecutedAmount: order.AmountFilled, + RemainingAmount: (order.Amount - order.AmountFilled), + Exchange: i.Name, + OrderDate: orderDate, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} diff --git a/exchanges/kraken/kraken.go b/exchanges/kraken/kraken.go index 032ce552..6b60109d 100644 --- a/exchanges/kraken/kraken.go +++ b/exchanges/kraken/kraken.go @@ -540,15 +540,15 @@ func (k *Kraken) GetTradeBalance(args ...TradeBalanceOptions) (TradeBalanceInfo, } // GetOpenOrders returns all current open orders -func (k *Kraken) GetOpenOrders(args ...OrderInfoOptions) (OpenOrders, error) { +func (k *Kraken) GetOpenOrders(args OrderInfoOptions) (OpenOrders, error) { params := url.Values{} - if args[0].Trades { + if args.Trades { params.Set("trades", "true") } - if args[0].UserRef != 0 { - params.Set("userref", strconv.FormatInt(int64(args[0].UserRef), 10)) + if args.UserRef != 0 { + params.Set("userref", strconv.FormatInt(int64(args.UserRef), 10)) } var response struct { @@ -564,33 +564,31 @@ func (k *Kraken) GetOpenOrders(args ...OrderInfoOptions) (OpenOrders, error) { } // GetClosedOrders returns a list of closed orders -func (k *Kraken) GetClosedOrders(args ...GetClosedOrdersOptions) (ClosedOrders, error) { +func (k *Kraken) GetClosedOrders(args GetClosedOrdersOptions) (ClosedOrders, error) { params := url.Values{} - if args != nil { - if args[0].Trades { - params.Set("trades", "true") - } + if args.Trades { + params.Set("trades", "true") + } - if args[0].UserRef != 0 { - params.Set("userref", strconv.FormatInt(int64(args[0].UserRef), 10)) - } + if args.UserRef != 0 { + params.Set("userref", strconv.FormatInt(int64(args.UserRef), 10)) + } - if len(args[0].Start) != 0 { - params.Set("start", args[0].Start) - } + if len(args.Start) != 0 { + params.Set("start", args.Start) + } - if len(args[0].End) != 0 { - params.Set("end", args[0].End) - } + if len(args.End) != 0 { + params.Set("end", args.End) + } - if args[0].Ofs != 0 { - params.Set("ofs", strconv.FormatInt(args[0].Ofs, 10)) - } + if args.Ofs != 0 { + params.Set("ofs", strconv.FormatInt(args.Ofs, 10)) + } - if len(args[0].CloseTime) != 0 { - params.Set("closetime", args[0].CloseTime) - } + if len(args.CloseTime) != 0 { + params.Set("closetime", args.CloseTime) } var response struct { diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index fc2531ef..0c7e8791 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -30,7 +30,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - kraken Setup() init error", err) } - krakenConfig.AuthenticatedAPISupport = true krakenConfig.APIKey = apiKey krakenConfig.APISecret = apiSecret @@ -319,17 +318,48 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange k.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawCryptoWith2FAText + " & " + exchange.AutoWithdrawFiatWithSetupText + " & " + exchange.WithdrawFiatWith2FAText - // Act + withdrawPermissions := k.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + k.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := k.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + k.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := k.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -353,7 +383,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.XBT, SecondCurrency: symbol.CAD, } - response, err := k.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 10, "hi") + response, err := k.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -362,7 +392,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange k.SetDefaults() TestSetup(t) @@ -379,12 +408,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := k.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -392,7 +418,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange k.SetDefaults() TestSetup(t) @@ -409,12 +434,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := k.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -463,7 +486,7 @@ func TestWithdraw(t *testing.T) { _, err := k.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -488,7 +511,7 @@ func TestWithdrawFiat(t *testing.T) { _, err := k.WithdrawFiatFunds(withdrawFiatRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) @@ -513,7 +536,7 @@ func TestWithdrawInternationalBank(t *testing.T) { _, err := k.WithdrawFiatFundsToInternationalBank(withdrawFiatRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 4439095b..5ec83791 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -2,8 +2,10 @@ package kraken import ( "errors" + "fmt" "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -290,3 +292,77 @@ func (k *Kraken) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (k *Kraken) GetWithdrawCapabilities() uint32 { return k.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (k *Kraken) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := k.GetOpenOrders(OrderInfoOptions{}) + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for ID, order := range resp.Open { + symbol := pair.NewCurrencyPairDelimiter(order.Descr.Pair, k.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(int64(order.StartTm), 0) + side := exchange.OrderSide(strings.ToUpper(order.Descr.Type)) + + orders = append(orders, exchange.OrderDetail{ + ID: ID, + Amount: order.Vol, + RemainingAmount: (order.Vol - order.VolExec), + ExecutedAmount: order.VolExec, + Exchange: k.Name, + OrderDate: orderDate, + Price: order.Price, + OrderSide: side, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (k *Kraken) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + request := GetClosedOrdersOptions{} + if getOrdersRequest.StartTicks.Unix() > 0 { + request.Start = fmt.Sprintf("%v", getOrdersRequest.StartTicks.Unix()) + } + if getOrdersRequest.EndTicks.Unix() > 0 { + request.End = fmt.Sprintf("%v", getOrdersRequest.EndTicks.Unix()) + } + + resp, err := k.GetClosedOrders(request) + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for ID, order := range resp.Closed { + symbol := pair.NewCurrencyPairDelimiter(order.Descr.Pair, k.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(int64(order.StartTm), 0) + side := exchange.OrderSide(strings.ToUpper(order.Descr.Type)) + + orders = append(orders, exchange.OrderDetail{ + ID: ID, + Amount: order.Vol, + RemainingAmount: (order.Vol - order.VolExec), + ExecutedAmount: order.VolExec, + Exchange: k.Name, + OrderDate: orderDate, + Price: order.Price, + OrderSide: side, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} diff --git a/exchanges/lakebtc/lakebtc_test.go b/exchanges/lakebtc/lakebtc_test.go index 6299ff10..9892c7db 100644 --- a/exchanges/lakebtc/lakebtc_test.go +++ b/exchanges/lakebtc/lakebtc_test.go @@ -30,7 +30,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - LakeBTC Setup() init error") } - lakebtcConfig.AuthenticatedAPISupport = true lakebtcConfig.APIKey = apiKey lakebtcConfig.APISecret = apiSecret @@ -227,17 +226,48 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange l.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - // Act + withdrawPermissions := l.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + l.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := l.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + l.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := l.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -261,7 +291,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.EUR, } - response, err := l.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 10, "hi") + response, err := l.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -270,7 +300,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange l.SetDefaults() TestSetup(t) @@ -287,12 +316,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := l.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -300,7 +326,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange l.SetDefaults() TestSetup(t) @@ -317,12 +342,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := l.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -356,7 +379,7 @@ func TestWithdraw(t *testing.T) { _, err := l.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/lakebtc/lakebtc_wrapper.go b/exchanges/lakebtc/lakebtc_wrapper.go index ae3c4cc0..194d4566 100644 --- a/exchanges/lakebtc/lakebtc_wrapper.go +++ b/exchanges/lakebtc/lakebtc_wrapper.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -150,7 +151,7 @@ func (l *LakeBTC) GetExchangeHistory(p pair.CurrencyPair, assetType string) ([]e // SubmitOrder submits a new order func (l *LakeBTC) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - isBuyOrder := side == exchange.Buy + isBuyOrder := side == exchange.BuyOrderSide response, err := l.Trade(isBuyOrder, amount, price, common.StringToLower(p.Pair().String())) if response.ID > 0 { @@ -264,3 +265,70 @@ func (l *LakeBTC) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) func (l *LakeBTC) GetWithdrawCapabilities() uint32 { return l.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (l *LakeBTC) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := l.GetOpenOrders() + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, order := range resp { + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, l.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(order.At, 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + + orders = append(orders, exchange.OrderDetail{ + Amount: order.Amount, + ID: fmt.Sprintf("%v", order.ID), + Price: order.Price, + OrderSide: side, + OrderDate: orderDate, + CurrencyPair: symbol, + Exchange: l.Name, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (l *LakeBTC) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := l.GetOrders([]int64{}) + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, order := range resp { + if order.State == "active" { + continue + } + + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, l.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(order.At, 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + + orders = append(orders, exchange.OrderDetail{ + Amount: order.Amount, + ID: fmt.Sprintf("%v", order.ID), + Price: order.Price, + OrderSide: side, + OrderDate: orderDate, + CurrencyPair: symbol, + Exchange: l.Name, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + + return orders, nil +} diff --git a/exchanges/liqui/liqui.go b/exchanges/liqui/liqui.go index be7f6ddc..9013bdba 100644 --- a/exchanges/liqui/liqui.go +++ b/exchanges/liqui/liqui.go @@ -212,8 +212,8 @@ func (l *Liqui) Trade(pair, orderType string, amount, price float64) (float64, e return result.OrderID, err } -// GetActiveOrders returns the list of your active orders. -func (l *Liqui) GetActiveOrders(pair string) (map[string]ActiveOrders, error) { +// GetOpenOrders returns the list of your active orders. +func (l *Liqui) GetOpenOrders(pair string) (map[string]ActiveOrders, error) { result := make(map[string]ActiveOrders) req := url.Values{} diff --git a/exchanges/liqui/liqui_test.go b/exchanges/liqui/liqui_test.go index ae0dc811..2081d228 100644 --- a/exchanges/liqui/liqui_test.go +++ b/exchanges/liqui/liqui_test.go @@ -30,7 +30,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - liqui Setup() init error") } - liquiConfig.AuthenticatedAPISupport = true liquiConfig.APIKey = apiKey liquiConfig.APISecret = apiSecret @@ -90,7 +89,7 @@ func TestAuthRequests(t *testing.T) { t.Error("Test Failed - liqui Trade() error", err) } - _, err = l.GetActiveOrders("eth_btc") + _, err = l.GetOpenOrders("eth_btc") if err == nil { t.Error("Test Failed - liqui GetActiveOrders() error", err) } @@ -223,17 +222,47 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange l.SetDefaults() expectedResult := exchange.WithdrawCryptoWithAPIPermissionText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := l.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + l.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.BTC, symbol.USDT)}, + } + + _, err := l.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + l.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := l.GetOrderHistory(getOrdersRequest) + if err != common.ErrFunctionNotSupported { + t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err) + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -256,7 +285,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.EUR, } - response, err := l.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 10, "hi") + response, err := l.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -265,7 +294,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange l.SetDefaults() TestSetup(t) @@ -281,11 +309,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := l.CancelOrder(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -293,7 +319,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange l.SetDefaults() TestSetup(t) @@ -309,11 +334,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := l.CancelAllOrders(orderCancellation) - // Assert + if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -347,7 +371,7 @@ func TestWithdraw(t *testing.T) { _, err := l.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/liqui/liqui_wrapper.go b/exchanges/liqui/liqui_wrapper.go index 02d06302..3ff6c157 100644 --- a/exchanges/liqui/liqui_wrapper.go +++ b/exchanges/liqui/liqui_wrapper.go @@ -1,9 +1,12 @@ package liqui import ( + "errors" "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -192,7 +195,7 @@ func (l *Liqui) CancelAllOrders(orderCancellation exchange.OrderCancellation) (e cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ OrderStatus: make(map[string]string), } - activeOrders, err := l.GetActiveOrders("") + activeOrders, err := l.GetOpenOrders("") if err != nil { return cancelAllOrdersResponse, err } @@ -260,3 +263,45 @@ func (l *Liqui) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (l *Liqui) GetWithdrawCapabilities() uint32 { return l.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (l *Liqui) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if len(getOrdersRequest.Currencies) <= 0 { + return nil, errors.New("Currency must be supplied") + } + + var orders []exchange.OrderDetail + for _, currency := range getOrdersRequest.Currencies { + resp, err := l.GetOpenOrders(exchange.FormatExchangeCurrency(l.Name, currency).String()) + if err != nil { + return nil, err + } + + for ID, order := range resp { + symbol := pair.NewCurrencyPairDelimiter(order.Pair, l.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(int64(order.TimestampCreated), 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + + orders = append(orders, exchange.OrderDetail{ + Amount: order.Amount, + ID: ID, + Price: order.Rate, + OrderSide: side, + OrderDate: orderDate, + Exchange: l.Name, + CurrencyPair: symbol, + }) + } + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (l *Liqui) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + return nil, common.ErrFunctionNotSupported +} diff --git a/exchanges/localbitcoins/localbitcoins.go b/exchanges/localbitcoins/localbitcoins.go index ff80f2e3..2b5de929 100644 --- a/exchanges/localbitcoins/localbitcoins.go +++ b/exchanges/localbitcoins/localbitcoins.go @@ -98,6 +98,8 @@ const ( localbitcoinsAuthRate = 0 localbitcoinsUnauthRate = 1 + // String response used with order status + null = "null" ) var ( diff --git a/exchanges/localbitcoins/localbitcoins_test.go b/exchanges/localbitcoins/localbitcoins_test.go index 748dd855..c69dae32 100644 --- a/exchanges/localbitcoins/localbitcoins_test.go +++ b/exchanges/localbitcoins/localbitcoins_test.go @@ -31,7 +31,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - LakeBTC Setup() init error") } - localbitcoinsConfig.AuthenticatedAPISupport = true localbitcoinsConfig.APIKey = apiKey localbitcoinsConfig.APISecret = apiSecret @@ -188,17 +187,48 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange l.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - // Act + withdrawPermissions := l.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + l.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := l.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + l.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := l.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -222,7 +252,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.EUR, } - response, err := l.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 10, "hi") + response, err := l.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -231,7 +261,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange l.SetDefaults() TestSetup(t) @@ -248,12 +277,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := l.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -261,7 +287,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange l.SetDefaults() TestSetup(t) @@ -278,12 +303,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := l.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -317,7 +340,7 @@ func TestWithdraw(t *testing.T) { _, err := l.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 24b7cf0a..6e7e4c7b 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/currency/symbol" @@ -293,3 +294,108 @@ func (l *LocalBitcoins) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, e func (l *LocalBitcoins) GetWithdrawCapabilities() uint32 { return l.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (l *LocalBitcoins) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := l.GetDashboardInfo() + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for _, trade := range resp { + orderDate, err := time.Parse(time.RFC3339, trade.Data.CreatedAt) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + l.Name, "GetActiveOrders", trade.Data.Advertisement.ID, trade.Data.CreatedAt) + } + + var side exchange.OrderSide + if trade.Data.IsBuying { + side = exchange.BuyOrderSide + } else if trade.Data.IsSelling { + side = exchange.SellOrderSide + } + + orders = append(orders, exchange.OrderDetail{ + Amount: trade.Data.AmountBTC, + Price: trade.Data.Amount, + ID: fmt.Sprintf("%v", trade.Data.Advertisement.ID), + OrderDate: orderDate, + Fee: trade.Data.FeeBTC, + OrderSide: side, + CurrencyPair: pair.NewCurrencyPairWithDelimiter(symbol.BTC, trade.Data.Currency, l.ConfigCurrencyPairFormat.Delimiter), + Exchange: l.Name, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (l *LocalBitcoins) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var allTrades []DashBoardInfo + resp, err := l.GetDashboardCancelledTrades() + if err != nil { + return nil, err + } + allTrades = append(allTrades, resp...) + + resp, err = l.GetDashboardClosedTrades() + if err != nil { + return nil, err + } + allTrades = append(allTrades, resp...) + + resp, err = l.GetDashboardReleasedTrades() + if err != nil { + return nil, err + } + allTrades = append(allTrades, resp...) + + var orders []exchange.OrderDetail + for _, trade := range allTrades { + orderDate, err := time.Parse(time.RFC3339, trade.Data.CreatedAt) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + l.Name, "GetActiveOrders", trade.Data.Advertisement.ID, trade.Data.CreatedAt) + } + + var side exchange.OrderSide + if trade.Data.IsBuying { + side = exchange.BuyOrderSide + } else if trade.Data.IsSelling { + side = exchange.SellOrderSide + } + + status := "" + if trade.Data.ReleasedAt != "" && trade.Data.ReleasedAt != null { + status = "Released" + } else if trade.Data.CanceledAt != "" && trade.Data.CanceledAt != null { + status = "Cancelled" + } else if trade.Data.ClosedAt != "" && trade.Data.ClosedAt != null { + status = "Closed" + } + + orders = append(orders, exchange.OrderDetail{ + Amount: trade.Data.AmountBTC, + Price: trade.Data.Amount, + ID: fmt.Sprintf("%v", trade.Data.Advertisement.ID), + OrderDate: orderDate, + Fee: trade.Data.FeeBTC, + OrderSide: side, + Status: status, + CurrencyPair: pair.NewCurrencyPairWithDelimiter(symbol.BTC, trade.Data.Currency, l.ConfigCurrencyPairFormat.Delimiter), + Exchange: l.Name, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/okcoin/okcoin.go b/exchanges/okcoin/okcoin.go index 2b7e7714..10be8d25 100644 --- a/exchanges/okcoin/okcoin.go +++ b/exchanges/okcoin/okcoin.go @@ -533,11 +533,11 @@ func (o *OKCoin) GetOrderInfoBatch(orderID []int64, symbol string) ([]OrderInfo, return result.Orders, nil } -// GetOrderHistory returns a history of orders -func (o *OKCoin) GetOrderHistory(pageLength, currentPage int64, status, symbol string) (OrderHistory, error) { +// GetOrderHistoryForCurrency returns a history of orders +func (o *OKCoin) GetOrderHistoryForCurrency(pageLength, currentPage, status int64, symbol string) (OrderHistory, error) { v := url.Values{} v.Set("symbol", symbol) - v.Set("status", status) + v.Set("status", strconv.FormatInt(status, 10)) v.Set("current_page", strconv.FormatInt(currentPage, 10)) v.Set("page_length", strconv.FormatInt(pageLength, 10)) result := OrderHistory{} diff --git a/exchanges/okcoin/okcoin_test.go b/exchanges/okcoin/okcoin_test.go index 73d1c5fa..c7bff661 100644 --- a/exchanges/okcoin/okcoin_test.go +++ b/exchanges/okcoin/okcoin_test.go @@ -31,7 +31,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - OKCoin Setup() init error") } - okcoinConfig.AuthenticatedAPISupport = true okcoinConfig.APIKey = apiKey okcoinConfig.APISecret = apiSecret @@ -138,17 +137,50 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange o.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - // Act + withdrawPermissions := o.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + o.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := o.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + o.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := o.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -172,7 +204,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.EUR, } - response, err := o.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 10, "hi") + response, err := o.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -181,7 +213,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange o.SetDefaults() TestSetup(t) if areTestAPIKeysSet() && !canManipulateRealOrders { @@ -197,11 +228,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := o.CancelOrder(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -209,7 +238,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange o.SetDefaults() TestSetup(t) @@ -226,12 +254,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := o.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -267,7 +293,7 @@ func TestWithdraw(t *testing.T) { _, err := o.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go index 9801c3f0..0cb5a066 100644 --- a/exchanges/okcoin/okcoin_wrapper.go +++ b/exchanges/okcoin/okcoin_wrapper.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -202,14 +204,14 @@ func (o *OKCoin) GetExchangeHistory(p pair.CurrencyPair, assetType string) ([]ex func (o *OKCoin) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse var oT string - if orderType == exchange.Limit { - if side == exchange.Buy { + if orderType == exchange.LimitOrderType { + if side == exchange.BuyOrderSide { oT = "buy" } else { oT = "sell" } - } else if orderType == exchange.Market { - if side == exchange.Buy { + } else if orderType == exchange.MarketOrderType { + if side == exchange.BuyOrderSide { oT = "buy_market" } else { oT = "sell_market" @@ -329,3 +331,75 @@ func (o *OKCoin) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (o *OKCoin) GetWithdrawCapabilities() uint32 { return o.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (o *OKCoin) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var allOrders []OrderInfo + for _, currency := range getOrdersRequest.Currencies { + resp, err := o.GetOrderHistoryForCurrency(200, 0, 0, exchange.FormatExchangeCurrency(o.Name, currency).String()) + if err != nil { + return nil, err + } + + allOrders = append(allOrders, resp.Orders...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + // Status 2 == Filled, -1 == Cancelled. + if order.Status == 2 || order.Status == -1 { + continue + } + + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, o.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(order.Created, 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.OrderID), + Amount: order.Amount, + OrderDate: orderDate, + Price: order.Price, + OrderSide: side, + CurrencyPair: symbol, + Exchange: o.Name, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (o *OKCoin) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var allOrders []OrderInfo + for _, currency := range getOrdersRequest.Currencies { + resp, err := o.GetOrderInformation(-1, exchange.FormatExchangeCurrency(o.Name, currency).String()) + if err != nil { + return nil, err + } + allOrders = append(allOrders, resp...) + } + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, o.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(order.Created, 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.OrderID), + Amount: order.Amount, + OrderDate: orderDate, + Price: order.Price, + OrderSide: side, + CurrencyPair: symbol, + Exchange: o.Name, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/okex/okex.go b/exchanges/okex/okex.go index 7c97f113..91f172e5 100644 --- a/exchanges/okex/okex.go +++ b/exchanges/okex/okex.go @@ -64,7 +64,8 @@ const ( spotTrade = "trade" spotBatchTrade = "batch_trade" spotCancelTrade = "cancel_order" - spotOrderInfo = "order_info" + spotOrderInfo = "order_info.do" + spotOrderHistory = "order_history.do" spotMultiOrderInfo = "orders_info" spotWithdraw = "withdraw.do" spotCancelWithdraw = "cancel_withdraw" @@ -1256,3 +1257,44 @@ func (o *OKEX) Withdrawal(symbol string, fee float64, tradePWD, address string, return resp.WithdrawID, nil } + +// GetOrderInformation withdraws a cryptocurrency to a supplied address +func (o *OKEX) GetOrderInformation(orderID int64, symbol string) ([]OrderInfo, error) { + type Response struct { + Result bool `json:"result"` + Orders []OrderInfo `json:"orders"` + } + + v := url.Values{} + v.Set("symbol", symbol) + v.Set("order_id", strconv.FormatInt(orderID, 10)) + result := Response{} + + err := o.SendAuthenticatedHTTPRequest(spotOrderInfo, v, &result) + if err != nil { + return nil, err + } + + if !result.Result { + return nil, errors.New("unable to retrieve order info") + } + + return result.Orders, nil +} + +// GetOrderHistoryForCurrency returns a history of orders +func (o *OKEX) GetOrderHistoryForCurrency(pageLength, currentPage, status int64, symbol string) (OrderHistory, error) { + v := url.Values{} + v.Set("symbol", symbol) + v.Set("status", strconv.FormatInt(status, 10)) + v.Set("current_page", strconv.FormatInt(currentPage, 10)) + v.Set("page_length", strconv.FormatInt(pageLength, 10)) + result := OrderHistory{} + + err := o.SendAuthenticatedHTTPRequest(spotOrderHistory, v, &result) + if err != nil { + return result, err + } + + return result, nil +} diff --git a/exchanges/okex/okex_test.go b/exchanges/okex/okex_test.go index b54f58f9..2eb9efef 100644 --- a/exchanges/okex/okex_test.go +++ b/exchanges/okex/okex_test.go @@ -33,7 +33,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - Okex Setup() init error") } - okexConfig.AuthenticatedAPISupport = true okexConfig.APIKey = apiKey okexConfig.APISecret = apiSecret @@ -387,17 +386,50 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange o.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := o.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + o.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := o.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + o.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := o.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -421,7 +453,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.EUR, } - response, err := o.SubmitOrder(p, exchange.Buy, exchange.Market, 1, 10, "hi") + response, err := o.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -430,7 +462,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange o.SetDefaults() TestSetup(t) @@ -447,12 +478,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := o.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -460,7 +488,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange o.SetDefaults() TestSetup(t) @@ -477,12 +504,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := o.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -532,7 +557,7 @@ func TestWithdraw(t *testing.T) { _, err := o.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/okex/okex_types.go b/exchanges/okex/okex_types.go index 197644e7..9f06b244 100644 --- a/exchanges/okex/okex_types.go +++ b/exchanges/okex/okex_types.go @@ -469,3 +469,26 @@ type WithdrawalResponse struct { WithdrawID int `json:"withdraw_id"` Result bool `json:"result"` } + +// OrderInfo holds data on an order +type OrderInfo struct { + Amount float64 `json:"amount"` + AvgPrice float64 `json:"avg_price"` + Created int64 `json:"create_date"` + DealAmount float64 `json:"deal_amount"` + OrderID int64 `json:"order_id"` + OrdersID int64 `json:"orders_id"` + Price float64 `json:"price"` + Status int `json:"status"` + Symbol string `json:"symbol"` + Type string `json:"type"` +} + +// OrderHistory holds information on order history +type OrderHistory struct { + CurrentPage int `json:"current_page"` + Orders []OrderInfo `json:"orders"` + PageLength int `json:"page_length"` + Result bool `json:"result"` + Total int `json:"total"` +} diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index b29b1a31..055d706d 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -194,14 +196,14 @@ func (o *OKEX) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderTy var submitOrderResponse exchange.SubmitOrderResponse var oT SpotNewOrderRequestType - if orderType == exchange.Limit { - if side == exchange.Buy { + if orderType == exchange.LimitOrderType { + if side == exchange.BuyOrderSide { oT = SpotNewOrderRequestTypeBuy } else { oT = SpotNewOrderRequestTypeSell } - } else if orderType == exchange.Market { - if side == exchange.Buy { + } else if orderType == exchange.MarketOrderType { + if side == exchange.BuyOrderSide { oT = SpotNewOrderRequestTypeBuyMarket } else { oT = SpotNewOrderRequestTypeSellMarket @@ -322,3 +324,77 @@ func (o *OKEX) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (o *OKEX) GetWithdrawCapabilities() uint32 { return o.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (o *OKEX) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var allOrders []OrderInfo + for _, currency := range getOrdersRequest.Currencies { + resp, err := o.GetOrderHistoryForCurrency(200, 0, 0, exchange.FormatExchangeCurrency(o.Name, currency).String()) + if err != nil { + return nil, err + } + + allOrders = append(allOrders, resp.Orders...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + // Status 2 == Filled, -1 == Cancelled. + if order.Status == 2 || order.Status == -1 { + continue + } + + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, o.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(order.Created, 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.OrderID), + Amount: order.Amount, + OrderDate: orderDate, + Price: order.Price, + OrderSide: side, + CurrencyPair: symbol, + Exchange: o.Name, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (o *OKEX) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var allOrders []OrderInfo + for _, currency := range getOrdersRequest.Currencies { + resp, err := o.GetOrderInformation(-1, exchange.FormatExchangeCurrency(o.Name, currency).String()) + if err != nil { + return nil, err + } + + allOrders = append(allOrders, resp...) + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Symbol, o.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(order.Created, 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.OrderID), + Amount: order.Amount, + OrderDate: orderDate, + Price: order.Price, + OrderSide: side, + CurrencyPair: symbol, + Exchange: o.Name, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/poloniex/poloniex.go b/exchanges/poloniex/poloniex.go index c329fdb4..687f450f 100644 --- a/exchanges/poloniex/poloniex.go +++ b/exchanges/poloniex/poloniex.go @@ -456,35 +456,24 @@ func (p *Poloniex) GetOpenOrdersForAllCurrencies() (OpenOrdersResponseAll, error return result, nil } -// GetAuthenticatedTradeHistory returns account trade history -func (p *Poloniex) GetAuthenticatedTradeHistory(currency, start, end, limit string) (interface{}, error) { +// GetAuthenticatedTradeHistoryForCurrency returns account trade history +func (p *Poloniex) GetAuthenticatedTradeHistoryForCurrency(currency string, start, end, limit int64) (AuthenticatedTradeHistoryResponse, error) { values := url.Values{} - if start != "" { - values.Set("start", start) + if start > 0 { + values.Set("start", strconv.FormatInt(start, 10)) } - if limit != "" { - values.Set("limit", limit) + if limit > 0 { + values.Set("limit", strconv.FormatInt(limit, 10)) } - if end != "" { - values.Set("end", end) + if end > 0 { + values.Set("end", strconv.FormatInt(end, 10)) } - if currency != "" && currency != "all" { - values.Set("currencyPair", currency) - result := AuthenticatedTradeHistoryResponse{} - - err := p.SendAuthenticatedHTTPRequest("POST", poloniexTradeHistory, values, &result.Data) - if err != nil { - return result, err - } - - return result, nil - } - values.Set("currencyPair", "all") - result := AuthenticatedTradeHistoryAll{} + values.Set("currencyPair", currency) + result := AuthenticatedTradeHistoryResponse{} err := p.SendAuthenticatedHTTPRequest("POST", poloniexTradeHistory, values, &result.Data) if err != nil { @@ -494,6 +483,39 @@ func (p *Poloniex) GetAuthenticatedTradeHistory(currency, start, end, limit stri return result, nil } +// GetAuthenticatedTradeHistory returns account trade history +func (p *Poloniex) GetAuthenticatedTradeHistory(start, end, limit int64) (AuthenticatedTradeHistoryAll, error) { + values := url.Values{} + + if start > 0 { + values.Set("start", strconv.FormatInt(start, 10)) + } + + if limit > 0 { + values.Set("limit", strconv.FormatInt(limit, 10)) + } + + if end > 0 { + values.Set("end", strconv.FormatInt(end, 10)) + } + + values.Set("currencyPair", "all") + var result interface{} + + err := p.SendAuthenticatedHTTPRequest("POST", poloniexTradeHistory, values, &result) + if err != nil { + return AuthenticatedTradeHistoryAll{}, err + } + + // If there are no orders, Poloniex returns an empty array + switch r := result.(type) { + case AuthenticatedTradeHistoryAll: + return r, nil + default: + return AuthenticatedTradeHistoryAll{}, nil + } +} + // PlaceOrder places a new order on the exchange func (p *Poloniex) PlaceOrder(currency string, rate, amount float64, immediate, fillOrKill, buy bool) (OrderResponse, error) { result := OrderResponse{} diff --git a/exchanges/poloniex/poloniex_test.go b/exchanges/poloniex/poloniex_test.go index dea71b89..8d92ec75 100644 --- a/exchanges/poloniex/poloniex_test.go +++ b/exchanges/poloniex/poloniex_test.go @@ -31,7 +31,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - Poloniex Setup() init error") } - poloniexConfig.AuthenticatedAPISupport = true poloniexConfig.APIKey = apiKey poloniexConfig.APISecret = apiSecret @@ -183,17 +182,48 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange p.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := p.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + p.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := p.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + p.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + } + + _, err := p.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -217,7 +247,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.LTC, } - response, err := p.SubmitOrder(pair, exchange.Buy, exchange.Market, 1, 10, "hi") + response, err := p.SubmitOrder(pair, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -226,7 +256,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange p.SetDefaults() TestSetup(t) @@ -243,12 +272,10 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := p.CancelOrder(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -256,7 +283,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange p.SetDefaults() TestSetup(t) @@ -273,12 +299,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := p.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -312,7 +336,7 @@ func TestWithdraw(t *testing.T) { _, err := p.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index 7bc8ea78..9a7fc0d2 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -3,7 +3,9 @@ package poloniex import ( "fmt" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -164,8 +166,8 @@ func (p *Poloniex) GetExchangeHistory(currencyPair pair.CurrencyPair, assetType // SubmitOrder submits a new order func (p *Poloniex) SubmitOrder(currencyPair pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - fillOrKill := orderType == exchange.Market - isBuyOrder := side == exchange.Buy + fillOrKill := orderType == exchange.MarketOrderType + isBuyOrder := side == exchange.BuyOrderSide response, err := p.PlaceOrder(currencyPair.Pair().String(), price, amount, false, fillOrKill, isBuyOrder) if response.OrderNumber > 0 { @@ -289,3 +291,79 @@ func (p *Poloniex) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) func (p *Poloniex) GetWithdrawCapabilities() uint32 { return p.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (p *Poloniex) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := p.GetOpenOrdersForAllCurrencies() + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for currencyPair, openOrders := range resp.Data { + symbol := pair.NewCurrencyPairDelimiter(currencyPair, p.ConfigCurrencyPairFormat.Delimiter) + + for _, order := range openOrders { + orderSide := exchange.OrderSide(strings.ToUpper(order.Type)) + orderDate, err := time.Parse(time.RFC3339, order.Date) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + p.Name, "GetActiveOrders", order.OrderNumber, order.Date) + } + + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.OrderNumber), + OrderSide: orderSide, + Amount: order.Amount, + OrderDate: orderDate, + Price: order.Rate, + CurrencyPair: symbol, + Exchange: p.Name, + }) + } + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (p *Poloniex) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + resp, err := p.GetAuthenticatedTradeHistory(getOrdersRequest.StartTicks.Unix(), getOrdersRequest.EndTicks.Unix(), 10000) + if err != nil { + return nil, err + } + + var orders []exchange.OrderDetail + for currencyPair, historicOrders := range resp.Data { + symbol := pair.NewCurrencyPairDelimiter(currencyPair, p.ConfigCurrencyPairFormat.Delimiter) + + for _, order := range historicOrders { + orderSide := exchange.OrderSide(strings.ToUpper(order.Type)) + orderDate, err := time.Parse(time.RFC3339, order.Date) + if err != nil { + log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v", + p.Name, "GetActiveOrders", order.OrderNumber, order.Date) + } + + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.GlobalTradeID), + OrderSide: orderSide, + Amount: order.Amount, + OrderDate: orderDate, + Price: order.Rate, + CurrencyPair: symbol, + Exchange: p.Name, + }) + } + } + + exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/wex/wex.go b/exchanges/wex/wex.go index 65f63ba6..6e1db9dd 100644 --- a/exchanges/wex/wex.go +++ b/exchanges/wex/wex.go @@ -189,8 +189,8 @@ func (w *WEX) GetAccountInformation() (AccountInfo, error) { return result, nil } -// GetActiveOrders returns the active orders for a specific currency -func (w *WEX) GetActiveOrders(pair string) (map[string]ActiveOrders, error) { +// GetOpenOrders returns the active orders for a specific currency +func (w *WEX) GetOpenOrders(pair string) (map[string]ActiveOrders, error) { req := url.Values{} req.Add("pair", pair) @@ -266,20 +266,20 @@ func (w *WEX) GetTransactionHistory(TIDFrom, Count, TIDEnd int64, order, since, } // GetTradeHistory returns the trade history -func (w *WEX) GetTradeHistory(TIDFrom, Count, TIDEnd int64, order, since, end, pair string) (map[string]TradeHistory, error) { +func (w *WEX) GetTradeHistory(TIDFrom, Count, TIDEnd, since, end int64, order, pair string) (map[string]TradeHistory, error) { req := url.Values{} req.Add("from", strconv.FormatInt(TIDFrom, 10)) req.Add("count", strconv.FormatInt(Count, 10)) req.Add("from_id", strconv.FormatInt(TIDFrom, 10)) req.Add("end_id", strconv.FormatInt(TIDEnd, 10)) req.Add("order", order) - req.Add("since", since) - req.Add("end", end) + req.Add("since", strconv.FormatInt(since, 10)) + req.Add("end", strconv.FormatInt(end, 10)) req.Add("pair", pair) - var result map[string]TradeHistory + result := TradeHistoryResponse{} - return result, w.SendAuthenticatedHTTPRequest(wexTradeHistory, req, &result) + return result.Data, w.SendAuthenticatedHTTPRequest(wexTradeHistory, req, &result) } // WithdrawCoins withdraws coins for a specific coin diff --git a/exchanges/wex/wex_test.go b/exchanges/wex/wex_test.go index 76300a9a..0c3d529b 100644 --- a/exchanges/wex/wex_test.go +++ b/exchanges/wex/wex_test.go @@ -1,7 +1,9 @@ package wex import ( + "math" "testing" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -104,14 +106,14 @@ func TestGetAccountInfo(t *testing.T) { } } -func TestGetActiveOrders(t *testing.T) { +func GetOpenOrders(t *testing.T) { if isWexEncounteringIssues { t.Skip() } t.Parallel() - _, err := w.GetActiveOrders("") + _, err := w.GetOpenOrders("") if err == nil { - t.Error("Test Failed - GetActiveOrders() error", err) + t.Error("Test Failed - GetOpenOrders() error", err) } } @@ -159,17 +161,6 @@ func TestGetTransactionHistory(t *testing.T) { } } -func TestGetTradeHistory(t *testing.T) { - if isWexEncounteringIssues { - t.Skip() - } - t.Parallel() - _, err := w.GetTradeHistory(0, 0, 0, "", "", "", "") - if err == nil { - t.Error("Test Failed - GetTradeHistory() error", err) - } -} - func TestWithdrawCoins(t *testing.T) { if isWexEncounteringIssues { t.Skip() @@ -313,17 +304,53 @@ func TestFormatWithdrawPermissions(t *testing.T) { if isWexEncounteringIssues { t.Skip() } - // Arrange + w.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := w.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + w.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := w.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + w.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + StartTicks: time.Unix(0, 0), + EndTicks: time.Unix(math.MaxInt64, 0), + } + + _, err := w.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -350,7 +377,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.USD, } - response, err := w.SubmitOrder(pair, exchange.Buy, exchange.Market, 1, 10, "hi") + response, err := w.SubmitOrder(pair, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -362,7 +389,7 @@ func TestCancelExchangeOrder(t *testing.T) { if isWexEncounteringIssues { t.Skip() } - // Arrange + w.SetDefaults() TestSetup(t) @@ -379,12 +406,10 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := w.CancelOrder(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -395,7 +420,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { if isWexEncounteringIssues { t.Skip() } - // Arrange + w.SetDefaults() TestSetup(t) @@ -412,12 +437,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := w.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -451,7 +474,7 @@ func TestWithdraw(t *testing.T) { _, err := w.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/wex/wex_types.go b/exchanges/wex/wex_types.go index 9bfc40eb..45378248 100644 --- a/exchanges/wex/wex_types.go +++ b/exchanges/wex/wex_types.go @@ -114,6 +114,12 @@ type TransHistory struct { Timestamp float64 `json:"timestamp"` } +//TradeHistoryResponse returns all your trade history +type TradeHistoryResponse struct { + Success int64 `json:"success"` + Data map[string]TradeHistory `json:"return,omitempty"` +} + // TradeHistory stores trade history type TradeHistory struct { Pair string `json:"pair"` diff --git a/exchanges/wex/wex_wrapper.go b/exchanges/wex/wex_wrapper.go index f066f7a9..c2cc4ca9 100644 --- a/exchanges/wex/wex_wrapper.go +++ b/exchanges/wex/wex_wrapper.go @@ -2,8 +2,11 @@ package wex import ( "fmt" + "math" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -205,7 +208,7 @@ func (w *WEX) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exc var allActiveOrders map[string]ActiveOrders for _, pair := range w.EnabledPairs { - activeOrders, err := w.GetActiveOrders(pair) + activeOrders, err := w.GetOpenOrders(pair) if err != nil { return cancelAllOrdersResponse, err } @@ -274,3 +277,70 @@ func (w *WEX) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (w *WEX) GetWithdrawCapabilities() uint32 { return w.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (w *WEX) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var orders []exchange.OrderDetail + for _, currency := range getOrdersRequest.Currencies { + resp, err := w.GetOpenOrders(exchange.FormatExchangeCurrency(w.Name, currency).String()) + if err != nil { + return nil, err + } + + for ID, order := range resp { + symbol := pair.NewCurrencyPairDelimiter(order.Pair, w.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(int64(order.TimestampCreated), 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + orders = append(orders, exchange.OrderDetail{ + ID: ID, + Amount: order.Amount, + Price: order.Rate, + OrderSide: side, + OrderDate: orderDate, + CurrencyPair: symbol, + Exchange: w.Name, + }) + } + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (w *WEX) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var allOrders []TradeHistory + for _, currency := range getOrdersRequest.Currencies { + resp, err := w.GetTradeHistory(0, 10000, math.MaxInt64, getOrdersRequest.StartTicks.Unix(), getOrdersRequest.EndTicks.Unix(), "DESC", exchange.FormatExchangeCurrency(w.Name, currency).String()) + if err != nil { + return nil, err + } + + for _, order := range resp { + allOrders = append(allOrders, order) + } + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Pair, w.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(int64(order.Timestamp), 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.OrderID), + Amount: order.Amount, + Price: order.Rate, + OrderSide: side, + OrderDate: orderDate, + CurrencyPair: symbol, + Exchange: w.Name, + }) + } + + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/yobit/yobit.go b/exchanges/yobit/yobit.go index f5269d61..5033777b 100644 --- a/exchanges/yobit/yobit.go +++ b/exchanges/yobit/yobit.go @@ -194,8 +194,8 @@ func (y *Yobit) Trade(pair, orderType string, amount, price float64) (int64, err return int64(result.OrderID), nil } -// GetActiveOrders returns the active orders for a specific currency -func (y *Yobit) GetActiveOrders(pair string) (map[string]ActiveOrders, error) { +// GetOpenOrders returns the active orders for a specific currency +func (y *Yobit) GetOpenOrders(pair string) (map[string]ActiveOrders, error) { req := url.Values{} req.Add("pair", pair) @@ -232,20 +232,28 @@ func (y *Yobit) CancelExistingOrder(OrderID int64) (bool, error) { } // GetTradeHistory returns the trade history -func (y *Yobit) GetTradeHistory(TIDFrom, Count, TIDEnd int64, order, since, end, pair string) (map[string]TradeHistory, error) { +func (y *Yobit) GetTradeHistory(TIDFrom, Count, TIDEnd, since, end int64, order, pair string) (map[string]TradeHistory, error) { req := url.Values{} req.Add("from", strconv.FormatInt(TIDFrom, 10)) req.Add("count", strconv.FormatInt(Count, 10)) req.Add("from_id", strconv.FormatInt(TIDFrom, 10)) req.Add("end_id", strconv.FormatInt(TIDEnd, 10)) req.Add("order", order) - req.Add("since", since) - req.Add("end", end) + req.Add("since", strconv.FormatInt(since, 10)) + req.Add("end", strconv.FormatInt(end, 10)) req.Add("pair", pair) - result := map[string]TradeHistory{} + result := TradeHistoryResponse{} - return result, y.SendAuthenticatedHTTPRequest(privateTradeHistory, req, &result) + err := y.SendAuthenticatedHTTPRequest(privateTradeHistory, req, &result) + if err != nil { + return nil, err + } + if result.Success == 0 { + return nil, errors.New(result.Error) + } + + return result.Data, nil } // GetCryptoDepositAddress returns the deposit address for a specific currency diff --git a/exchanges/yobit/yobit_test.go b/exchanges/yobit/yobit_test.go index 2838f4c7..9687ad73 100644 --- a/exchanges/yobit/yobit_test.go +++ b/exchanges/yobit/yobit_test.go @@ -1,7 +1,9 @@ package yobit import ( + "math" "testing" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -77,11 +79,11 @@ func TestGetAccountInfo(t *testing.T) { } } -func TestGetActiveOrders(t *testing.T) { +func TestGetOpenOrders(t *testing.T) { t.Parallel() - _, err := y.GetActiveOrders("") + _, err := y.GetOpenOrders("") if err == nil { - t.Error("Test Failed - GetActiveOrders() error", err) + t.Error("Test Failed - GetOpenOrders() error", err) } } @@ -109,14 +111,6 @@ func TestTrade(t *testing.T) { } } -func TestGetTradeHistory(t *testing.T) { - t.Parallel() - _, err := y.GetTradeHistory(0, 0, 0, "", "", "", "") - if err == nil { - t.Error("Test Failed - GetTradeHistory() error", err) - } -} - func TestWithdrawCoinsToAddress(t *testing.T) { t.Parallel() _, err := y.WithdrawCoinsToAddress("", 0, "") @@ -294,17 +288,52 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange y.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText - // Act + withdrawPermissions := y.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + y.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := y.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + y.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + StartTicks: time.Unix(0, 0), + EndTicks: time.Unix(math.MaxInt64, 0), + } + + _, err := y.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -328,7 +357,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.BTC, SecondCurrency: symbol.USD, } - response, err := y.SubmitOrder(pair, exchange.Buy, exchange.Market, 1, 10, "hi") + response, err := y.SubmitOrder(pair, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -337,7 +366,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange y.SetDefaults() TestSetup(t) @@ -354,12 +382,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := y.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -367,7 +392,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange y.SetDefaults() TestSetup(t) @@ -384,12 +408,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := y.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -423,7 +445,7 @@ func TestWithdraw(t *testing.T) { _, err := y.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/yobit/yobit_types.go b/exchanges/yobit/yobit_types.go index a2d40331..42fc436e 100644 --- a/exchanges/yobit/yobit_types.go +++ b/exchanges/yobit/yobit_types.go @@ -105,6 +105,13 @@ type Trade struct { Error string `json:"error"` } +//TradeHistoryResponse returns all your trade history +type TradeHistoryResponse struct { + Success int64 `json:"success"` + Data map[string]TradeHistory `json:"return,omitempty"` + Error string `json:"error,omitempty"` +} + // TradeHistory stores trade history type TradeHistory struct { Pair string `json:"pair"` diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index ef63a3ce..9116dc3a 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -3,8 +3,11 @@ package yobit import ( "errors" "fmt" + "math" "strconv" + "strings" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -187,7 +190,7 @@ func (y *Yobit) CancelAllOrders(orderCancellation exchange.OrderCancellation) (e var allActiveOrders []map[string]ActiveOrders for _, pair := range y.EnabledPairs { - activeOrdersForPair, err := y.GetActiveOrders(pair) + activeOrdersForPair, err := y.GetOpenOrders(pair) if err != nil { return cancelAllOrdersResponse, err } @@ -267,3 +270,70 @@ func (y *Yobit) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (y *Yobit) GetWithdrawCapabilities() uint32 { return y.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +func (y *Yobit) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var orders []exchange.OrderDetail + for _, currency := range getOrdersRequest.Currencies { + resp, err := y.GetOpenOrders(exchange.FormatExchangeCurrency(y.Name, currency).String()) + if err != nil { + return nil, err + } + + for ID, order := range resp { + symbol := pair.NewCurrencyPairDelimiter(order.Pair, y.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(int64(order.TimestampCreated), 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + orders = append(orders, exchange.OrderDetail{ + ID: ID, + Amount: order.Amount, + Price: order.Rate, + OrderSide: side, + OrderDate: orderDate, + CurrencyPair: symbol, + Exchange: y.Name, + }) + } + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (y *Yobit) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var allOrders []TradeHistory + for _, currency := range getOrdersRequest.Currencies { + resp, err := y.GetTradeHistory(0, 10000, math.MaxInt64, getOrdersRequest.StartTicks.Unix(), getOrdersRequest.EndTicks.Unix(), "DESC", exchange.FormatExchangeCurrency(y.Name, currency).String()) + if err != nil { + return nil, err + } + + for _, order := range resp { + allOrders = append(allOrders, order) + } + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Pair, y.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(int64(order.Timestamp), 0) + side := exchange.OrderSide(strings.ToUpper(order.Type)) + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.OrderID), + Amount: order.Amount, + Price: order.Rate, + OrderSide: side, + OrderDate: orderDate, + CurrencyPair: symbol, + Exchange: y.Name, + }) + } + + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} diff --git a/exchanges/zb/zb.go b/exchanges/zb/zb.go index af865c3c..644bd3f8 100644 --- a/exchanges/zb/zb.go +++ b/exchanges/zb/zb.go @@ -33,6 +33,7 @@ const ( zbTickers = "allTicker" zbDepth = "depth" zbUnfinishedOrdersIgnoreTradeType = "getUnfinishedOrdersIgnoreTradeType" + zbGetOrdersGet = "getOrders" zbWithdraw = "withdraw" zbDepositAddress = "getUserAddress" @@ -194,14 +195,14 @@ func (z *ZB) GetAccountInformation() (AccountsResponse, error) { } // GetUnfinishedOrdersIgnoreTradeType returns unfinished orders -func (z *ZB) GetUnfinishedOrdersIgnoreTradeType(currency, pageindex, pagesize string) ([]UnfinishedOpenOrder, error) { - var result []UnfinishedOpenOrder +func (z *ZB) GetUnfinishedOrdersIgnoreTradeType(currency string, pageindex, pagesize int64) ([]Order, error) { + var result []Order vals := url.Values{} vals.Set("accesskey", z.APIKey) vals.Set("method", zbUnfinishedOrdersIgnoreTradeType) vals.Set("currency", currency) - vals.Set("pageIndex", pageindex) - vals.Set("pageSize", pagesize) + vals.Set("pageIndex", strconv.FormatInt(pageindex, 10)) + vals.Set("pageSize", strconv.FormatInt(pagesize, 10)) err := z.SendAuthenticatedHTTPRequest("GET", vals, &result) if err != nil { @@ -211,6 +212,24 @@ func (z *ZB) GetUnfinishedOrdersIgnoreTradeType(currency, pageindex, pagesize st return result, nil } +// GetOrders returns finished orders +func (z *ZB) GetOrders(currency string, pageindex, side int64) ([]Order, error) { + var response []Order + vals := url.Values{} + vals.Set("accesskey", z.APIKey) + vals.Set("method", zbGetOrdersGet) + vals.Set("currency", currency) + vals.Set("pageIndex", strconv.FormatInt(pageindex, 10)) + vals.Set("tradeType", strconv.FormatInt(side, 10)) + + err := z.SendAuthenticatedHTTPRequest("GET", vals, &response) + if err != nil { + return response, err + } + + return response, 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 ffd5de5f..66bddd10 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -31,7 +31,6 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - ZB Setup() init error") } - zbConfig.AuthenticatedAPISupport = true zbConfig.APIKey = apiKey zbConfig.APISecret = apiSecret @@ -220,17 +219,51 @@ func TestGetFee(t *testing.T) { } func TestFormatWithdrawPermissions(t *testing.T) { - // Arrange z.SetDefaults() expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText - // Act + withdrawPermissions := z.FormatWithdrawPermissions() - // Assert + if withdrawPermissions != expectedResult { t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions) } } +func TestGetActiveOrders(t *testing.T) { + z.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := z.GetActiveOrders(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get open orders: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + +func TestGetOrderHistory(t *testing.T) { + z.SetDefaults() + TestSetup(t) + + var getOrdersRequest = exchange.GetOrdersRequest{ + OrderType: exchange.AnyOrderType, + OrderSide: exchange.BuyOrderSide, + Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)}, + } + + _, err := z.GetOrderHistory(getOrdersRequest) + if areTestAPIKeysSet() && err != nil { + t.Errorf("Could not get order history: %s", err) + } else if !areTestAPIKeysSet() && err == nil { + t.Error("Expecting an error when no keys are set") + } +} + // Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- func areTestAPIKeysSet() bool { @@ -253,7 +286,7 @@ func TestSubmitOrder(t *testing.T) { FirstCurrency: symbol.QTUM, SecondCurrency: symbol.USDT, } - response, err := z.SubmitOrder(pair, exchange.Buy, exchange.Market, 1, 10, "hi") + response, err := z.SubmitOrder(pair, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi") if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { @@ -262,7 +295,6 @@ func TestSubmitOrder(t *testing.T) { } func TestCancelExchangeOrder(t *testing.T) { - // Arrange z.SetDefaults() TestSetup(t) @@ -279,12 +311,9 @@ func TestCancelExchangeOrder(t *testing.T) { CurrencyPair: currencyPair, } - // Act err := z.CancelOrder(orderCancellation) - - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -292,7 +321,6 @@ func TestCancelExchangeOrder(t *testing.T) { } func TestCancelAllExchangeOrders(t *testing.T) { - // Arrange z.SetDefaults() TestSetup(t) @@ -309,12 +337,10 @@ func TestCancelAllExchangeOrders(t *testing.T) { CurrencyPair: currencyPair, } - // Act resp, err := z.CancelAllOrders(orderCancellation) - // Assert if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Could not cancel orders: %v", err) @@ -363,7 +389,7 @@ func TestWithdraw(t *testing.T) { _, err := z.WithdrawCryptocurrencyFunds(withdrawCryptoRequest) if !areTestAPIKeysSet() && err == nil { - t.Errorf("Expecting an error when no keys are set: %v", err) + t.Error("Expecting an error when no keys are set") } if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) diff --git a/exchanges/zb/zb_types.go b/exchanges/zb/zb_types.go index 346cdcf5..73791558 100644 --- a/exchanges/zb/zb_types.go +++ b/exchanges/zb/zb_types.go @@ -1,7 +1,11 @@ package zb -import "time" -import "github.com/thrasher-/gocryptotrader/currency/symbol" +import ( + "time" + + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" +) // OrderbookResponse holds the orderbook data for a symbol type OrderbookResponse struct { @@ -29,8 +33,8 @@ type AccountsBaseResponse struct { AuthMobileEnabled bool `json:"auth_mobile_enabled"` //是否开通手机验证 } -// UnfinishedOpenOrder is the order details for retrieving all open orders -type UnfinishedOpenOrder struct { +// Order is the order details for retrieving all orders +type Order struct { Currency string `json:"currency"` ID int64 `json:"id"` Price int `json:"price"` @@ -39,7 +43,7 @@ type UnfinishedOpenOrder struct { TradeAmount int `json:"trade_amount"` TradeDate int `json:"trade_date"` TradeMoney int `json:"trade_money"` - Type int `json:"type"` + Type int64 `json:"type"` } // AccountsResponse 用户基本信息 @@ -235,3 +239,9 @@ var WithdrawalFees = map[string]float64{ symbol.PAX: 5, symbol.XTZ: 0.1, } + +// orderSideMap holds order type info based on Alphapoint data +var orderSideMap = map[int64]exchange.OrderSide{ + 0: exchange.BuyOrderSide, + 1: exchange.SellOrderSide, +} diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index b75a8944..f6f3f5e2 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -1,9 +1,11 @@ package zb import ( + "errors" "fmt" "strconv" "sync" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -170,7 +172,7 @@ func (z *ZB) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderType var submitOrderResponse exchange.SubmitOrderResponse var oT SpotNewOrderRequestParamsType - if side == exchange.Buy { + if side == exchange.BuyOrderSide { oT = SpotNewOrderRequestParamsTypeBuy } else { oT = SpotNewOrderRequestParamsTypeSell @@ -217,14 +219,23 @@ func (z *ZB) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exch cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{ OrderStatus: make(map[string]string), } - var allOpenOrders []UnfinishedOpenOrder + var allOpenOrders []Order for _, currency := range z.GetEnabledCurrencies() { - openOrders, err := z.GetUnfinishedOrdersIgnoreTradeType(exchange.FormatExchangeCurrency(z.Name, currency).String(), "1", "10") - if err != nil { - return cancelAllOrdersResponse, err - } + var pageNumber int64 + // Limiting to 10 pages + for i := 0; i < 10; i++ { + openOrders, err := z.GetUnfinishedOrdersIgnoreTradeType(exchange.FormatExchangeCurrency(z.Name, currency).String(), 1, 10) + if err != nil { + return cancelAllOrdersResponse, err + } - allOpenOrders = append(allOpenOrders, openOrders...) + if len(openOrders) == 0 { + break + } + + allOpenOrders = append(allOpenOrders, openOrders...) + pageNumber++ + } } for _, openOrder := range allOpenOrders { @@ -285,3 +296,100 @@ func (z *ZB) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { func (z *ZB) GetWithdrawCapabilities() uint32 { return z.GetWithdrawPermissions() } + +// GetActiveOrders retrieves any orders that are active/open +// This function is not concurrency safe due to orderSide/orderType maps +func (z *ZB) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + var allOrders []Order + for _, currency := range getOrdersRequest.Currencies { + var pageNumber int64 + // Limiting to 10 pages + for i := 0; i < 10; i++ { + resp, err := z.GetUnfinishedOrdersIgnoreTradeType(exchange.FormatExchangeCurrency(z.Name, currency).String(), pageNumber, 10) + if err != nil { + return nil, err + } + if len(resp) == 0 { + break + } + + allOrders = append(allOrders, resp...) + pageNumber++ + } + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Currency, z.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(int64(order.TradeDate), 0) + orderSide := orderSideMap[order.Type] + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.ID), + Amount: order.TotalAmount, + Exchange: z.Name, + OrderDate: orderDate, + Price: float64(order.Price), + OrderSide: orderSide, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) + + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +// This function is not concurrency safe due to orderSide/orderType maps +func (z *ZB) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) { + if getOrdersRequest.OrderSide == exchange.AnyOrderSide || getOrdersRequest.OrderSide == "" { + return nil, errors.New("Specific order side is required") + } + + var allOrders []Order + + var side int64 + if getOrdersRequest.OrderSide == exchange.BuyOrderSide { + side = 1 + } + + for _, currency := range getOrdersRequest.Currencies { + var pageNumber int64 + // Limiting to 10 pages + for i := 0; i < 10; i++ { + resp, err := z.GetOrders(exchange.FormatExchangeCurrency(z.Name, currency).String(), pageNumber, side) + if err != nil { + return nil, err + } + + if len(resp) == 0 { + break + } + + allOrders = append(allOrders, resp...) + pageNumber++ + } + } + + var orders []exchange.OrderDetail + for _, order := range allOrders { + symbol := pair.NewCurrencyPairDelimiter(order.Currency, z.ConfigCurrencyPairFormat.Delimiter) + orderDate := time.Unix(int64(order.TradeDate), 0) + orderSide := orderSideMap[order.Type] + orders = append(orders, exchange.OrderDetail{ + ID: fmt.Sprintf("%v", order.ID), + Amount: order.TotalAmount, + Exchange: z.Name, + OrderDate: orderDate, + Price: float64(order.Price), + OrderSide: orderSide, + CurrencyPair: symbol, + }) + } + + exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + + return orders, nil +}