From a12cd3d73394d32288ce525c753f4d803a3b4242 Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Mon, 6 Jun 2022 11:52:15 +1000 Subject: [PATCH] orders: Add methods to derive SubmitResponse and Detail types (#955) * orders: deprecate SubmitResponse return and change to *order.Detail construct detail from order.Submit struct * orders: add coverage, fix tests * coinut: rm test for checking * orders: revert change for return and change field ID to a more explicit name OrderID * orders: Add method to see if the order was placed * order: change field name in Cancel type to be more explicit * orders: standardize field -> OrderID * backtester: populate change * orders: add test * gctscript: fix field name * linter: fix issues * linter: more fixes * linter: forever * exchanges_tests: populate order.Submit field exchange name * Update exchanges/order/order_types.go Co-authored-by: Scott * Update exchanges/order/orders.go Co-authored-by: Scott * glorious: nits * glorious: nits * thrasher: nits Co-authored-by: Ryan O'Hara-Reid Co-authored-by: Scott --- backtester/eventhandlers/exchange/exchange.go | 65 ++-- .../portfolio/holdings/holdings_test.go | 8 +- cmd/exchange_template/wrapper_file.tmpl | 28 +- cmd/exchange_wrapper_issues/main.go | 19 +- docs/EXCHANGE_API.md | 5 +- engine/order_manager.go | 188 +++++------ engine/order_manager_test.go | 147 ++++----- engine/order_manager_types.go | 3 +- engine/rpcserver.go | 29 +- engine/rpcserver_test.go | 24 +- engine/websocketroutine_manager.go | 9 +- engine/websocketroutine_manager_test.go | 10 +- exchanges/alphapoint/alphapoint_test.go | 7 +- exchanges/alphapoint/alphapoint_wrapper.go | 25 +- exchanges/binance/binance_test.go | 11 +- exchanges/binance/binance_websocket.go | 2 +- exchanges/binance/binance_wrapper.go | 78 ++--- exchanges/bitfinex/bitfinex_test.go | 7 +- exchanges/bitfinex/bitfinex_websocket.go | 6 +- exchanges/bitfinex/bitfinex_wrapper.go | 46 +-- exchanges/bitflyer/bitflyer_test.go | 5 +- exchanges/bitflyer/bitflyer_wrapper.go | 4 +- exchanges/bithumb/bithumb_test.go | 9 +- exchanges/bithumb/bithumb_wrapper.go | 27 +- exchanges/bitmex/bitmex_test.go | 9 +- exchanges/bitmex/bitmex_websocket.go | 6 +- exchanges/bitmex/bitmex_wrapper.go | 34 +- exchanges/bitstamp/bitstamp_test.go | 5 +- exchanges/bitstamp/bitstamp_wrapper.go | 31 +- exchanges/bittrex/bittrex_test.go | 7 +- exchanges/bittrex/bittrex_websocket.go | 2 +- exchanges/bittrex/bittrex_wrapper.go | 22 +- exchanges/btcmarkets/btcmarkets_test.go | 5 +- exchanges/btcmarkets/btcmarkets_websocket.go | 2 +- exchanges/btcmarkets/btcmarkets_wrapper.go | 27 +- exchanges/btse/btse_test.go | 7 +- exchanges/btse/btse_websocket.go | 2 +- exchanges/btse/btse_wrapper.go | 32 +- exchanges/coinbasepro/coinbasepro_test.go | 7 +- .../coinbasepro/coinbasepro_websocket.go | 4 +- exchanges/coinbasepro/coinbasepro_wrapper.go | 34 +- exchanges/coinut/coinut_test.go | 7 +- exchanges/coinut/coinut_websocket.go | 8 +- exchanges/coinut/coinut_wrapper.go | 85 ++--- exchanges/exmo/exmo_test.go | 7 +- exchanges/exmo/exmo_wrapper.go | 26 +- exchanges/ftx/ftx_test.go | 5 +- exchanges/ftx/ftx_websocket.go | 2 +- exchanges/ftx/ftx_websocket_test.go | 2 +- exchanges/ftx/ftx_wrapper.go | 34 +- exchanges/gateio/gateio_test.go | 7 +- exchanges/gateio/gateio_websocket.go | 2 +- exchanges/gateio/gateio_wrapper.go | 30 +- exchanges/gemini/gemini_test.go | 7 +- exchanges/gemini/gemini_websocket.go | 2 +- exchanges/gemini/gemini_wrapper.go | 25 +- exchanges/hitbtc/hitbtc_test.go | 7 +- exchanges/hitbtc/hitbtc_websocket.go | 2 +- exchanges/hitbtc/hitbtc_wrapper.go | 46 +-- exchanges/huobi/huobi_test.go | 7 +- exchanges/huobi/huobi_websocket.go | 2 +- exchanges/huobi/huobi_wrapper.go | 71 ++-- exchanges/interfaces.go | 2 +- exchanges/itbit/itbit_test.go | 7 +- exchanges/itbit/itbit_wrapper.go | 30 +- exchanges/kraken/kraken_test.go | 7 +- exchanges/kraken/kraken_websocket.go | 8 +- exchanges/kraken/kraken_wrapper.go | 77 ++--- exchanges/lbank/lbank_test.go | 5 +- exchanges/lbank/lbank_wrapper.go | 20 +- exchanges/localbitcoins/localbitcoins_test.go | 7 +- .../localbitcoins/localbitcoins_wrapper.go | 55 ++-- exchanges/okcoin/okcoin_test.go | 7 +- exchanges/okex/okex_test.go | 7 +- exchanges/okgroup/okgroup_websocket.go | 2 +- exchanges/okgroup/okgroup_wrapper.go | 26 +- exchanges/order/futures.go | 37 ++- exchanges/order/futures_test.go | 28 +- exchanges/order/order_test.go | 304 +++++++++++++++--- exchanges/order/order_types.go | 100 +++--- exchanges/order/orders.go | 209 +++++++++--- exchanges/poloniex/poloniex_test.go | 13 +- exchanges/poloniex/poloniex_websocket.go | 10 +- exchanges/poloniex/poloniex_wrapper.go | 34 +- exchanges/sharedtestvalues/customex.go | 4 +- exchanges/yobit/yobit_test.go | 7 +- exchanges/yobit/yobit_wrapper.go | 24 +- exchanges/zb/zb_test.go | 5 +- exchanges/zb/zb_websocket.go | 4 +- exchanges/zb/zb_wrapper.go | 81 ++--- gctscript/modules/gct/exchange.go | 8 +- gctscript/modules/wrapper_types.go | 11 +- gctscript/wrappers/gct/exchange/exchange.go | 11 +- gctscript/wrappers/validator/validator.go | 13 +- gctscript/wrappers/wrappers.go | 2 +- 95 files changed, 1377 insertions(+), 1170 deletions(-) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index b9e01d2b..678ddb86 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -140,7 +140,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * ords := orderManager.GetOrdersSnapshot(gctorder.UnknownStatus) for i := range ords { - if ords[i].ID != orderID { + if ords[i].OrderID != orderID { continue } ords[i].Date = o.GetTime() @@ -224,53 +224,42 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal if f == nil { return "", common.ErrNilEvent } - u, err := uuid.NewV4() + orderID, err := uuid.NewV4() if err != nil { return "", err } - var orderID string - p := price.InexactFloat64() - fee := f.ExchangeFee.InexactFloat64() - o := &gctorder.Submit{ - Price: p, - Amount: amount.InexactFloat64(), - Fee: fee, - Exchange: f.Exchange, - ID: u.String(), - Side: f.Direction, - AssetType: f.AssetType, - Date: f.GetTime(), - LastUpdated: f.GetTime(), - Pair: f.Pair(), - Type: gctorder.Market, + + submit := &gctorder.Submit{ + Price: price.InexactFloat64(), + Amount: amount.InexactFloat64(), + Exchange: f.Exchange, + Side: f.Direction, + AssetType: f.AssetType, + Pair: f.Pair(), + Type: gctorder.Market, } + var resp *engine.OrderSubmitResponse if useRealOrders { - resp, err := orderManager.Submit(ctx, o) - if resp != nil { - orderID = resp.OrderID - } - if err != nil { - return orderID, err - } + resp, err = orderManager.Submit(ctx, submit) } else { - submitResponse := gctorder.SubmitResponse{ - IsOrderPlaced: true, - OrderID: u.String(), - Rate: f.Amount.InexactFloat64(), - Fee: fee, - Cost: p, - FullyMatched: true, - } - resp, err := orderManager.SubmitFakeOrder(o, submitResponse, useExchangeLimits) - if resp != nil { - orderID = resp.OrderID - } + var submitResponse *gctorder.SubmitResponse + submitResponse, err = submit.DeriveSubmitResponse(orderID.String()) if err != nil { - return orderID, err + return orderID.String(), err } + submitResponse.Status = gctorder.Filled + submitResponse.OrderID = orderID.String() + submitResponse.Fee = f.ExchangeFee.InexactFloat64() + submitResponse.Cost = submit.Price + submitResponse.LastUpdated = f.GetTime() + submitResponse.Date = f.GetTime() + resp, err = orderManager.SubmitFakeOrder(submit, submitResponse, useExchangeLimits) } - return orderID, nil + if err != nil { + return orderID.String(), err + } + return resp.OrderID, nil } func (e *Exchange) sizeOfflineOrder(high, low, volume decimal.Decimal, cs *Settings, f *fill.Fill) (adjustedPrice, adjustedAmount decimal.Decimal, err error) { diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index e1764482..1ce7c32c 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -119,7 +119,7 @@ func TestUpdateBuyStats(t *testing.T) { Price: 500, Amount: 1, Exchange: testExchange, - ID: "decimal.NewFromInt(1337)", + OrderID: "decimal.NewFromInt(1337)", Type: order.Limit, Side: order.Buy, Status: order.New, @@ -179,7 +179,7 @@ func TestUpdateBuyStats(t *testing.T) { Price: 500, Amount: 0.5, Exchange: testExchange, - ID: "decimal.NewFromInt(1337)", + OrderID: "decimal.NewFromInt(1337)", Type: order.Limit, Side: order.Buy, Status: order.New, @@ -247,7 +247,7 @@ func TestUpdateSellStats(t *testing.T) { Price: 500, Amount: 1, Exchange: testExchange, - ID: "decimal.NewFromInt(1337)", + OrderID: "decimal.NewFromInt(1337)", Type: order.Limit, Side: order.Buy, Status: order.New, @@ -310,7 +310,7 @@ func TestUpdateSellStats(t *testing.T) { Price: 500, Amount: 1, Exchange: testExchange, - ID: "decimal.NewFromInt(1337)", + OrderID: "decimal.NewFromInt(1337)", Type: order.Limit, Side: order.Sell, Status: order.New, diff --git a/cmd/exchange_template/wrapper_file.tmpl b/cmd/exchange_template/wrapper_file.tmpl index 7d810d4c..b01c5169 100644 --- a/cmd/exchange_template/wrapper_file.tmpl +++ b/cmd/exchange_template/wrapper_file.tmpl @@ -393,20 +393,36 @@ func ({{.Variable}} *{{.CapitalName}}) GetHistoricTrades (ctx context.Context, p } // SubmitOrder submits a new order -func ({{.Variable}} *{{.CapitalName}}) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func ({{.Variable}} *{{.CapitalName}}) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } - return submitOrderResponse, common.ErrNotYetImplemented + // When an order has been submitted you can use this helpful constructor to + // return. Please add any additional order details to the + // order.SubmitResponse if you think they are applicable. + // resp, err := s.DeriveSubmitResponse( /*newOrderID*/) + // if err != nil { + // return nil, nil + // } + // resp.Date = exampleTime // e.g. If this is supplied by the exchanges API. + // return resp, nil + return nil, common.ErrNotYetImplemented } // ModifyOrder will allow of changing orderbook placement and limit to // market conversion func ({{.Variable}} *{{.CapitalName}}) ModifyOrder(ctx context.Context, action *order.Modify) (*order.ModifyResponse, error) { - // if err := action.Validate(); err != nil { - // return "", err + if err := action.Validate(); err != nil { + return nil, err + } + // When an order has been modified you can use this helpful constructor to + // return. Please add any additional order details to the + // order.ModifyResponse if you think they are applicable. + // resp, err := action.DeriveModifyResponse() + // if err != nil { + // return nil, nil // } + // resp.OrderID = maybeANewOrderID // e.g. If this is supplied by the exchanges API. return nil, common.ErrNotYetImplemented } diff --git a/cmd/exchange_wrapper_issues/main.go b/cmd/exchange_wrapper_issues/main.go index 64ea0c9b..b8f3c84f 100644 --- a/cmd/exchange_wrapper_issues/main.go +++ b/cmd/exchange_wrapper_issues/main.go @@ -556,6 +556,7 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) }) s := &order.Submit{ + Exchange: e.GetName(), Pair: p, Side: testOrderSide, Type: testOrderType, @@ -564,7 +565,7 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) ClientID: config.OrderSubmission.OrderID, AssetType: assetTypes[i], } - var submitOrderResponse order.SubmitResponse + var submitOrderResponse *order.SubmitResponse submitOrderResponse, err = e.SubmitOrder(context.TODO(), s) msg = "" if err != nil { @@ -579,12 +580,12 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) }) modifyRequest := order.Modify{ - ID: config.OrderSubmission.OrderID, - Type: testOrderType, - Side: testOrderSide, - Pair: p, - Price: config.OrderSubmission.Price, - Amount: config.OrderSubmission.Amount, + OrderID: config.OrderSubmission.OrderID, + Type: testOrderType, + Side: testOrderSide, + Pair: p, + Price: config.OrderSubmission.Price, + Amount: config.OrderSubmission.Amount, } modifyOrderResponse, err := e.ModifyOrder(context.TODO(), &modifyRequest) msg = "" @@ -602,7 +603,7 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) cancelRequest := order.Cancel{ Side: testOrderSide, Pair: p, - ID: config.OrderSubmission.OrderID, + OrderID: config.OrderSubmission.OrderID, AssetType: assetTypes[i], } err = e.CancelOrder(context.TODO(), &cancelRequest) @@ -622,7 +623,7 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) request = append(request, order.Cancel{ Side: testOrderSide, Pair: p, - ID: config.OrderSubmission.OrderID, + OrderID: config.OrderSubmission.OrderID, AssetType: assetTypes[i], }) diff --git a/docs/EXCHANGE_API.md b/docs/EXCHANGE_API.md index 64e7598b..f0ed9a76 100644 --- a/docs/EXCHANGE_API.md +++ b/docs/EXCHANGE_API.md @@ -75,9 +75,10 @@ supplied meet the requirements to make an authenticated request. o := &order.Submit{ + Exchange: b.Name, // or method GetName() if exchange.IBotInterface Pair: currency.NewPair(currency.BTC, currency.USD), - OrderSide: order.Sell, - OrderType: order.Limit, + Side: order.Sell, + Type: order.Limit, Price: 1000000, Amount: 0.1, AssetType: asset.Spot, diff --git a/engine/order_manager.go b/engine/order_manager.go index 57b2e9e1..f5bea174 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -173,7 +173,7 @@ func (m *OrderManager) Cancel(ctx context.Context, cancel *order.Cancel) error { err = errors.New("order exchange name is empty") return err } - if cancel.ID == "" { + if cancel.OrderID == "" { err = errors.New("order id is empty") return err } @@ -189,16 +189,17 @@ func (m *OrderManager) Cancel(ctx context.Context, cancel *order.Cancel) error { } log.Debugf(log.OrderMgr, "Order manager: Cancelling order ID %v [%+v]", - cancel.ID, cancel) + cancel.OrderID, cancel) err = exch.CancelOrder(ctx, cancel) if err != nil { err = fmt.Errorf("%v - Failed to cancel order: %w", cancel.Exchange, err) return err } - od, err := m.orderStore.getByExchangeAndID(cancel.Exchange, cancel.ID) + od, err := m.orderStore.getByExchangeAndID(cancel.Exchange, cancel.OrderID) if err != nil { - err = fmt.Errorf("%v - Failed to retrieve order %v to update cancelled status: %w", cancel.Exchange, cancel.ID, err) + err = fmt.Errorf("%v - Failed to retrieve order %v to update cancelled status: %w", + cancel.Exchange, cancel.OrderID, err) return err } od.Status = order.Cancelled @@ -209,7 +210,7 @@ func (m *OrderManager) Cancel(ctx context.Context, cancel *order.Cancel) error { } msg := fmt.Sprintf("Order manager: Exchange %s order ID=%v cancelled.", - od.Exchange, od.ID) + od.Exchange, od.OrderID) log.Debugln(log.OrderMgr, msg) m.orderStore.commsManager.PushEvent(base.Event{Type: "order", Message: msg}) return nil @@ -350,7 +351,7 @@ func (m *OrderManager) Modify(ctx context.Context, mod *order.Modify) (*order.Mo } // Fetch details from locally managed order store. - det, err := m.orderStore.getByExchangeAndID(mod.Exchange, mod.ID) + det, err := m.orderStore.getByExchangeAndID(mod.Exchange, mod.OrderID) if det == nil || err != nil { return nil, fmt.Errorf("order does not exist: %w", err) } @@ -381,7 +382,7 @@ func (m *OrderManager) Modify(ctx context.Context, mod *order.Modify) (*order.Mo message := fmt.Sprintf( "Order manager: Exchange %s order ID=%v: failed to modify", mod.Exchange, - mod.ID, + mod.OrderID, ) m.orderStore.commsManager.PushEvent(base.Event{ Type: "order", @@ -394,7 +395,7 @@ func (m *OrderManager) Modify(ctx context.Context, mod *order.Modify) (*order.Mo // // XXX: This comes with a race condition, because [request -> changes] are not // atomic. - err = m.orderStore.modifyExisting(mod.ID, res) + err = m.orderStore.modifyExisting(mod.OrderID, res) // Notify observers. var message string @@ -458,12 +459,12 @@ func (m *OrderManager) Submit(ctx context.Context, newOrder *order.Submit) (*Ord return nil, err } - return m.processSubmittedOrder(newOrder, result) + return m.processSubmittedOrder(result) } // SubmitFakeOrder runs through the same process as order submission // but does not touch live endpoints -func (m *OrderManager) SubmitFakeOrder(newOrder *order.Submit, resultingOrder order.SubmitResponse, checkExchangeLimits bool) (*OrderSubmitResponse, error) { +func (m *OrderManager) SubmitFakeOrder(newOrder *order.Submit, resultingOrder *order.SubmitResponse, checkExchangeLimits bool) (*OrderSubmitResponse, error) { if m == nil { return nil, fmt.Errorf("order manager %w", ErrNilSubsystem) } @@ -494,7 +495,7 @@ func (m *OrderManager) SubmitFakeOrder(newOrder *order.Submit, resultingOrder or err) } } - return m.processSubmittedOrder(newOrder, resultingOrder) + return m.processSubmittedOrder(resultingOrder) } // GetOrdersSnapshot returns a snapshot of all orders in the orderstore. It optionally filters any orders that do not match the status @@ -542,82 +543,45 @@ func (m *OrderManager) GetOrdersActive(f *order.Filter) ([]order.Detail, error) } // processSubmittedOrder adds a new order to the manager -func (m *OrderManager) processSubmittedOrder(newOrder *order.Submit, result order.SubmitResponse) (*OrderSubmitResponse, error) { - if !result.IsOrderPlaced { - return nil, errUnableToPlaceOrder +func (m *OrderManager) processSubmittedOrder(newOrderResp *order.SubmitResponse) (*OrderSubmitResponse, error) { + if newOrderResp == nil { + return nil, order.ErrOrderDetailIsNil } id, err := uuid.NewV4() if err != nil { - log.Warnf(log.OrderMgr, - "Order manager: Unable to generate UUID. Err: %s", - err) + log.Warnf(log.OrderMgr, "Order manager: Unable to generate UUID. Err: %s", err) } - if newOrder.Date.IsZero() { - newOrder.Date = time.Now() + + detail, err := newOrderResp.DeriveDetail(id) + if err != nil { + return nil, err } + msg := fmt.Sprintf("Order manager: Exchange %s submitted order ID=%v [Ours: %v] pair=%v price=%v amount=%v quoteAmount=%v side=%v type=%v for time %v.", - newOrder.Exchange, - result.OrderID, - id.String(), - newOrder.Pair, - newOrder.Price, - newOrder.Amount, - newOrder.QuoteAmount, - newOrder.Side, - newOrder.Type, - newOrder.Date) + detail.Exchange, + detail.OrderID, + detail.InternalOrderID.String(), + detail.Pair, + detail.Price, + detail.Amount, + detail.QuoteAmount, + detail.Side, + detail.Type, + detail.Date) log.Debugln(log.OrderMgr, msg) - m.orderStore.commsManager.PushEvent(base.Event{ - Type: "order", - Message: msg, - }) - status := order.New - if result.FullyMatched { - status = order.Filled - } - err = m.orderStore.add(&order.Detail{ - ImmediateOrCancel: newOrder.ImmediateOrCancel, - HiddenOrder: newOrder.HiddenOrder, - FillOrKill: newOrder.FillOrKill, - PostOnly: newOrder.PostOnly, - Price: newOrder.Price, - Amount: newOrder.Amount, - LimitPriceUpper: newOrder.LimitPriceUpper, - LimitPriceLower: newOrder.LimitPriceLower, - TriggerPrice: newOrder.TriggerPrice, - QuoteAmount: newOrder.QuoteAmount, - ExecutedAmount: newOrder.ExecutedAmount, - RemainingAmount: newOrder.RemainingAmount, - Fee: newOrder.Fee, - Exchange: newOrder.Exchange, - InternalOrderID: id.String(), - ID: result.OrderID, - AccountID: newOrder.AccountID, - ClientID: newOrder.ClientID, - ClientOrderID: newOrder.ClientOrderID, - WalletAddress: newOrder.WalletAddress, - Type: newOrder.Type, - Side: newOrder.Side, - Status: status, - AssetType: newOrder.AssetType, - Date: time.Now(), - LastUpdated: time.Now(), - Pair: newOrder.Pair, - Leverage: newOrder.Leverage, - }) - if err != nil { - return nil, fmt.Errorf("unable to add %v order %v to orderStore: %s", newOrder.Exchange, result.OrderID, err) + if m.orderStore.commsManager != nil { + m.orderStore.commsManager.PushEvent(base.Event{Type: "order", Message: msg}) } - return &OrderSubmitResponse{ - SubmitResponse: order.SubmitResponse{ - IsOrderPlaced: result.IsOrderPlaced, - OrderID: result.OrderID, - }, - InternalOrderID: id.String(), - }, nil + err = m.orderStore.add(detail.CopyToPointer()) + if err != nil { + return nil, fmt.Errorf("unable to add %v order %v to orderStore: %s", + detail.Exchange, detail.OrderID, err) + } + + return &OrderSubmitResponse{Detail: detail, InternalOrderID: id.String()}, nil } // processOrders iterates over all exchange orders via API @@ -730,7 +694,7 @@ func (m *OrderManager) FetchAndUpdateExchangeOrder(exch exchange.IBotExchange, o if ord == nil { return errors.New("order manager: Order is nil") } - fetchedOrder, err := exch.GetOrderInfo(context.TODO(), ord.ID, ord.Pair, assetType) + fetchedOrder, err := exch.GetOrderInfo(context.TODO(), ord.OrderID, ord.Pair, assetType) if err != nil { ord.Status = order.UnknownStatus return err @@ -765,14 +729,7 @@ func (m *OrderManager) GetByExchangeAndID(exchangeName, id string) (*order.Detai if atomic.LoadInt32(&m.started) == 0 { return nil, fmt.Errorf("order manager %w", ErrSubSystemNotStarted) } - - o, err := m.orderStore.getByExchangeAndID(exchangeName, id) - if err != nil { - return nil, err - } - var cpy order.Detail - cpy.UpdateOrderFromDetail(o) - return &cpy, nil + return m.orderStore.getByExchangeAndID(exchangeName, id) } // UpdateExistingOrder will update an existing order in the orderstore @@ -813,7 +770,7 @@ func (m *OrderManager) UpsertOrder(od *order.Detail) (resp *OrderUpsertResponse, if err != nil { msg = fmt.Sprintf( "Order manager: Exchange %s unable to upsert order ID=%v internal ID=%v pair=%v price=%.8f amount=%.8f side=%v type=%v status=%v: %s", - od.Exchange, od.ID, od.InternalOrderID, od.Pair, od.Price, od.Amount, od.Side, od.Type, od.Status, err) + od.Exchange, od.OrderID, od.InternalOrderID, od.Pair, od.Price, od.Amount, od.Side, od.Type, od.Status, err) return nil, err } @@ -822,7 +779,7 @@ func (m *OrderManager) UpsertOrder(od *order.Detail) (resp *OrderUpsertResponse, status = "added" } msg = fmt.Sprintf("Order manager: Exchange %s %s order ID=%v internal ID=%v pair=%v price=%.8f amount=%.8f side=%v type=%v status=%v.", - upsertResponse.OrderDetails.Exchange, status, upsertResponse.OrderDetails.ID, upsertResponse.OrderDetails.InternalOrderID, + upsertResponse.OrderDetails.Exchange, status, upsertResponse.OrderDetails.OrderID, upsertResponse.OrderDetails.InternalOrderID, upsertResponse.OrderDetails.Pair, upsertResponse.OrderDetails.Price, upsertResponse.OrderDetails.Amount, upsertResponse.OrderDetails.Side, upsertResponse.OrderDetails.Type, upsertResponse.OrderDetails.Status) if upsertResponse.IsNewOrder { @@ -854,7 +811,7 @@ func (s *store) getByExchangeAndID(exchange, id string) (*order.Detail, error) { } for x := range r { - if r[x].ID == id { + if r[x].OrderID == id { return r[x].CopyToPointer(), nil } } @@ -874,14 +831,17 @@ func (s *store) updateExisting(od *order.Detail) error { return ErrExchangeNotFound } for x := range r { - if r[x].ID != od.ID { + if r[x].OrderID != od.OrderID { continue } - r[x].UpdateOrderFromDetail(od) + err := r[x].UpdateOrderFromDetail(od) + if err != nil { + return err + } if !r[x].AssetType.IsFutures() { return nil } - err := s.futuresPositionController.TrackNewOrder(r[x]) + err = s.futuresPositionController.TrackNewOrder(r[x]) if err != nil && !errors.Is(err, order.ErrPositionClosed) { return err } @@ -900,7 +860,7 @@ func (s *store) modifyExisting(id string, mod *order.ModifyResponse) error { return ErrExchangeNotFound } for x := range r { - if r[x].ID != id { + if r[x].OrderID != id { continue } r[x].UpdateOrderFromModifyResponse(mod) @@ -935,18 +895,21 @@ func (s *store) upsert(od *order.Detail) (*OrderUpsertResponse, error) { return nil, err } } - r, ok := s.Orders[lName] - if !ok { - od.GenerateInternalOrderID() - s.Orders[lName] = []*order.Detail{od} - return &OrderUpsertResponse{OrderDetails: od.Copy(), IsNewOrder: true}, nil - } - for x := range r { - if r[x].ID != od.ID { + // TODO: Return pointer to slice because new orders we are accessing map + // twice for lookup. + exchangeOrders := s.Orders[lName] + for x := range exchangeOrders { + if exchangeOrders[x].OrderID != od.OrderID { continue } - r[x].UpdateOrderFromDetail(od) - return &OrderUpsertResponse{OrderDetails: r[x].Copy(), IsNewOrder: false}, nil + err := exchangeOrders[x].UpdateOrderFromDetail(od) + if err != nil { + return nil, err + } + return &OrderUpsertResponse{ + OrderDetails: exchangeOrders[x].Copy(), + IsNewOrder: false, + }, nil } // Untracked websocket orders will not have internalIDs yet od.GenerateInternalOrderID() @@ -961,13 +924,9 @@ func (s *store) exists(det *order.Detail) bool { } s.m.RLock() defer s.m.RUnlock() - r, ok := s.Orders[strings.ToLower(det.Exchange)] - if !ok { - return false - } - - for x := range r { - if r[x].ID == det.ID { + exchangeOrders := s.Orders[strings.ToLower(det.Exchange)] + for x := range exchangeOrders { + if exchangeOrders[x].OrderID == det.OrderID { return true } } @@ -979,21 +938,20 @@ func (s *store) add(det *order.Detail) error { if det == nil { return errNilOrder } - _, err := s.exchangeManager.GetExchangeByName(det.Exchange) + name := strings.ToLower(det.Exchange) + _, err := s.exchangeManager.GetExchangeByName(name) if err != nil { return err } - if s.exists(det) { + if s.exists(det) { // TODO: Error on conflict; remove unnecessary locking. return ErrOrdersAlreadyExists } + // Untracked websocket orders will not have internalIDs yet det.GenerateInternalOrderID() s.m.Lock() defer s.m.Unlock() - orders := s.Orders[strings.ToLower(det.Exchange)] - orders = append(orders, det) - s.Orders[strings.ToLower(det.Exchange)] = orders - + s.Orders[name] = append(s.Orders[name], det) if !det.AssetType.IsFutures() { return nil } diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 2fff8c07..c36e0f08 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -45,7 +45,7 @@ func (f omfExchange) GetOrderInfo(ctx context.Context, orderID string, pair curr Side: order.Buy, Status: order.Active, LastUpdated: time.Now().Add(-time.Hour), - ID: "Order1-unknown-to-active", + OrderID: "Order1-unknown-to-active", }, nil case "Order2-active-to-inactive": return order.Detail{ @@ -56,13 +56,13 @@ func (f omfExchange) GetOrderInfo(ctx context.Context, orderID string, pair curr Side: order.Sell, Status: order.Cancelled, LastUpdated: time.Now().Add(-time.Hour), - ID: "Order2-active-to-inactive", + OrderID: "Order2-active-to-inactive", }, nil } return order.Detail{ Exchange: testExchange, - ID: orderID, + OrderID: orderID, Pair: pair, AssetType: assetType, Status: order.Cancelled, @@ -79,7 +79,7 @@ func (f omfExchange) GetActiveOrders(ctx context.Context, req *order.GetOrdersRe Side: order.Sell, Status: order.Active, LastUpdated: time.Now().Add(-time.Hour), - ID: "Order3-unknown-to-active", + OrderID: "Order3-unknown-to-active", }}, nil } @@ -228,14 +228,14 @@ func TestOrdersAdd(t *testing.T) { m := OrdersSetup(t) err := m.orderStore.add(&order.Detail{ Exchange: testExchange, - ID: "TestOrdersAdd", + OrderID: "TestOrdersAdd", }) if err != nil { t.Error(err) } err = m.orderStore.add(&order.Detail{ Exchange: "testTest", - ID: "TestOrdersAdd", + OrderID: "TestOrdersAdd", }) if err == nil { t.Error("Expected error from non existent exchange") @@ -248,7 +248,7 @@ func TestOrdersAdd(t *testing.T) { err = m.orderStore.add(&order.Detail{ Exchange: testExchange, - ID: "TestOrdersAdd", + OrderID: "TestOrdersAdd", }) if err == nil { t.Error("Expected error re-adding order") @@ -259,7 +259,7 @@ func TestGetByExchangeAndID(t *testing.T) { m := OrdersSetup(t) err := m.orderStore.add(&order.Detail{ Exchange: testExchange, - ID: "TestGetByExchangeAndID", + OrderID: "TestGetByExchangeAndID", }) if err != nil { t.Error(err) @@ -269,7 +269,7 @@ func TestGetByExchangeAndID(t *testing.T) { if err != nil { t.Error(err) } - if o.ID != "TestGetByExchangeAndID" { + if o.OrderID != "TestGetByExchangeAndID" { t.Error("Expected to retrieve order") } @@ -291,7 +291,7 @@ func TestExists(t *testing.T) { } o := &order.Detail{ Exchange: testExchange, - ID: "TestExists", + OrderID: "TestExists", } if err := m.orderStore.add(o); err != nil { t.Error(err) @@ -311,7 +311,7 @@ func TestStore_modifyOrder(t *testing.T) { Exchange: testExchange, AssetType: asset.Spot, Pair: pair, - ID: "fake_order_id", + OrderID: "fake_order_id", Price: 8, Amount: 128, @@ -322,10 +322,9 @@ func TestStore_modifyOrder(t *testing.T) { err = m.orderStore.modifyExisting("fake_order_id", &order.ModifyResponse{ Exchange: testExchange, - - OrderID: "another_fake_order_id", - Price: 16, - Amount: 256, + OrderID: "another_fake_order_id", + Price: 16, + Amount: 256, }) if err != nil { t.Error(err) @@ -341,10 +340,10 @@ func TestStore_modifyOrder(t *testing.T) { if det == nil || err != nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("Failed to fetch order details") } - if det.ID != "another_fake_order_id" || det.Price != 16 || det.Amount != 256 { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings + if det.OrderID != "another_fake_order_id" || det.Price != 16 || det.Amount != 256 { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Errorf( "have (%s,%f,%f), want (%s,%f,%f)", - det.ID, det.Price, det.Amount, + det.OrderID, det.Price, det.Amount, "another_fake_order_id", 16., 256., ) } @@ -371,25 +370,24 @@ func TestCancelOrder(t *testing.T) { } err = m.Cancel(context.Background(), &order.Cancel{ - ID: "ID", + OrderID: "ID", }) if err == nil { t.Error("Expected error due to no Exchange") } - err = m.Cancel(context.Background(), - &order.Cancel{ - ID: "ID", - Exchange: testExchange, - AssetType: asset.Binary, - }) + err = m.Cancel(context.Background(), &order.Cancel{ + OrderID: "ID", + Exchange: testExchange, + AssetType: asset.Binary, + }) if err == nil { t.Error("Expected error due to bad asset type") } o := &order.Detail{ Exchange: testExchange, - ID: "1337", + OrderID: "1337", Status: order.New, } err = m.orderStore.add(o) @@ -397,12 +395,11 @@ func TestCancelOrder(t *testing.T) { t.Error(err) } - err = m.Cancel(context.Background(), - &order.Cancel{ - ID: "Unknown", - Exchange: testExchange, - AssetType: asset.Spot, - }) + err = m.Cancel(context.Background(), &order.Cancel{ + OrderID: "Unknown", + Exchange: testExchange, + AssetType: asset.Spot, + }) if err == nil { t.Error("Expected error due to no order found") } @@ -414,7 +411,7 @@ func TestCancelOrder(t *testing.T) { cancel := &order.Cancel{ Exchange: testExchange, - ID: "1337", + OrderID: "1337", Side: order.Sell, AssetType: asset.Spot, Pair: pair, @@ -442,7 +439,7 @@ func TestGetOrderInfo(t *testing.T) { if err != nil { t.Error(err) } - if result.ID != "1337" { + if result.OrderID != "1337" { t.Error("unexpected order returned") } @@ -451,7 +448,7 @@ func TestGetOrderInfo(t *testing.T) { if err != nil { t.Error(err) } - if result.ID != "1337" { + if result.OrderID != "1337" { t.Error("unexpected order returned") } } @@ -460,7 +457,7 @@ func TestCancelAllOrders(t *testing.T) { m := OrdersSetup(t) o := &order.Detail{ Exchange: testExchange, - ID: "TestCancelAllOrders", + OrderID: "TestCancelAllOrders", Status: order.New, } if err := m.orderStore.add(o); err != nil { @@ -499,12 +496,7 @@ func TestSubmit(t *testing.T) { t.Error("Expected error from nil order") } - o := &order.Submit{ - Exchange: "", - ID: "FakePassingExchangeOrder", - Status: order.New, - Type: order.Market, - } + o := &order.Submit{Type: order.Market} _, err = m.Submit(context.Background(), o) if err == nil { t.Error("Expected error from empty exchange") @@ -566,7 +558,7 @@ func TestSubmit(t *testing.T) { err = m.orderStore.add(&order.Detail{ Exchange: testExchange, - ID: "FakePassingExchangeOrder", + OrderID: "FakePassingExchangeOrder", }) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) @@ -576,7 +568,7 @@ func TestSubmit(t *testing.T) { if err != nil { t.Error(err) } - if o2.InternalOrderID == "" { + if o2.InternalOrderID.IsNil() { t.Error("Failed to assign internal order id") } } @@ -594,7 +586,7 @@ func TestOrderManager_Modify(t *testing.T) { Exchange: testExchange, AssetType: asset.Spot, Pair: pair, - ID: "fake_order_id", + OrderID: "fake_order_id", Price: 8, Amount: 128, }) @@ -620,10 +612,10 @@ func TestOrderManager_Modify(t *testing.T) { if err != nil { t.Fatal(err) } - if det.ID != resp.OrderID || det.Price != price || det.Amount != amount { + if det.OrderID != resp.OrderID || det.Price != price || det.Amount != amount { t.Errorf( "have (%s,%f,%f), want (%s,%f,%f)", - det.ID, det.Price, det.Amount, + det.OrderID, det.Price, det.Amount, resp.OrderID, price, amount, ) } @@ -634,7 +626,7 @@ func TestOrderManager_Modify(t *testing.T) { Exchange: testExchange, AssetType: asset.Spot, Pair: pair, - ID: "fake_order_id", + OrderID: "fake_order_id", // These fields modify the order. Price: 0, Amount: 0, @@ -642,7 +634,7 @@ func TestOrderManager_Modify(t *testing.T) { // [1] Test if nonexistent order returns an error. one := model - one.ID = "nonexistent_order_id" + one.OrderID = "nonexistent_order_id" f(one, true, 0, 0) // [2] Test if price of 0 is ignored. @@ -752,7 +744,7 @@ func TestProcessOrders(t *testing.T) { Side: order.Buy, Status: order.UnknownStatus, LastUpdated: time.Now().Add(-time.Hour), - ID: "Order1-unknown-to-active", + OrderID: "Order1-unknown-to-active", }, { Exchange: testExchange, @@ -762,7 +754,7 @@ func TestProcessOrders(t *testing.T) { Side: order.Sell, Status: order.Active, LastUpdated: time.Now().Add(-time.Hour), - ID: "Order2-active-to-inactive", + OrderID: "Order2-active-to-inactive", }, { Exchange: testExchange, @@ -772,7 +764,7 @@ func TestProcessOrders(t *testing.T) { Side: order.Sell, Status: order.UnknownStatus, LastUpdated: time.Now().Add(-time.Hour), - ID: "Order3-unknown-to-active", + OrderID: "Order3-unknown-to-active", }, } for i := range orders { @@ -785,7 +777,7 @@ func TestProcessOrders(t *testing.T) { // Order1 is not returned by exch.GetActiveOrders() // It will be fetched by exch.GetOrderInfo(), which will say it is active - res, err := m.GetOrdersFiltered(&order.Filter{ID: "Order1-unknown-to-active"}) + res, err := m.GetOrdersFiltered(&order.Filter{OrderID: "Order1-unknown-to-active"}) if err != nil { t.Error(err) } @@ -798,7 +790,7 @@ func TestProcessOrders(t *testing.T) { // Order2 is not returned by exch.GetActiveOrders() // It will be fetched by exch.GetOrderInfo(), which will say it is cancelled - res, err = m.GetOrdersFiltered(&order.Filter{ID: "Order2-active-to-inactive"}) + res, err = m.GetOrdersFiltered(&order.Filter{OrderID: "Order2-active-to-inactive"}) if err != nil { t.Error(err) } @@ -810,7 +802,7 @@ func TestProcessOrders(t *testing.T) { } // Order3 is returned by exch.GetActiveOrders(), which will say it is active - res, err = m.GetOrdersFiltered(&order.Filter{ID: "Order3-unknown-to-active"}) + res, err = m.GetOrdersFiltered(&order.Filter{OrderID: "Order3-unknown-to-active"}) if err != nil { t.Error(err) } @@ -831,11 +823,11 @@ func TestGetOrdersFiltered(t *testing.T) { orders := []order.Detail{ { Exchange: testExchange, - ID: "Test1", + OrderID: "Test1", }, { Exchange: testExchange, - ID: "Test2", + OrderID: "Test2", }, } for i := range orders { @@ -843,7 +835,7 @@ func TestGetOrdersFiltered(t *testing.T) { t.Error(err) } } - res, err := m.GetOrdersFiltered(&order.Filter{ID: "Test2"}) + res, err := m.GetOrdersFiltered(&order.Filter{OrderID: "Test2"}) if err != nil { t.Error(err) } @@ -863,11 +855,11 @@ func Test_getFilteredOrders(t *testing.T) { orders := []order.Detail{ { Exchange: testExchange, - ID: "Test1", + OrderID: "Test1", }, { Exchange: testExchange, - ID: "Test2", + OrderID: "Test2", }, } for i := range orders { @@ -875,7 +867,7 @@ func Test_getFilteredOrders(t *testing.T) { t.Error(err) } } - res, err := m.orderStore.getFilteredOrders(&order.Filter{ID: "Test1"}) + res, err := m.orderStore.getFilteredOrders(&order.Filter{OrderID: "Test1"}) if err != nil { t.Error(err) } @@ -893,14 +885,14 @@ func TestGetOrdersActive(t *testing.T) { Amount: 1.0, Side: order.Buy, Status: order.Cancelled, - ID: "Test1", + OrderID: "Test1", }, { Exchange: testExchange, Amount: 1.0, Side: order.Sell, Status: order.Active, - ID: "Test2", + OrderID: "Test2", }, } for i := range orders { @@ -940,12 +932,12 @@ func Test_processMatchingOrders(t *testing.T) { orders := []order.Detail{ { Exchange: testExchange, - ID: "Test2", + OrderID: "Test2", LastUpdated: time.Now(), }, { Exchange: testExchange, - ID: "Test4", + OrderID: "Test4", LastUpdated: time.Now().Add(-time.Hour), }, } @@ -960,7 +952,7 @@ func Test_processMatchingOrders(t *testing.T) { if len(res) != 1 { t.Errorf("Expected 1 result, got: %d", len(res)) } - if res[0].ID != "Test4" { + if res[0].OrderID != "Test4" { t.Error("Order Test4 should have been fetched and updated") } } @@ -980,7 +972,7 @@ func TestFetchAndUpdateExchangeOrder(t *testing.T) { Amount: 1.0, Side: order.Sell, Status: order.Active, - ID: "Test", + OrderID: "Test", } err = m.FetchAndUpdateExchangeOrder(exch, o, asset.Spot) if err != nil { @@ -1020,14 +1012,14 @@ func Test_getActiveOrders(t *testing.T) { Amount: 1.0, Side: order.Buy, Status: order.Cancelled, - ID: "Test1", + OrderID: "Test1", }, { Exchange: testExchange, Amount: 1.0, Side: order.Sell, Status: order.Active, - ID: "Test2", + OrderID: "Test2", }, } for i := range orders { @@ -1074,7 +1066,7 @@ func TestGetFuturesPositionsForExchange(t *testing.T) { } err = o.orderStore.futuresPositionController.TrackNewOrder(&order.Detail{ - ID: "test", + OrderID: "test", Date: time.Now(), Exchange: "test", AssetType: asset.Futures, @@ -1125,7 +1117,7 @@ func TestClearFuturesPositionsForExchange(t *testing.T) { } err = o.orderStore.futuresPositionController.TrackNewOrder(&order.Detail{ - ID: "test", + OrderID: "test", Date: time.Now(), Exchange: "test", AssetType: asset.Futures, @@ -1180,7 +1172,7 @@ func TestUpdateOpenPositionUnrealisedPNL(t *testing.T) { } err = o.orderStore.futuresPositionController.TrackNewOrder(&order.Detail{ - ID: "test", + OrderID: "test", Date: time.Now(), Exchange: "test", AssetType: asset.Futures, @@ -1209,7 +1201,7 @@ func TestUpdateOpenPositionUnrealisedPNL(t *testing.T) { func TestSubmitFakeOrder(t *testing.T) { t.Parallel() o := &OrderManager{} - resp := order.SubmitResponse{} + resp := &order.SubmitResponse{} _, err := o.SubmitFakeOrder(nil, resp, false) if !errors.Is(err, ErrSubSystemNotStarted) { t.Errorf("received '%v', expected '%v'", err, ErrSubSystemNotStarted) @@ -1240,13 +1232,12 @@ func TestSubmitFakeOrder(t *testing.T) { em.Add(exch) o.orderStore.exchangeManager = em - _, err = o.SubmitFakeOrder(ord, resp, false) - if !errors.Is(err, errUnableToPlaceOrder) { - t.Errorf("received '%v', expected '%v'", err, errUnableToPlaceOrder) + resp, err = ord.DeriveSubmitResponse("1234") + if err != nil { + t.Fatal(err) } - resp.IsOrderPlaced = true - resp.FullyMatched = true + resp.Status = order.Filled o.orderStore.commsManager = &CommunicationManager{} o.orderStore.Orders = make(map[string][]*order.Detail) _, err = o.SubmitFakeOrder(ord, resp, false) @@ -1295,7 +1286,7 @@ func TestUpdateExisting(t *testing.T) { t.Errorf("received '%v', expected '%v'", err, ErrOrderNotFound) } od.AssetType = asset.Futures - od.ID = "123" + od.OrderID = "123" od.Pair = currency.NewPair(currency.BTC, currency.USDT) od.Side = order.Buy od.Type = order.Market diff --git a/engine/order_manager_types.go b/engine/order_manager_types.go index b8166fd5..5b738890 100644 --- a/engine/order_manager_types.go +++ b/engine/order_manager_types.go @@ -24,7 +24,6 @@ var ( errNilCommunicationsManager = errors.New("cannot start with nil communications manager") errNilOrder = errors.New("nil order received") errFuturesTrackerNotSetup = errors.New("futures position tracker not setup") - errUnableToPlaceOrder = errors.New("cannot process order, order not placed") orderManagerDelay = time.Second * 10 ) @@ -61,7 +60,7 @@ type OrderManager struct { // OrderSubmitResponse contains the order response along with an internal order ID type OrderSubmitResponse struct { - order.SubmitResponse + *order.Detail InternalOrderID string } diff --git a/engine/rpcserver.go b/engine/rpcserver.go index a2ef554e..eae4154b 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -970,7 +970,7 @@ func (s *RPCServer) GetOrders(ctx context.Context, r *gctrpc.GetOrdersRequest) ( } o := &gctrpc.OrderDetails{ Exchange: r.Exchange, - Id: resp[x].ID, + Id: resp[x].OrderID, ClientOrderId: resp[x].ClientOrderID, BaseCurrency: resp[x].Pair.Base.String(), QuoteCurrency: resp[x].Pair.Quote.String(), @@ -1059,7 +1059,7 @@ func (s *RPCServer) GetManagedOrders(_ context.Context, r *gctrpc.GetOrdersReque } o := &gctrpc.OrderDetails{ Exchange: r.Exchange, - Id: resp[x].ID, + Id: resp[x].OrderID, ClientOrderId: resp[x].ClientOrderID, BaseCurrency: resp[x].Pair.Base.String(), QuoteCurrency: resp[x].Pair.Quote.String(), @@ -1150,7 +1150,7 @@ func (s *RPCServer) GetOrder(ctx context.Context, r *gctrpc.GetOrderRequest) (*g return &gctrpc.OrderDetails{ Exchange: result.Exchange, - Id: result.ID, + Id: result.OrderID, ClientOrderId: result.ClientOrderID, BaseCurrency: result.Pair.Base.String(), QuoteCurrency: result.Pair.Quote.String(), @@ -1236,9 +1236,9 @@ func (s *RPCServer) SubmitOrder(ctx context.Context, r *gctrpc.SubmitOrderReques return &gctrpc.SubmitOrderResponse{ OrderId: resp.OrderID, - OrderPlaced: resp.IsOrderPlaced, + OrderPlaced: resp.WasOrderPlaced(), Trades: trades, - }, err + }, nil } // SimulateOrder simulates an order specified by exchange, currency pair and asset @@ -1384,7 +1384,7 @@ func (s *RPCServer) CancelOrder(ctx context.Context, r *gctrpc.CancelOrderReques &order.Cancel{ Exchange: r.Exchange, AccountID: r.AccountId, - ID: r.OrderId, + OrderID: r.OrderId, Side: side, WalletAddress: r.WalletAddress, Pair: p, @@ -1434,7 +1434,7 @@ func (s *RPCServer) CancelBatchOrders(ctx context.Context, r *gctrpc.CancelBatch status[orderID] = order.Cancelled.String() request[x] = order.Cancel{ AccountID: r.AccountId, - ID: orderID, + OrderID: orderID, Side: side, WalletAddress: r.WalletAddress, Pair: pair, @@ -1495,17 +1495,14 @@ func (s *RPCServer) ModifyOrder(ctx context.Context, r *gctrpc.ModifyOrderReques if err != nil { return nil, err } - - mod := order.Modify{ + resp, err := s.OrderManager.Modify(ctx, &order.Modify{ Exchange: r.Exchange, AssetType: assetType, Pair: pair, - ID: r.OrderId, - - Amount: r.Amount, - Price: r.Price, - } - resp, err := s.OrderManager.Modify(ctx, &mod) + OrderID: r.OrderId, + Amount: r.Amount, + Price: r.Price, + }) if err != nil { return nil, err } @@ -4304,7 +4301,7 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture } od := &gctrpc.OrderDetails{ Exchange: pos[i].Orders[j].Exchange, - Id: pos[i].Orders[j].ID, + Id: pos[i].Orders[j].OrderID, ClientOrderId: pos[i].Orders[j].ClientOrderID, BaseCurrency: pos[i].Orders[j].Pair.Base.String(), QuoteCurrency: pos[i].Orders[j].Pair.Quote.String(), diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index ee464255..c8ea4681 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -143,7 +143,7 @@ func (f fExchange) GetFuturesPositions(_ context.Context, a asset.Item, cp curre Fee: 1.337, FeeAsset: currency.Code{}, Exchange: f.GetName(), - ID: "test", + OrderID: "test", Side: order.Long, Status: order.Open, AssetType: a, @@ -1827,20 +1827,14 @@ func TestGetManagedOrders(t *testing.T) { } o := order.Detail{ - Price: 100000, - Amount: 0.002, - Exchange: "Binance", - InternalOrderID: "", - ID: "", - ClientOrderID: "", - AccountID: "", - ClientID: "", - WalletAddress: "", - Type: order.Limit, - Side: order.Sell, - Status: order.New, - AssetType: asset.Spot, - Pair: currency.NewPair(currency.BTC, currency.USDT), + Price: 100000, + Amount: 0.002, + Exchange: "Binance", + Type: order.Limit, + Side: order.Sell, + Status: order.New, + AssetType: asset.Spot, + Pair: currency.NewPair(currency.BTC, currency.USDT), } err = om.Add(&o) if err != nil { diff --git a/engine/websocketroutine_manager.go b/engine/websocketroutine_manager.go index 217a030a..735e8693 100644 --- a/engine/websocketroutine_manager.go +++ b/engine/websocketroutine_manager.go @@ -240,11 +240,14 @@ func (m *websocketRoutineManager) websocketDataHandler(exchName string, data int } m.printOrderSummary(d, false) } else { - od, err := m.orderManager.GetByExchangeAndID(d.Exchange, d.ID) + od, err := m.orderManager.GetByExchangeAndID(d.Exchange, d.OrderID) + if err != nil { + return err + } + err = od.UpdateOrderFromDetail(d) if err != nil { return err } - od.UpdateOrderFromDetail(d) err = m.orderManager.UpdateExistingOrder(od) if err != nil { @@ -310,7 +313,7 @@ func (m *websocketRoutineManager) printOrderSummary(o *order.Detail, isUpdate bo o.Status, o.Type, o.Side, - o.ID, + o.OrderID, o.ClientOrderID, o.Price, o.Amount, diff --git a/engine/websocketroutine_manager_test.go b/engine/websocketroutine_manager_test.go index dcd482ab..8c01e900 100644 --- a/engine/websocketroutine_manager_test.go +++ b/engine/websocketroutine_manager_test.go @@ -174,7 +174,7 @@ func TestWebsocketRoutineManagerHandleData(t *testing.T) { } origOrder := &order.Detail{ Exchange: exchName, - ID: orderID, + OrderID: orderID, Amount: 1337, Price: 1337, } @@ -185,13 +185,13 @@ func TestWebsocketRoutineManagerHandleData(t *testing.T) { // Send it again since it exists now err = m.websocketDataHandler(exchName, &order.Detail{ Exchange: exchName, - ID: orderID, + OrderID: orderID, Amount: 1338, }) if err != nil { t.Error(err) } - updated, err := m.orderManager.GetByExchangeAndID(origOrder.Exchange, origOrder.ID) + updated, err := m.orderManager.GetByExchangeAndID(origOrder.Exchange, origOrder.OrderID) if err != nil { t.Error(err) } @@ -201,13 +201,13 @@ func TestWebsocketRoutineManagerHandleData(t *testing.T) { err = m.websocketDataHandler(exchName, &order.Detail{ Exchange: "Bitstamp", - ID: orderID, + OrderID: orderID, Status: order.Active, }) if err != nil { t.Error(err) } - updated, err = m.orderManager.GetByExchangeAndID(origOrder.Exchange, origOrder.ID) + updated, err = m.orderManager.GetByExchangeAndID(origOrder.Exchange, origOrder.OrderID) if err != nil { t.Error(err) } diff --git a/exchanges/alphapoint/alphapoint_test.go b/exchanges/alphapoint/alphapoint_test.go index ea4b41e7..8399b0cd 100644 --- a/exchanges/alphapoint/alphapoint_test.go +++ b/exchanges/alphapoint/alphapoint_test.go @@ -477,6 +477,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: a.Name, Pair: currency.Pair{ Delimiter: "_", Base: currency.BTC, @@ -497,7 +498,7 @@ func TestSubmitOrder(t *testing.T) { if areTestAPIKeysSet() && err != nil { t.Errorf("Withdraw failed to be placed: %v", err) - if !response.IsOrderPlaced { + if response.Status != order.New { t.Errorf("Order failed to be placed: %v", err) } } @@ -511,7 +512,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.BTC, currency.LTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -535,7 +536,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.BTC, currency.LTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index 2b4f04e4..b6495eee 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -243,15 +243,14 @@ func (a *Alphapoint) GetHistoricTrades(_ context.Context, _ currency.Pair, _ ass // SubmitOrder submits a new order and returns a true value when // successfully submitted -func (a *Alphapoint) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (a *Alphapoint) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } fPair, err := a.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } response, err := a.CreateOrder(ctx, @@ -261,17 +260,9 @@ func (a *Alphapoint) SubmitOrder(ctx context.Context, s *order.Submit) (order.Su s.Amount, s.Price) if err != nil { - return submitOrderResponse, err + return nil, err } - if response > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response, 10) - } - if s.Type == order.Market { - submitOrderResponse.FullyMatched = true - } - submitOrderResponse.IsOrderPlaced = true - - return submitOrderResponse, nil + return s.DeriveSubmitResponse(strconv.FormatInt(response, 10)) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -285,7 +276,7 @@ func (a *Alphapoint) CancelOrder(ctx context.Context, o *order.Cancel) error { if err := o.Validate(o.StandardCancel()); err != nil { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -384,7 +375,7 @@ func (a *Alphapoint) GetActiveOrders(ctx context.Context, req *order.GetOrdersRe Exchange: a.Name, ExecutedAmount: resp[x].OpenOrders[y].QtyTotal - resp[x].OpenOrders[y].QtyRemaining, AccountID: strconv.FormatInt(int64(resp[x].OpenOrders[y].AccountID), 10), - ID: strconv.FormatInt(int64(resp[x].OpenOrders[y].ServerOrderID), 10), + OrderID: strconv.FormatInt(int64(resp[x].OpenOrders[y].ServerOrderID), 10), Price: resp[x].OpenOrders[y].Price, RemainingAmount: resp[x].OpenOrders[y].QtyRemaining, } @@ -430,7 +421,7 @@ func (a *Alphapoint) GetOrderHistory(ctx context.Context, req *order.GetOrdersRe AccountID: strconv.FormatInt(int64(resp[x].OpenOrders[y].AccountID), 10), Exchange: a.Name, ExecutedAmount: resp[x].OpenOrders[y].QtyTotal - resp[x].OpenOrders[y].QtyRemaining, - ID: strconv.FormatInt(int64(resp[x].OpenOrders[y].ServerOrderID), 10), + OrderID: strconv.FormatInt(int64(resp[x].OpenOrders[y].ServerOrderID), 10), Price: resp[x].OpenOrders[y].Price, RemainingAmount: resp[x].OpenOrders[y].QtyRemaining, } diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index 645d38cc..9b90d951 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -1770,6 +1770,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: b.Name, Pair: currency.Pair{ Delimiter: "_", Base: currency.LTC, @@ -1801,7 +1802,7 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currency.NewPair(currency.LTC, currency.BTC), @@ -1826,7 +1827,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currency.NewPair(currency.LTC, currency.BTC), @@ -1959,7 +1960,7 @@ func TestCancelOrder(t *testing.T) { err = b.CancelOrder(context.Background(), &order.Cancel{ AssetType: asset.CoinMarginedFutures, Pair: fpair, - ID: "1234", + OrderID: "1234", }) if err != nil { t.Error(err) @@ -1976,7 +1977,7 @@ func TestCancelOrder(t *testing.T) { err = b.CancelOrder(context.Background(), &order.Cancel{ AssetType: asset.USDTMarginedFutures, Pair: fpair2, - ID: "1234", + OrderID: "1234", }) if err != nil { t.Error(err) @@ -2653,7 +2654,7 @@ func TestWsOrderExecutionReport(t *testing.T) { Fee: 0, FeeAsset: currency.BTC, Exchange: "Binance", - ID: "5340845958", + OrderID: "5340845958", ClientOrderID: "c4wyKsIhoAaittTYlIVLqk", Type: order.Limit, Side: order.Buy, diff --git a/exchanges/binance/binance_websocket.go b/exchanges/binance/binance_websocket.go index 278c6d29..c6949b06 100644 --- a/exchanges/binance/binance_websocket.go +++ b/exchanges/binance/binance_websocket.go @@ -272,7 +272,7 @@ func (b *Binance) wsHandleData(respRaw []byte) error { Fee: data.Data.Commission, FeeAsset: feeAsset, Exchange: b.Name, - ID: orderID, + OrderID: orderID, ClientOrderID: clientOrderID, Type: orderType, Side: orderSide, diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index 40d4716f..8718cb94 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -903,11 +903,13 @@ func (a *AggregatedTrade) toTradeData(p currency.Pair, exchange string, aType as } // SubmitOrder submits a new order -func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } + var orderID string + status := order.New + var trades []order.TradeHistory switch s.AssetType { case asset.Spot, asset.Margin: var sideType string @@ -926,8 +928,7 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi case order.Limit: requestParamsOrderType = BinanceRequestParamsOrderLimit default: - submitOrderResponse.IsOrderPlaced = false - return submitOrderResponse, errors.New("unsupported order type") + return nil, errors.New("unsupported order type") } var orderRequest = NewOrderRequest{ @@ -941,26 +942,23 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi } response, err := b.NewOrder(ctx, &orderRequest) if err != nil { - return submitOrderResponse, err + return nil, err } - if response.OrderID > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10) - } + orderID = strconv.FormatInt(response.OrderID, 10) if response.ExecutedQty == response.OrigQty { - submitOrderResponse.FullyMatched = true + status = order.Filled } - submitOrderResponse.IsOrderPlaced = true + trades = make([]order.TradeHistory, len(response.Fills)) for i := range response.Fills { - submitOrderResponse.Trades = append(submitOrderResponse.Trades, order.TradeHistory{ + trades[i] = order.TradeHistory{ Price: response.Fills[i].Price, Amount: response.Fills[i].Qty, Fee: response.Fills[i].Commission, FeeAsset: response.Fills[i].CommissionAsset, - }) + } } - case asset.CoinMarginedFutures: var reqSide string switch s.Side { @@ -969,7 +967,7 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi case order.Sell: reqSide = "SELL" default: - return submitOrderResponse, fmt.Errorf("invalid side") + return nil, fmt.Errorf("invalid side") } var ( @@ -994,7 +992,7 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi case order.TrailingStop: oType = cfuturesTrailingStopMarket default: - return submitOrderResponse, errors.New("invalid type, check api docs for updates") + return nil, errors.New("invalid type, check api docs for updates") } o, err := b.FuturesNewOrder( @@ -1011,10 +1009,9 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi }, ) if err != nil { - return submitOrderResponse, err + return nil, err } - submitOrderResponse.OrderID = strconv.FormatInt(o.OrderID, 10) - submitOrderResponse.IsOrderPlaced = true + orderID = strconv.FormatInt(o.OrderID, 10) case asset.USDTMarginedFutures: var reqSide string switch s.Side { @@ -1023,7 +1020,7 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi case order.Sell: reqSide = "SELL" default: - return submitOrderResponse, fmt.Errorf("invalid side") + return nil, fmt.Errorf("invalid side") } var oType string switch s.Type { @@ -1042,7 +1039,7 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi case order.TrailingStop: oType = "TRAILING_STOP_MARKET" default: - return submitOrderResponse, errors.New("invalid type, check api docs for updates") + return nil, errors.New("invalid type, check api docs for updates") } order, err := b.UFuturesNewOrder(ctx, s.Pair, reqSide, @@ -1050,15 +1047,20 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi s.ClientOrderID, "", "", s.Amount, s.Price, 0, 0, 0, s.ReduceOnly) if err != nil { - return submitOrderResponse, err + return nil, err } - submitOrderResponse.OrderID = strconv.FormatInt(order.OrderID, 10) - submitOrderResponse.IsOrderPlaced = true + orderID = strconv.FormatInt(order.OrderID, 10) default: - return submitOrderResponse, fmt.Errorf("assetType not supported") + return nil, fmt.Errorf("assetType not supported") } - return submitOrderResponse, nil + resp, err := s.DeriveSubmitResponse(orderID) + if err != nil { + return nil, err + } + resp.Trades = trades + resp.Status = status + return resp, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -1074,7 +1076,7 @@ func (b *Binance) CancelOrder(ctx context.Context, o *order.Cancel) error { } switch o.AssetType { case asset.Spot, asset.Margin: - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -1086,12 +1088,12 @@ func (b *Binance) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } case asset.CoinMarginedFutures: - _, err := b.FuturesCancelOrder(ctx, o.Pair, o.ID, "") + _, err := b.FuturesCancelOrder(ctx, o.Pair, o.OrderID, "") if err != nil { return err } case asset.USDTMarginedFutures: - _, err := b.UCancelOrder(ctx, o.Pair, o.ID, "") + _, err := b.UCancelOrder(ctx, o.Pair, o.OrderID, "") if err != nil { return err } @@ -1198,7 +1200,7 @@ func (b *Binance) GetOrderInfo(ctx context.Context, orderID string, pair currenc return order.Detail{ Amount: resp.OrigQty, Exchange: b.Name, - ID: strconv.FormatInt(resp.OrderID, 10), + OrderID: strconv.FormatInt(resp.OrderID, 10), ClientOrderID: resp.ClientOrderID, Side: side, Type: orderType, @@ -1231,7 +1233,7 @@ func (b *Binance) GetOrderInfo(ctx context.Context, orderID string, pair currenc respData.Exchange = b.Name respData.ExecutedAmount = orderData.ExecutedQuantity respData.Fee = fee - respData.ID = orderID + respData.OrderID = orderID respData.Pair = pair respData.Price = orderData.Price respData.RemainingAmount = orderData.OriginalQuantity - orderData.ExecutedQuantity @@ -1260,7 +1262,7 @@ func (b *Binance) GetOrderInfo(ctx context.Context, orderID string, pair currenc respData.Exchange = b.Name respData.ExecutedAmount = orderData.ExecutedQuantity respData.Fee = fee - respData.ID = orderID + respData.OrderID = orderID respData.Pair = pair respData.Price = orderData.Price respData.RemainingAmount = orderData.OriginalQuantity - orderData.ExecutedQuantity @@ -1372,7 +1374,7 @@ func (b *Binance) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque Amount: resp[x].OrigQty, Date: resp[x].Time, Exchange: b.Name, - ID: strconv.FormatInt(resp[x].OrderID, 10), + OrderID: strconv.FormatInt(resp[x].OrderID, 10), ClientOrderID: resp[x].ClientOrderID, Side: side, Type: orderType, @@ -1405,7 +1407,7 @@ func (b *Binance) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque RemainingAmount: openOrders[y].OrigQty - openOrders[y].ExecutedQty, Fee: fee, Exchange: b.Name, - ID: strconv.FormatInt(openOrders[y].OrderID, 10), + OrderID: strconv.FormatInt(openOrders[y].OrderID, 10), ClientOrderID: openOrders[y].ClientOrderID, Type: orderVars.OrderType, Side: orderVars.Side, @@ -1438,7 +1440,7 @@ func (b *Binance) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque RemainingAmount: openOrders[y].OriginalQuantity - openOrders[y].ExecutedQuantity, Fee: fee, Exchange: b.Name, - ID: strconv.FormatInt(openOrders[y].OrderID, 10), + OrderID: strconv.FormatInt(openOrders[y].OrderID, 10), ClientOrderID: openOrders[y].ClientOrderID, Type: orderVars.OrderType, Side: orderVars.Side, @@ -1519,7 +1521,7 @@ func (b *Binance) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque Date: resp[i].Time, LastUpdated: resp[i].UpdateTime, Exchange: b.Name, - ID: strconv.FormatInt(resp[i].OrderID, 10), + OrderID: strconv.FormatInt(resp[i].OrderID, 10), Side: side, Type: orderType, Price: resp[i].Price, @@ -1577,7 +1579,7 @@ func (b *Binance) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque RemainingAmount: orderHistory[y].OrigQty - orderHistory[y].ExecutedQty, Fee: fee, Exchange: b.Name, - ID: strconv.FormatInt(orderHistory[y].OrderID, 10), + OrderID: strconv.FormatInt(orderHistory[y].OrderID, 10), ClientOrderID: orderHistory[y].ClientOrderID, Type: orderVars.OrderType, Side: orderVars.Side, @@ -1635,7 +1637,7 @@ func (b *Binance) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque RemainingAmount: orderHistory[y].OrigQty - orderHistory[y].ExecutedQty, Fee: fee, Exchange: b.Name, - ID: strconv.FormatInt(orderHistory[y].OrderID, 10), + OrderID: strconv.FormatInt(orderHistory[y].OrderID, 10), ClientOrderID: orderHistory[y].ClientOrderID, Type: orderVars.OrderType, Side: orderVars.Side, diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index d8556906..53d3db91 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -875,6 +875,7 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } var orderSubmission = &order.Submit{ + Exchange: b.Name, Pair: currency.Pair{ Delimiter: "_", Base: currency.XRP, @@ -892,7 +893,7 @@ func TestSubmitOrder(t *testing.T) { if areTestAPIKeysSet() && err != nil { t.Errorf("Could not place order: %v", err) } - if areTestAPIKeysSet() && !response.IsOrderPlaced { + if areTestAPIKeysSet() && response.Status != order.New { t.Error("Order not placed") } if !areTestAPIKeysSet() && err == nil { @@ -908,7 +909,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -932,7 +933,7 @@ func TestCancelAllExchangeOrdera(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/bitfinex/bitfinex_websocket.go b/exchanges/bitfinex/bitfinex_websocket.go index 7758b9c1..f2e5357d 100644 --- a/exchanges/bitfinex/bitfinex_websocket.go +++ b/exchanges/bitfinex/bitfinex_websocket.go @@ -1292,7 +1292,7 @@ func (b *Bitfinex) wsHandleOrder(data []interface{}) { od.Exchange = b.Name if data[0] != nil { if id, ok := data[0].(float64); ok { - od.ID = strconv.FormatFloat(id, 'f', -1, 64) + od.OrderID = strconv.FormatFloat(id, 'f', -1, 64) } } if data[16] != nil { @@ -1340,7 +1340,7 @@ func (b *Bitfinex) wsHandleOrder(data []interface{}) { if err != nil { b.Websocket.DataHandler <- order.ClassificationError{ Exchange: b.Name, - OrderID: od.ID, + OrderID: od.OrderID, Err: err, } } @@ -1353,7 +1353,7 @@ func (b *Bitfinex) wsHandleOrder(data []interface{}) { if err != nil { b.Websocket.DataHandler <- order.ClassificationError{ Exchange: b.Name, - OrderID: od.ID, + OrderID: od.OrderID, Err: err, } } diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index 9d611ce8..0bbc28fc 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -620,20 +620,21 @@ allTrades: } // SubmitOrder submits a new order -func (b *Bitfinex) SubmitOrder(ctx context.Context, o *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (b *Bitfinex) SubmitOrder(ctx context.Context, o *order.Submit) (*order.SubmitResponse, error) { err := o.Validate() if err != nil { - return submitOrderResponse, err + return nil, err } fpair, err := b.FormatExchangeCurrency(o.Pair, o.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } + var orderID string + status := order.New if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() { - submitOrderResponse.OrderID, err = b.WsNewOrder(&WsNewOrderRequest{ + orderID, err = b.WsNewOrder(&WsNewOrderRequest{ CustomID: b.Websocket.AuthConn.GenerateMessageID(false), Type: o.Type.String(), Symbol: fpair.String(), @@ -641,11 +642,10 @@ func (b *Bitfinex) SubmitOrder(ctx context.Context, o *order.Submit) (order.Subm Price: o.Price, }) if err != nil { - return submitOrderResponse, err + return nil, err } } else { var response Order - isBuying := o.Side == order.Buy b.appendOptionalDelimiter(&fpair) orderType := o.Type.Lower() if o.AssetType == asset.Spot { @@ -656,21 +656,23 @@ func (b *Bitfinex) SubmitOrder(ctx context.Context, o *order.Submit) (order.Subm orderType, o.Amount, o.Price, - isBuying, + o.Side == order.Buy, false) if err != nil { - return submitOrderResponse, err - } - if response.ID > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10) - } - if response.RemainingAmount == 0 { - submitOrderResponse.FullyMatched = true + return nil, err } + orderID = strconv.FormatInt(response.ID, 10) - submitOrderResponse.IsOrderPlaced = true + if response.RemainingAmount == 0 { + status = order.Filled + } } - return submitOrderResponse, err + resp, err := o.DeriveSubmitResponse(orderID) + if err != nil { + return nil, err + } + resp.Status = status + return resp, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -684,9 +686,9 @@ func (b *Bitfinex) ModifyOrder(ctx context.Context, action *order.Modify) (*orde return nil, err } - orderIDInt, err := strconv.ParseInt(action.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(action.OrderID, 10, 64) if err != nil { - return &order.ModifyResponse{OrderID: action.ID}, err + return &order.ModifyResponse{OrderID: action.OrderID}, err } wsRequest := WsUpdateOrderRequest{ @@ -710,7 +712,7 @@ func (b *Bitfinex) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -916,7 +918,7 @@ func (b *Bitfinex) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ Amount: resp[i].OriginalAmount, Date: time.Unix(int64(timestamp), 0), Exchange: b.Name, - ID: strconv.FormatInt(resp[i].ID, 10), + OrderID: strconv.FormatInt(resp[i].ID, 10), Side: side, Price: resp[i].Price, RemainingAmount: resp[i].RemainingAmount, @@ -996,7 +998,7 @@ func (b *Bitfinex) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ Amount: resp[i].OriginalAmount, Date: orderDate, Exchange: b.Name, - ID: strconv.FormatInt(resp[i].ID, 10), + OrderID: strconv.FormatInt(resp[i].ID, 10), Side: side, Price: resp[i].Price, AverageExecutedPrice: resp[i].AverageExecutionPrice, diff --git a/exchanges/bitflyer/bitflyer_test.go b/exchanges/bitflyer/bitflyer_test.go index 78afd280..c1bb2471 100644 --- a/exchanges/bitflyer/bitflyer_test.go +++ b/exchanges/bitflyer/bitflyer_test.go @@ -333,6 +333,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: b.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.LTC, @@ -358,7 +359,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -380,7 +381,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/bitflyer/bitflyer_wrapper.go b/exchanges/bitflyer/bitflyer_wrapper.go index a0a52cd3..fbf6238e 100644 --- a/exchanges/bitflyer/bitflyer_wrapper.go +++ b/exchanges/bitflyer/bitflyer_wrapper.go @@ -389,8 +389,8 @@ func (b *Bitflyer) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset } // SubmitOrder submits a new order -func (b *Bitflyer) SubmitOrder(_ context.Context, _ *order.Submit) (order.SubmitResponse, error) { - return order.SubmitResponse{}, common.ErrNotYetImplemented +func (b *Bitflyer) SubmitOrder(_ context.Context, _ *order.Submit) (*order.SubmitResponse, error) { + return nil, common.ErrNotYetImplemented } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/bithumb/bithumb_test.go b/exchanges/bithumb/bithumb_test.go index af281a57..51acd5c1 100644 --- a/exchanges/bithumb/bithumb_test.go +++ b/exchanges/bithumb/bithumb_test.go @@ -416,6 +416,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: b.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.LTC, @@ -428,7 +429,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := b.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -443,7 +444,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -467,7 +468,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -510,7 +511,7 @@ func TestModifyOrder(t *testing.T) { t.Fatal(err) } _, err = b.ModifyOrder(context.Background(), &order.Modify{ - ID: "1337", + OrderID: "1337", Price: 100, Amount: 1000, Side: order.Sell, diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 05ad80aa..2156fc01 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -473,15 +473,14 @@ func (b *Bithumb) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset. // SubmitOrder submits a new order // TODO: Fill this out to support limit orders -func (b *Bithumb) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (b *Bithumb) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } fPair, err := b.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } var orderID string @@ -489,24 +488,18 @@ func (b *Bithumb) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi var result MarketBuy result, err = b.MarketBuyOrder(ctx, fPair, s.Amount) if err != nil { - return submitOrderResponse, err + return nil, err } orderID = result.OrderID } else if s.Side == order.Sell { var result MarketSell result, err = b.MarketSellOrder(ctx, fPair, s.Amount) if err != nil { - return submitOrderResponse, err + return nil, err } orderID = result.OrderID } - if orderID != "" { - submitOrderResponse.OrderID = orderID - submitOrderResponse.FullyMatched = true - } - submitOrderResponse.IsOrderPlaced = true - - return submitOrderResponse, nil + return s.DeriveSubmitResponse(orderID) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -521,9 +514,7 @@ func (b *Bithumb) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - _, err := b.CancelTrade(ctx, o.Side.String(), - o.ID, - o.Pair.Base.String()) + _, err := b.CancelTrade(ctx, o.Side.String(), o.OrderID, o.Pair.Base.String()) return err } @@ -692,7 +683,7 @@ func (b *Bithumb) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque Amount: resp.Data[i].Units, Exchange: b.Name, ExecutedAmount: resp.Data[i].Units - resp.Data[i].UnitsRemaining, - ID: resp.Data[i].OrderID, + OrderID: resp.Data[i].OrderID, Date: resp.Data[i].OrderDate.Time(), Price: resp.Data[i].Price, RemainingAmount: resp.Data[i].UnitsRemaining, @@ -755,7 +746,7 @@ func (b *Bithumb) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque ExecutedAmount: resp.Data[i].Units - resp.Data[i].UnitsRemaining, RemainingAmount: resp.Data[i].UnitsRemaining, Exchange: b.Name, - ID: resp.Data[i].OrderID, + OrderID: resp.Data[i].OrderID, Date: resp.Data[i].OrderDate.Time(), Price: resp.Data[i].Price, Pair: currency.NewPairWithDelimiter(resp.Data[i].OrderCurrency, diff --git a/exchanges/bitmex/bitmex_test.go b/exchanges/bitmex/bitmex_test.go index 4a4767b3..c3599d6f 100644 --- a/exchanges/bitmex/bitmex_test.go +++ b/exchanges/bitmex/bitmex_test.go @@ -663,6 +663,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: b.Name, Pair: currency.Pair{ Base: currency.XBT, Quote: currency.USD, @@ -675,7 +676,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Futures, } response, err := b.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -690,7 +691,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "123456789012345678901234567890123456", + OrderID: "123456789012345678901234567890123456", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -714,7 +715,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "123456789012345678901234567890123456", + OrderID: "123456789012345678901234567890123456", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -766,7 +767,7 @@ func TestModifyOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } _, err := b.ModifyOrder(context.Background(), - &order.Modify{ID: "1337", AssetType: asset.Futures}) + &order.Modify{OrderID: "1337", AssetType: asset.Futures}) if err == nil { t.Error("ModifyOrder() error") } diff --git a/exchanges/bitmex/bitmex_websocket.go b/exchanges/bitmex/bitmex_websocket.go index 1d8cfa85..6e8efa66 100644 --- a/exchanges/bitmex/bitmex_websocket.go +++ b/exchanges/bitmex/bitmex_websocket.go @@ -315,7 +315,7 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error { } b.Websocket.DataHandler <- &order.Detail{ Exchange: b.Name, - ID: response.Data[i].OrderID, + OrderID: response.Data[i].OrderID, AccountID: strconv.FormatInt(response.Data[i].Account, 10), AssetType: a, Pair: p, @@ -379,7 +379,7 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error { Price: response.Data[x].Price, Amount: response.Data[x].OrderQuantity, Exchange: b.Name, - ID: response.Data[x].OrderID, + OrderID: response.Data[x].OrderID, AccountID: strconv.FormatInt(response.Data[x].Account, 10), Type: oType, Side: oSide, @@ -428,7 +428,7 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error { Price: response.Data[x].Price, Amount: response.Data[x].OrderQuantity, Exchange: b.Name, - ID: response.Data[x].OrderID, + OrderID: response.Data[x].OrderID, AccountID: strconv.FormatInt(response.Data[x].Account, 10), Type: oType, Side: oSide, diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index 7f538865..dcc34277 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -565,20 +565,19 @@ allTrades: } // SubmitOrder submits a new order -func (b *Bitmex) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (b *Bitmex) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } if math.Mod(s.Amount, 1) != 0 { - return submitOrderResponse, + return nil, errors.New("order contract amount can not have decimals") } fPair, err := b.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } var orderNewParams = OrderNewParams{ @@ -594,17 +593,9 @@ func (b *Bitmex) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit response, err := b.CreateOrder(ctx, &orderNewParams) if err != nil { - return submitOrderResponse, err + return nil, err } - if response.OrderID != "" { - submitOrderResponse.OrderID = response.OrderID - } - if s.Type == order.Market { - submitOrderResponse.FullyMatched = true - } - submitOrderResponse.IsOrderPlaced = true - - return submitOrderResponse, nil + return s.DeriveSubmitResponse(response.OrderID) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -619,7 +610,7 @@ func (b *Bitmex) ModifyOrder(ctx context.Context, action *order.Modify) (*order. } o, err := b.AmendOrder(ctx, &OrderAmendParams{ - OrderID: action.ID, + OrderID: action.OrderID, OrderQty: int32(action.Amount), Price: action.Price}) if err != nil { @@ -642,10 +633,9 @@ func (b *Bitmex) CancelOrder(ctx context.Context, o *order.Cancel) error { if err := o.Validate(o.StandardCancel()); err != nil { return err } - var params = OrderCancelParams{ - OrderID: o.ID, - } - _, err := b.CancelOrders(ctx, ¶ms) + _, err := b.CancelOrders(ctx, &OrderCancelParams{ + OrderID: o.OrderID, + }) return err } @@ -781,7 +771,7 @@ func (b *Bitmex) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques ExecutedAmount: resp[i].CumQty, RemainingAmount: resp[i].LeavesQty, Exchange: b.Name, - ID: resp[i].OrderID, + OrderID: resp[i].OrderID, Side: orderSideMap[resp[i].Side], Status: orderStatus, Type: oType, @@ -848,7 +838,7 @@ func (b *Bitmex) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques Date: resp[i].TransactTime, CloseTime: resp[i].Timestamp, Exchange: b.Name, - ID: resp[i].OrderID, + OrderID: resp[i].OrderID, Side: orderSide, Status: orderStatus, Type: oType, diff --git a/exchanges/bitstamp/bitstamp_test.go b/exchanges/bitstamp/bitstamp_test.go index 809b3fde..a11139e2 100644 --- a/exchanges/bitstamp/bitstamp_test.go +++ b/exchanges/bitstamp/bitstamp_test.go @@ -397,6 +397,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: b.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.USD, @@ -410,7 +411,7 @@ func TestSubmitOrder(t *testing.T) { } response, err := b.SubmitOrder(context.Background(), orderSubmission) switch { - case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) && !mockTests: + case areTestAPIKeysSet() && (err != nil || response.Status != order.New) && !mockTests: t.Errorf("Order failed to be placed: %v", err) case !areTestAPIKeysSet() && err == nil && !mockTests: t.Error("Expecting an error when no keys are set") @@ -427,7 +428,7 @@ func TestCancelExchangeOrder(t *testing.T) { } orderCancellation := &order.Cancel{ - ID: "1234", + OrderID: "1234", AssetType: asset.Spot, } err := b.CancelOrder(context.Background(), orderCancellation) diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index 89f6d2ef..e9b6fb47 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -526,37 +526,26 @@ func (b *Bitstamp) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset } // SubmitOrder submits a new order -func (b *Bitstamp) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (b *Bitstamp) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } fPair, err := b.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } - buy := s.Side == order.Buy - market := s.Type == order.Market response, err := b.PlaceOrder(ctx, fPair.String(), s.Price, s.Amount, - buy, - market) + s.Side == order.Buy, + s.Type == order.Market) if err != nil { - return submitOrderResponse, err + return nil, err } - if response.ID > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10) - } - - submitOrderResponse.IsOrderPlaced = true - if s.Type == order.Market { - submitOrderResponse.FullyMatched = true - } - return submitOrderResponse, nil + return s.DeriveSubmitResponse(strconv.FormatInt(response.ID, 10)) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -571,7 +560,7 @@ func (b *Bitstamp) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -751,7 +740,7 @@ func (b *Bitstamp) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ orders[i] = order.Detail{ Amount: resp[i].Amount, - ID: strconv.FormatInt(resp[i].ID, 10), + OrderID: strconv.FormatInt(resp[i].ID, 10), Price: resp[i].Price, Type: order.Limit, Side: orderSide, @@ -841,7 +830,7 @@ func (b *Bitstamp) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ } orders = append(orders, order.Detail{ - ID: strconv.FormatInt(resp[i].OrderID, 10), + OrderID: strconv.FormatInt(resp[i].OrderID, 10), Date: tm, Exchange: b.Name, Pair: currPair, diff --git a/exchanges/bittrex/bittrex_test.go b/exchanges/bittrex/bittrex_test.go index 76e56e6b..899c9ff7 100644 --- a/exchanges/bittrex/bittrex_test.go +++ b/exchanges/bittrex/bittrex_test.go @@ -525,6 +525,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: b.GetName(), Pair: currency.Pair{ Delimiter: currency.DashDelimiter, Base: currency.BTC, @@ -538,7 +539,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := b.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -552,7 +553,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -575,7 +576,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/bittrex/bittrex_websocket.go b/exchanges/bittrex/bittrex_websocket.go index 1c28ae3f..a82a3edb 100644 --- a/exchanges/bittrex/bittrex_websocket.go +++ b/exchanges/bittrex/bittrex_websocket.go @@ -624,7 +624,7 @@ func (b *Bittrex) WsProcessUpdateOrder(data *OrderUpdateMessage) error { RemainingAmount: data.Delta.Quantity - data.Delta.FillQuantity, ExecutedAmount: data.Delta.FillQuantity, Exchange: b.Name, - ID: data.Delta.ID, + OrderID: data.Delta.ID, Type: orderType, Side: orderSide, Status: orderStatus, diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index 94660a90..108fc3f8 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -549,9 +549,9 @@ func (b *Bittrex) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset. } // SubmitOrder submits a new order -func (b *Bittrex) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { +func (b *Bittrex) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return order.SubmitResponse{}, err + return nil, err } if s.Side == order.Ask { @@ -564,7 +564,7 @@ func (b *Bittrex) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi formattedPair, err := b.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return order.SubmitResponse{}, err + return nil, err } orderData, err := b.Order(ctx, @@ -576,13 +576,9 @@ func (b *Bittrex) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi s.Amount, 0.0) if err != nil { - return order.SubmitResponse{}, err + return nil, err } - - return order.SubmitResponse{ - IsOrderPlaced: true, - OrderID: orderData.ID, - }, nil + return s.DeriveSubmitResponse(orderData.ID) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -596,7 +592,7 @@ func (b *Bittrex) CancelOrder(ctx context.Context, ord *order.Cancel) error { if err := ord.Validate(ord.StandardCancel()); err != nil { return err } - _, err := b.CancelExistingOrder(ctx, ord.ID) + _, err := b.CancelExistingOrder(ctx, ord.OrderID) return err } @@ -697,7 +693,7 @@ func (b *Bittrex) ConstructOrderDetail(orderData *OrderData) (order.Detail, erro RemainingAmount: orderData.Quantity - orderData.FillQuantity, Price: orderData.Limit, Date: orderData.CreatedAt, - ID: orderData.ID, + OrderID: orderData.ID, Exchange: b.Name, Type: orderType, Pair: orderPair, @@ -809,7 +805,7 @@ func (b *Bittrex) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque ExecutedAmount: orderData[i].FillQuantity, Price: orderData[i].Limit, Date: orderData[i].CreatedAt, - ID: orderData[i].ID, + OrderID: orderData[i].ID, Exchange: b.Name, Type: orderType, Side: orderSide, @@ -895,7 +891,7 @@ func (b *Bittrex) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque Price: orderData[i].Limit, Date: orderData[i].CreatedAt, CloseTime: orderData[i].ClosedAt, - ID: orderData[i].ID, + OrderID: orderData[i].ID, Exchange: b.Name, Type: orderType, Side: orderSide, diff --git a/exchanges/btcmarkets/btcmarkets_test.go b/exchanges/btcmarkets/btcmarkets_test.go index ae957b10..bb6e1f94 100644 --- a/exchanges/btcmarkets/btcmarkets_test.go +++ b/exchanges/btcmarkets/btcmarkets_test.go @@ -217,6 +217,7 @@ func TestGetTradeByID(t *testing.T) { func TestSubmitOrder(t *testing.T) { t.Parallel() _, err := b.SubmitOrder(context.Background(), &order.Submit{ + Exchange: b.Name, Price: 100, Amount: 1, Type: order.TrailingStop, @@ -229,6 +230,7 @@ func TestSubmitOrder(t *testing.T) { t.Fatalf("received: '%v' but expected: '%v'", err, order.ErrTypeIsInvalid) } _, err = b.SubmitOrder(context.Background(), &order.Submit{ + Exchange: b.Name, Price: 100, Amount: 1, Type: order.Limit, @@ -245,6 +247,7 @@ func TestSubmitOrder(t *testing.T) { t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") } _, err = b.SubmitOrder(context.Background(), &order.Submit{ + Exchange: b.Name, Price: 100, Amount: 1, Type: order.Limit, @@ -1083,7 +1086,7 @@ func TestWrapperModifyOrder(t *testing.T) { AssetType: asset.Spot, Price: 100000, Amount: 0.001, - ID: "8207123461", + OrderID: "8207123461", ClientOrderID: "bruh3", }) if !errors.Is(err, nil) { diff --git a/exchanges/btcmarkets/btcmarkets_websocket.go b/exchanges/btcmarkets/btcmarkets_websocket.go index cbaa7ccc..1c91f3ad 100644 --- a/exchanges/btcmarkets/btcmarkets_websocket.go +++ b/exchanges/btcmarkets/btcmarkets_websocket.go @@ -300,7 +300,7 @@ func (b *BTCMarkets) wsHandleData(respRaw []byte) error { Amount: originalAmount, RemainingAmount: orderData.OpenVolume, Exchange: b.Name, - ID: orderID, + OrderID: orderID, ClientID: creds.ClientID, Type: oType, Side: oSide, diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 557f5261..882bfec4 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -530,10 +530,9 @@ func (b *BTCMarkets) GetHistoricTrades(_ context.Context, _ currency.Pair, _ ass } // SubmitOrder submits a new order -func (b *BTCMarkets) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var resp order.SubmitResponse +func (b *BTCMarkets) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return resp, err + return nil, err } if s.Side == order.Sell { @@ -545,17 +544,17 @@ func (b *BTCMarkets) SubmitOrder(ctx context.Context, s *order.Submit) (order.Su fpair, err := b.FormatExchangeCurrency(s.Pair, asset.Spot) if err != nil { - return resp, err + return nil, err } fOrderType, err := b.formatOrderType(s.Type) if err != nil { - return resp, err + return nil, err } fOrderSide, err := b.formatOrderSide(s.Side) if err != nil { - return resp, err + return nil, err } tempResp, err := b.NewOrder(ctx, @@ -571,11 +570,9 @@ func (b *BTCMarkets) SubmitOrder(ctx context.Context, s *order.Submit) (order.Su s.ClientID, s.PostOnly) if err != nil { - return resp, err + return nil, err } - resp.IsOrderPlaced = true - resp.OrderID = tempResp.OrderID - return resp, nil + return s.DeriveSubmitResponse(tempResp.OrderID) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -584,7 +581,7 @@ func (b *BTCMarkets) ModifyOrder(ctx context.Context, action *order.Modify) (*or if err := action.Validate(); err != nil { return nil, err } - resp, err := b.ReplaceOrder(ctx, action.ID, action.ClientOrderID, action.Price, action.Amount) + resp, err := b.ReplaceOrder(ctx, action.OrderID, action.ClientOrderID, action.Price, action.Amount) if err != nil { return nil, err } @@ -622,7 +619,7 @@ func (b *BTCMarkets) CancelOrder(ctx context.Context, o *order.Cancel) error { if err != nil { return err } - _, err = b.RemoveOrder(ctx, o.ID) + _, err = b.RemoveOrder(ctx, o.OrderID) return err } @@ -675,7 +672,7 @@ func (b *BTCMarkets) GetOrderInfo(ctx context.Context, orderID string, pair curr } resp.Exchange = b.Name - resp.ID = orderID + resp.OrderID = orderID resp.Pair = p resp.Price = o.Price resp.Date = o.CreationTime @@ -828,7 +825,7 @@ func (b *BTCMarkets) GetActiveOrders(ctx context.Context, req *order.GetOrdersRe var tempResp order.Detail tempResp.Exchange = b.Name tempResp.Pair = req.Pairs[x] - tempResp.ID = tempData[y].OrderID + tempResp.OrderID = tempData[y].OrderID tempResp.Side = order.Bid if tempData[y].Side == ask { tempResp.Side = order.Ask @@ -945,7 +942,7 @@ func (b *BTCMarkets) GetOrderHistory(ctx context.Context, req *order.GetOrdersRe if tempData.Orders[c].Side == ask { tempResp.Side = order.Ask } - tempResp.ID = tempData.Orders[c].OrderID + tempResp.OrderID = tempData.Orders[c].OrderID tempResp.Date = tempData.Orders[c].CreationTime tempResp.Price = tempData.Orders[c].Price tempResp.Amount = tempData.Orders[c].Amount diff --git a/exchanges/btse/btse_test.go b/exchanges/btse/btse_test.go index 6fcc803d..8ee17556 100644 --- a/exchanges/btse/btse_test.go +++ b/exchanges/btse/btse_test.go @@ -585,6 +585,7 @@ func TestSubmitOrder(t *testing.T) { t.Skip("skipping test, either api keys are unset or canManipulateRealOrders is false") } var orderSubmission = &order.Submit{ + Exchange: b.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.USD, @@ -597,7 +598,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := b.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -625,7 +626,7 @@ func TestCancelExchangeOrder(t *testing.T) { currency.USD.String(), "-") var orderCancellation = &order.Cancel{ - ID: "b334ecef-2b42-4998-b8a4-b6b14f6d2671", + OrderID: "b334ecef-2b42-4998-b8a4-b6b14f6d2671", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -657,7 +658,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currency.USD.String(), "-") var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/btse/btse_websocket.go b/exchanges/btse/btse_websocket.go index d6c6a787..22c9d7c3 100644 --- a/exchanges/btse/btse_websocket.go +++ b/exchanges/btse/btse_websocket.go @@ -215,7 +215,7 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { Amount: notification.Data[i].Size, TriggerPrice: notification.Data[i].TriggerPrice, Exchange: b.Name, - ID: notification.Data[i].OrderID, + OrderID: notification.Data[i].OrderID, Type: oType, Side: oSide, Status: oStatus, diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go index bb261a2a..bb597bc6 100644 --- a/exchanges/btse/btse_wrapper.go +++ b/exchanges/btse/btse_wrapper.go @@ -509,19 +509,18 @@ func (b *BTSE) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.Ite } // SubmitOrder submits a new order -func (b *BTSE) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var resp order.SubmitResponse +func (b *BTSE) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return resp, err + return nil, err } fPair, err := b.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return resp, err + return nil, err } inLimits := b.withinLimits(fPair, s.Amount) if !inLimits { - return resp, errors.New("order outside of limits") + return nil, errors.New("order outside of limits") } r, err := b.CreateOrder(ctx, @@ -537,16 +536,14 @@ func (b *BTSE) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitRe "", s.Type.String()) if err != nil { - return resp, err + return nil, err } - resp.IsOrderPlaced = true - resp.OrderID = r[0].OrderID - - if s.Type == order.Market { - resp.FullyMatched = true + var orderID string + if len(r) > 0 { + orderID = r[0].OrderID } - return resp, nil + return s.DeriveSubmitResponse(orderID) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -561,13 +558,12 @@ func (b *BTSE) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - fPair, err := b.FormatExchangeCurrency(o.Pair, - o.AssetType) + fPair, err := b.FormatExchangeCurrency(o.Pair, o.AssetType) if err != nil { return err } - _, err = b.CancelExistingOrder(ctx, o.ID, fPair.String(), o.ClientOrderID) + _, err = b.CancelExistingOrder(ctx, o.OrderID, fPair.String(), o.ClientOrderID) if err != nil { return err } @@ -656,7 +652,7 @@ func (b *BTSE) GetOrderInfo(ctx context.Context, orderID string, pair currency.P } od.Exchange = b.Name od.Amount = o[i].Size - od.ID = o[i].OrderID + od.OrderID = o[i].OrderID od.Date = time.Unix(o[i].Timestamp, 0) od.Side = side @@ -824,7 +820,7 @@ func (b *BTSE) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest) Amount: resp[i].Size, ExecutedAmount: resp[i].FilledSize, RemainingAmount: resp[i].Size - resp[i].FilledSize, - ID: resp[i].OrderID, + OrderID: resp[i].OrderID, Date: time.Unix(resp[i].Timestamp, 0), Side: side, Price: resp[i].Price, @@ -934,7 +930,7 @@ func (b *BTSE) GetOrderHistory(ctx context.Context, getOrdersRequest *order.GetO } orderTime := time.UnixMilli(currentOrder[y].Timestamp) tempOrder := order.Detail{ - ID: currentOrder[y].OrderID, + OrderID: currentOrder[y].OrderID, ClientID: currentOrder[y].ClOrderID, Exchange: b.Name, Price: currentOrder[y].Price, diff --git a/exchanges/coinbasepro/coinbasepro_test.go b/exchanges/coinbasepro/coinbasepro_test.go index d1efd4a9..6641cef0 100644 --- a/exchanges/coinbasepro/coinbasepro_test.go +++ b/exchanges/coinbasepro/coinbasepro_test.go @@ -494,6 +494,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: c.Name, Pair: currency.Pair{ Delimiter: "-", Base: currency.BTC, @@ -507,7 +508,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := c.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -520,7 +521,7 @@ func TestCancelExchangeOrder(t *testing.T) { } var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: testPair, @@ -542,7 +543,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { } var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: testPair, diff --git a/exchanges/coinbasepro/coinbasepro_websocket.go b/exchanges/coinbasepro/coinbasepro_websocket.go index 6626b5b4..450558f7 100644 --- a/exchanges/coinbasepro/coinbasepro_websocket.go +++ b/exchanges/coinbasepro/coinbasepro_websocket.go @@ -190,7 +190,7 @@ func (c *CoinbasePro) wsHandleData(respRaw []byte) error { RemainingAmount: wsOrder.RemainingSize, Fee: wsOrder.TakerFeeRate, Exchange: c.Name, - ID: wsOrder.OrderID, + OrderID: wsOrder.OrderID, AccountID: wsOrder.ProfileID, ClientID: creds.ClientID, Type: oType, @@ -223,7 +223,7 @@ func (c *CoinbasePro) wsHandleData(respRaw []byte) error { if wsOrder.UserID != "" { c.Websocket.DataHandler <- &order.Detail{ - ID: wsOrder.OrderID, + OrderID: wsOrder.OrderID, Pair: p, AssetType: a, Trades: []order.TradeHistory{ diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index 96d8e987..b509778c 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -526,21 +526,20 @@ func (c *CoinbasePro) GetHistoricTrades(_ context.Context, _ currency.Pair, _ as } // SubmitOrder submits a new order -func (c *CoinbasePro) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (c *CoinbasePro) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } fpair, err := c.FormatExchangeCurrency(s.Pair, asset.Spot) if err != nil { - return submitOrderResponse, err + return nil, err } - var response string + var orderID string switch s.Type { case order.Market: - response, err = c.PlaceMarketOrder(ctx, + orderID, err = c.PlaceMarketOrder(ctx, "", s.Amount, s.Amount, @@ -548,7 +547,7 @@ func (c *CoinbasePro) SubmitOrder(ctx context.Context, s *order.Submit) (order.S fpair.String(), "") case order.Limit: - response, err = c.PlaceLimitOrder(ctx, + orderID, err = c.PlaceLimitOrder(ctx, "", s.Price, s.Amount, @@ -562,18 +561,9 @@ func (c *CoinbasePro) SubmitOrder(ctx context.Context, s *order.Submit) (order.S err = errors.New("order type not supported") } if err != nil { - return submitOrderResponse, err + return nil, err } - if s.Type == order.Market { - submitOrderResponse.FullyMatched = true - } - if response != "" { - submitOrderResponse.OrderID = response - } - - submitOrderResponse.IsOrderPlaced = true - - return submitOrderResponse, nil + return s.DeriveSubmitResponse(orderID) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -587,7 +577,7 @@ func (c *CoinbasePro) CancelOrder(ctx context.Context, o *order.Cancel) error { if err := o.Validate(o.StandardCancel()); err != nil { return err } - return c.CancelExistingOrder(ctx, o.ID) + return c.CancelExistingOrder(ctx, o.OrderID) } // CancelBatchOrders cancels an orders by their corresponding ID numbers @@ -627,7 +617,7 @@ func (c *CoinbasePro) GetOrderInfo(ctx context.Context, orderID string, pair cur response := order.Detail{ Exchange: c.GetName(), - ID: genOrderDetail.ID, + OrderID: genOrderDetail.ID, Pair: p, Side: ss, Type: tt, @@ -793,7 +783,7 @@ func (c *CoinbasePro) GetActiveOrders(ctx context.Context, req *order.GetOrdersR log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) } orders[i] = order.Detail{ - ID: respOrders[i].ID, + OrderID: respOrders[i].ID, Amount: respOrders[i].Size, ExecutedAmount: respOrders[i].FilledSize, Type: orderType, @@ -873,7 +863,7 @@ func (c *CoinbasePro) GetOrderHistory(ctx context.Context, req *order.GetOrdersR log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) } detail := order.Detail{ - ID: respOrders[i].ID, + OrderID: respOrders[i].ID, Amount: respOrders[i].Size, ExecutedAmount: respOrders[i].FilledSize, RemainingAmount: respOrders[i].Size - respOrders[i].FilledSize, diff --git a/exchanges/coinut/coinut_test.go b/exchanges/coinut/coinut_test.go index f0dc8f96..84502a7b 100644 --- a/exchanges/coinut/coinut_test.go +++ b/exchanges/coinut/coinut_test.go @@ -298,6 +298,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: c.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.USD, @@ -310,7 +311,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := c.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -323,7 +324,7 @@ func TestCancelExchangeOrder(t *testing.T) { } currencyPair := currency.NewPair(currency.BTC, currency.USD) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -346,7 +347,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/coinut/coinut_websocket.go b/exchanges/coinut/coinut_websocket.go index c797c223..470cd7b9 100644 --- a/exchanges/coinut/coinut_websocket.go +++ b/exchanges/coinut/coinut_websocket.go @@ -196,7 +196,7 @@ func (c *COINUT) wsHandleData(ctx context.Context, respRaw []byte) error { } c.Websocket.DataHandler <- &order.Detail{ Exchange: c.Name, - ID: strconv.FormatInt(cancel.OrderID, 10), + OrderID: strconv.FormatInt(cancel.OrderID, 10), Status: order.Cancelled, LastUpdated: time.Now(), AssetType: asset.Spot, @@ -210,7 +210,7 @@ func (c *COINUT) wsHandleData(ctx context.Context, respRaw []byte) error { for i := range cancels.Results { c.Websocket.DataHandler <- &order.Detail{ Exchange: c.Name, - ID: strconv.FormatInt(cancels.Results[i].OrderID, 10), + OrderID: strconv.FormatInt(cancels.Results[i].OrderID, 10), Status: order.Cancelled, LastUpdated: time.Now(), AssetType: asset.Spot, @@ -447,7 +447,7 @@ func (c *COINUT) parseOrderContainer(oContainer *wsOrderContainer) (*order.Detai ExecutedAmount: oContainer.FillQuantity, RemainingAmount: oContainer.OpenQuantity, Exchange: c.Name, - ID: orderID, + OrderID: orderID, Side: oSide, Status: oStatus, Date: time.Unix(0, oContainer.Timestamp), @@ -464,7 +464,7 @@ func (c *COINUT) parseOrderContainer(oContainer *wsOrderContainer) (*order.Detai } o.RemainingAmount = oContainer.Order.OpenQuantity o.Amount = oContainer.Order.Quantity - o.ID = strconv.FormatInt(oContainer.Order.OrderID, 10) + o.OrderID = strconv.FormatInt(oContainer.Order.OrderID, 10) o.LastUpdated = time.Unix(0, oContainer.Timestamp) o.Pair, o.AssetType, err = c.GetRequestFormattedPairAndAssetType(c.instrumentMap.LookupInstrument(oContainer.Order.InstrumentID)) if err != nil { diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index 0a86e1bc..0db4514b 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -587,17 +587,19 @@ func (c *COINUT) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.I } // SubmitOrder submits a new order -func (c *COINUT) SubmitOrder(ctx context.Context, o *order.Submit) (order.SubmitResponse, error) { - if err := o.Validate(); err != nil { - return order.SubmitResponse{}, err +func (c *COINUT) SubmitOrder(ctx context.Context, o *order.Submit) (*order.SubmitResponse, error) { + err := o.Validate() + if err != nil { + return nil, err } - var submitOrderResponse order.SubmitResponse - var err error if _, err = strconv.Atoi(o.ClientID); err != nil { - return submitOrderResponse, fmt.Errorf("%s - ClientID must be a number, received: %s", c.Name, o.ClientID) + return nil, fmt.Errorf("%s - ClientID must be a number, received: %s", + c.Name, o.ClientID) } + var orderID string + status := order.New if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() { var response *order.Detail response, err = c.wsSubmitOrder(&WsSubmitOrderParameters{ @@ -607,74 +609,73 @@ func (c *COINUT) SubmitOrder(ctx context.Context, o *order.Submit) (order.Submit Price: o.Price, }) if err != nil { - return submitOrderResponse, err + return nil, err } - submitOrderResponse.OrderID = response.ID - submitOrderResponse.IsOrderPlaced = true + orderID = response.OrderID } else { err = c.loadInstrumentsIfNotLoaded() if err != nil { - return submitOrderResponse, err + return nil, err } - fpair, err := c.FormatExchangeCurrency(o.Pair, asset.Spot) + var fPair currency.Pair + fPair, err = c.FormatExchangeCurrency(o.Pair, asset.Spot) if err != nil { - return submitOrderResponse, err + return nil, err } - currencyID := c.instrumentMap.LookupID(fpair.String()) + currencyID := c.instrumentMap.LookupID(fPair.String()) if currencyID == 0 { - return submitOrderResponse, errLookupInstrumentID + return nil, errLookupInstrumentID } var APIResponse interface{} var clientIDInt uint64 - isBuyOrder := o.Side == order.Buy - clientIDInt, err = strconv.ParseUint(o.ClientID, 0, 32) + clientIDInt, err = strconv.ParseUint(o.ClientID, 10, 32) if err != nil { - return submitOrderResponse, err + return nil, err } - clientIDUint := uint32(clientIDInt) APIResponse, err = c.NewOrder(ctx, currencyID, o.Amount, o.Price, - isBuyOrder, - clientIDUint) + o.Side == order.Buy, + uint32(clientIDInt)) if err != nil { - return submitOrderResponse, err + return nil, err } responseMap, ok := APIResponse.(map[string]interface{}) if !ok { - return submitOrderResponse, errors.New("unable to type assert responseMap") + return nil, errors.New("unable to type assert responseMap") } orderType, ok := responseMap["reply"].(string) if !ok { - return submitOrderResponse, errors.New("unable to type assert orderType") + return nil, errors.New("unable to type assert orderType") } switch orderType { case "order_rejected": - return submitOrderResponse, fmt.Errorf("clientOrderID: %v was rejected: %v", o.ClientID, responseMap["reasons"]) + return nil, fmt.Errorf("clientOrderID: %v was rejected: %v", o.ClientID, responseMap["reasons"]) case "order_filled": - orderID, ok := responseMap["order_id"].(float64) + orderIDResp, ok := responseMap["order_id"].(float64) if !ok { - return submitOrderResponse, errors.New("unable to type assert orderID") + return nil, errors.New("unable to type assert orderID") } - submitOrderResponse.OrderID = strconv.FormatFloat(orderID, 'f', -1, 64) - submitOrderResponse.IsOrderPlaced = true - submitOrderResponse.FullyMatched = true - return submitOrderResponse, nil + orderID = strconv.FormatFloat(orderIDResp, 'f', -1, 64) + status = order.Filled case "order_accepted": - orderID, ok := responseMap["order_id"].(float64) + orderIDResp, ok := responseMap["order_id"].(float64) if !ok { - return submitOrderResponse, errors.New("unable to type assert orderID") + return nil, errors.New("unable to type assert orderID") } - submitOrderResponse.OrderID = strconv.FormatFloat(orderID, 'f', -1, 64) - submitOrderResponse.IsOrderPlaced = true - return submitOrderResponse, nil + orderID = strconv.FormatFloat(orderIDResp, 'f', -1, 64) } } - return submitOrderResponse, nil + resp, err := o.DeriveSubmitResponse(orderID) + if err != nil { + return nil, err + } + resp.Status = status + return resp, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -693,7 +694,7 @@ func (c *COINUT) CancelOrder(ctx context.Context, o *order.Cancel) error { if err != nil { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -715,7 +716,7 @@ func (c *COINUT) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } if len(resp.Status) >= 1 && resp.Status[0] != "OK" { - return errors.New(c.Name + " - Failed to cancel order " + o.ID) + return errors.New(c.Name + " - Failed to cancel order " + o.OrderID) } } else { if currencyID == 0 { @@ -912,7 +913,7 @@ func (c *COINUT) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques orders = append(orders, order.Detail{ Exchange: c.Name, - ID: strconv.FormatInt(openOrders.Orders[i].OrderID, 10), + OrderID: strconv.FormatInt(openOrders.Orders[i].OrderID, 10), Pair: fPair, Side: side, Date: time.Unix(0, openOrders.Orders[i].Timestamp), @@ -972,7 +973,7 @@ func (c *COINUT) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques } orders = append(orders, order.Detail{ - ID: strconv.FormatInt(openOrders.Orders[y].OrderID, 10), + OrderID: strconv.FormatInt(openOrders.Orders[y].OrderID, 10), Amount: openOrders.Orders[y].Quantity, Price: openOrders.Orders[y].Price, Exchange: c.Name, @@ -1028,7 +1029,7 @@ func (c *COINUT) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques detail := order.Detail{ Exchange: c.Name, - ID: strconv.FormatInt(trades.Trades[x].OrderID, 10), + OrderID: strconv.FormatInt(trades.Trades[x].OrderID, 10), Pair: p, Side: side, Date: time.Unix(0, trades.Trades[x].Timestamp), @@ -1097,7 +1098,7 @@ func (c *COINUT) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques } allOrders = append(allOrders, order.Detail{ - ID: strconv.FormatInt(orders.Trades[y].Order.OrderID, 10), + OrderID: strconv.FormatInt(orders.Trades[y].Order.OrderID, 10), Amount: orders.Trades[y].Order.Quantity, Price: orders.Trades[y].Order.Price, Exchange: c.Name, diff --git a/exchanges/exmo/exmo_test.go b/exchanges/exmo/exmo_test.go index f06cfed6..889ca193 100644 --- a/exchanges/exmo/exmo_test.go +++ b/exchanges/exmo/exmo_test.go @@ -310,6 +310,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: e.Name, Pair: currency.Pair{ Delimiter: "_", Base: currency.BTC, @@ -323,7 +324,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := e.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -337,7 +338,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -360,7 +361,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index 72992f84..54deb3a8 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -460,16 +460,15 @@ func (e *EXMO) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.Ite } // SubmitOrder submits a new order -func (e *EXMO) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (e *EXMO) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } var oT string switch s.Type { case order.Limit: - return submitOrderResponse, errors.New("unsupported order type") + return nil, errors.New("unsupported order type") case order.Market: if s.Side == order.Sell { oT = "market_sell" @@ -480,22 +479,15 @@ func (e *EXMO) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitRe fPair, err := e.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } response, err := e.CreateOrder(ctx, fPair.String(), oT, s.Price, s.Amount) if err != nil { - return submitOrderResponse, err - } - if response > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response, 10) + return nil, err } - submitOrderResponse.IsOrderPlaced = true - if s.Type == order.Market { - submitOrderResponse.FullyMatched = true - } - return submitOrderResponse, nil + return s.DeriveSubmitResponse(strconv.FormatInt(response, 10)) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -510,7 +502,7 @@ func (e *EXMO) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -656,7 +648,7 @@ func (e *EXMO) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest) return nil, err } orders = append(orders, order.Detail{ - ID: strconv.FormatInt(resp[i].OrderID, 10), + OrderID: strconv.FormatInt(resp[i].OrderID, 10), Amount: resp[i].Quantity, Date: orderDate, Price: resp[i].Price, @@ -714,7 +706,7 @@ func (e *EXMO) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest) return nil, err } detail := order.Detail{ - ID: strconv.FormatInt(allTrades[i].TradeID, 10), + OrderID: strconv.FormatInt(allTrades[i].TradeID, 10), Amount: allTrades[i].Quantity, ExecutedAmount: allTrades[i].Quantity, Cost: allTrades[i].Amount, diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 6cda5403..cc84e415 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -656,6 +656,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: f.Name, Pair: currencyPair, Side: order.Sell, Type: order.Limit, @@ -698,7 +699,7 @@ func TestCancelOrder(t *testing.T) { } c := order.Cancel{ - ID: "12366984218", + OrderID: "12366984218", Pair: currencyPair, AssetType: asset.Spot, } @@ -2040,7 +2041,7 @@ func TestCalculatePNL(t *testing.T) { orders[i] = order.Detail{ Side: positions[i].Side, Pair: pair, - ID: positions[i].ID, + OrderID: positions[i].OrderID, Price: positions[i].Price, Amount: positions[i].Amount, AssetType: asset.Futures, diff --git a/exchanges/ftx/ftx_websocket.go b/exchanges/ftx/ftx_websocket.go index 10ca2439..b74159cf 100644 --- a/exchanges/ftx/ftx_websocket.go +++ b/exchanges/ftx/ftx_websocket.go @@ -371,7 +371,7 @@ func (f *FTX) wsHandleData(respRaw []byte) error { resp.Cost = resp.AverageExecutedPrice * resultData.OrderData.FilledSize // Fee: orderVars.Fee is incorrect. resp.Exchange = f.Name - resp.ID = strconv.FormatInt(resultData.OrderData.ID, 10) + resp.OrderID = strconv.FormatInt(resultData.OrderData.ID, 10) resp.ClientOrderID = resultData.OrderData.ClientID resp.Type = orderVars.OrderType resp.Side = orderVars.Side diff --git a/exchanges/ftx/ftx_websocket_test.go b/exchanges/ftx/ftx_websocket_test.go index b67afc6f..69f8d68f 100644 --- a/exchanges/ftx/ftx_websocket_test.go +++ b/exchanges/ftx/ftx_websocket_test.go @@ -101,7 +101,7 @@ func TestFTX_wsHandleData_Details(t *testing.T) { } // "reduceOnly" and "liquidation" do not have corresponding fields in // order.Detail. - if x.ID != "69350095302" || + if x.OrderID != "69350095302" || x.ClientOrderID != "192ab87ae99970b79f624ef8bd783351" || x.Pair.Base.Item.Symbol != "BTC" || x.Pair.Quote.Item.Symbol != "USDT" || diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 5fdb4738..d655e37f 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -644,10 +644,9 @@ allTrades: } // SubmitOrder submits a new order -func (f *FTX) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var resp order.SubmitResponse +func (f *FTX) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return resp, err + return nil, err } if s.Side == order.Ask { @@ -659,7 +658,7 @@ func (f *FTX) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitRes fPair, err := f.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return resp, err + return nil, err } tempResp, err := f.Order(ctx, @@ -673,11 +672,10 @@ func (f *FTX) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitRes s.Price, s.Amount) if err != nil { - return resp, err + return nil, err } - resp.IsOrderPlaced = true - resp.OrderID = strconv.FormatInt(tempResp.ID, 10) - return resp, nil + + return s.DeriveSubmitResponse(strconv.FormatInt(tempResp.ID, 10)) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -693,7 +691,7 @@ func (f *FTX) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Mod case action.TriggerPrice != 0: var a TriggerOrderData a, err := f.ModifyTriggerOrder(ctx, - action.ID, + action.OrderID, action.Type.String(), action.Amount, action.TriggerPrice, @@ -704,7 +702,7 @@ func (f *FTX) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Mod } id = strconv.FormatInt(a.ID, 10) remainingAmount = a.Size - a.FilledSize - case action.ID == "": + case action.OrderID == "": o, err := f.ModifyOrderByClientID(ctx, action.ClientOrderID, action.ClientOrderID, @@ -717,7 +715,7 @@ func (f *FTX) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Mod remainingAmount = o.RemainingSize default: o, err := f.ModifyPlacedOrder(ctx, - action.ID, + action.OrderID, action.ClientOrderID, action.Price, action.Amount) @@ -747,7 +745,7 @@ func (f *FTX) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - _, err := f.DeleteOrder(ctx, o.ID) + _, err := f.DeleteOrder(ctx, o.OrderID) return err } @@ -845,7 +843,7 @@ func (f *FTX) GetOrderInfo(ctx context.Context, orderID string, _ currency.Pair, if err != nil { return resp, err } - resp.ID = strconv.FormatInt(orderData.ID, 10) + resp.OrderID = strconv.FormatInt(orderData.ID, 10) resp.Amount = orderData.Size resp.ClientOrderID = orderData.ClientID resp.Date = orderData.CreatedAt @@ -949,7 +947,7 @@ func (f *FTX) GetActiveOrders(ctx context.Context, getOrdersRequest *order.GetOr return nil, err } - tempResp.ID = strconv.FormatInt(orderData[y].ID, 10) + tempResp.OrderID = strconv.FormatInt(orderData[y].ID, 10) tempResp.Amount = orderData[y].Size tempResp.AssetType = assetType tempResp.ClientOrderID = orderData[y].ClientID @@ -989,7 +987,7 @@ func (f *FTX) GetActiveOrders(ctx context.Context, getOrdersRequest *order.GetOr if err != nil { return nil, err } - tempResp.ID = strconv.FormatInt(triggerOrderData[z].ID, 10) + tempResp.OrderID = strconv.FormatInt(triggerOrderData[z].ID, 10) tempResp.Amount = triggerOrderData[z].Size tempResp.AssetType = assetType tempResp.Date = triggerOrderData[z].CreatedAt @@ -1053,7 +1051,7 @@ func (f *FTX) GetOrderHistory(ctx context.Context, getOrdersRequest *order.GetOr if err != nil { return nil, err } - tempResp.ID = strconv.FormatInt(orderData[y].ID, 10) + tempResp.OrderID = strconv.FormatInt(orderData[y].ID, 10) tempResp.Amount = orderData[y].Size tempResp.AssetType = assetType tempResp.AverageExecutedPrice = orderData[y].AvgFillPrice @@ -1097,7 +1095,7 @@ func (f *FTX) GetOrderHistory(ctx context.Context, getOrdersRequest *order.GetOr if err != nil { return nil, err } - tempResp.ID = strconv.FormatInt(triggerOrderData[z].ID, 10) + tempResp.OrderID = strconv.FormatInt(triggerOrderData[z].ID, 10) tempResp.Amount = triggerOrderData[z].Size tempResp.AssetType = assetType tempResp.Date = triggerOrderData[z].CreatedAt @@ -1668,7 +1666,7 @@ func (f *FTX) GetFuturesPositions(ctx context.Context, a asset.Item, cp currency resp[i] = order.Detail{ Side: side, Pair: cp, - ID: strconv.FormatInt(fills[i].ID, 10), + OrderID: strconv.FormatInt(fills[i].ID, 10), Price: price, Amount: fills[i].Size, AssetType: a, diff --git a/exchanges/gateio/gateio_test.go b/exchanges/gateio/gateio_test.go index e4cd3c22..b63a6fa3 100644 --- a/exchanges/gateio/gateio_test.go +++ b/exchanges/gateio/gateio_test.go @@ -341,6 +341,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: g.Name, Pair: currency.Pair{ Delimiter: "_", Base: currency.LTC, @@ -354,7 +355,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := g.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -368,7 +369,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -391,7 +392,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/gateio/gateio_websocket.go b/exchanges/gateio/gateio_websocket.go index 2e4b4c4d..6944e886 100644 --- a/exchanges/gateio/gateio_websocket.go +++ b/exchanges/gateio/gateio_websocket.go @@ -334,7 +334,7 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { RemainingAmount: left, Fee: fee, Exchange: g.Name, - ID: strconv.FormatFloat(orderID, 'f', -1, 64), + OrderID: strconv.FormatFloat(orderID, 'f', -1, 64), Type: oType, Side: oSide, Status: oStatus, diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index 697d3445..7c4fc8a9 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -502,10 +502,9 @@ func (g *Gateio) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.I // SubmitOrder submits a new order // TODO: support multiple order types (IOC) -func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } var orderTypeFormat string @@ -517,7 +516,7 @@ func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit fPair, err := g.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } var spotNewOrderRequestParams = SpotNewOrderRequestParams{ @@ -529,17 +528,16 @@ func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit response, err := g.SpotNewOrder(ctx, spotNewOrderRequestParams) if err != nil { - return submitOrderResponse, err + return nil, err } - if response.OrderNumber > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10) + subResp, err := s.DeriveSubmitResponse(strconv.FormatInt(response.OrderNumber, 10)) + if err != nil { + return nil, err } if response.LeftAmount == 0 { - submitOrderResponse.FullyMatched = true + subResp.Status = order.Filled } - submitOrderResponse.IsOrderPlaced = true - - return submitOrderResponse, nil + return subResp, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -554,7 +552,7 @@ func (g *Gateio) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -620,7 +618,7 @@ func (g *Gateio) GetOrderInfo(ctx context.Context, orderID string, pair currency continue } orderDetail.Exchange = g.Name - orderDetail.ID = orders.Orders[x].OrderNumber + orderDetail.OrderID = orders.Orders[x].OrderNumber orderDetail.RemainingAmount = orders.Orders[x].InitialAmount - orders.Orders[x].FilledAmount orderDetail.ExecutedAmount = orders.Orders[x].FilledAmount orderDetail.Amount = orders.Orders[x].InitialAmount @@ -750,7 +748,7 @@ func (g *Gateio) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques orders = append(orders, order.Detail{ Exchange: g.Name, AccountID: strconv.FormatInt(resp.WebSocketOrderQueryRecords[j].User, 10), - ID: strconv.FormatInt(resp.WebSocketOrderQueryRecords[j].ID, 10), + OrderID: strconv.FormatInt(resp.WebSocketOrderQueryRecords[j].ID, 10), Pair: p, Side: orderSide, Type: orderType, @@ -798,7 +796,7 @@ func (g *Gateio) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques } orderDate := time.Unix(resp.Orders[i].Timestamp, 0) orders = append(orders, order.Detail{ - ID: resp.Orders[i].OrderNumber, + OrderID: resp.Orders[i].OrderNumber, Amount: resp.Orders[i].Amount, ExecutedAmount: resp.Orders[i].Amount - resp.Orders[i].FilledAmount, RemainingAmount: resp.Orders[i].FilledAmount, @@ -854,7 +852,7 @@ func (g *Gateio) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques } orderDate := time.Unix(trades[i].TimeUnix, 0) detail := order.Detail{ - ID: strconv.FormatInt(trades[i].OrderID, 10), + OrderID: strconv.FormatInt(trades[i].OrderID, 10), Amount: trades[i].Amount, ExecutedAmount: trades[i].Amount, Price: trades[i].Rate, diff --git a/exchanges/gemini/gemini_test.go b/exchanges/gemini/gemini_test.go index 485bf4f9..124547bb 100644 --- a/exchanges/gemini/gemini_test.go +++ b/exchanges/gemini/gemini_test.go @@ -413,6 +413,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: g.Name, Pair: currency.Pair{ Delimiter: "_", Base: currency.LTC, @@ -428,7 +429,7 @@ func TestSubmitOrder(t *testing.T) { response, err := g.SubmitOrder(context.Background(), orderSubmission) switch { - case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced): + case areTestAPIKeysSet() && (err != nil || response.Status != order.New): t.Errorf("Order failed to be placed: %v", err) case !areTestAPIKeysSet() && err == nil && !mockTests: t.Error("Expecting an error when no keys are set") @@ -443,7 +444,7 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } var orderCancellation = &order.Cancel{ - ID: "266029865", + OrderID: "266029865", AssetType: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USDT), } @@ -467,7 +468,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/gemini/gemini_websocket.go b/exchanges/gemini/gemini_websocket.go index 48f9d0ef..77c3ca5c 100644 --- a/exchanges/gemini/gemini_websocket.go +++ b/exchanges/gemini/gemini_websocket.go @@ -323,7 +323,7 @@ func (g *Gemini) wsHandleData(respRaw []byte) error { ExecutedAmount: result[i].ExecutedAmount, RemainingAmount: result[i].RemainingAmount, Exchange: g.Name, - ID: result[i].OrderID, + OrderID: result[i].OrderID, Type: oType, Side: oSide, Status: oStatus, diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 62ecc49f..22666372 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -537,20 +537,18 @@ allTrades: } // SubmitOrder submits a new order -func (g *Gemini) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (g *Gemini) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } if s.Type != order.Limit { - return submitOrderResponse, - errors.New("only limit orders are enabled through this exchange") + return nil, errors.New("only limit orders are enabled through this exchange") } fpair, err := g.FormatExchangeCurrency(s.Pair, asset.Spot) if err != nil { - return submitOrderResponse, err + return nil, err } response, err := g.NewOrder(ctx, @@ -560,15 +558,10 @@ func (g *Gemini) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit s.Side.String(), "exchange limit") if err != nil { - return submitOrderResponse, err - } - if response > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response, 10) + return nil, err } - submitOrderResponse.IsOrderPlaced = true - - return submitOrderResponse, nil + return s.DeriveSubmitResponse(strconv.FormatInt(response, 10)) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -583,7 +576,7 @@ func (g *Gemini) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -719,7 +712,7 @@ func (g *Gemini) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques orders[i] = order.Detail{ Amount: resp[i].OriginalAmount, RemainingAmount: resp[i].RemainingAmount, - ID: strconv.FormatInt(resp[i].OrderID, 10), + OrderID: strconv.FormatInt(resp[i].OrderID, 10), ExecutedAmount: resp[i].ExecutedAmount, Exchange: g.Name, Type: orderType, @@ -787,7 +780,7 @@ func (g *Gemini) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques orderDate := time.Unix(trades[i].Timestamp, 0) detail := order.Detail{ - ID: strconv.FormatInt(trades[i].OrderID, 10), + OrderID: strconv.FormatInt(trades[i].OrderID, 10), Amount: trades[i].Amount, ExecutedAmount: trades[i].Amount, Exchange: g.Name, diff --git a/exchanges/hitbtc/hitbtc_test.go b/exchanges/hitbtc/hitbtc_test.go index 55f9c623..741278f4 100644 --- a/exchanges/hitbtc/hitbtc_test.go +++ b/exchanges/hitbtc/hitbtc_test.go @@ -327,6 +327,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: h.Name, Pair: currency.Pair{ Base: currency.DGD, Quote: currency.BTC, @@ -339,7 +340,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := h.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -353,7 +354,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -376,7 +377,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/hitbtc/hitbtc_websocket.go b/exchanges/hitbtc/hitbtc_websocket.go index de56ebb2..b8949f40 100644 --- a/exchanges/hitbtc/hitbtc_websocket.go +++ b/exchanges/hitbtc/hitbtc_websocket.go @@ -398,7 +398,7 @@ func (h *HitBTC) wsHandleOrderData(o *wsOrderData) error { ExecutedAmount: o.CumQuantity, RemainingAmount: o.Quantity - o.CumQuantity, Exchange: h.Name, - ID: o.ID, + OrderID: o.ID, Type: oType, Side: oSide, Status: oStatus, diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index ce7a8a72..fb466267 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -556,26 +556,29 @@ allTrades: } // SubmitOrder submits a new order -func (h *HitBTC) SubmitOrder(ctx context.Context, o *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (h *HitBTC) SubmitOrder(ctx context.Context, o *order.Submit) (*order.SubmitResponse, error) { err := o.Validate() if err != nil { - return submitOrderResponse, err + return nil, err } + + var orderID string + status := order.New if h.Websocket.IsConnected() && h.Websocket.CanUseAuthenticatedEndpoints() { var response *WsSubmitOrderSuccessResponse response, err = h.wsPlaceOrder(o.Pair, o.Side.String(), o.Amount, o.Price) if err != nil { - return submitOrderResponse, err + return nil, err } - submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10) + orderID = strconv.FormatInt(response.ID, 10) if response.Result.CumQuantity == o.Amount { - submitOrderResponse.FullyMatched = true + status = order.Filled } } else { - fPair, err := h.FormatExchangeCurrency(o.Pair, o.AssetType) + var fPair currency.Pair + fPair, err = h.FormatExchangeCurrency(o.Pair, o.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } var response OrderResponse @@ -583,21 +586,22 @@ func (h *HitBTC) SubmitOrder(ctx context.Context, o *order.Submit) (order.Submit fPair.String(), o.Price, o.Amount, - strings.ToLower(o.Type.String()), - strings.ToLower(o.Side.String())) + o.Type.Lower(), + o.Side.Lower()) if err != nil { - return submitOrderResponse, err - } - if response.OrderNumber > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10) + return nil, err } + orderID = strconv.FormatInt(response.OrderNumber, 10) if o.Type == order.Market { - submitOrderResponse.FullyMatched = true + status = order.Filled } } - submitOrderResponse.IsOrderPlaced = true - - return submitOrderResponse, nil + resp, err := o.DeriveSubmitResponse(orderID) + if err != nil { + return nil, err + } + resp.Status = status + return resp, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -612,7 +616,7 @@ func (h *HitBTC) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -748,7 +752,7 @@ func (h *HitBTC) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques return nil, err } orders[i] = order.Detail{ - ID: allOrders[i].ID, + OrderID: allOrders[i].ID, Amount: allOrders[i].Quantity, Exchange: h.Name, Price: allOrders[i].Price, @@ -810,7 +814,7 @@ func (h *HitBTC) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques log.Errorf(log.ExchangeSys, "%s %v", h.Name, err) } detail := order.Detail{ - ID: allOrders[i].ID, + OrderID: allOrders[i].ID, Amount: allOrders[i].Quantity, ExecutedAmount: allOrders[i].CumQuantity, RemainingAmount: allOrders[i].Quantity - allOrders[i].CumQuantity, diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index 00461dbb..731bac8c 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -2040,6 +2040,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: h.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.USDT, @@ -2052,7 +2053,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := h.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } } @@ -2064,7 +2065,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -2087,7 +2088,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { } currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/huobi/huobi_websocket.go b/exchanges/huobi/huobi_websocket.go index 230c3835..273c67e9 100644 --- a/exchanges/huobi/huobi_websocket.go +++ b/exchanges/huobi/huobi_websocket.go @@ -313,7 +313,7 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { ExecutedAmount: response.Data.FilledAmount, RemainingAmount: response.Data.UnfilledAmount, Exchange: h.Name, - ID: orderID, + OrderID: orderID, Type: oType, Side: oSide, Status: oStatus, diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index d4d53dba..4b748fe9 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -878,16 +878,18 @@ func (h *HUOBI) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.It } // SubmitOrder submits a new order -func (h *HUOBI) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (h *HUOBI) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } + + var orderID string + status := order.New switch s.AssetType { case asset.Spot: accountID, err := strconv.ParseInt(s.ClientID, 10, 64) if err != nil { - return submitOrderResponse, err + return nil, err } var formattedType SpotNewOrderRequestParamsType var params = SpotNewOrderRequestParams{ @@ -911,14 +913,12 @@ func (h *HUOBI) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR params.Type = formattedType response, err := h.SpotNewOrder(ctx, ¶ms) if err != nil { - return submitOrderResponse, err + return nil, err } - if response > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response, 10) - } - submitOrderResponse.IsOrderPlaced = true + orderID = strconv.FormatInt(response, 10) + if s.Type == order.Market { - submitOrderResponse.FullyMatched = true + status = order.Filled } case asset.CoinMarginedFutures: var oDirection string @@ -935,20 +935,23 @@ func (h *HUOBI) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR case order.PostOnly: oType = "post_only" } - order, err := h.PlaceSwapOrders(ctx, + offset := "open" + if s.ReduceOnly { + offset = "close" + } + orderResp, err := h.PlaceSwapOrders(ctx, s.Pair, s.ClientOrderID, oDirection, - s.Offset, + offset, oType, s.Price, s.Amount, s.Leverage) if err != nil { - return submitOrderResponse, err + return nil, err } - submitOrderResponse.OrderID = order.Data.OrderIDString - submitOrderResponse.IsOrderPlaced = true + orderID = orderResp.Data.OrderIDString case asset.Futures: var oDirection string switch s.Side { @@ -977,24 +980,32 @@ func (h *HUOBI) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR case order.PostOnly: oType = "post_only" } + offset := "open" + if s.ReduceOnly { + offset = "close" + } order, err := h.FOrder(ctx, s.Pair, "", "", s.ClientOrderID, oDirection, - s.Offset, + offset, oType, s.Price, s.Amount, s.Leverage) if err != nil { - return submitOrderResponse, err + return nil, err } - submitOrderResponse.OrderID = order.Data.OrderIDStr - submitOrderResponse.IsOrderPlaced = true + orderID = order.Data.OrderIDStr } - return submitOrderResponse, nil + resp, err := s.DeriveSubmitResponse(orderID) + if err != nil { + return nil, err + } + resp.Status = status + return resp, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -1012,13 +1023,13 @@ func (h *HUOBI) CancelOrder(ctx context.Context, o *order.Cancel) error { switch o.AssetType { case asset.Spot: var orderIDInt int64 - orderIDInt, err = strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err = strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } _, err = h.CancelExistingOrder(ctx, orderIDInt) case asset.CoinMarginedFutures: - _, err = h.CancelSwapOrder(ctx, o.ID, o.ClientID, o.Pair) + _, err = h.CancelSwapOrder(ctx, o.OrderID, o.ClientID, o.Pair) case asset.Futures: _, err = h.FCancelOrder(ctx, o.Pair.Base, o.ClientID, o.ClientOrderID) default: @@ -1205,7 +1216,7 @@ func (h *HUOBI) GetOrderInfo(ctx context.Context, orderID string, pair currency. } orderDetail = order.Detail{ Exchange: h.Name, - ID: orderID, + OrderID: orderID, AccountID: strconv.FormatInt(respData.AccountID, 10), Pair: p, Type: orderType, @@ -1391,7 +1402,7 @@ func (h *HUOBI) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest orders = append(orders, order.Detail{ Exchange: h.Name, AccountID: strconv.FormatInt(resp.Data[j].AccountID, 10), - ID: orderID, + OrderID: orderID, Pair: req.Pairs[i], Type: orderType, Side: orderSide, @@ -1421,7 +1432,7 @@ func (h *HUOBI) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest } for x := range resp { orderDetail := order.Detail{ - ID: strconv.FormatInt(resp[x].ID, 10), + OrderID: strconv.FormatInt(resp[x].ID, 10), Price: resp[x].Price, Amount: resp[x].Amount, ExecutedAmount: resp[x].FilledAmount, @@ -1469,7 +1480,7 @@ func (h *HUOBI) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest Fee: openOrders.Data.Orders[x].Fee, Exchange: h.Name, AssetType: req.AssetType, - ID: openOrders.Data.Orders[x].OrderIDString, + OrderID: openOrders.Data.Orders[x].OrderIDString, Side: orderVars.Side, Type: orderVars.OrderType, Status: orderVars.Status, @@ -1511,7 +1522,7 @@ func (h *HUOBI) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest Fee: openOrders.Data.Orders[x].Fee, Exchange: h.Name, AssetType: req.AssetType, - ID: openOrders.Data.Orders[x].OrderIDString, + OrderID: openOrders.Data.Orders[x].OrderIDString, Side: orderVars.Side, Type: orderVars.OrderType, Status: orderVars.Status, @@ -1560,7 +1571,7 @@ func (h *HUOBI) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest } for x := range resp { orderDetail := order.Detail{ - ID: strconv.FormatInt(resp[x].ID, 10), + OrderID: strconv.FormatInt(resp[x].ID, 10), Price: resp[x].Price, Amount: resp[x].Amount, ExecutedAmount: resp[x].FilledAmount, @@ -1617,7 +1628,7 @@ func (h *HUOBI) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest Fee: orderHistory.Data.Orders[x].Fee, Exchange: h.Name, AssetType: req.AssetType, - ID: orderHistory.Data.Orders[x].OrderIDString, + OrderID: orderHistory.Data.Orders[x].OrderIDString, Side: orderVars.Side, Type: orderVars.OrderType, Status: orderVars.Status, @@ -1675,7 +1686,7 @@ func (h *HUOBI) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest Fee: openOrders.Data.Orders[x].Fee, Exchange: h.Name, AssetType: req.AssetType, - ID: openOrders.Data.Orders[x].OrderIDString, + OrderID: openOrders.Data.Orders[x].OrderIDString, Side: orderVars.Side, Type: orderVars.OrderType, Status: orderVars.Status, diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index 351197c4..97a0957e 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -91,7 +91,7 @@ type IBotExchange interface { // OrderManagement defines functionality for order management type OrderManagement interface { - SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) + SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) ModifyOrder(ctx context.Context, action *order.Modify) (*order.ModifyResponse, error) CancelOrder(ctx context.Context, o *order.Cancel) error CancelBatchOrders(ctx context.Context, o []order.Cancel) (order.CancelBatchResponse, error) diff --git a/exchanges/itbit/itbit_test.go b/exchanges/itbit/itbit_test.go index ff47c986..a275b161 100644 --- a/exchanges/itbit/itbit_test.go +++ b/exchanges/itbit/itbit_test.go @@ -320,6 +320,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: i.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.USD, @@ -332,7 +333,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := i.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -346,7 +347,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -370,7 +371,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index 0f6c7271..b540c436 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -372,16 +372,15 @@ func (i *ItBit) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.It } // SubmitOrder submits a new order -func (i *ItBit) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (i *ItBit) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } var wallet string wallets, err := i.GetWallets(ctx, url.Values{}) if err != nil { - return submitOrderResponse, err + return nil, err } // Determine what wallet ID to use if there is any actual available currency to make the trade! @@ -395,7 +394,7 @@ func (i *ItBit) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR } if wallet == "" { - return submitOrderResponse, + return nil, fmt.Errorf("no wallet found with currency: %s with amount >= %v", s.Pair.Base, s.Amount) @@ -403,7 +402,7 @@ func (i *ItBit) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR fPair, err := i.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } response, err := i.PlaceOrder(ctx, @@ -416,17 +415,16 @@ func (i *ItBit) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR fPair.String(), "") if err != nil { - return submitOrderResponse, err + return nil, err } - if response.ID != "" { - submitOrderResponse.OrderID = response.ID + subResp, err := s.DeriveSubmitResponse(response.ID) + if err != nil { + return nil, err } - if response.AmountFilled == s.Amount { - submitOrderResponse.FullyMatched = true + subResp.Status = order.Filled } - submitOrderResponse.IsOrderPlaced = true - return submitOrderResponse, nil + return subResp, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -440,7 +438,7 @@ func (i *ItBit) CancelOrder(ctx context.Context, o *order.Cancel) error { if err := o.Validate(o.StandardCancel()); err != nil { return err } - return i.CancelExistingOrder(ctx, o.WalletAddress, o.ID) + return i.CancelExistingOrder(ctx, o.WalletAddress, o.OrderID) } // CancelBatchOrders cancels an orders by their corresponding ID numbers @@ -572,7 +570,7 @@ func (i *ItBit) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest } orders = append(orders, order.Detail{ - ID: allOrders[j].ID, + OrderID: allOrders[j].ID, Side: side, Amount: allOrders[j].Amount, ExecutedAmount: allOrders[j].AmountFilled, @@ -652,7 +650,7 @@ func (i *ItBit) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest } detail := order.Detail{ - ID: allOrders[j].ID, + OrderID: allOrders[j].ID, Side: side, Status: status, Amount: allOrders[j].Amount, diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index 43140648..c58895dd 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -921,6 +921,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: k.Name, Pair: currency.Pair{ Base: currency.XBT, Quote: currency.USD, @@ -933,7 +934,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := k.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -948,7 +949,7 @@ func TestCancelExchangeOrder(t *testing.T) { } var orderCancellation = &order.Cancel{ - ID: "OGEX6P-B5Q74-IGZ72R", + OrderID: "OGEX6P-B5Q74-IGZ72R", AssetType: asset.Spot, } @@ -977,7 +978,7 @@ func TestCancelBatchExchangeOrder(t *testing.T) { var ordersCancellation []order.Cancel ordersCancellation = append(ordersCancellation, order.Cancel{ Pair: pair, - ID: "OGEX6P-B5Q74-IGZ72R,OGEX6P-B5Q74-IGZ722", + OrderID: "OGEX6P-B5Q74-IGZ72R,OGEX6P-B5Q74-IGZ722", AssetType: asset.Spot, }) diff --git a/exchanges/kraken/kraken_websocket.go b/exchanges/kraken/kraken_websocket.go index 7f0c3197..3f1e2409 100644 --- a/exchanges/kraken/kraken_websocket.go +++ b/exchanges/kraken/kraken_websocket.go @@ -339,7 +339,7 @@ func (k *Kraken) wsHandleData(respRaw []byte) error { k.Websocket.DataHandler <- &order.Detail{ Exchange: k.Name, - ID: status.TransactionID, + OrderID: status.TransactionID, Status: order.New, } @@ -526,7 +526,7 @@ func (k *Kraken) wsProcessOwnTrades(ownOrders interface{}) error { } k.Websocket.DataHandler <- &order.Detail{ Exchange: k.Name, - ID: val.OrderTransactionID, + OrderID: val.OrderTransactionID, Trades: []order.TradeHistory{trade}, } } @@ -602,7 +602,7 @@ func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) error { RemainingAmount: val.Volume - val.ExecutedVolume, Fee: val.Fee, Exchange: k.Name, - ID: key, + OrderID: key, Type: oType, Side: oSide, Status: oStatus, @@ -613,7 +613,7 @@ func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) error { } else { k.Websocket.DataHandler <- &order.Detail{ Exchange: k.Name, - ID: key, + OrderID: key, Status: oStatus, } } diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index ef315b65..6f6328d6 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -708,17 +708,19 @@ func (k *Kraken) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.I } // SubmitOrder submits a new order -func (k *Kraken) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse - if err := s.Validate(); err != nil { - return submitOrderResponse, err +func (k *Kraken) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { + err := s.Validate() + if err != nil { + return nil, err } + + var orderID string + status := order.New switch s.AssetType { case asset.Spot: if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() { - var resp string s.Pair.Delimiter = "/" // required pair format: ISO 4217-A3 - resp, err := k.wsAddOrder(&WsAddOrderRequest{ + orderID, err = k.wsAddOrder(&WsAddOrderRequest{ OrderType: s.Type.Lower(), OrderSide: s.Side.Lower(), Pair: s.Pair.String(), @@ -726,13 +728,11 @@ func (k *Kraken) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit Volume: s.Amount, }) if err != nil { - return submitOrderResponse, err + return nil, err } - submitOrderResponse.OrderID = resp - submitOrderResponse.IsOrderPlaced = true } else { var response AddOrderResponse - response, err := k.AddOrder(ctx, + response, err = k.AddOrder(ctx, s.Pair, s.Side.String(), s.Type.String(), @@ -742,18 +742,18 @@ func (k *Kraken) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit 0, &AddOrderOptions{}) if err != nil { - return submitOrderResponse, err + return nil, err } if len(response.TransactionIds) > 0 { - submitOrderResponse.OrderID = strings.Join(response.TransactionIds, ", ") + orderID = strings.Join(response.TransactionIds, ", ") } } if s.Type == order.Market { - submitOrderResponse.FullyMatched = true + status = order.Filled } - submitOrderResponse.IsOrderPlaced = true case asset.Futures: - order, err := k.FuturesSendOrder(ctx, + var fOrder FuturesSendOrderData + fOrder, err = k.FuturesSendOrder(ctx, s.Type, s.Pair, s.Side.Lower(), @@ -766,22 +766,23 @@ func (k *Kraken) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit 0, ) if err != nil { - return submitOrderResponse, err + return nil, err } // check the status, anything that is not placed we error out - if order.SendStatus.Status != "placed" { - return submitOrderResponse, - fmt.Errorf("submit order failed: %s", - order.SendStatus.Status) + if fOrder.SendStatus.Status != "placed" { + return nil, fmt.Errorf("submit order failed: %s", fOrder.SendStatus.Status) } - - submitOrderResponse.OrderID = order.SendStatus.OrderID - submitOrderResponse.IsOrderPlaced = true + orderID = fOrder.SendStatus.OrderID default: - return submitOrderResponse, fmt.Errorf("invalid assetType") + return nil, fmt.Errorf("%w %v", asset.ErrNotSupported, s.AssetType) } - return submitOrderResponse, nil + resp, err := s.DeriveSubmitResponse(orderID) + if err != nil { + return nil, err + } + resp.Status = status + return resp, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -798,12 +799,12 @@ func (k *Kraken) CancelOrder(ctx context.Context, o *order.Cancel) error { switch o.AssetType { case asset.Spot: if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() { - return k.wsCancelOrders([]string{o.ID}) + return k.wsCancelOrders([]string{o.OrderID}) } - _, err := k.CancelExistingOrder(ctx, o.ID) + _, err := k.CancelExistingOrder(ctx, o.OrderID) return err case asset.Futures: - _, err := k.FuturesCancelOrder(ctx, o.ID, "") + _, err := k.FuturesCancelOrder(ctx, o.OrderID, "") if err != nil { return err } @@ -822,7 +823,7 @@ func (k *Kraken) CancelBatchOrders(ctx context.Context, orders []order.Cancel) ( if err := orders[i].Validate(orders[i].StandardCancel()); err != nil { return order.CancelBatchResponse{}, err } - ordersList[i] = orders[i].ID + ordersList[i] = orders[i].OrderID } err := k.wsCancelOrders(ordersList) @@ -942,7 +943,7 @@ func (k *Kraken) GetOrderInfo(ctx context.Context, orderID string, pair currency orderDetail = order.Detail{ Exchange: k.Name, - ID: orderID, + OrderID: orderID, Pair: p, Side: side, Type: oType, @@ -983,7 +984,7 @@ func (k *Kraken) GetOrderInfo(ctx context.Context, orderID string, pair currency return orderDetail, err } orderDetail = order.Detail{ - ID: orderID, + OrderID: orderID, Price: orderInfo.Fills[y].Price, Amount: orderInfo.Fills[y].Size, Side: oSide, @@ -1138,7 +1139,7 @@ func (k *Kraken) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques log.Errorf(log.ExchangeSys, "%s %v", k.Name, err) } orders = append(orders, order.Detail{ - ID: i, + OrderID: i, Amount: resp.Open[i].Volume, RemainingAmount: (resp.Open[i].Volume - resp.Open[i].VolumeExecuted), ExecutedAmount: resp.Open[i].VolumeExecuted, @@ -1187,7 +1188,7 @@ func (k *Kraken) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques return orders, err } orders = append(orders, order.Detail{ - ID: activeOrders.OpenOrders[a].OrderID, + OrderID: activeOrders.OpenOrders[a].OrderID, Price: activeOrders.OpenOrders[a].LimitPrice, Amount: activeOrders.OpenOrders[a].FilledSize, Side: oSide, @@ -1270,7 +1271,7 @@ func (k *Kraken) GetOrderHistory(ctx context.Context, getOrdersRequest *order.Ge log.Errorf(log.ExchangeSys, "%s %v", k.Name, err) } detail := order.Detail{ - ID: i, + OrderID: i, Amount: resp.Closed[i].Volume, ExecutedAmount: resp.Closed[i].VolumeExecuted, RemainingAmount: resp.Closed[i].Volume - resp.Closed[i].VolumeExecuted, @@ -1327,7 +1328,7 @@ func (k *Kraken) GetOrderHistory(ctx context.Context, getOrdersRequest *order.Ge ExecutedAmount: orderHistory.OrderEvents[o].Event.ExecutionEvent.Execution.TakerOrder.Filled, RemainingAmount: orderHistory.OrderEvents[o].Event.ExecutionEvent.Execution.TakerOrder.Quantity - orderHistory.OrderEvents[o].Event.ExecutionEvent.Execution.TakerOrder.Filled, - ID: orderHistory.OrderEvents[o].Event.ExecutionEvent.Execution.TakerOrder.UID, + OrderID: orderHistory.OrderEvents[o].Event.ExecutionEvent.Execution.TakerOrder.UID, ClientID: orderHistory.OrderEvents[o].Event.ExecutionEvent.Execution.TakerOrder.ClientID, AssetType: asset.Futures, Type: oType, @@ -1356,7 +1357,7 @@ func (k *Kraken) GetOrderHistory(ctx context.Context, getOrdersRequest *order.Ge ExecutedAmount: orderHistory.OrderEvents[o].Event.OrderRejected.RecentOrder.Filled, RemainingAmount: orderHistory.OrderEvents[o].Event.OrderRejected.RecentOrder.Quantity - orderHistory.OrderEvents[o].Event.OrderRejected.RecentOrder.Filled, - ID: orderHistory.OrderEvents[o].Event.OrderRejected.RecentOrder.UID, + OrderID: orderHistory.OrderEvents[o].Event.OrderRejected.RecentOrder.UID, ClientID: orderHistory.OrderEvents[o].Event.OrderRejected.RecentOrder.AccountID, AssetType: asset.Futures, Type: oType, @@ -1386,7 +1387,7 @@ func (k *Kraken) GetOrderHistory(ctx context.Context, getOrdersRequest *order.Ge ExecutedAmount: orderHistory.OrderEvents[o].Event.OrderCancelled.RecentOrder.Filled, RemainingAmount: orderHistory.OrderEvents[o].Event.OrderCancelled.RecentOrder.Quantity - orderHistory.OrderEvents[o].Event.OrderCancelled.RecentOrder.Filled, - ID: orderHistory.OrderEvents[o].Event.OrderCancelled.RecentOrder.UID, + OrderID: orderHistory.OrderEvents[o].Event.OrderCancelled.RecentOrder.UID, ClientID: orderHistory.OrderEvents[o].Event.OrderCancelled.RecentOrder.AccountID, AssetType: asset.Futures, Type: oType, @@ -1416,7 +1417,7 @@ func (k *Kraken) GetOrderHistory(ctx context.Context, getOrdersRequest *order.Ge ExecutedAmount: orderHistory.OrderEvents[o].Event.OrderPlaced.RecentOrder.Filled, RemainingAmount: orderHistory.OrderEvents[o].Event.OrderPlaced.RecentOrder.Quantity - orderHistory.OrderEvents[o].Event.OrderPlaced.RecentOrder.Filled, - ID: orderHistory.OrderEvents[o].Event.OrderPlaced.RecentOrder.UID, + OrderID: orderHistory.OrderEvents[o].Event.OrderPlaced.RecentOrder.UID, ClientID: orderHistory.OrderEvents[o].Event.OrderPlaced.RecentOrder.AccountID, AssetType: asset.Futures, Type: oType, diff --git a/exchanges/lbank/lbank_test.go b/exchanges/lbank/lbank_test.go index 5691f939..6afb2605 100644 --- a/exchanges/lbank/lbank_test.go +++ b/exchanges/lbank/lbank_test.go @@ -339,6 +339,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: l.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.USDT, @@ -352,7 +353,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := l.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -368,7 +369,7 @@ func TestCancelOrder(t *testing.T) { var a order.Cancel a.Pair = cp a.AssetType = asset.Spot - a.ID = "24f7ce27-af1d-4dca-a8c1-ef1cbeec1b23" + a.OrderID = "24f7ce27-af1d-4dca-a8c1-ef1cbeec1b23" err := l.CancelOrder(context.Background(), &a) if err != nil { t.Error(err) diff --git a/exchanges/lbank/lbank_wrapper.go b/exchanges/lbank/lbank_wrapper.go index 7e7c8086..9c0d3844 100644 --- a/exchanges/lbank/lbank_wrapper.go +++ b/exchanges/lbank/lbank_wrapper.go @@ -450,21 +450,20 @@ allTrades: } // SubmitOrder submits a new order -func (l *Lbank) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var resp order.SubmitResponse +func (l *Lbank) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return resp, err + return nil, err } if s.Side != order.Buy && s.Side != order.Sell { - return resp, + return nil, fmt.Errorf("%s order side is not supported by the exchange", s.Side) } fpair, err := l.FormatExchangeCurrency(s.Pair, asset.Spot) if err != nil { - return resp, err + return nil, err } tempResp, err := l.CreateOrder(ctx, @@ -473,14 +472,9 @@ func (l *Lbank) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR s.Amount, s.Price) if err != nil { - return resp, err + return nil, err } - resp.IsOrderPlaced = true - resp.OrderID = tempResp.OrderID - if s.Type == order.Market { - resp.FullyMatched = true - } - return resp, nil + return s.DeriveSubmitResponse(tempResp.OrderID) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -498,7 +492,7 @@ func (l *Lbank) CancelOrder(ctx context.Context, o *order.Cancel) error { if err != nil { return err } - _, err = l.RemoveOrder(ctx, fpair.String(), o.ID) + _, err = l.RemoveOrder(ctx, fpair.String(), o.OrderID) return err } diff --git a/exchanges/localbitcoins/localbitcoins_test.go b/exchanges/localbitcoins/localbitcoins_test.go index a9a8d3cf..9afd19ec 100644 --- a/exchanges/localbitcoins/localbitcoins_test.go +++ b/exchanges/localbitcoins/localbitcoins_test.go @@ -283,6 +283,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: l.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.EUR, @@ -296,7 +297,7 @@ func TestSubmitOrder(t *testing.T) { } response, err := l.SubmitOrder(context.Background(), orderSubmission) switch { - case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) && !mockTests: + case areTestAPIKeysSet() && (err != nil || response.Status != order.New) && !mockTests: t.Errorf("Order failed to be placed: %v", err) case !areTestAPIKeysSet() && err == nil && !mockTests: t.Error("Expecting an error when no keys are set") @@ -312,7 +313,7 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currency.NewPair(currency.LTC, currency.BTC), @@ -337,7 +338,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currency.NewPair(currency.LTC, currency.BTC), diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 2253d26b..f1443e07 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -369,15 +369,14 @@ func (l *LocalBitcoins) GetHistoricTrades(_ context.Context, _ currency.Pair, _ } // SubmitOrder submits a new order -func (l *LocalBitcoins) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (l *LocalBitcoins) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } fPair, err := l.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } // These are placeholder details @@ -405,15 +404,17 @@ func (l *LocalBitcoins) SubmitOrder(ctx context.Context, s *order.Submit) (order // Does not return any orderID, so create the add, then get the order err = l.CreateAd(ctx, ¶ms) if err != nil { - return submitOrderResponse, err + return nil, err } - submitOrderResponse.IsOrderPlaced = true - // Now to figure out what ad we just submitted // The only details we have are the params above - var adID string ads, err := l.Getads(ctx) + if err != nil { + return nil, err + } + + var adID string for i := range ads.AdList { if ads.AdList[i].Data.PriceEquation == params.PriceEquation && ads.AdList[i].Data.Lat == float64(params.Latitude) && @@ -431,16 +432,14 @@ func (l *LocalBitcoins) SubmitOrder(ctx context.Context, s *order.Submit) (order ads.AdList[i].Data.TradeType == params.TradeType && ads.AdList[i].Data.MinAmount == strconv.FormatInt(int64(params.MinAmount), 10) { adID = strconv.FormatInt(ads.AdList[i].Data.AdID, 10) + break } } - if adID != "" { - submitOrderResponse.OrderID = adID - } else { - return submitOrderResponse, errors.New("ad placed, but not found via API") + if adID == "" { + return nil, errors.New("ad placed, but not found via API") } - - return submitOrderResponse, err + return s.DeriveSubmitResponse(adID) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -454,7 +453,7 @@ func (l *LocalBitcoins) CancelOrder(ctx context.Context, o *order.Cancel) error if err := o.Validate(o.StandardCancel()); err != nil { return err } - return l.DeleteAd(ctx, o.ID) + return l.DeleteAd(ctx, o.OrderID) } // CancelBatchOrders cancels an orders by their corresponding ID numbers @@ -578,12 +577,12 @@ func (l *LocalBitcoins) GetActiveOrders(ctx context.Context, getOrdersRequest *o } orders[i] = order.Detail{ - Amount: resp[i].Data.AmountBTC, - Price: resp[i].Data.Amount, - ID: strconv.FormatInt(resp[i].Data.Advertisement.ID, 10), - Date: orderDate, - Fee: resp[i].Data.FeeBTC, - Side: side, + Amount: resp[i].Data.AmountBTC, + Price: resp[i].Data.Amount, + OrderID: strconv.FormatInt(resp[i].Data.Advertisement.ID, 10), + Date: orderDate, + Fee: resp[i].Data.FeeBTC, + Side: side, Pair: currency.NewPairWithDelimiter(currency.BTC.String(), resp[i].Data.Currency, format.Delimiter), @@ -672,13 +671,13 @@ func (l *LocalBitcoins) GetOrderHistory(ctx context.Context, getOrdersRequest *o } orders[i] = order.Detail{ - Amount: allTrades[i].Data.AmountBTC, - Price: allTrades[i].Data.Amount, - ID: strconv.FormatInt(allTrades[i].Data.Advertisement.ID, 10), - Date: orderDate, - Fee: allTrades[i].Data.FeeBTC, - Side: side, - Status: orderStatus, + Amount: allTrades[i].Data.AmountBTC, + Price: allTrades[i].Data.Amount, + OrderID: strconv.FormatInt(allTrades[i].Data.Advertisement.ID, 10), + Date: orderDate, + Fee: allTrades[i].Data.FeeBTC, + Side: side, + Status: orderStatus, Pair: currency.NewPairWithDelimiter(currency.BTC.String(), allTrades[i].Data.Currency, format.Delimiter), diff --git a/exchanges/okcoin/okcoin_test.go b/exchanges/okcoin/okcoin_test.go index dba24a89..64641363 100644 --- a/exchanges/okcoin/okcoin_test.go +++ b/exchanges/okcoin/okcoin_test.go @@ -978,6 +978,7 @@ func TestFormatWithdrawPermissions(t *testing.T) { func TestSubmitOrder(t *testing.T) { TestSetRealOrderDefaults(t) var orderSubmission = &order.Submit{ + Exchange: o.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.USD, @@ -990,7 +991,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := o.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -1002,7 +1003,7 @@ func TestCancelExchangeOrder(t *testing.T) { TestSetRealOrderDefaults(t) currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -1017,7 +1018,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { TestSetRealOrderDefaults(t) currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/okex/okex_test.go b/exchanges/okex/okex_test.go index 5812d59b..62f39984 100644 --- a/exchanges/okex/okex_test.go +++ b/exchanges/okex/okex_test.go @@ -1846,6 +1846,7 @@ func TestSubmitOrder(t *testing.T) { TestSetRealOrderDefaults(t) t.Parallel() var orderSubmission = &order.Submit{ + Exchange: o.Name, Pair: currency.Pair{ Base: currency.BTC, Quote: currency.USDT, @@ -1858,7 +1859,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := o.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -1871,7 +1872,7 @@ func TestCancelExchangeOrder(t *testing.T) { t.Parallel() currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -1887,7 +1888,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { t.Parallel() currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/okgroup/okgroup_websocket.go b/exchanges/okgroup/okgroup_websocket.go index f5c1a725..e5b34eec 100644 --- a/exchanges/okgroup/okgroup_websocket.go +++ b/exchanges/okgroup/okgroup_websocket.go @@ -385,7 +385,7 @@ func (o *OKGroup) wsProcessOrder(respRaw []byte) error { ExecutedAmount: resp.Data[i].LastFillQty, RemainingAmount: resp.Data[i].Size - resp.Data[i].LastFillQty, Exchange: o.Name, - ID: resp.Data[i].OrderID, + OrderID: resp.Data[i].OrderID, Type: oType, Side: oSide, Status: oStatus, diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go index d1841a1b..52261268 100644 --- a/exchanges/okgroup/okgroup_wrapper.go +++ b/exchanges/okgroup/okgroup_wrapper.go @@ -278,14 +278,14 @@ func (o *OKGroup) GetFundingHistory(ctx context.Context) (resp []exchange.FundHi } // SubmitOrder submits a new order -func (o *OKGroup) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { +func (o *OKGroup) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return order.SubmitResponse{}, err + return nil, err } fpair, err := o.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return order.SubmitResponse{}, err + return nil, err } request := PlaceOrderRequest{ @@ -301,17 +301,13 @@ func (o *OKGroup) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi orderResponse, err := o.PlaceSpotOrder(ctx, &request) if err != nil { - return order.SubmitResponse{}, err + return nil, err } - var resp order.SubmitResponse - resp.IsOrderPlaced = orderResponse.Result - resp.OrderID = orderResponse.OrderID - if s.Type == order.Market { - resp.FullyMatched = true + if !orderResponse.Result { + return nil, order.ErrUnableToPlaceOrder } - - return resp, nil + return s.DeriveSubmitResponse(orderResponse.OrderID) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -327,7 +323,7 @@ func (o *OKGroup) CancelOrder(ctx context.Context, cancel *order.Cancel) (err er return } - orderID, err := strconv.ParseInt(cancel.ID, 10, 64) + orderID, err := strconv.ParseInt(cancel.OrderID, 10, 64) if err != nil { return } @@ -358,7 +354,7 @@ func (o *OKGroup) CancelAllOrders(ctx context.Context, orderCancellation *order. return order.CancelAllResponse{}, err } - orderIDs := strings.Split(orderCancellation.ID, ",") + orderIDs := strings.Split(orderCancellation.OrderID, ",") resp := order.CancelAllResponse{} resp.Status = make(map[string]string) orderIDNumbers := make([]int64, 0, len(orderIDs)) @@ -539,7 +535,7 @@ func (o *OKGroup) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque log.Errorf(log.ExchangeSys, "%s %v", o.Name, err) } resp = append(resp, order.Detail{ - ID: spotOpenOrders[i].OrderID, + OrderID: spotOpenOrders[i].OrderID, Price: spotOpenOrders[i].Price, Amount: spotOpenOrders[i].Size, Pair: req.Pairs[x], @@ -595,7 +591,7 @@ func (o *OKGroup) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque log.Errorf(log.ExchangeSys, "%s %v", o.Name, err) } detail := order.Detail{ - ID: spotOrders[i].OrderID, + OrderID: spotOrders[i].OrderID, Price: spotOrders[i].Price, AverageExecutedPrice: spotOrders[i].PriceAvg, Amount: spotOrders[i].Size, diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index fe43a32f..e48c88ea 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -30,7 +30,8 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { return errNilOrder } if !d.AssetType.IsFutures() { - return fmt.Errorf("order %v %v %v %v %w", d.Exchange, d.AssetType, d.Pair, d.ID, ErrNotFuturesAsset) + return fmt.Errorf("order %v %v %v %v %w", + d.Exchange, d.AssetType, d.Pair, d.OrderID, ErrNotFuturesAsset) } if c == nil { return fmt.Errorf("position controller %w", common.ErrNilPointer) @@ -235,7 +236,7 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { if d.AssetType != e.asset { return errAssetMismatch } - if tracker, ok := e.orderPositions[d.ID]; ok { + if tracker, ok := e.orderPositions[d.OrderID]; ok { // this has already been associated // update the tracker return tracker.TrackNewOrder(d) @@ -251,7 +252,7 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { if err != nil && !errors.Is(err, ErrPositionClosed) { return err } - e.orderPositions[d.ID] = e.positions[len(e.positions)-1] + e.orderPositions[d.OrderID] = e.positions[len(e.positions)-1] return nil } } @@ -272,7 +273,7 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { if err != nil { return err } - e.orderPositions[d.ID] = tracker + e.orderPositions[d.OrderID] = tracker return nil } @@ -414,22 +415,26 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { return ErrSubmissionIsNil } if !p.contractPair.Equal(d.Pair) { - return fmt.Errorf("%w pair '%v' received: '%v'", errOrderNotEqualToTracker, d.Pair, p.contractPair) + return fmt.Errorf("%w pair '%v' received: '%v'", + errOrderNotEqualToTracker, d.Pair, p.contractPair) } if !strings.EqualFold(p.exchange, d.Exchange) { - return fmt.Errorf("%w exchange '%v' received: '%v'", errOrderNotEqualToTracker, d.Exchange, p.exchange) + return fmt.Errorf("%w exchange '%v' received: '%v'", + errOrderNotEqualToTracker, d.Exchange, p.exchange) } if p.asset != d.AssetType { - return fmt.Errorf("%w asset '%v' received: '%v'", errOrderNotEqualToTracker, d.AssetType, p.asset) + return fmt.Errorf("%w asset '%v' received: '%v'", + errOrderNotEqualToTracker, d.AssetType, p.asset) } if d.Side == UnknownSide { return ErrSideIsInvalid } - if d.ID == "" { + if d.OrderID == "" { return ErrOrderIDNotSet } if d.Date.IsZero() { - return fmt.Errorf("%w for %v %v %v order ID: %v unset", errTimeUnset, d.Exchange, d.AssetType, d.Pair, d.ID) + return fmt.Errorf("%w for %v %v %v order ID: %v unset", + errTimeUnset, d.Exchange, d.AssetType, d.Pair, d.OrderID) } if len(p.shortPositions) == 0 && len(p.longPositions) == 0 { p.entryPrice = decimal.NewFromFloat(d.Price) @@ -437,21 +442,27 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { var updated bool for i := range p.shortPositions { - if p.shortPositions[i].ID != d.ID { + if p.shortPositions[i].OrderID != d.OrderID { continue } ord := p.shortPositions[i].Copy() - ord.UpdateOrderFromDetail(d) + err := ord.UpdateOrderFromDetail(d) + if err != nil { + return err + } p.shortPositions[i] = ord updated = true break } for i := range p.longPositions { - if p.longPositions[i].ID != d.ID { + if p.longPositions[i].OrderID != d.OrderID { continue } ord := p.longPositions[i].Copy() - ord.UpdateOrderFromDetail(d) + err := ord.UpdateOrderFromDetail(d) + if err != nil { + return err + } p.longPositions[i] = ord updated = true break diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 274e403b..8effaad4 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -92,7 +92,7 @@ func TestTrackNewOrder(t *testing.T) { Exchange: exch, AssetType: item, Pair: pair, - ID: "1", + OrderID: "1", Price: 1337, } err = f.TrackNewOrder(od) @@ -102,7 +102,7 @@ func TestTrackNewOrder(t *testing.T) { od.Side = Long od.Amount = 1 - od.ID = "2" + od.OrderID = "2" err = f.TrackNewOrder(od) if !errors.Is(err, errTimeUnset) { t.Error(err) @@ -129,7 +129,7 @@ func TestTrackNewOrder(t *testing.T) { od.Date = od.Date.Add(1) od.Amount = 0.4 od.Side = Short - od.ID = "3" + od.OrderID = "3" err = f.TrackNewOrder(od) if !errors.Is(err, nil) { t.Error(err) @@ -147,7 +147,7 @@ func TestTrackNewOrder(t *testing.T) { od.Date = od.Date.Add(1) od.Amount = 0.8 od.Side = Short - od.ID = "4" + od.OrderID = "4" od.Fee = 0.1 err = f.TrackNewOrder(od) if !errors.Is(err, nil) { @@ -161,7 +161,7 @@ func TestTrackNewOrder(t *testing.T) { } od.Date = od.Date.Add(1) - od.ID = "5" + od.OrderID = "5" od.Side = Long od.Amount = 0.2 err = f.TrackNewOrder(od) @@ -264,7 +264,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { AssetType: item, Pair: pair, Side: Short, - ID: "1", + OrderID: "1", Amount: 1, }) if !errors.Is(err, nil) { @@ -280,7 +280,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { AssetType: item, Pair: pair, Side: Short, - ID: "2", + OrderID: "2", Amount: 1, }) if !errors.Is(err, nil) { @@ -296,7 +296,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { AssetType: item, Pair: pair, Side: Long, - ID: "3", + OrderID: "3", Amount: 2, }) if !errors.Is(err, nil) { @@ -316,7 +316,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { AssetType: item, Pair: pair, Side: Long, - ID: "4", + OrderID: "4", Amount: 2, }) if !errors.Is(err, errPositionDiscrepancy) { @@ -331,7 +331,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { AssetType: item, Pair: pair, Side: Long, - ID: "4", + OrderID: "4", Amount: 2, }) if !errors.Is(err, nil) { @@ -348,7 +348,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { Pair: pair, AssetType: asset.USDTMarginedFutures, Side: Long, - ID: "5", + OrderID: "5", Amount: 2, }) if !errors.Is(err, errAssetMismatch) { @@ -378,7 +378,7 @@ func TestPositionControllerTestTrackNewOrder(t *testing.T) { Pair: currency.NewPair(currency.BTC, currency.USDT), AssetType: asset.Spot, Side: Long, - ID: "lol", + OrderID: "lol", }) if !errors.Is(err, ErrNotFuturesAsset) { t.Error(err) @@ -390,7 +390,7 @@ func TestPositionControllerTestTrackNewOrder(t *testing.T) { Pair: currency.NewPair(currency.BTC, currency.USDT), AssetType: asset.Futures, Side: Long, - ID: "lol", + OrderID: "lol", }) if !errors.Is(err, nil) { t.Error(err) @@ -765,7 +765,7 @@ func TestUpdateOpenPositionUnrealisedPNL(t *testing.T) { Pair: currency.NewPair(currency.BTC, currency.USDT), AssetType: asset.Futures, Side: Long, - ID: "lol", + OrderID: "lol", Price: 1, Amount: 1, }) diff --git a/exchanges/order/order_test.go b/exchanges/order/order_test.go index 3d75d870..d529e259 100644 --- a/exchanges/order/order_test.go +++ b/exchanges/order/order_test.go @@ -2,6 +2,7 @@ package order import ( "errors" + "fmt" "reflect" "strings" "testing" @@ -16,7 +17,7 @@ import ( var errValidationCheckFailed = errors.New("validation check failed") -func TestValidate(t *testing.T) { +func TestSubmit_Validate(t *testing.T) { t.Parallel() testPair := currency.NewPair(currency.BTC, currency.LTC) tester := []struct { @@ -29,22 +30,38 @@ func TestValidate(t *testing.T) { Submit: nil, }, // nil struct { - ExpectedErr: ErrPairIsEmpty, + ExpectedErr: errExchangeNameUnset, Submit: &Submit{}, + }, // empty exchange + { + ExpectedErr: ErrPairIsEmpty, + Submit: &Submit{Exchange: "test"}, }, // empty pair { ExpectedErr: ErrAssetNotSet, - Submit: &Submit{Pair: testPair}, + Submit: &Submit{Exchange: "test", Pair: testPair}, + }, // valid pair but invalid asset + { + ExpectedErr: asset.ErrNotSupported, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, + AssetType: 255, + }, }, // valid pair but invalid asset { - ExpectedErr: ErrSideIsInvalid, - Submit: &Submit{Pair: testPair, AssetType: asset.Spot}, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, + AssetType: asset.Spot, + }, }, // valid pair but invalid order side { ExpectedErr: errTimeInForceConflict, Submit: &Submit{ + Exchange: "test", Pair: testPair, AssetType: asset.Spot, Side: Ask, @@ -55,61 +72,107 @@ func TestValidate(t *testing.T) { }, { ExpectedErr: ErrTypeIsInvalid, - Submit: &Submit{Pair: testPair, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, Side: Buy, - AssetType: asset.Spot}, + AssetType: asset.Spot, + }, }, // valid pair and order side but invalid order type { ExpectedErr: ErrTypeIsInvalid, - Submit: &Submit{Pair: testPair, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, Side: Sell, - AssetType: asset.Spot}, + AssetType: asset.Spot, + }, }, // valid pair and order side but invalid order type { ExpectedErr: ErrTypeIsInvalid, - Submit: &Submit{Pair: testPair, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, Side: Bid, - AssetType: asset.Spot}, + AssetType: asset.Spot, + }, }, // valid pair and order side but invalid order type { ExpectedErr: ErrTypeIsInvalid, - Submit: &Submit{Pair: testPair, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, Side: Ask, - AssetType: asset.Spot}, + AssetType: asset.Spot, + }, }, // valid pair and order side but invalid order type { ExpectedErr: ErrAmountIsInvalid, - Submit: &Submit{Pair: testPair, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, Side: Ask, Type: Market, - AssetType: asset.Spot}, + AssetType: asset.Spot, + }, + }, // valid pair, order side, type but invalid amount + { + ExpectedErr: ErrAmountIsInvalid, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, + Side: Ask, + Type: Market, + AssetType: asset.Spot, + Amount: -1, + }, + }, // valid pair, order side, type but invalid amount + { + ExpectedErr: ErrAmountIsInvalid, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, + Side: Ask, + Type: Market, + AssetType: asset.Spot, + QuoteAmount: -1, + }, }, // valid pair, order side, type but invalid amount { ExpectedErr: ErrPriceMustBeSetIfLimitOrder, - Submit: &Submit{Pair: testPair, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, Side: Ask, Type: Limit, Amount: 1, - AssetType: asset.Spot}, + AssetType: asset.Spot, + }, }, // valid pair, order side, type, amount but invalid price { ExpectedErr: errValidationCheckFailed, - Submit: &Submit{Pair: testPair, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, Side: Ask, Type: Limit, Amount: 1, Price: 1000, - AssetType: asset.Spot}, + AssetType: asset.Spot, + }, ValidOpts: validate.Check(func() error { return errValidationCheckFailed }), }, // custom validation error check { ExpectedErr: nil, - Submit: &Submit{Pair: testPair, + Submit: &Submit{ + Exchange: "test", + Pair: testPair, Side: Ask, Type: Limit, Amount: 1, Price: 1000, - AssetType: asset.Spot}, + AssetType: asset.Spot, + }, ValidOpts: validate.Check(func() error { return nil }), }, // valid order! } @@ -122,6 +185,66 @@ func TestValidate(t *testing.T) { } } +func TestSubmit_DeriveSubmitResponse(t *testing.T) { + t.Parallel() + var s *Submit + _, err := s.DeriveSubmitResponse("") + if !errors.Is(err, errOrderSubmitIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, errOrderSubmitIsNil) + } + + s = &Submit{} + _, err = s.DeriveSubmitResponse("") + if !errors.Is(err, ErrOrderIDNotSet) { + t.Fatalf("received: '%v' but expected: '%v'", err, ErrOrderIDNotSet) + } + + resp, err := s.DeriveSubmitResponse("1337") + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if resp.OrderID != "1337" { + t.Fatal("unexpected value") + } + + if resp.Status != New { + t.Fatal("unexpected value") + } + + if resp.Date.IsZero() { + t.Fatal("unexpected value") + } + + if resp.LastUpdated.IsZero() { + t.Fatal("unexpected value") + } +} + +func TestSubmitResponse_DeriveDetail(t *testing.T) { + t.Parallel() + var s *SubmitResponse + _, err := s.DeriveDetail(uuid.Nil) + if !errors.Is(err, errOrderSubmitResponseIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, errOrderSubmitResponseIsNil) + } + + id, err := uuid.NewV4() + if err != nil { + t.Fatal(err) + } + + s = &SubmitResponse{} + deets, err := s.DeriveDetail(id) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if deets.InternalOrderID != id { + t.Fatal("unexpected value") + } +} + func TestOrderSides(t *testing.T) { t.Parallel() @@ -842,8 +965,8 @@ func BenchmarkStringToOrderStatus(b *testing.B) { } } -func TestUpdateOrderFromModify(t *testing.T) { - od := Detail{ID: "1"} +func TestUpdateOrderFromModifyResponse(t *testing.T) { + od := Detail{OrderID: "1"} updated := time.Now() pair, err := currency.NewPairFromString("BTCUSD") @@ -868,9 +991,7 @@ func TestUpdateOrderFromModify(t *testing.T) { } od.UpdateOrderFromModifyResponse(&om) - if od.InternalOrderID == "1" { - t.Error("Should not be able to update the internal order ID") - } + if !od.ImmediateOrCancel { t.Error("Failed to update") } @@ -892,7 +1013,7 @@ func TestUpdateOrderFromModify(t *testing.T) { if od.Exchange != "" { t.Error("Should not be able to update exchange via modify") } - if od.ID != "1" { + if od.OrderID != "1" { t.Error("Failed to update") } if od.Type != 1 { @@ -920,7 +1041,7 @@ func TestUpdateOrderFromModify(t *testing.T) { func TestUpdateOrderFromDetail(t *testing.T) { var leet = "1337" - od := Detail{Exchange: "test"} + updated := time.Now() pair, err := currency.NewPairFromString("BTCUSD") @@ -928,7 +1049,18 @@ func TestUpdateOrderFromDetail(t *testing.T) { t.Fatal(err) } - om := Detail{ + id, err := uuid.NewV4() + if err != nil { + t.Fatal(err) + } + + var od *Detail + err = od.UpdateOrderFromDetail(nil) + if !errors.Is(err, ErrOrderDetailIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, ErrOrderDetailIsNil) + } + + om := &Detail{ ImmediateOrCancel: true, HiddenOrder: true, FillOrKill: true, @@ -944,8 +1076,8 @@ func TestUpdateOrderFromDetail(t *testing.T) { RemainingAmount: 1, Fee: 1, Exchange: "1", - InternalOrderID: "1", - ID: "1", + InternalOrderID: id, + OrderID: "1", AccountID: "1", ClientID: "1", WalletAddress: "1", @@ -958,8 +1090,18 @@ func TestUpdateOrderFromDetail(t *testing.T) { Trades: []TradeHistory{}, } - od.UpdateOrderFromDetail(&om) - if od.InternalOrderID != "1" { + od = &Detail{Exchange: "test"} + + err = od.UpdateOrderFromDetail(nil) + if !errors.Is(err, ErrOrderDetailIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, ErrOrderDetailIsNil) + } + + err = od.UpdateOrderFromDetail(om) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + if od.InternalOrderID != id { t.Error("Failed to initialize the internal order ID") } if !od.ImmediateOrCancel { @@ -1007,7 +1149,7 @@ func TestUpdateOrderFromDetail(t *testing.T) { if od.Exchange != "test" { t.Error("Should not be able to update exchange via modify") } - if od.ID != "1" { + if od.OrderID != "1" { t.Error("Failed to update") } if od.ClientID != "1" { @@ -1039,7 +1181,10 @@ func TestUpdateOrderFromDetail(t *testing.T) { } om.Trades = append(om.Trades, TradeHistory{TID: "1"}, TradeHistory{TID: "2"}) - od.UpdateOrderFromDetail(&om) + err = od.UpdateOrderFromDetail(om) + if err != nil { + t.Fatal(err) + } if len(od.Trades) != 2 { t.Error("Failed to add trades") } @@ -1052,7 +1197,10 @@ func TestUpdateOrderFromDetail(t *testing.T) { om.Trades[0].Side = UnknownSide om.Trades[0].Type = UnknownType om.Trades[0].Amount = 1337 - od.UpdateOrderFromDetail(&om) + err = od.UpdateOrderFromDetail(om) + if err != nil { + t.Fatal(err) + } if od.Trades[0].Exchange == leet { t.Error("Should not be able to update exchange from update") } @@ -1081,12 +1229,20 @@ func TestUpdateOrderFromDetail(t *testing.T) { t.Error("Failed to update trades") } - om = Detail{ - InternalOrderID: "2", + id, err = uuid.NewV4() + if err != nil { + t.Fatal(err) } - od.UpdateOrderFromDetail(&om) - if od.InternalOrderID == "2" { + om = &Detail{ + InternalOrderID: id, + } + + err = od.UpdateOrderFromDetail(om) + if err != nil { + t.Fatal(err) + } + if od.InternalOrderID == id { t.Error("Should not be able to update the internal order ID after initialization") } } @@ -1140,7 +1296,7 @@ func TestValidationOnOrderTypes(t *testing.T) { })) != nil { t.Fatal("should return nil") } - cancelMe.ID = "1337" + cancelMe.OrderID = "1337" if cancelMe.Validate(cancelMe.StandardCancel()) != nil { t.Fatal("should return nil") } @@ -1211,11 +1367,16 @@ func TestValidationOnOrderTypes(t *testing.T) { } func TestMatchFilter(t *testing.T) { + t.Parallel() + id, err := uuid.NewV4() + if err != nil { + t.Fatal(err) + } filters := map[int]Filter{ 0: {}, 1: {Exchange: "Binance"}, - 2: {InternalOrderID: "1234"}, - 3: {ID: "2222"}, + 2: {InternalOrderID: id}, + 3: {OrderID: "2222"}, 4: {ClientOrderID: "3333"}, 5: {ClientID: "4444"}, 6: {WalletAddress: "5555"}, @@ -1235,8 +1396,8 @@ func TestMatchFilter(t *testing.T) { orders := map[int]Detail{ 0: {}, 1: {Exchange: "Binance"}, - 2: {InternalOrderID: "1234"}, - 3: {ID: "2222"}, + 2: {InternalOrderID: id}, + 3: {OrderID: "2222"}, 4: {ClientOrderID: "3333"}, 5: {ClientID: "4444"}, 6: {WalletAddress: "5555"}, @@ -1448,22 +1609,59 @@ func BenchmarkIsInactive(b *testing.B) { } } +func TestIsOrderPlaced(t *testing.T) { + t.Parallel() + statusTests := map[int]struct { + o Detail + expRes bool + }{ + 0: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AnyStatus}, false}, + 1: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: New}, true}, + 2: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Active}, true}, + 3: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: PartiallyCancelled}, true}, + 4: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: PartiallyFilled}, true}, + 5: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Filled}, true}, + 6: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Cancelled}, true}, + 7: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: PendingCancel}, true}, + 8: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: InsufficientBalance}, false}, + 9: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: MarketUnavailable}, false}, + 10: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Rejected}, false}, + 11: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Expired}, true}, + 12: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Hidden}, true}, + 13: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: UnknownStatus}, false}, + 14: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Open}, true}, + 15: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AutoDeleverage}, true}, + 16: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Closed}, true}, + 17: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Pending}, true}, + } + // specific tests + for num, tt := range statusTests { + tt := tt + t.Run(fmt.Sprintf("TEST CASE: %d", num), func(t *testing.T) { + t.Parallel() + if tt.o.WasOrderPlaced() != tt.expRes { + t.Errorf("statusTests[%v] failed", num) + } + }) + } +} + func TestGenerateInternalOrderID(t *testing.T) { id, err := uuid.NewV4() if err != nil { t.Errorf("unable to create uuid: %s", err) } od := Detail{ - InternalOrderID: id.String(), + InternalOrderID: id, } od.GenerateInternalOrderID() - if od.InternalOrderID != id.String() { + if od.InternalOrderID != id { t.Error("Should not be able to generate a new internal order ID") } od = Detail{} od.GenerateInternalOrderID() - if od.InternalOrderID == "" { + if od.InternalOrderID.IsNil() { t.Error("unable to generate internal order ID") } } @@ -1558,7 +1756,7 @@ func TestDeriveModify(t *testing.T) { o = &Detail{ Exchange: "wow", - ID: "wow2", + OrderID: "wow2", ClientOrderID: "wow3", Type: Market, Side: Long, @@ -1576,7 +1774,7 @@ func TestDeriveModify(t *testing.T) { } if mod.Exchange != "wow" || - mod.ID != "wow2" || + mod.OrderID != "wow2" || mod.ClientOrderID != "wow3" || mod.Type != Market || mod.Side != Long || @@ -1597,7 +1795,7 @@ func TestDeriveModifyResponse(t *testing.T) { mod = &Modify{ Exchange: "wow", - ID: "wow2", + OrderID: "wow2", ClientOrderID: "wow3", Type: Market, Side: Long, @@ -1636,7 +1834,7 @@ func TestDeriveCancel(t *testing.T) { o = &Detail{ Exchange: "wow", - ID: "wow1", + OrderID: "wow1", AccountID: "wow2", ClientID: "wow3", ClientOrderID: "wow4", @@ -1651,7 +1849,7 @@ func TestDeriveCancel(t *testing.T) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } if cancel.Exchange != "wow" || - cancel.ID != "wow1" || + cancel.OrderID != "wow1" || cancel.AccountID != "wow2" || cancel.ClientID != "wow3" || cancel.ClientOrderID != "wow4" || diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index b13715b8..e8527455 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -4,6 +4,7 @@ import ( "errors" "time" + "github.com/gofrs/uuid" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) @@ -12,6 +13,7 @@ import ( var ( ErrSubmissionIsNil = errors.New("order submission is nil") ErrCancelOrderIsNil = errors.New("cancel order is nil") + ErrOrderDetailIsNil = errors.New("order detail is nil") ErrGetOrdersRequestIsNil = errors.New("get order request is nil") ErrModifyOrderIsNil = errors.New("modify order request is nil") ErrPairIsEmpty = errors.New("order pair is empty") @@ -28,54 +30,63 @@ var ( // Each exchange has their own requirements, so not all fields // are required to be populated type Submit struct { - ImmediateOrCancel bool - HiddenOrder bool - FillOrKill bool - PostOnly bool - ReduceOnly bool - Leverage float64 - Price float64 + Exchange string + Type Type + Side Side + Pair currency.Pair + AssetType asset.Item + // Time in force values ------ TODO: Time In Force uint8 + ImmediateOrCancel bool + FillOrKill bool + + PostOnly bool + // ReduceOnly reduces a position instead of opening an opposing + // position; this also equates to closing the position in huobi_wrapper.go + // swaps. + ReduceOnly bool + // Leverage is the amount of leverage that will be used: see huobi_wrapper.go + Leverage float64 + Price float64 // Amount in base terms Amount float64 // QuoteAmount is the max amount in quote currency when purchasing base. // This is only used in Market orders. QuoteAmount float64 - - StopPrice float64 - LimitPriceUpper float64 - LimitPriceLower float64 - TriggerPrice float64 - ExecutedAmount float64 - RemainingAmount float64 - Fee float64 - Exchange string - InternalOrderID string - ID string - AccountID string - ClientID string - ClientOrderID string - WalletAddress string - Offset string - Type Type - Side Side - Status Status - AssetType asset.Item - Date time.Time - LastUpdated time.Time - Pair currency.Pair - Trades []TradeHistory + // TriggerPrice is mandatory if order type `Stop, Stop Limit or Take Profit` + // See btcmarkets_wrapper.go. + TriggerPrice float64 + ClientID string // TODO: Shift to credentials + ClientOrderID string } // SubmitResponse is what is returned after submitting an order to an exchange type SubmitResponse struct { - IsOrderPlaced bool - FullyMatched bool - OrderID string - Rate float64 - Fee float64 - Cost float64 - Trades []TradeHistory + Exchange string + Type Type + Side Side + Pair currency.Pair + AssetType asset.Item + + ImmediateOrCancel bool + FillOrKill bool + PostOnly bool + ReduceOnly bool + Leverage float64 + Price float64 + Amount float64 + QuoteAmount float64 + TriggerPrice float64 + ClientID string + ClientOrderID string + + LastUpdated time.Time + Date time.Time + Status Status + OrderID string + Trades []TradeHistory + Fee float64 + Cost float64 } // Modify contains all properties of an order @@ -85,7 +96,7 @@ type SubmitResponse struct { type Modify struct { // Order Identifiers Exchange string - ID string + OrderID string ClientOrderID string Type Type Side Side @@ -134,6 +145,7 @@ type Detail struct { HiddenOrder bool FillOrKill bool PostOnly bool + ReduceOnly bool Leverage float64 Price float64 Amount float64 @@ -149,8 +161,8 @@ type Detail struct { Fee float64 FeeAsset currency.Code Exchange string - InternalOrderID string - ID string + InternalOrderID uuid.UUID + OrderID string ClientOrderID string AccountID string ClientID string @@ -170,8 +182,8 @@ type Detail struct { // empty strings indicate to ignore the property otherwise all need to match type Filter struct { Exchange string - InternalOrderID string - ID string + InternalOrderID uuid.UUID + OrderID string ClientOrderID string AccountID string ClientID string @@ -189,7 +201,7 @@ type Filter struct { // are required to be populated type Cancel struct { Exchange string - ID string + OrderID string ClientOrderID string AccountID string ClientID string diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index e0405fc0..b8d5418c 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -20,16 +20,22 @@ const ( longSide = Long | Buy | Bid inactiveStatuses = Filled | Cancelled | InsufficientBalance | MarketUnavailable | Rejected | PartiallyCancelled | Expired | Closed | AnyStatus | Cancelling activeStatuses = Active | Open | PartiallyFilled | New | PendingCancel | Hidden | AutoDeleverage | Pending - bypassSideFilter = UnknownSide | AnySide - bypassTypeFilter = UnknownType | AnyType + notPlaced = InsufficientBalance | MarketUnavailable | Rejected ) var ( - errTimeInForceConflict = errors.New("multiple time in force options applied") - errUnrecognisedOrderSide = errors.New("unrecognised order side") - errUnrecognisedOrderType = errors.New("unrecognised order type") - errUnrecognisedOrderStatus = errors.New("unrecognised order status") - errOrderDetailIsNil = errors.New("order detail is nil") + // ErrUnableToPlaceOrder defines an error when an order submission has + // failed. + ErrUnableToPlaceOrder = errors.New("order not placed") + + errTimeInForceConflict = errors.New("multiple time in force options applied") + errUnrecognisedOrderSide = errors.New("unrecognised order side") + errUnrecognisedOrderType = errors.New("unrecognised order type") + errUnrecognisedOrderStatus = errors.New("unrecognised order status") + errExchangeNameUnset = errors.New("exchange name unset") + errOrderSubmitIsNil = errors.New("order submit is nil") + errOrderSubmitResponseIsNil = errors.New("order submit response is nil") + errOrderDetailIsNil = errors.New("order detail is nil") ) // Validate checks the supplied data and returns whether or not it's valid @@ -38,6 +44,10 @@ func (s *Submit) Validate(opt ...validate.Checker) error { return ErrSubmissionIsNil } + if s.Exchange == "" { + return errExchangeNameUnset + } + if s.Pair.IsEmpty() { return ErrPairIsEmpty } @@ -46,6 +56,10 @@ func (s *Submit) Validate(opt ...validate.Checker) error { return ErrAssetNotSet } + if !s.AssetType.IsValid() { + return fmt.Errorf("'%s' %w", s.AssetType, asset.ErrNotSupported) + } + if s.Side == UnknownSide || orderSubmissionValidSides&s.Side != s.Side { return ErrSideIsInvalid } @@ -86,7 +100,15 @@ func (s *Submit) Validate(opt ...validate.Checker) error { // UpdateOrderFromDetail Will update an order detail (used in order management) // by comparing passed in and existing values -func (d *Detail) UpdateOrderFromDetail(m *Detail) { +func (d *Detail) UpdateOrderFromDetail(m *Detail) error { + if d == nil { + return ErrOrderDetailIsNil + } + + if m == nil { + return fmt.Errorf("incoming %w", ErrOrderDetailIsNil) + } + var updated bool if d.ImmediateOrCancel != m.ImmediateOrCancel { d.ImmediateOrCancel = m.ImmediateOrCancel @@ -234,20 +256,21 @@ func (d *Detail) UpdateOrderFromDetail(m *Detail) { if d.Exchange == "" { d.Exchange = m.Exchange } - if d.ID == "" { - d.ID = m.ID + if d.OrderID == "" { + d.OrderID = m.OrderID } - if d.InternalOrderID == "" { + if d.InternalOrderID.IsNil() { d.InternalOrderID = m.InternalOrderID } + return nil } // UpdateOrderFromModifyResponse Will update an order detail (used in order management) // by comparing passed in and existing values func (d *Detail) UpdateOrderFromModifyResponse(m *ModifyResponse) { var updated bool - if m.OrderID != "" && d.ID != m.OrderID { - d.ID = m.OrderID + if m.OrderID != "" && d.OrderID != m.OrderID { + d.OrderID = m.OrderID updated = true } if d.ImmediateOrCancel != m.ImmediateOrCancel { @@ -308,43 +331,34 @@ func (d *Detail) UpdateOrderFromModifyResponse(m *ModifyResponse) { // MatchFilter will return true if a detail matches the filter criteria // empty elements are ignored func (d *Detail) MatchFilter(f *Filter) bool { - if f.Exchange != "" && !strings.EqualFold(d.Exchange, f.Exchange) { + switch { + case f.Exchange != "" && !strings.EqualFold(d.Exchange, f.Exchange): return false - } - if f.AssetType != asset.Empty && d.AssetType != f.AssetType { + case f.AssetType != asset.Empty && d.AssetType != f.AssetType: return false - } - if !f.Pair.IsEmpty() && !d.Pair.Equal(f.Pair) { + case !f.Pair.IsEmpty() && !d.Pair.Equal(f.Pair): return false - } - if f.ID != "" && d.ID != f.ID { + case f.OrderID != "" && d.OrderID != f.OrderID: return false - } - if f.Type != UnknownType && f.Type != AnyType && d.Type != f.Type { + case f.Type != UnknownType && f.Type != AnyType && d.Type != f.Type: return false - } - if f.Side != UnknownSide && f.Side != AnySide && d.Side != f.Side { + case f.Side != UnknownSide && f.Side != AnySide && d.Side != f.Side: return false - } - if f.Status != UnknownStatus && f.Status != AnyStatus && d.Status != f.Status { + case f.Status != UnknownStatus && f.Status != AnyStatus && d.Status != f.Status: return false - } - if f.ClientOrderID != "" && d.ClientOrderID != f.ClientOrderID { + case f.ClientOrderID != "" && d.ClientOrderID != f.ClientOrderID: return false - } - if f.ClientID != "" && d.ClientID != f.ClientID { + case f.ClientID != "" && d.ClientID != f.ClientID: return false - } - if f.InternalOrderID != "" && d.InternalOrderID != f.InternalOrderID { + case !f.InternalOrderID.IsNil() && d.InternalOrderID != f.InternalOrderID: return false - } - if f.AccountID != "" && d.AccountID != f.AccountID { + case f.AccountID != "" && d.AccountID != f.AccountID: return false - } - if f.WalletAddress != "" && d.WalletAddress != f.WalletAddress { + case f.WalletAddress != "" && d.WalletAddress != f.WalletAddress: return false + default: + return true } - return true } // IsActive returns true if an order has a status that indicates it is currently @@ -364,16 +378,25 @@ func (d *Detail) IsInactive() bool { inactiveStatuses&d.Status == d.Status } +// WasOrderPlaced returns true if an order has a status that indicates that it +// was accepted by an exchange. +func (d *Detail) WasOrderPlaced() bool { + if d.Status == UnknownStatus || d.Status == AnyStatus { + return false + } + return notPlaced&d.Status != d.Status +} + // GenerateInternalOrderID sets a new V4 order ID or a V5 order ID if // the V4 function returns an error func (d *Detail) GenerateInternalOrderID() { - if d.InternalOrderID == "" { - var id uuid.UUID - id, err := uuid.NewV4() - if err != nil { - id = uuid.NewV5(uuid.UUID{}, d.ID) - } - d.InternalOrderID = id.String() + if !d.InternalOrderID.IsNil() { + return + } + var err error + d.InternalOrderID, err = uuid.NewV4() + if err != nil { + d.InternalOrderID = uuid.NewV5(uuid.UUID{}, d.OrderID) } } @@ -394,6 +417,90 @@ func (d *Detail) Copy() Detail { return c } +// DeriveSubmitResponse will construct an order SubmitResponse when a successful +// submission has occurred. NOTE: order status is populated as order.Filled for a +// market order else order.New if an order is accepted as default, date and +// lastupdated fields have been populated as time.Now(). All fields can be +// customized in caller scope if needed. +func (s *Submit) DeriveSubmitResponse(orderID string) (*SubmitResponse, error) { + if s == nil { + return nil, errOrderSubmitIsNil + } + + if orderID == "" { + return nil, ErrOrderIDNotSet + } + + status := New + if s.Type == Market { // NOTE: This will need to be scrutinized. + status = Filled + } + + return &SubmitResponse{ + Exchange: s.Exchange, + Type: s.Type, + Side: s.Side, + Pair: s.Pair, + AssetType: s.AssetType, + + ImmediateOrCancel: s.ImmediateOrCancel, + FillOrKill: s.FillOrKill, + PostOnly: s.PostOnly, + ReduceOnly: s.ReduceOnly, + Leverage: s.Leverage, + Price: s.Price, + Amount: s.Amount, + QuoteAmount: s.QuoteAmount, + TriggerPrice: s.TriggerPrice, + ClientID: s.ClientID, + ClientOrderID: s.ClientOrderID, + + LastUpdated: time.Now(), + Date: time.Now(), + Status: status, + OrderID: orderID, + }, nil +} + +// DeriveDetail will construct an order detail when a successful submission +// has occurred. Has an optional parameter field internal uuid for internal +// management. +func (s *SubmitResponse) DeriveDetail(internal uuid.UUID) (*Detail, error) { + if s == nil { + return nil, errOrderSubmitResponseIsNil + } + + return &Detail{ + Exchange: s.Exchange, + Type: s.Type, + Side: s.Side, + Pair: s.Pair, + AssetType: s.AssetType, + + ImmediateOrCancel: s.ImmediateOrCancel, + FillOrKill: s.FillOrKill, + PostOnly: s.PostOnly, + ReduceOnly: s.ReduceOnly, + Leverage: s.Leverage, + Price: s.Price, + Amount: s.Amount, + QuoteAmount: s.QuoteAmount, + TriggerPrice: s.TriggerPrice, + ClientID: s.ClientID, + ClientOrderID: s.ClientOrderID, + + InternalOrderID: internal, + + LastUpdated: s.LastUpdated, + Date: s.Date, + Status: s.Status, + OrderID: s.OrderID, + Trades: s.Trades, + Fee: s.Fee, + Cost: s.Cost, + }, nil +} + // CopyPointerOrderSlice returns a copy of all order detail and returns a slice // of pointers. func CopyPointerOrderSlice(old []*Detail) []*Detail { @@ -413,7 +520,7 @@ func (d *Detail) DeriveModify() (*Modify, error) { } return &Modify{ Exchange: d.Exchange, - ID: d.ID, + OrderID: d.OrderID, ClientOrderID: d.ClientOrderID, Type: d.Type, Side: d.Side, @@ -431,7 +538,7 @@ func (m *Modify) DeriveModifyResponse() (*ModifyResponse, error) { } return &ModifyResponse{ Exchange: m.Exchange, - OrderID: m.ID, + OrderID: m.OrderID, ClientOrderID: m.ClientOrderID, Type: m.Type, Side: m.Side, @@ -452,7 +559,7 @@ func (d *Detail) DeriveCancel() (*Cancel, error) { } return &Cancel{ Exchange: d.Exchange, - ID: d.ID, + OrderID: d.OrderID, AccountID: d.AccountID, ClientID: d.ClientID, ClientOrderID: d.ClientOrderID, @@ -643,7 +750,7 @@ func (d *Detail) InferCostsAndTimes() { // FilterOrdersBySide removes any order details that don't match the order // status provided func FilterOrdersBySide(orders *[]Detail, side Side) { - if bypassSideFilter&side == side || len(*orders) == 0 { + if AnySide == side || len(*orders) == 0 { return } @@ -660,7 +767,7 @@ func FilterOrdersBySide(orders *[]Detail, side Side) { // FilterOrdersByType removes any order details that don't match the order type // provided func FilterOrdersByType(orders *[]Detail, orderType Type) { - if bypassTypeFilter&orderType == orderType || len(*orders) == 0 { + if AnyType == orderType || len(*orders) == 0 { return } @@ -941,7 +1048,7 @@ func (o *ClassificationError) Error() string { // for a standard cancel func (c *Cancel) StandardCancel() validate.Checker { return validate.Check(func() error { - if c.ID == "" { + if c.OrderID == "" { return errors.New("ID not set") } return nil @@ -1030,7 +1137,7 @@ func (m *Modify) Validate(opt ...validate.Checker) error { if errs != nil { return errs } - if m.ClientOrderID == "" && m.ID == "" { + if m.ClientOrderID == "" && m.OrderID == "" { return ErrOrderIDNotSet } return nil diff --git a/exchanges/poloniex/poloniex_test.go b/exchanges/poloniex/poloniex_test.go index b68a59d1..08024244 100644 --- a/exchanges/poloniex/poloniex_test.go +++ b/exchanges/poloniex/poloniex_test.go @@ -371,6 +371,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: p.Name, Pair: currency.Pair{ Delimiter: currency.UnderscoreDelimiter, Base: currency.BTC, @@ -386,7 +387,7 @@ func TestSubmitOrder(t *testing.T) { response, err := p.SubmitOrder(context.Background(), orderSubmission) switch { - case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced): + case areTestAPIKeysSet() && (err != nil || response.Status != order.Filled): t.Errorf("Order failed to be placed: %v", err) case !areTestAPIKeysSet() && !mockTests && err == nil: t.Error("Expecting an error when no keys are set") @@ -401,7 +402,7 @@ func TestCancelExchangeOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currency.NewPair(currency.LTC, currency.BTC), @@ -427,7 +428,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -454,10 +455,12 @@ func TestModifyOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - _, err := p.ModifyOrder(context.Background(), &order.Modify{ID: "1337", + _, err := p.ModifyOrder(context.Background(), &order.Modify{ + OrderID: "1337", Price: 1337, AssetType: asset.Spot, - Pair: currency.NewPair(currency.BTC, currency.USDT)}) + Pair: currency.NewPair(currency.BTC, currency.USDT), + }) switch { case areTestAPIKeysSet() && err != nil && mockTests: t.Error("ModifyOrder() error", err) diff --git a/exchanges/poloniex/poloniex_websocket.go b/exchanges/poloniex/poloniex_websocket.go index 5b98a374..080d6f6d 100644 --- a/exchanges/poloniex/poloniex_websocket.go +++ b/exchanges/poloniex/poloniex_websocket.go @@ -753,7 +753,7 @@ func (p *Poloniex) processAccountPendingOrder(notification []interface{}) error p.Websocket.DataHandler <- &order.Detail{ Exchange: p.Name, - ID: strconv.FormatFloat(orderID, 'f', -1, 64), + OrderID: strconv.FormatFloat(orderID, 'f', -1, 64), Pair: pair, AssetType: asset.Spot, Side: orderSide, @@ -827,7 +827,7 @@ func (p *Poloniex) processAccountOrderUpdate(notification []interface{}) error { RemainingAmount: cancelledAmount, Amount: amount + cancelledAmount, ExecutedAmount: amount, - ID: strconv.FormatFloat(orderID, 'f', -1, 64), + OrderID: strconv.FormatFloat(orderID, 'f', -1, 64), Type: order.Limit, Status: oStatus, AssetType: asset.Spot, @@ -915,7 +915,7 @@ func (p *Poloniex) processAccountOrderLimit(notification []interface{}) error { RemainingAmount: orderAmount, ExecutedAmount: origOrderAmount - orderAmount, Amount: origOrderAmount, - ID: strconv.FormatFloat(orderID, 'f', -1, 64), + OrderID: strconv.FormatFloat(orderID, 'f', -1, 64), Type: order.Limit, Side: orderSide, Status: order.New, @@ -1050,7 +1050,7 @@ func (p *Poloniex) processAccountTrades(notification []interface{}) error { p.Websocket.DataHandler <- &order.Detail{ Exchange: p.Name, - ID: strconv.FormatFloat(orderID, 'f', -1, 64), + OrderID: strconv.FormatFloat(orderID, 'f', -1, 64), Fee: totalFee, Trades: []order.TradeHistory{{ Price: rate, @@ -1082,7 +1082,7 @@ func (p *Poloniex) processAccountKilledOrder(notification []interface{}) error { p.Websocket.DataHandler <- &order.Detail{ Exchange: p.Name, - ID: strconv.FormatFloat(orderID, 'f', -1, 64), + OrderID: strconv.FormatFloat(orderID, 'f', -1, 64), Status: order.Cancelled, AssetType: asset.Spot, ClientOrderID: clientOrderID, diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index f0feb125..cee92067 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -531,38 +531,26 @@ allTrades: } // SubmitOrder submits a new order -func (p *Poloniex) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (p *Poloniex) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } fPair, err := p.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } - - fillOrKill := s.Type == order.Market - isBuyOrder := s.Side == order.Buy response, err := p.PlaceOrder(ctx, fPair.String(), s.Price, s.Amount, false, - fillOrKill, - isBuyOrder) + s.Type == order.Market, + s.Side == order.Buy) if err != nil { - return submitOrderResponse, err + return nil, err } - if response.OrderNumber > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10) - } - - submitOrderResponse.IsOrderPlaced = true - if s.Type == order.Market { - submitOrderResponse.FullyMatched = true - } - return submitOrderResponse, nil + return s.DeriveSubmitResponse(strconv.FormatInt(response.OrderNumber, 10)) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -572,7 +560,7 @@ func (p *Poloniex) ModifyOrder(ctx context.Context, action *order.Modify) (*orde return nil, err } - oID, err := strconv.ParseInt(action.ID, 10, 64) + oID, err := strconv.ParseInt(action.OrderID, 10, 64) if err != nil { return nil, err } @@ -601,7 +589,7 @@ func (p *Poloniex) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -855,7 +843,7 @@ func (p *Poloniex) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ } orders = append(orders, order.Detail{ - ID: strconv.FormatInt(resp.Data[key][i].OrderNumber, 10), + OrderID: strconv.FormatInt(resp.Data[key][i].OrderNumber, 10), Side: orderSide, Amount: resp.Data[key][i].Amount, Date: orderDate, @@ -921,7 +909,7 @@ func (p *Poloniex) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ } detail := order.Detail{ - ID: strconv.FormatInt(resp.Data[key][i].GlobalTradeID, 10), + OrderID: strconv.FormatInt(resp.Data[key][i].GlobalTradeID, 10), Side: orderSide, Amount: resp.Data[key][i].Amount, ExecutedAmount: resp.Data[key][i].Amount, diff --git a/exchanges/sharedtestvalues/customex.go b/exchanges/sharedtestvalues/customex.go index d685c1ac..438fbf39 100644 --- a/exchanges/sharedtestvalues/customex.go +++ b/exchanges/sharedtestvalues/customex.go @@ -142,8 +142,8 @@ func (c *CustomEx) GetFundingHistory(ctx context.Context) ([]exchange.FundHistor return nil, nil } -func (c *CustomEx) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - return order.SubmitResponse{}, nil +func (c *CustomEx) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { + return nil, nil } func (c *CustomEx) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) { diff --git a/exchanges/yobit/yobit_test.go b/exchanges/yobit/yobit_test.go index c7cfeb6e..d9d03ce1 100644 --- a/exchanges/yobit/yobit_test.go +++ b/exchanges/yobit/yobit_test.go @@ -378,6 +378,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: y.Name, Pair: currency.Pair{ Delimiter: "_", Base: currency.BTC, @@ -391,7 +392,7 @@ func TestSubmitOrder(t *testing.T) { AssetType: asset.Spot, } response, err := y.SubmitOrder(context.Background(), orderSubmission) - if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { + if areTestAPIKeysSet() && (err != nil || response.Status != order.New) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { t.Error("Expecting an error when no keys are set") @@ -405,7 +406,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -428,7 +429,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.LTC, currency.BTC) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index 836b9ba0..8558c375 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -406,19 +406,18 @@ func (y *Yobit) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.It // SubmitOrder submits a new order // Yobit only supports limit orders -func (y *Yobit) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (y *Yobit) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { if err := s.Validate(); err != nil { - return submitOrderResponse, err + return nil, err } if s.Type != order.Limit { - return submitOrderResponse, errors.New("only limit orders are allowed") + return nil, errors.New("only limit orders are allowed") } fPair, err := y.FormatExchangeCurrency(s.Pair, s.AssetType) if err != nil { - return submitOrderResponse, err + return nil, err } response, err := y.Trade(ctx, @@ -427,14 +426,9 @@ func (y *Yobit) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR s.Amount, s.Price) if err != nil { - return submitOrderResponse, err + return nil, err } - if response > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response, 10) - } - - submitOrderResponse.IsOrderPlaced = true - return submitOrderResponse, nil + return s.DeriveSubmitResponse(strconv.FormatInt(response, 10)) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -449,7 +443,7 @@ func (y *Yobit) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -606,7 +600,7 @@ func (y *Yobit) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest return nil, err } orders = append(orders, order.Detail{ - ID: id, + OrderID: id, Amount: resp[id].Amount, Price: resp[id].Rate, Side: side, @@ -674,7 +668,7 @@ func (y *Yobit) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest return nil, err } detail := order.Detail{ - ID: strconv.FormatFloat(allOrders[i].OrderID, 'f', -1, 64), + OrderID: strconv.FormatFloat(allOrders[i].OrderID, 'f', -1, 64), Amount: allOrders[i].Amount, ExecutedAmount: allOrders[i].Amount, Price: allOrders[i].Rate, diff --git a/exchanges/zb/zb_test.go b/exchanges/zb/zb_test.go index 3c47d0c8..c97daee2 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -294,6 +294,7 @@ func TestSubmitOrder(t *testing.T) { } var orderSubmission = &order.Submit{ + Exchange: z.Name, Pair: currency.Pair{ Delimiter: "_", Base: currency.XRP, @@ -327,7 +328,7 @@ func TestCancelExchangeOrder(t *testing.T) { currencyPair := currency.NewPair(currency.XRP, currency.USDT) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, @@ -352,7 +353,7 @@ func TestCancelAllExchangeOrders(t *testing.T) { currencyPair := currency.NewPair(currency.XRP, currency.USDT) var orderCancellation = &order.Cancel{ - ID: "1", + OrderID: "1", WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currencyPair, diff --git a/exchanges/zb/zb_websocket.go b/exchanges/zb/zb_websocket.go index 2979730c..b337a46c 100644 --- a/exchanges/zb/zb_websocket.go +++ b/exchanges/zb/zb_websocket.go @@ -196,7 +196,7 @@ func (z *ZB) wsHandleData(respRaw []byte) error { } z.Websocket.DataHandler <- &order.Detail{ Exchange: z.Name, - ID: strconv.FormatInt(o.Data.EntrustID, 10), + OrderID: strconv.FormatInt(o.Data.EntrustID, 10), Pair: p, AssetType: a, } @@ -221,7 +221,7 @@ func (z *ZB) wsHandleData(respRaw []byte) error { z.Websocket.DataHandler <- &order.Detail{ Exchange: z.Name, - ID: strconv.FormatInt(o.Data.EntrustID, 10), + OrderID: strconv.FormatInt(o.Data.EntrustID, 10), Pair: p, Status: order.Cancelled, } diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index d693c839..52a676e7 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -472,12 +472,12 @@ func (z *ZB) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.Item, } // SubmitOrder submits a new order -func (z *ZB) SubmitOrder(ctx context.Context, o *order.Submit) (order.SubmitResponse, error) { - var submitOrderResponse order.SubmitResponse +func (z *ZB) SubmitOrder(ctx context.Context, o *order.Submit) (*order.SubmitResponse, error) { err := o.Validate() if err != nil { - return submitOrderResponse, err + return nil, err } + if z.Websocket.CanUseAuthenticatedWebsocketForWrapper() { var isBuyOrder int64 if o.Side == order.Buy { @@ -488,42 +488,32 @@ func (z *ZB) SubmitOrder(ctx context.Context, o *order.Submit) (order.SubmitResp var response *WsSubmitOrderResponse response, err = z.wsSubmitOrder(ctx, o.Pair, o.Amount, o.Price, isBuyOrder) if err != nil { - return submitOrderResponse, err - } - submitOrderResponse.OrderID = strconv.FormatInt(response.Data.EntrustID, 10) - } else { - var oT SpotNewOrderRequestParamsType - if o.Side == order.Buy { - oT = SpotNewOrderRequestParamsTypeBuy - } else { - oT = SpotNewOrderRequestParamsTypeSell - } - - fPair, err := z.FormatExchangeCurrency(o.Pair, o.AssetType) - if err != nil { - return submitOrderResponse, err - } - - var params = SpotNewOrderRequestParams{ - Amount: o.Amount, - Price: o.Price, - Symbol: fPair.Lower().String(), - Type: oT, - } - var response int64 - response, err = z.SpotNewOrder(ctx, params) - if err != nil { - return submitOrderResponse, err - } - if response > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response, 10) + return nil, err } + return o.DeriveSubmitResponse(strconv.FormatInt(response.Data.EntrustID, 10)) } - submitOrderResponse.IsOrderPlaced = true - if o.Type == order.Market { - submitOrderResponse.FullyMatched = true + var oT = SpotNewOrderRequestParamsTypeSell + if o.Side == order.Buy { + oT = SpotNewOrderRequestParamsTypeBuy } - return submitOrderResponse, nil + + fPair, err := z.FormatExchangeCurrency(o.Pair, o.AssetType) + if err != nil { + return nil, err + } + + var params = SpotNewOrderRequestParams{ + Amount: o.Amount, + Price: o.Price, + Symbol: fPair.Lower().String(), + Type: oT, + } + var response int64 + response, err = z.SpotNewOrder(ctx, params) + if err != nil { + return nil, err + } + return o.DeriveSubmitResponse(strconv.FormatInt(response, 10)) } // ModifyOrder will allow of changing orderbook placement and limit to @@ -538,7 +528,7 @@ func (z *ZB) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } - orderIDInt, err := strconv.ParseInt(o.ID, 10, 64) + orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64) if err != nil { return err } @@ -550,7 +540,7 @@ func (z *ZB) CancelOrder(ctx context.Context, o *order.Cancel) error { return err } if !response.Success { - return fmt.Errorf("%v - Could not cancel order %v", z.Name, o.ID) + return fmt.Errorf("%v - Could not cancel order %v", z.Name, o.OrderID) } return nil } @@ -611,12 +601,11 @@ func (z *ZB) CancelAllOrders(ctx context.Context, _ *order.Cancel) (order.Cancel continue } - err = z.CancelOrder(ctx, - &order.Cancel{ - ID: strconv.FormatInt(allOpenOrders[i].ID, 10), - Pair: p, - AssetType: asset.Spot, - }) + err = z.CancelOrder(ctx, &order.Cancel{ + OrderID: strconv.FormatInt(allOpenOrders[i].ID, 10), + Pair: p, + AssetType: asset.Spot, + }) if err != nil { cancelAllOrdersResponse.Status[strconv.FormatInt(allOpenOrders[i].ID, 10)] = err.Error() } @@ -755,7 +744,7 @@ func (z *ZB) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest) ( orderDate := time.Unix(int64(allOrders[i].TradeDate), 0) orderSide := orderSideMap[allOrders[i].Type] orders[i] = order.Detail{ - ID: strconv.FormatInt(allOrders[i].ID, 10), + OrderID: strconv.FormatInt(allOrders[i].ID, 10), Amount: allOrders[i].TotalAmount, Exchange: z.Name, Date: orderDate, @@ -842,7 +831,7 @@ func (z *ZB) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest) ( orderDate := time.Unix(int64(allOrders[i].TradeDate), 0) orderSide := orderSideMap[allOrders[i].Type] detail := order.Detail{ - ID: strconv.FormatInt(allOrders[i].ID, 10), + OrderID: strconv.FormatInt(allOrders[i].ID, 10), Amount: allOrders[i].TotalAmount, ExecutedAmount: allOrders[i].TradeAmount, RemainingAmount: allOrders[i].TotalAmount - allOrders[i].TradeAmount, diff --git a/gctscript/modules/gct/exchange.go b/gctscript/modules/gct/exchange.go index c24c1773..fed4b6d6 100644 --- a/gctscript/modules/gct/exchange.go +++ b/gctscript/modules/gct/exchange.go @@ -322,7 +322,7 @@ func ExchangeOrderQuery(args ...objects.Object) (objects.Object, error) { data := make(map[string]objects.Object, 14) data["exchange"] = &objects.String{Value: orderDetails.Exchange} - data["id"] = &objects.String{Value: orderDetails.ID} + data["id"] = &objects.String{Value: orderDetails.OrderID} data["accountid"] = &objects.String{Value: orderDetails.AccountID} data["currencypair"] = &objects.String{Value: orderDetails.Pair.String()} data["price"] = &objects.Float{Value: orderDetails.Price} @@ -480,11 +480,7 @@ func ExchangeOrderSubmit(args ...objects.Object) (objects.Object, error) { data := make(map[string]objects.Object, 2) data["orderid"] = &objects.String{Value: rtn.OrderID} - if rtn.IsOrderPlaced { - data["isorderplaced"] = objects.TrueValue - } else { - data["isorderplaced"] = objects.FalseValue - } + data["isorderplaced"] = objects.TrueValue return &objects.Map{ Value: data, diff --git a/gctscript/modules/wrapper_types.go b/gctscript/modules/wrapper_types.go index 77eecfe3..b1038c9c 100644 --- a/gctscript/modules/wrapper_types.go +++ b/gctscript/modules/wrapper_types.go @@ -23,15 +23,10 @@ const ( ) // Wrapper instance of GCT to use for modules -var Wrapper GCT - -// GCT interface requirements -type GCT interface { - Exchange -} +var Wrapper GCTExchange // Exchange interface requirements -type Exchange interface { +type GCTExchange interface { Exchanges(enabledOnly bool) []string IsEnabled(exch string) bool Orderbook(ctx context.Context, exch string, pair currency.Pair, item asset.Item) (*orderbook.Base, error) @@ -48,6 +43,6 @@ type Exchange interface { } // SetModuleWrapper link the wrapper and interface to use for modules -func SetModuleWrapper(wrapper GCT) { +func SetModuleWrapper(wrapper GCTExchange) { Wrapper = wrapper } diff --git a/gctscript/wrappers/gct/exchange/exchange.go b/gctscript/wrappers/gct/exchange/exchange.go index d3c1f7af..756ad329 100644 --- a/gctscript/wrappers/gct/exchange/exchange.go +++ b/gctscript/wrappers/gct/exchange/exchange.go @@ -94,7 +94,14 @@ func (e Exchange) SubmitOrder(ctx context.Context, submit *order.Submit) (*order return nil, err } - return &r.SubmitResponse, nil + resp, err := submit.DeriveSubmitResponse(r.OrderID) + if err != nil { + return nil, err + } + resp.Status = r.Status + resp.Trades = make([]order.TradeHistory, len(r.Trades)) + copy(resp.Trades, r.Trades) + return resp, nil } // CancelOrder wrapper to cancel order on exchange @@ -106,7 +113,7 @@ func (e Exchange) CancelOrder(ctx context.Context, exch, orderID string, cp curr cancel := &order.Cancel{ AccountID: orderDetails.AccountID, - ID: orderDetails.ID, + OrderID: orderDetails.OrderID, Pair: orderDetails.Pair, Side: orderDetails.Side, AssetType: orderDetails.AssetType, diff --git a/gctscript/wrappers/validator/validator.go b/gctscript/wrappers/validator/validator.go index 1ec2abc9..a068893b 100644 --- a/gctscript/wrappers/validator/validator.go +++ b/gctscript/wrappers/validator/validator.go @@ -122,7 +122,7 @@ func (w Wrapper) QueryOrder(ctx context.Context, exch, _ string, _ currency.Pair return &order.Detail{ Exchange: exch, AccountID: "hello", - ID: "1", + OrderID: "1", Pair: pair, Side: order.Ask, Type: order.Limit, @@ -157,16 +157,17 @@ func (w Wrapper) SubmitOrder(ctx context.Context, o *order.Submit) (*order.Submi return nil, errTestFailed } - tempOrder := &order.SubmitResponse{ - IsOrderPlaced: false, - OrderID: o.Exchange, + resp, err := o.DeriveSubmitResponse(o.Exchange) + if err != nil { + return nil, err } + resp.Status = order.Rejected if o.Exchange == "true" { - tempOrder.IsOrderPlaced = true + resp.Status = order.New } - return tempOrder, nil + return resp, nil } // CancelOrder validator for test execution/scripts diff --git a/gctscript/wrappers/wrappers.go b/gctscript/wrappers/wrappers.go index fb1cabab..a855e1a2 100644 --- a/gctscript/wrappers/wrappers.go +++ b/gctscript/wrappers/wrappers.go @@ -6,7 +6,7 @@ import ( ) // GetWrapper returns the instance of each wrapper to use -func GetWrapper() modules.GCT { +func GetWrapper() modules.GCTExchange { if validator.IsTestExecution.Load() == true { return validator.Wrapper{} }