mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
* ordermanager: fix residual test issue from #917 and reduce some racey action * glorious: nits; also removed functions that weren't being used and were unexported * rm: pew * linter: fix issues * glourious: nits * credentials: fix test issue with racey racey horse basey
This commit is contained in:
@@ -47,10 +47,7 @@ func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager i
|
||||
|
||||
// IsRunning safely checks whether the subsystem is running
|
||||
func (m *OrderManager) IsRunning() bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
return atomic.LoadInt32(&m.started) == 1
|
||||
return m != nil && atomic.LoadInt32(&m.started) == 1
|
||||
}
|
||||
|
||||
// Start runs the subsystem
|
||||
@@ -63,6 +60,7 @@ func (m *OrderManager) Start() error {
|
||||
}
|
||||
log.Debugln(log.OrderMgr, "Order manager starting...")
|
||||
m.shutdown = make(chan struct{})
|
||||
m.orderStore.wg.Add(1)
|
||||
go m.run()
|
||||
return nil
|
||||
}
|
||||
@@ -75,48 +73,44 @@ func (m *OrderManager) Stop() error {
|
||||
if atomic.LoadInt32(&m.started) == 0 {
|
||||
return fmt.Errorf("order manager %w", ErrSubSystemNotStarted)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
atomic.CompareAndSwapInt32(&m.started, 1, 0)
|
||||
}()
|
||||
|
||||
log.Debugln(log.OrderMgr, "Order manager shutting down...")
|
||||
close(m.shutdown)
|
||||
atomic.CompareAndSwapInt32(&m.started, 1, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// gracefulShutdown cancels all orders (if enabled) before shutting down
|
||||
func (m *OrderManager) gracefulShutdown() {
|
||||
if m.cfg.CancelOrdersOnShutdown {
|
||||
log.Debugln(log.OrderMgr, "Order manager: Cancelling any open orders...")
|
||||
exchanges, err := m.orderStore.exchangeManager.GetExchanges()
|
||||
if err != nil {
|
||||
log.Errorf(log.OrderMgr, "Order manager cannot get exchanges: %v", err)
|
||||
return
|
||||
}
|
||||
m.CancelAllOrders(context.TODO(), exchanges)
|
||||
if !m.cfg.CancelOrdersOnShutdown {
|
||||
return
|
||||
}
|
||||
log.Debugln(log.OrderMgr, "Order manager: Cancelling any open orders...")
|
||||
exchanges, err := m.orderStore.exchangeManager.GetExchanges()
|
||||
if err != nil {
|
||||
log.Errorf(log.OrderMgr, "Order manager cannot get exchanges: %v", err)
|
||||
return
|
||||
}
|
||||
m.CancelAllOrders(context.TODO(), exchanges)
|
||||
}
|
||||
|
||||
// run will periodically process orders
|
||||
func (m *OrderManager) run() {
|
||||
log.Debugln(log.OrderMgr, "Order manager started.")
|
||||
m.processOrders()
|
||||
tick := time.NewTicker(orderManagerDelay)
|
||||
m.orderStore.wg.Add(1)
|
||||
defer func() {
|
||||
log.Debugln(log.OrderMgr, "Order manager shutdown.")
|
||||
tick.Stop()
|
||||
m.orderStore.wg.Done()
|
||||
}()
|
||||
|
||||
timer := time.NewTimer(orderManagerDelay)
|
||||
for {
|
||||
select {
|
||||
case <-m.shutdown:
|
||||
m.gracefulShutdown()
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
m.orderStore.wg.Done()
|
||||
log.Debugln(log.OrderMgr, "Order manager shutdown.")
|
||||
return
|
||||
case <-tick.C:
|
||||
case <-timer.C:
|
||||
// Process orders go routine allows shutdown procedures to continue
|
||||
go m.processOrders()
|
||||
timer.Reset(orderManagerDelay)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,7 +132,9 @@ func (m *OrderManager) CancelAllOrders(ctx context.Context, exchangeNames []exch
|
||||
continue
|
||||
}
|
||||
for j := range exchangeOrders {
|
||||
log.Debugf(log.OrderMgr, "Order manager: Cancelling order(s) for exchange %s.", exchangeNames[i].GetName())
|
||||
log.Debugf(log.OrderMgr,
|
||||
"Order manager: Cancelling order(s) for exchange %s.",
|
||||
exchangeNames[i].GetName())
|
||||
err := m.Cancel(ctx, &order.Cancel{
|
||||
Exchange: exchangeOrders[j].Exchange,
|
||||
ID: exchangeOrders[j].ID,
|
||||
@@ -207,22 +203,22 @@ func (m *OrderManager) Cancel(ctx context.Context, cancel *order.Cancel) error {
|
||||
err = fmt.Errorf("%v - Failed to cancel order: %w", cancel.Exchange, err)
|
||||
return err
|
||||
}
|
||||
var od *order.Detail
|
||||
od, err = m.orderStore.getByExchangeAndID(cancel.Exchange, cancel.ID)
|
||||
od, err := m.orderStore.getByExchangeAndID(cancel.Exchange, cancel.ID)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%v - Failed to retrieve order %v to update cancelled status: %w", cancel.Exchange, cancel.ID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
od.Status = order.Cancelled
|
||||
err = m.orderStore.updateExisting(od)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%v - Failed to update existing order when cancelled: %w", cancel.Exchange, err)
|
||||
return err
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Order manager: Exchange %s order ID=%v cancelled.",
|
||||
od.Exchange, od.ID)
|
||||
log.Debugln(log.OrderMgr, msg)
|
||||
m.orderStore.commsManager.PushEvent(base.Event{
|
||||
Type: "order",
|
||||
Message: msg,
|
||||
})
|
||||
|
||||
m.orderStore.commsManager.PushEvent(base.Event{Type: "order", Message: msg})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -534,9 +530,6 @@ func (m *OrderManager) GetOrdersFiltered(f *order.Filter) ([]order.Detail, error
|
||||
if m == nil {
|
||||
return nil, fmt.Errorf("order manager %w", ErrNilSubsystem)
|
||||
}
|
||||
if f == nil {
|
||||
return nil, fmt.Errorf("order manager, GetOrdersFiltered: Filter is nil")
|
||||
}
|
||||
if atomic.LoadInt32(&m.started) == 0 {
|
||||
return nil, fmt.Errorf("order manager %w", ErrSubSystemNotStarted)
|
||||
}
|
||||
@@ -640,9 +633,8 @@ func (m *OrderManager) processOrders() {
|
||||
if !atomic.CompareAndSwapInt32(&m.processingOrders, 0, 1) {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
atomic.StoreInt32(&m.processingOrders, 0)
|
||||
}()
|
||||
defer atomic.StoreInt32(&m.processingOrders, 0)
|
||||
|
||||
exchanges, err := m.orderStore.exchangeManager.GetExchanges()
|
||||
if err != nil {
|
||||
log.Errorf(log.OrderMgr, "Order manager cannot get exchanges: %v", err)
|
||||
@@ -657,14 +649,14 @@ func (m *OrderManager) processOrders() {
|
||||
"Order manager: Processing orders for exchange %v.",
|
||||
exchanges[i].GetName())
|
||||
|
||||
supportedAssets := exchanges[i].GetAssetTypes(true)
|
||||
for y := range supportedAssets {
|
||||
pairs, err := exchanges[i].GetEnabledPairs(supportedAssets[y])
|
||||
enabledAssets := exchanges[i].GetAssetTypes(true)
|
||||
for y := range enabledAssets {
|
||||
pairs, err := exchanges[i].GetEnabledPairs(enabledAssets[y])
|
||||
if err != nil {
|
||||
log.Errorf(log.OrderMgr,
|
||||
"Order manager: Unable to get enabled pairs for %s and asset type %s: %s",
|
||||
exchanges[i].GetName(),
|
||||
supportedAssets[y],
|
||||
enabledAssets[y],
|
||||
err)
|
||||
continue
|
||||
}
|
||||
@@ -674,33 +666,26 @@ func (m *OrderManager) processOrders() {
|
||||
log.Debugf(log.OrderMgr,
|
||||
"Order manager: No pairs enabled for %s and asset type %s, skipping...",
|
||||
exchanges[i].GetName(),
|
||||
supportedAssets[y])
|
||||
enabledAssets[y])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
filter := &order.Filter{
|
||||
Exchange: exchanges[i].GetName(),
|
||||
}
|
||||
filter := &order.Filter{Exchange: exchanges[i].GetName()}
|
||||
orders := m.orderStore.getActiveOrders(filter)
|
||||
order.FilterOrdersByPairs(&orders, pairs)
|
||||
requiresProcessing := make(map[string]bool, len(orders))
|
||||
for x := range orders {
|
||||
requiresProcessing[orders[x].InternalOrderID] = true
|
||||
}
|
||||
|
||||
req := order.GetOrdersRequest{
|
||||
result, err := exchanges[i].GetActiveOrders(context.TODO(), &order.GetOrdersRequest{
|
||||
Side: order.AnySide,
|
||||
Type: order.AnyType,
|
||||
Pairs: pairs,
|
||||
AssetType: supportedAssets[y],
|
||||
}
|
||||
result, err := exchanges[i].GetActiveOrders(context.TODO(), &req)
|
||||
AssetType: enabledAssets[y],
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf(log.OrderMgr,
|
||||
"Order manager: Unable to get active orders for %s and asset type %s: %s",
|
||||
exchanges[i].GetName(),
|
||||
supportedAssets[y],
|
||||
enabledAssets[y],
|
||||
err)
|
||||
continue
|
||||
}
|
||||
@@ -713,36 +698,38 @@ func (m *OrderManager) processOrders() {
|
||||
if err != nil {
|
||||
log.Error(log.OrderMgr, err)
|
||||
} else {
|
||||
requiresProcessing[upsertResponse.OrderDetails.InternalOrderID] = false
|
||||
for i := range orders {
|
||||
if orders[i].InternalOrderID != upsertResponse.OrderDetails.InternalOrderID {
|
||||
continue
|
||||
}
|
||||
orders[i] = orders[len(orders)-1]
|
||||
orders = orders[:len(orders)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
if !exchanges[i].GetBase().GetSupportedFeatures().RESTCapabilities.GetOrder {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go m.processMatchingOrders(exchanges[i], orders, requiresProcessing, &wg)
|
||||
go m.processMatchingOrders(exchanges[i], orders, &wg)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (m *OrderManager) processMatchingOrders(exch exchange.IBotExchange, orders []order.Detail, requiresProcessing map[string]bool, wg *sync.WaitGroup) {
|
||||
defer func() {
|
||||
if wg != nil {
|
||||
wg.Done()
|
||||
}
|
||||
}()
|
||||
func (m *OrderManager) processMatchingOrders(exch exchange.IBotExchange, orders []order.Detail, wg *sync.WaitGroup) {
|
||||
for x := range orders {
|
||||
if time.Since(orders[x].LastUpdated) < time.Minute {
|
||||
continue
|
||||
}
|
||||
if requiresProcessing[orders[x].InternalOrderID] {
|
||||
err := m.FetchAndUpdateExchangeOrder(exch, &orders[x], orders[x].AssetType)
|
||||
if err != nil {
|
||||
log.Error(log.OrderMgr, err)
|
||||
}
|
||||
err := m.FetchAndUpdateExchangeOrder(exch, &orders[x], orders[x].AssetType)
|
||||
if err != nil {
|
||||
log.Error(log.OrderMgr, err)
|
||||
}
|
||||
}
|
||||
if wg != nil {
|
||||
wg.Done()
|
||||
}
|
||||
}
|
||||
|
||||
// FetchAndUpdateExchangeOrder calls the exchange to upsert an order to the order store
|
||||
@@ -762,11 +749,7 @@ func (m *OrderManager) FetchAndUpdateExchangeOrder(exch exchange.IBotExchange, o
|
||||
|
||||
// Exists checks whether an order exists in the order store
|
||||
func (m *OrderManager) Exists(o *order.Detail) bool {
|
||||
if m == nil || atomic.LoadInt32(&m.started) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return m.orderStore.exists(o)
|
||||
return m != nil && atomic.LoadInt32(&m.started) != 0 && m.orderStore.exists(o)
|
||||
}
|
||||
|
||||
// Add adds an order to the orderstore
|
||||
@@ -857,11 +840,13 @@ func (m *OrderManager) UpsertOrder(od *order.Detail) (resp *OrderUpsertResponse,
|
||||
return upsertResponse, nil
|
||||
}
|
||||
|
||||
// get returns all orders for all exchanges
|
||||
// should not be exported as it can have large impact if used improperly
|
||||
// get returns a copy of all orders for all exchanges.
|
||||
func (s *store) get() map[string][]*order.Detail {
|
||||
orders := make(map[string][]*order.Detail)
|
||||
s.m.Lock()
|
||||
orders := s.Orders
|
||||
for k, val := range s.Orders {
|
||||
orders[k] = order.CopyPointerOrderSlice(val)
|
||||
}
|
||||
s.m.Unlock()
|
||||
return orders
|
||||
}
|
||||
@@ -877,7 +862,7 @@ func (s *store) getByExchangeAndID(exchange, id string) (*order.Detail, error) {
|
||||
|
||||
for x := range r {
|
||||
if r[x].ID == id {
|
||||
return r[x], nil
|
||||
return r[x].CopyToPointer(), nil
|
||||
}
|
||||
}
|
||||
return nil, ErrOrderNotFound
|
||||
@@ -896,20 +881,19 @@ func (s *store) updateExisting(od *order.Detail) error {
|
||||
return ErrExchangeNotFound
|
||||
}
|
||||
for x := range r {
|
||||
if r[x].ID == od.ID {
|
||||
r[x].UpdateOrderFromDetail(od)
|
||||
if r[x].AssetType.IsFutures() {
|
||||
err := s.futuresPositionController.TrackNewOrder(r[x])
|
||||
if err != nil {
|
||||
if !errors.Is(err, order.ErrPositionClosed) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if r[x].ID != od.ID {
|
||||
continue
|
||||
}
|
||||
r[x].UpdateOrderFromDetail(od)
|
||||
if !r[x].AssetType.IsFutures() {
|
||||
return nil
|
||||
}
|
||||
err := s.futuresPositionController.TrackNewOrder(r[x])
|
||||
if err != nil && !errors.Is(err, order.ErrPositionClosed) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrOrderNotFound
|
||||
}
|
||||
|
||||
@@ -923,30 +907,30 @@ func (s *store) modifyExisting(id string, mod *order.Modify) error {
|
||||
return ErrExchangeNotFound
|
||||
}
|
||||
for x := range r {
|
||||
if r[x].ID == id {
|
||||
r[x].UpdateOrderFromModify(mod)
|
||||
if r[x].AssetType.IsFutures() {
|
||||
err := s.futuresPositionController.TrackNewOrder(r[x])
|
||||
if err != nil {
|
||||
if !errors.Is(err, order.ErrPositionClosed) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if r[x].ID != id {
|
||||
continue
|
||||
}
|
||||
r[x].UpdateOrderFromModify(mod)
|
||||
if !r[x].AssetType.IsFutures() {
|
||||
return nil
|
||||
}
|
||||
err := s.futuresPositionController.TrackNewOrder(r[x])
|
||||
if err != nil && !errors.Is(err, order.ErrPositionClosed) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return ErrOrderNotFound
|
||||
}
|
||||
|
||||
// upsert (1) checks if such an exchange exists in the exchangeManager, (2) checks if
|
||||
// order exists and updates/creates it.
|
||||
func (s *store) upsert(od *order.Detail) (resp *OrderUpsertResponse, err error) {
|
||||
func (s *store) upsert(od *order.Detail) (*OrderUpsertResponse, error) {
|
||||
if od == nil {
|
||||
return nil, errNilOrder
|
||||
}
|
||||
lName := strings.ToLower(od.Exchange)
|
||||
_, err = s.exchangeManager.GetExchangeByName(lName)
|
||||
_, err := s.exchangeManager.GetExchangeByName(lName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -954,66 +938,27 @@ func (s *store) upsert(od *order.Detail) (resp *OrderUpsertResponse, err error)
|
||||
defer s.m.Unlock()
|
||||
if od.AssetType.IsFutures() {
|
||||
err = s.futuresPositionController.TrackNewOrder(od)
|
||||
if err != nil {
|
||||
if !errors.Is(err, order.ErrPositionClosed) {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil && !errors.Is(err, order.ErrPositionClosed) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
r, ok := s.Orders[lName]
|
||||
if !ok {
|
||||
od.GenerateInternalOrderID()
|
||||
s.Orders[lName] = []*order.Detail{od}
|
||||
resp = &OrderUpsertResponse{
|
||||
OrderDetails: od.Copy(),
|
||||
IsNewOrder: true,
|
||||
}
|
||||
return resp, nil
|
||||
return &OrderUpsertResponse{OrderDetails: od.Copy(), IsNewOrder: true}, nil
|
||||
}
|
||||
for x := range r {
|
||||
if r[x].ID == od.ID {
|
||||
r[x].UpdateOrderFromDetail(od)
|
||||
resp = &OrderUpsertResponse{
|
||||
OrderDetails: r[x].Copy(),
|
||||
IsNewOrder: false,
|
||||
}
|
||||
return resp, nil
|
||||
if r[x].ID != od.ID {
|
||||
continue
|
||||
}
|
||||
r[x].UpdateOrderFromDetail(od)
|
||||
return &OrderUpsertResponse{OrderDetails: r[x].Copy(), IsNewOrder: false}, nil
|
||||
}
|
||||
// Untracked websocket orders will not have internalIDs yet
|
||||
od.GenerateInternalOrderID()
|
||||
s.Orders[lName] = append(s.Orders[lName], od)
|
||||
resp = &OrderUpsertResponse{
|
||||
OrderDetails: od.Copy(),
|
||||
IsNewOrder: true,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// getByExchange returns orders by exchange
|
||||
func (s *store) getByExchange(exchange string) ([]*order.Detail, error) {
|
||||
s.m.RLock()
|
||||
defer s.m.RUnlock()
|
||||
r, ok := s.Orders[strings.ToLower(exchange)]
|
||||
if !ok {
|
||||
return nil, ErrExchangeNotFound
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// getByInternalOrderID will search all orders for our internal orderID
|
||||
// and return the order
|
||||
func (s *store) getByInternalOrderID(internalOrderID string) (*order.Detail, error) {
|
||||
s.m.RLock()
|
||||
defer s.m.RUnlock()
|
||||
for _, v := range s.Orders {
|
||||
for x := range v {
|
||||
if v[x].InternalOrderID == internalOrderID {
|
||||
return v[x], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, ErrOrderNotFound
|
||||
return &OrderUpsertResponse{OrderDetails: od.Copy(), IsNewOrder: true}, nil
|
||||
}
|
||||
|
||||
// exists verifies if the orderstore contains the provided order
|
||||
@@ -1056,13 +1001,10 @@ func (s *store) add(det *order.Detail) error {
|
||||
orders = append(orders, det)
|
||||
s.Orders[strings.ToLower(det.Exchange)] = orders
|
||||
|
||||
if det.AssetType.IsFutures() {
|
||||
err = s.futuresPositionController.TrackNewOrder(det)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !det.AssetType.IsFutures() {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
return s.futuresPositionController.TrackNewOrder(det)
|
||||
}
|
||||
|
||||
// getFilteredOrders returns a filtered copy of the orders
|
||||
@@ -1107,7 +1049,7 @@ func (s *store) getActiveOrders(f *order.Filter) []order.Detail {
|
||||
case f == nil:
|
||||
for _, e := range s.Orders {
|
||||
for i := range e {
|
||||
if !e[i].IsActive() {
|
||||
if e[i].Status != order.UnknownStatus && !e[i].IsActive() {
|
||||
continue
|
||||
}
|
||||
orders = append(orders, e[i].Copy())
|
||||
@@ -1117,7 +1059,7 @@ func (s *store) getActiveOrders(f *order.Filter) []order.Detail {
|
||||
// optimization if Exchange is filtered
|
||||
if e, ok := s.Orders[strings.ToLower(f.Exchange)]; ok {
|
||||
for i := range e {
|
||||
if !e[i].IsActive() || !e[i].MatchFilter(f) {
|
||||
if e[i].Status != order.UnknownStatus && (!e[i].IsActive() || !e[i].MatchFilter(f)) {
|
||||
continue
|
||||
}
|
||||
orders = append(orders, e[i].Copy())
|
||||
@@ -1126,7 +1068,7 @@ func (s *store) getActiveOrders(f *order.Filter) []order.Detail {
|
||||
default:
|
||||
for _, e := range s.Orders {
|
||||
for i := range e {
|
||||
if !e[i].IsActive() || !e[i].MatchFilter(f) {
|
||||
if e[i].Status != order.UnknownStatus && (!e[i].IsActive() || !e[i].MatchFilter(f)) {
|
||||
continue
|
||||
}
|
||||
orders = append(orders, e[i].Copy())
|
||||
|
||||
@@ -253,95 +253,6 @@ func TestOrdersAdd(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetByInternalOrderID(t *testing.T) {
|
||||
m := OrdersSetup(t)
|
||||
err := m.orderStore.add(&order.Detail{
|
||||
Exchange: testExchange,
|
||||
ID: "TestGetByInternalOrderID",
|
||||
InternalOrderID: "internalTest",
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
o, err := m.orderStore.getByInternalOrderID("internalTest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if o == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings
|
||||
t.Fatal("Expected a matching order")
|
||||
}
|
||||
if o.ID != "TestGetByInternalOrderID" { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings
|
||||
t.Error("Expected to retrieve order")
|
||||
}
|
||||
|
||||
_, err = m.orderStore.getByInternalOrderID("NoOrder")
|
||||
if err != ErrOrderNotFound {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetByExchange(t *testing.T) {
|
||||
m := OrdersSetup(t)
|
||||
err := m.orderStore.add(&order.Detail{
|
||||
Exchange: testExchange,
|
||||
ID: "TestGetByExchange",
|
||||
InternalOrderID: "internalTestGetByExchange",
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = m.orderStore.add(&order.Detail{
|
||||
Exchange: testExchange,
|
||||
ID: "TestGetByExchange2",
|
||||
InternalOrderID: "internalTestGetByExchange2",
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = m.orderStore.add(&order.Detail{
|
||||
Exchange: testExchange,
|
||||
ID: "TestGetByExchange3",
|
||||
InternalOrderID: "internalTest3",
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
var o []*order.Detail
|
||||
o, err = m.orderStore.getByExchange(testExchange)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if o == nil {
|
||||
t.Error("Expected non nil response")
|
||||
}
|
||||
var o1Found, o2Found bool
|
||||
for i := range o {
|
||||
if o[i].ID == "TestGetByExchange" && o[i].Exchange == testExchange {
|
||||
o1Found = true
|
||||
}
|
||||
if o[i].ID == "TestGetByExchange2" && o[i].Exchange == testExchange {
|
||||
o2Found = true
|
||||
}
|
||||
}
|
||||
if !o1Found || !o2Found {
|
||||
t.Error("Expected orders 'TestGetByExchange' and 'TestGetByExchange2' to be returned")
|
||||
}
|
||||
|
||||
_, err = m.orderStore.getByInternalOrderID("NoOrder")
|
||||
if err != ErrOrderNotFound {
|
||||
t.Error(err)
|
||||
}
|
||||
err = m.orderStore.add(&order.Detail{
|
||||
Exchange: "thisWillFail",
|
||||
})
|
||||
if err == nil {
|
||||
t.Error("Expected exchange not found error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetByExchangeAndID(t *testing.T) {
|
||||
m := OrdersSetup(t)
|
||||
err := m.orderStore.add(&order.Detail{
|
||||
@@ -557,7 +468,11 @@ func TestCancelAllOrders(t *testing.T) {
|
||||
}
|
||||
|
||||
m.CancelAllOrders(context.Background(), []exchange.IBotExchange{})
|
||||
if o.Status == order.Cancelled {
|
||||
checkDeets, err := m.orderStore.getByExchangeAndID(testExchange, "TestCancelAllOrders")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if checkDeets.Status == order.Cancelled {
|
||||
t.Error("Order should not be cancelled")
|
||||
}
|
||||
|
||||
@@ -567,14 +482,13 @@ func TestCancelAllOrders(t *testing.T) {
|
||||
}
|
||||
|
||||
m.CancelAllOrders(context.Background(), []exchange.IBotExchange{exch})
|
||||
if o.Status != order.Cancelled {
|
||||
t.Error("Order should be cancelled")
|
||||
checkDeets, err = m.orderStore.getByExchangeAndID(testExchange, "TestCancelAllOrders")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
o.Status = order.New
|
||||
m.CancelAllOrders(context.Background(), nil)
|
||||
if o.Status != order.New {
|
||||
t.Error("Order should not be cancelled")
|
||||
if checkDeets.Status != order.Cancelled {
|
||||
t.Error("Order should be cancelled", checkDeets.Status)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1024,39 +938,20 @@ func Test_processMatchingOrders(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
orders := []order.Detail{
|
||||
{
|
||||
Exchange: testExchange,
|
||||
ID: "Test1",
|
||||
LastUpdated: time.Now(),
|
||||
},
|
||||
{
|
||||
Exchange: testExchange,
|
||||
ID: "Test2",
|
||||
LastUpdated: time.Now(),
|
||||
},
|
||||
{
|
||||
Exchange: testExchange,
|
||||
ID: "Test3",
|
||||
LastUpdated: time.Now().Add(-time.Hour),
|
||||
},
|
||||
{
|
||||
Exchange: testExchange,
|
||||
ID: "Test4",
|
||||
LastUpdated: time.Now().Add(-time.Hour),
|
||||
},
|
||||
}
|
||||
requiresProcessing := make(map[string]bool, len(orders))
|
||||
for i := range orders {
|
||||
orders[i].GenerateInternalOrderID()
|
||||
if i%2 == 0 {
|
||||
requiresProcessing[orders[i].InternalOrderID] = false
|
||||
} else {
|
||||
requiresProcessing[orders[i].InternalOrderID] = true
|
||||
}
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
m.processMatchingOrders(exch, orders, requiresProcessing, &wg)
|
||||
go m.processMatchingOrders(exch, orders, &wg)
|
||||
wg.Wait()
|
||||
res, err := m.GetOrdersFiltered(&order.Filter{Exchange: testExchange})
|
||||
if err != nil {
|
||||
|
||||
@@ -212,7 +212,6 @@ func TestAreCredentialsValid(t *testing.T) {
|
||||
func TestValidateAPICredentials(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var b Base
|
||||
type tester struct {
|
||||
Key string
|
||||
Secret string
|
||||
@@ -247,7 +246,8 @@ func TestValidateAPICredentials(t *testing.T) {
|
||||
{RequiresBase64DecodeSecret: true, Secret: "aGVsbG8gd29ybGQ="},
|
||||
}
|
||||
|
||||
setupBase := func(b *Base, tData *tester) {
|
||||
setupBase := func(tData *tester) *Base {
|
||||
b := &Base{}
|
||||
b.API.SetKey(tData.Key)
|
||||
b.API.SetSecret(tData.Secret)
|
||||
b.API.SetClientID(tData.ClientID)
|
||||
@@ -257,13 +257,14 @@ func TestValidateAPICredentials(t *testing.T) {
|
||||
b.API.CredentialsValidator.RequiresPEM = tData.RequiresPEM
|
||||
b.API.CredentialsValidator.RequiresClientID = tData.RequiresClientID
|
||||
b.API.CredentialsValidator.RequiresBase64DecodeSecret = tData.RequiresBase64DecodeSecret
|
||||
return b
|
||||
}
|
||||
|
||||
for x := range testCases {
|
||||
testData := &testCases[x]
|
||||
t.Run("", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
setupBase(&b, testData)
|
||||
b := setupBase(testData)
|
||||
if err := b.ValidateAPICredentials(b.API.credentials); !errors.Is(err, testData.Expected) {
|
||||
t.Errorf("Test %d: expected: %v: got %v", x+1, testData.Expected, err)
|
||||
}
|
||||
|
||||
@@ -1554,6 +1554,7 @@ func TestGenerateInternalOrderID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetail_Copy(t *testing.T) {
|
||||
t.Parallel()
|
||||
d := []Detail{
|
||||
{
|
||||
Exchange: "Binance",
|
||||
@@ -1577,3 +1578,56 @@ func TestDetail_Copy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetail_CopyToPointer(t *testing.T) {
|
||||
t.Parallel()
|
||||
d := []Detail{
|
||||
{
|
||||
Exchange: "Binance",
|
||||
},
|
||||
{
|
||||
Exchange: "Binance",
|
||||
Trades: []TradeHistory{
|
||||
{Price: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := range d {
|
||||
r := d[i].CopyToPointer()
|
||||
if !reflect.DeepEqual(d[i], *r) {
|
||||
t.Errorf("[%d] Copy does not contain same elements, expected: %v\ngot:%v", i, d[i], r)
|
||||
}
|
||||
if len(d[i].Trades) > 0 {
|
||||
if &d[i].Trades[0] == &r.Trades[0] {
|
||||
t.Errorf("[%d]Trades point to the same data elements", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetail_CopyPointerOrderSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
d := []*Detail{
|
||||
{
|
||||
Exchange: "Binance",
|
||||
},
|
||||
{
|
||||
Exchange: "Binance",
|
||||
Trades: []TradeHistory{
|
||||
{Price: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sliceCopy := CopyPointerOrderSlice(d)
|
||||
for i := range sliceCopy {
|
||||
if !reflect.DeepEqual(*sliceCopy[i], *d[i]) {
|
||||
t.Errorf("[%d] Copy does not contain same elements, expected: %v\ngot:%v", i, sliceCopy[i], d[i])
|
||||
}
|
||||
if len(sliceCopy[i].Trades) > 0 {
|
||||
if &sliceCopy[i].Trades[0] == &d[i].Trades[0] {
|
||||
t.Errorf("[%d]Trades point to the same data elements", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,7 +466,14 @@ func (d *Detail) GenerateInternalOrderID() {
|
||||
}
|
||||
}
|
||||
|
||||
// Copy will return a copy of Detail
|
||||
// CopyToPointer will return the address of a new copy of the order Detail
|
||||
// WARNING: DO NOT DEREFERENCE USE METHOD Copy().
|
||||
func (d *Detail) CopyToPointer() *Detail {
|
||||
c := d.Copy()
|
||||
return &c
|
||||
}
|
||||
|
||||
// Copy makes a full copy of underlying details NOTE: This is Addressable.
|
||||
func (d *Detail) Copy() Detail {
|
||||
c := *d
|
||||
if len(d.Trades) > 0 {
|
||||
@@ -476,6 +483,16 @@ func (d *Detail) Copy() Detail {
|
||||
return c
|
||||
}
|
||||
|
||||
// CopyPointerOrderSlice returns a copy of all order detail and returns a slice
|
||||
// of pointers.
|
||||
func CopyPointerOrderSlice(old []*Detail) []*Detail {
|
||||
copySlice := make([]*Detail, len(old))
|
||||
for x := range old {
|
||||
copySlice[x] = old[x].CopyToPointer()
|
||||
}
|
||||
return copySlice
|
||||
}
|
||||
|
||||
// String implements the stringer interface
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
|
||||
Reference in New Issue
Block a user