From cbd3e7bacd779dc63df8982a56bbecf854d3c99c Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Fri, 7 Jun 2019 17:52:53 +1000 Subject: [PATCH] Order manager changes --- common/common.go | 6 + engine/engine.go | 27 +++-- engine/helpers.go | 12 ++ engine/orders.go | 130 +++++++++++++++++---- exchanges/btcmarkets/btcmarkets_wrapper.go | 3 +- go.mod | 12 +- go.sum | 31 +++-- 7 files changed, 169 insertions(+), 52 deletions(-) diff --git a/common/common.go b/common/common.go index 595c91bb..189490b9 100644 --- a/common/common.go +++ b/common/common.go @@ -18,6 +18,7 @@ import ( "strings" "time" + uuid "github.com/satori/go.uuid" log "github.com/thrasher-/gocryptotrader/logger" ) @@ -42,6 +43,11 @@ const ( WeiPerEther = 1000000000000000000 ) +// GetV4UUID returns a RFC 4122 UUID based on random numbers +func GetV4UUID() uuid.UUID { + return uuid.NewV4() +} + func initialiseHTTPClient() { // If the HTTPClient isn't set, start a new client with a default timeout of 15 seconds if HTTPClient == nil { diff --git a/engine/engine.go b/engine/engine.go index 74018480..28f92b5b 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -32,11 +32,11 @@ type Engine struct { Portfolio *portfolio.Base Exchanges []exchange.IBotExchange ExchangeCurrencyPairManager *ExchangeCurrencyPairSyncer - OrderManager *OrderManager + OrderManager orderManager PortfolioManager portfolioManager CommsRelayer *communications.Communications Connectivity *connchecker.Checker - Shutdown chan bool + Shutdown chan struct{} Settings Settings CryptocurrencyDepositAddresses map[string]map[string]string Uptime time.Time @@ -157,6 +157,7 @@ func ValidateSettings(b *Engine, s *Settings) { b.Settings.EnableConnectivityMonitor = s.EnableConnectivityMonitor b.Settings.EnableNTPClient = s.EnableNTPClient + b.Settings.EnableOrderManager = s.EnableOrderManager b.Settings.EnableExchangeSyncManager = s.EnableExchangeSyncManager b.Settings.EnableTickerSyncing = s.EnableTickerSyncing b.Settings.EnableOrderbookSyncing = s.EnableOrderbookSyncing @@ -219,7 +220,7 @@ func PrintSettings(s *Settings) { log.Debugf("\t Enable all exchanges: %v", s.EnableAllExchanges) log.Debugf("\t Enable all pairs: %v", s.EnableAllPairs) log.Debugf("\t Enable coinmarketcap analaysis: %v", s.EnableCoinmarketcapAnalysis) - log.Debugf("\t Enable portfolio watcher: %v", s.EnablePortfolioManager) + log.Debugf("\t Enable portfolio manager: %v", s.EnablePortfolioManager) log.Debugf("\t Enable gPRC: %v", s.EnableGRPC) log.Debugf("\t Enable gRPC Proxy: %v", s.EnableGRPCProxy) log.Debugf("\t Enable websocket RPC: %v", s.EnableWebsocketRPC) @@ -377,6 +378,12 @@ func (e *Engine) Start() { } } + if e.Settings.EnableOrderManager { + if err = e.OrderManager.Start(); err != nil { + log.Errorf("Order manager unable to start: %v", err) + } + } + if e.Settings.EnableExchangeSyncManager { exchangeSyncCfg := CurrencyPairSyncerConfig{ SyncTicker: e.Settings.EnableTickerSyncing, @@ -393,10 +400,6 @@ func (e *Engine) Start() { } } - if e.Settings.EnableOrderManager { - go StartOrderManagerRoutine() - } - if e.Settings.EnableEventManager { go events.EventManger() } @@ -413,6 +416,12 @@ func (e *Engine) Stop() { e.Config.Portfolio = portfolio.Portfolio } + if e.OrderManager.Started() { + if err := e.OrderManager.Stop(); err != nil { + log.Errorf("Order manager unable to stop. Error: %v", err) + } + } + if e.PortfolioManager.Started() { if err := e.PortfolioManager.Stop(); err != nil { log.Errorf("Fund manager unable to stop. Error: %v", err) @@ -436,11 +445,11 @@ func (e *Engine) Stop() { // shuts down the engine instance func (e *Engine) handleInterrupt() { c := make(chan os.Signal, 1) - e.Shutdown = make(chan bool) + e.Shutdown = make(chan struct{}) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { sig := <-c log.Debugf("Captured %v, shutdown requested.", sig) - e.Shutdown <- true + close(e.Shutdown) }() } diff --git a/engine/helpers.go b/engine/helpers.go index 07c8b863..826dab3e 100644 --- a/engine/helpers.go +++ b/engine/helpers.go @@ -26,6 +26,18 @@ import ( "github.com/thrasher-/gocryptotrader/utils" ) +// GetAuthAPISupportedExchanges returns a list of auth api enabled exchanges +func GetAuthAPISupportedExchanges() []string { + var exchanges []string + for x := range Bot.Exchanges { + if !Bot.Exchanges[x].GetAuthenticatedAPISupport() { + continue + } + exchanges = append(exchanges, Bot.Exchanges[x].GetName()) + } + return exchanges +} + // IsOnline returns whether or not the engine has Internet connectivity func IsOnline() bool { if Bot.Connectivity == nil { diff --git a/engine/orders.go b/engine/orders.go index b56e37ad..60fc3bae 100644 --- a/engine/orders.go +++ b/engine/orders.go @@ -1,47 +1,131 @@ package engine import ( + "errors" "sync" + "sync/atomic" "time" exchange "github.com/thrasher-/gocryptotrader/exchanges" log "github.com/thrasher-/gocryptotrader/logger" ) -// OrderManager manages orders for all enabled exchanges -type OrderManager struct { +// vars for the fund manager package +var ( + OrderManagerDelay = time.Second * 10 + ErrOrdersAlreadyExists = errors.New("order already exists") +) + +type orderStore struct { m sync.Mutex Orders map[string][]exchange.OrderDetail } -func (o *OrderManager) add() { +func (o *orderStore) exists(order *exchange.OrderDetail) bool { + r, ok := o.Orders[order.Exchange] + if !ok { + return false + } + + for x := range r { + if r[x].ID == order.ID { + return true + } + } + + return false +} + +func (o *orderStore) Add(order *exchange.OrderDetail) error { o.m.Lock() defer o.m.Unlock() + + if o.exists(order) { + return ErrOrdersAlreadyExists + } + + orders := o.Orders[order.Exchange] + orders = append(orders, *order) + o.Orders[order.Exchange] = orders + return nil } -// StartOrderManagerRoutine starts the orderbook manage routine -func StartOrderManagerRoutine() { - log.Debugln("Starting order manager routine") - if Bot.OrderManager == nil { - Bot.OrderManager = new(OrderManager) +type orderManager struct { + started int32 + stopped int32 + shutdown chan struct{} + orderStore orderStore +} + +func (o *orderManager) Started() bool { + return atomic.LoadInt32(&o.started) == 1 +} + +func (o *orderManager) Start() error { + if atomic.AddInt32(&o.started, 1) != 1 { + return errors.New("order manager already started") } + log.Debugln("Order manager starting...") + o.shutdown = make(chan struct{}) + o.orderStore.Orders = make(map[string][]exchange.OrderDetail) + go o.run() + return nil +} +func (o *orderManager) Stop() error { + if atomic.AddInt32(&o.stopped, 1) != 1 { + return errors.New("order manager is already stopped") + } + + log.Debugln("Order manager shutting down...") + close(o.shutdown) + return nil +} + +func (o *orderManager) run() { + log.Debugln("Order manager started.") + tick := time.NewTicker(OrderManagerDelay) + defer func() { + log.Debugf("Order manager shutdown.") + tick.Stop() + }() + for { - for x := range Bot.Exchanges { - if !Bot.Exchanges[x].IsEnabled() || !Bot.Exchanges[x].GetAuthenticatedAPISupport() { - continue - } - exchName := Bot.Exchanges[x].GetName() - log.Printf("Getting active orders for %s", exchName) - - orders, err := Bot.Exchanges[x].GetActiveOrders(&exchange.GetOrdersRequest{}) - if err != nil { - log.Printf("Get active orders failed: %s", err) - continue - } - - log.Printf("Orders for exchange %s: %v", exchName, orders) + select { + case <-o.shutdown: + return + case <-tick.C: + o.processOrders() + } + } +} + +func (o *orderManager) Cancel() {} + +func (o *orderManager) Place() {} + +func (o *orderManager) processOrders() { + authExchanges := GetAuthAPISupportedExchanges() + for x := range authExchanges { + log.Debugf("Order manager: Procesing orders for exchange %v.", authExchanges[x]) + exch := GetExchangeByName(authExchanges[x]) + req := exchange.GetOrdersRequest{ + OrderSide: exchange.AnyOrderSide, + OrderType: exchange.AnyOrderType, + } + result, err := exch.GetActiveOrders(&req) + if err != nil { + log.Debugf("Order manager: Unable to get active orders: %s", err) + continue + } + + for x := range result { + order := &result[x] + result := o.orderStore.Add(order) + if result != ErrOrdersAlreadyExists { + log.Debugf("Order manager: Exchange %s added order ID=%v pair=%v price=%v amount=%v side=%v type=%v.", + order.Exchange, order.ID, order.CurrencyPair, order.Price, order.Amount, order.OrderSide, order.OrderType) + } } - time.Sleep(time.Second * 1) } } diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index eba69d8a..2cc4b659 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -458,8 +458,7 @@ func (b *BTCMarkets) GetActiveOrders(getOrdersRequest *exchange.GetOrdersRequest Price: resp[i].Price, Status: resp[i].Status, CurrencyPair: currency.NewPairWithDelimiter(resp[i].Instrument, - resp[i].Currency, - b.CurrencyPairs.Get(assets.AssetTypeSpot).ConfigFormat.Delimiter), + resp[i].Currency, "-"), } for j := range resp[i].Trades { diff --git a/go.mod b/go.mod index 81f659b2..c814a465 100644 --- a/go.mod +++ b/go.mod @@ -3,17 +3,17 @@ module github.com/thrasher-/gocryptotrader go 1.12 require ( - github.com/boombuler/barcode v1.0.0 // indirect github.com/golang/protobuf v1.3.1 github.com/google/go-querystring v1.0.0 github.com/gorilla/mux v1.7.2 github.com/gorilla/websocket v1.4.0 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 github.com/grpc-ecosystem/grpc-gateway v1.9.0 - github.com/pquerna/otp v1.1.0 + github.com/pquerna/otp v1.2.0 + github.com/satori/go.uuid v1.2.0 github.com/urfave/cli v1.20.0 - golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f - golang.org/x/net v0.0.0-20190520210107-018c4d40a106 - google.golang.org/genproto v0.0.0-20190516172635-bb713bdc0e52 - google.golang.org/grpc v1.20.1 + golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 + golang.org/x/net v0.0.0-20190606173856-1492cefac77f + google.golang.org/genproto v0.0.0-20190605220351-eb0b1bdb6ae6 + google.golang.org/grpc v1.21.1 ) diff --git a/go.sum b/go.sum index ed94e9f4..a729b15d 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,16 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/boombuler/barcode v1.0.0 h1:s1TvRnXwL2xJRaccrdcBQMZxq6X7DvsMogtmJeHDdrc= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= @@ -22,14 +24,19 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pquerna/otp v1.1.0 h1:q2gMsMuMl3JzneUaAX1MRGxLvOG6bzXV51hivBaStf0= -github.com/pquerna/otp v1.1.0/go.mod h1:Zad1CMQfSQZI5KLpahDiSUX4tMMREnXw98IvL1nhgMk= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= +github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -40,8 +47,8 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190520210107-018c4d40a106 h1:EZofHp/BzEf3j39/+7CX1JvH0WaPG+ikBrqAdAPf+GM= -golang.org/x/net v0.0.0-20190520210107-018c4d40a106/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190606173856-1492cefac77f h1:IWHgpgFqnL5AhBUBZSgBdjl2vkQUEzcY+JNKWfcgAU0= +golang.org/x/net v0.0.0-20190606173856-1492cefac77f/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -57,11 +64,11 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190516172635-bb713bdc0e52 h1:LHc/6x2dMeCKkSsrVgo4DY+Z566T1OeoMwLtdfoy8LE= -google.golang.org/genproto v0.0.0-20190516172635-bb713bdc0e52/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190605220351-eb0b1bdb6ae6 h1:XRqWpmQ5ACYxWuYX495S0sHawhPGOVrh62WzgXsQnWs= +google.golang.org/genproto v0.0.0-20190605220351-eb0b1bdb6ae6/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=