Skip to content

Commit

Permalink
gctrpc/ordermanager/binance: Add new getManagedOrders command and var…
Browse files Browse the repository at this point in the history
…ious improvements (thrasher-corp#712)

* first draft of getmanaged orders RPC call

* - ClientIDs for binance, especially spot asset
- applied old ClientOrderId for cancelled orders
- added clientOrderId to GCTRPC

* added tests for Matchfilter and GetManagedOrders

* smaller fixes

* comment fix
added getFilteredOrders to store
changed store mutex to RWMutex
smaller fixes

* fixed bug in Detail Copy and added test

* fixes for Scotts review

* processSubmittedOrder was missing clientOrderId

* changed: TestGetOrdersFiltered expanded
fixed: warning, where variable name collided with package name
fixed: used req.AssetType in binance_wrapper.go

Co-authored-by: Mark Dzulko <[email protected]>
  • Loading branch information
MarkDzulko and MarkDzulko authored Jul 20, 2021
1 parent 6182dd6 commit e1eceea
Show file tree
Hide file tree
Showing 19 changed files with 1,061 additions and 313 deletions.
91 changes: 91 additions & 0 deletions cmd/gctcli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,97 @@ func getOrders(c *cli.Context) error {
return nil
}

var getManagedOrdersCommand = &cli.Command{
Name: "getmanagedorders",
Usage: "gets the current orders from the order manager",
ArgsUsage: "<exchange> <asset> <pair>",
Action: getManagedOrders,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "exchange",
Usage: "the exchange to get orders for",
},
&cli.StringFlag{
Name: "asset",
Usage: "the asset type to get orders for",
},
&cli.StringFlag{
Name: "pair",
Usage: "the currency pair to get orders for",
},
},
}

func getManagedOrders(c *cli.Context) error {
if c.NArg() == 0 && c.NumFlags() == 0 {
return cli.ShowCommandHelp(c, "getmanagedorders")
}

var exchangeName string
var assetType string
var currencyPair string

if c.IsSet("exchange") {
exchangeName = c.String("exchange")
} else {
exchangeName = c.Args().First()
}

if !validExchange(exchangeName) {
return errInvalidExchange
}

if c.IsSet("asset") {
assetType = c.String("asset")
} else {
assetType = c.Args().Get(1)
}

assetType = strings.ToLower(assetType)
if !validAsset(assetType) {
return errInvalidAsset
}

if c.IsSet("pair") {
currencyPair = c.String("pair")
} else {
currencyPair = c.Args().Get(2)
}

if !validPair(currencyPair) {
return errInvalidPair
}

p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
if err != nil {
return err
}

var conn *grpc.ClientConn
conn, err = setupClient()
if err != nil {
return err
}
defer conn.Close()

client := gctrpc.NewGoCryptoTraderClient(conn)
result, err := client.GetManagedOrders(context.Background(), &gctrpc.GetOrdersRequest{
Exchange: exchangeName,
AssetType: assetType,
Pair: &gctrpc.CurrencyPair{
Delimiter: p.Delimiter,
Base: p.Base.String(),
Quote: p.Quote.String(),
},
})
if err != nil {
return err
}

jsonOutput(result)
return nil
}

var getOrderCommand = &cli.Command{
Name: "getorder",
Usage: "gets the specified order info",
Expand Down
1 change: 1 addition & 0 deletions cmd/gctcli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ func main() {
getForexProvidersCommand,
getForexRatesCommand,
getOrdersCommand,
getManagedOrdersCommand,
getOrderCommand,
submitOrderCommand,
simulateOrderCommand,
Expand Down
64 changes: 55 additions & 9 deletions engine/order_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,15 +379,28 @@ func (m *OrderManager) GetOrdersSnapshot(s order.Status) ([]order.Detail, time.T
if v[i].LastUpdated.After(latestUpdate) {
latestUpdate = v[i].LastUpdated
}

cpy := *v[i]
os = append(os, cpy)
os = append(os, *v[i])
}
}

return os, latestUpdate
}

// GetOrdersFiltered returns a snapshot of all orders in the order store.
// Filtering is applied based on the order.Filter unless entries are empty
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)
}
return m.orderStore.getFilteredOrders(f)
}

// processSubmittedOrder adds a new order to the manager
func (m *OrderManager) processSubmittedOrder(newOrder *order.Submit, result order.SubmitResponse) (*OrderSubmitResponse, error) {
if !result.IsOrderPlaced {
Expand Down Expand Up @@ -439,6 +452,7 @@ func (m *OrderManager) processSubmittedOrder(newOrder *order.Submit, result orde
ID: result.OrderID,
AccountID: newOrder.AccountID,
ClientID: newOrder.ClientID,
ClientOrderID: newOrder.ClientOrderID,
WalletAddress: newOrder.WalletAddress,
Type: newOrder.Type,
Side: newOrder.Side,
Expand Down Expand Up @@ -661,8 +675,8 @@ func (s *store) upsert(od *order.Detail) error {

// getByExchange returns orders by exchange
func (s *store) getByExchange(exchange string) ([]*order.Detail, error) {
s.m.Lock()
defer s.m.Unlock()
s.m.RLock()
defer s.m.RUnlock()
r, ok := s.Orders[strings.ToLower(exchange)]
if !ok {
return nil, ErrExchangeNotFound
Expand All @@ -673,8 +687,8 @@ func (s *store) getByExchange(exchange string) ([]*order.Detail, error) {
// getByInternalOrderID will search all orders for our internal orderID
// and return the order
func (s *store) getByInternalOrderID(internalOrderID string) (*order.Detail, error) {
s.m.Lock()
defer s.m.Unlock()
s.m.RLock()
defer s.m.RUnlock()
for _, v := range s.Orders {
for x := range v {
if v[x].InternalOrderID == internalOrderID {
Expand All @@ -690,8 +704,8 @@ func (s *store) exists(det *order.Detail) bool {
if det == nil {
return false
}
s.m.Lock()
defer s.m.Unlock()
s.m.RLock()
defer s.m.RUnlock()
r, ok := s.Orders[strings.ToLower(det.Exchange)]
if !ok {
return false
Expand Down Expand Up @@ -736,3 +750,35 @@ func (s *store) add(det *order.Detail) error {

return nil
}

// getFilteredOrders returns a filtered copy of the orders
func (s *store) getFilteredOrders(f *order.Filter) ([]order.Detail, error) {
if f == nil {
return nil, errors.New("filter is nil")
}
s.m.RLock()
defer s.m.RUnlock()

var os []order.Detail
// optimization if Exchange is filtered
if f.Exchange != "" {
if e, ok := s.Orders[strings.ToLower(f.Exchange)]; ok {
for i := range e {
if !e[i].MatchFilter(f) {
continue
}
os = append(os, e[i].Copy())
}
}
} else {
for _, e := range s.Orders {
for i := range e {
if !e[i].MatchFilter(f) {
continue
}
os = append(os, e[i].Copy())
}
}
}
return os, nil
}
62 changes: 62 additions & 0 deletions engine/order_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,3 +559,65 @@ func TestProcessOrders(t *testing.T) {
m := OrdersSetup(t)
m.processOrders()
}

func TestGetOrdersFiltered(t *testing.T) {
m := OrdersSetup(t)
_, err := m.GetOrdersFiltered(nil)
if err == nil {
t.Error("Expected error from nil filter")
}
orders := []order.Detail{
{
Exchange: testExchange,
ID: "Test1",
},
{
Exchange: testExchange,
ID: "Test2",
},
}
for i := range orders {
if err = m.orderStore.add(&orders[i]); err != nil {
t.Error(err)
}
}
res, err := m.GetOrdersFiltered(&order.Filter{ID: "Test2"})
if err != nil {
t.Error(err)
}
if len(res) != 1 {
t.Errorf("Expected 1 result, got: %d", len(res))
}
}

func Test_getFilteredOrders(t *testing.T) {
m := OrdersSetup(t)

_, err := m.orderStore.getFilteredOrders(nil)
if err == nil {
t.Error("Error expected when Filter is nil")
}

orders := []order.Detail{
{
Exchange: testExchange,
ID: "Test1",
},
{
Exchange: testExchange,
ID: "Test2",
},
}
for i := range orders {
if err = m.orderStore.add(&orders[i]); err != nil {
t.Error(err)
}
}
res, err := m.orderStore.getFilteredOrders(&order.Filter{ID: "Test1"})
if err != nil {
t.Error(err)
}
if len(res) != 1 {
t.Errorf("Expected 1 result, got: %d", len(res))
}
}
2 changes: 1 addition & 1 deletion engine/order_manager_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type orderManagerConfig struct {

// store holds all orders by exchange
type store struct {
m sync.Mutex
m sync.RWMutex
Orders map[string][]*order.Detail
commsManager iCommsManager
exchangeManager iExchangeManager
Expand Down
Loading

0 comments on commit e1eceea

Please sign in to comment.