diff --git a/config/config.go b/config/config.go index 5c7e60e8..e8068c58 100644 --- a/config/config.go +++ b/config/config.go @@ -158,7 +158,7 @@ func (c *Config) UpdateClientBankAccounts(bankCfg *BankAccount) error { } // CheckClientBankAccounts checks client bank details -func (c *Config) CheckClientBankAccounts() error { +func (c *Config) CheckClientBankAccounts() { m.Lock() defer m.Unlock() @@ -175,24 +175,30 @@ func (c *Config) CheckClientBankAccounts() error { SupportedExchanges: "ANX,Kraken", }, ) - return nil + return } for i := range c.BankAccounts { if c.BankAccounts[i].Enabled { if c.BankAccounts[i].BankName == "" || c.BankAccounts[i].BankAddress == "" { - return fmt.Errorf("banking details for %s is enabled but variables not set correctly", + c.BankAccounts[i].Enabled = false + log.Warnf("banking details for %s is enabled but variables not set correctly", c.BankAccounts[i].BankName) + continue } if c.BankAccounts[i].AccountName == "" || c.BankAccounts[i].AccountNumber == "" { - return fmt.Errorf("banking account details for %s variables not set correctly", + c.BankAccounts[i].Enabled = false + log.Warnf("banking account details for %s variables not set correctly", c.BankAccounts[i].BankName) + continue } if c.BankAccounts[i].IBAN == "" && c.BankAccounts[i].SWIFTCode == "" && c.BankAccounts[i].BSBNumber == "" { - return fmt.Errorf("critical banking numbers not set for %s in %s account", + c.BankAccounts[i].Enabled = false + log.Warnf("critical banking numbers not set for %s in %s account", c.BankAccounts[i].BankName, c.BankAccounts[i].AccountName) + continue } if c.BankAccounts[i].SupportedExchanges == "" { @@ -200,7 +206,6 @@ func (c *Config) CheckClientBankAccounts() error { } } } - return nil } // PurgeExchangeAPICredentials purges the stored API credentials @@ -1254,7 +1259,7 @@ func (c *Config) CheckNTPConfig() { } if len(c.NTPClient.Pool) < 1 { - log.Warn("NTPClient enabled with no servers configured enabling default pool") + log.Warn("NTPClient enabled with no servers configured, enabling default pool.") c.NTPClient.Pool = []string{"pool.ntp.org:123"} } } @@ -1265,8 +1270,8 @@ func (c *Config) DisableNTPCheck(input io.Reader) (string, error) { defer m.Unlock() reader := bufio.NewReader(input) - log.Warn("Your system time is out of sync this may cause issues with trading") - log.Warn("How would you like to show future notifications? (a)lert / (w)arn / (d)isable \n") + log.Warn("Your system time is out of sync, this may cause issues with trading.") + log.Warn("How would you like to show future notifications? (a)lert / (w)arn / (d)isable. \n") var answered = false for ok := true; ok; ok = (!answered) { @@ -1291,7 +1296,7 @@ func (c *Config) DisableNTPCheck(input io.Reader) (string, error) { return "Future notications for out time sync have been disabled", nil } } - return "", errors.New("something went wrong NTPCheck should never make it this far") + return "", errors.New("something went wrong, NTPCheck should never make it this far") } // CheckConnectionMonitorConfig checks and if zero value assigns default values @@ -1502,15 +1507,11 @@ func (c *Config) SaveConfig(configPath string) error { return common.WriteFile(defaultPath, payload) } -// CheckConfig checks all config settings -func (c *Config) CheckConfig() error { - err := c.CheckExchangeConfigValues() - if err != nil { - return fmt.Errorf(ErrCheckingConfigValues, err) - } - - c.CheckConnectionMonitorConfig() - c.CheckCommunicationsConfig() +// CheckRemoteControlConfig checks to see if the old c.Webserver field is used +// and migrates the existing settings to the new RemoteControl struct +func (c *Config) CheckRemoteControlConfig() { + m.Lock() + defer m.Unlock() if c.Webserver != nil { port := common.ExtractPort(c.Webserver.ListenAddress) @@ -1548,6 +1549,25 @@ func (c *Config) CheckConfig() error { c.Webserver = nil } +} + +// CheckConfig checks all config settings +func (c *Config) CheckConfig() error { + err := c.CheckLoggerConfig() + if err != nil { + log.Errorf("Failed to configure logger. Err: %s", err) + } + + err = c.CheckExchangeConfigValues() + if err != nil { + return fmt.Errorf(ErrCheckingConfigValues, err) + } + + c.CheckConnectionMonitorConfig() + c.CheckCommunicationsConfig() + c.CheckClientBankAccounts() + c.CheckRemoteControlConfig() + err = c.CheckCurrencyConfigValues() if err != nil { return err @@ -1558,7 +1578,11 @@ func (c *Config) CheckConfig() error { c.GlobalHTTPTimeout = configDefaultHTTPTimeout } - return c.CheckClientBankAccounts() + if c.NTPClient.Level != 0 { + c.CheckNTPConfig() + } + + return nil } // LoadConfig loads your configuration file into your configuration object diff --git a/config/config_test.go b/config/config_test.go index 2161dbfe..68d55afb 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -130,7 +130,7 @@ func TestCheckClientBankAccounts(t *testing.T) { } cfg.BankAccounts = nil - err = cfg.CheckClientBankAccounts() + cfg.CheckClientBankAccounts() if err != nil || len(cfg.BankAccounts) == 0 { t.Error("Test failed. CheckClientBankAccounts error:", err) } @@ -140,33 +140,7 @@ func TestCheckClientBankAccounts(t *testing.T) { Enabled: true, BankName: "test", }) - err = cfg.CheckClientBankAccounts() - if err.Error() != "banking details for test is enabled but variables not set correctly" { - t.Error("Test failed. CheckClientBankAccounts unexpected error:", err) - } - - cfg.BankAccounts[0].BankAddress = "test" - err = cfg.CheckClientBankAccounts() - if err.Error() != "banking account details for test variables not set correctly" { - t.Error("Test failed. CheckClientBankAccounts unexpected error:", err) - } - - cfg.BankAccounts[0].AccountName = "Thrasher" - cfg.BankAccounts[0].AccountNumber = "1337" - err = cfg.CheckClientBankAccounts() - if err.Error() != "critical banking numbers not set for test in Thrasher account" { - t.Error("Test failed. CheckClientBankAccounts unexpected error:", err) - } - - cfg.BankAccounts[0].IBAN = "12345678" - err = cfg.CheckClientBankAccounts() - if err != nil { - t.Error("Test failed. CheckClientBankAccounts error:", err) - } - if cfg.BankAccounts[0].SupportedExchanges == "" { - t.Error("Test failed. CheckClientBankAccounts SupportedExchanges unexpectedly nil, data:", - cfg.BankAccounts[0]) - } + // TO-DO: Complete test coverage } func TestGetCommunicationsConfig(t *testing.T) { diff --git a/connchecker/connchecker.go b/connchecker/connchecker.go index 2c8e338c..6c9c4f99 100644 --- a/connchecker/connchecker.go +++ b/connchecker/connchecker.go @@ -86,7 +86,7 @@ func (c *Checker) Shutdown() { // Monitor determines internet connectivity via a DNS lookup func (c *Checker) Monitor(wg *sync.WaitGroup) { c.wg.Add(1) - tick := time.NewTicker(time.Second) + tick := time.NewTicker(c.CheckInterval) defer func() { tick.Stop(); c.wg.Done() }() wg.Done() for { diff --git a/currency/storage.go b/currency/storage.go index 57f75468..233457db 100644 --- a/currency/storage.go +++ b/currency/storage.go @@ -343,7 +343,7 @@ func (s *Storage) SeedCurrencyAnalysisData() error { // loads it into memory func (s *Storage) FetchCurrencyAnalysisData() error { if s.currencyAnalysis == nil { - log.Warn("Currency analysis system offline please set api keys for coinmarketcap") + log.Warn("Currency analysis system offline, please set api keys for coinmarketcap if you wish to use this feature.") return errors.New("currency analysis system offline") } diff --git a/engine/engine.go b/engine/engine.go index 412a74d0..74018480 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -33,6 +33,7 @@ type Engine struct { Exchanges []exchange.IBotExchange ExchangeCurrencyPairManager *ExchangeCurrencyPairSyncer OrderManager *OrderManager + PortfolioManager portfolioManager CommsRelayer *communications.Communications Connectivity *connchecker.Checker Shutdown chan bool @@ -86,11 +87,6 @@ func NewFromSettings(settings *Settings) (*Engine, error) { return nil, fmt.Errorf("failed to open/create data directory: %s. Err: %s", settings.DataDir, err) } - err = b.Config.CheckLoggerConfig() - if err != nil { - log.Errorf("Failed to configure logger. Err: %s", err) - } - err = log.SetupLogger() if err != nil { log.Errorf("Failed to setup logger. Err: %s", err) @@ -119,7 +115,7 @@ func ValidateSettings(b *Engine, s *Settings) { b.Settings.EnableDryRun = s.EnableDryRun b.Settings.EnableAllExchanges = s.EnableAllExchanges b.Settings.EnableAllPairs = s.EnableAllPairs - b.Settings.EnablePortfolioWatcher = s.EnablePortfolioWatcher + b.Settings.EnablePortfolioManager = s.EnablePortfolioManager b.Settings.EnableCoinmarketcapAnalysis = s.EnableCoinmarketcapAnalysis // TO-DO: FIXME @@ -159,10 +155,11 @@ func ValidateSettings(b *Engine, s *Settings) { } } + b.Settings.EnableConnectivityMonitor = s.EnableConnectivityMonitor b.Settings.EnableNTPClient = s.EnableNTPClient - b.Settings.EnableTickerRoutine = s.EnableTickerRoutine - b.Settings.EnableOrderbookRoutine = s.EnableOrderbookRoutine - b.Settings.EnableWebsocketRoutine = s.EnableWebsocketRoutine + b.Settings.EnableExchangeSyncManager = s.EnableExchangeSyncManager + b.Settings.EnableTickerSyncing = s.EnableTickerSyncing + b.Settings.EnableOrderbookSyncing = s.EnableOrderbookSyncing b.Settings.EnableExchangeAutoPairUpdates = s.EnableExchangeAutoPairUpdates b.Settings.EnableExchangeWebsocketSupport = s.EnableExchangeWebsocketSupport b.Settings.EnableExchangeRESTSupport = s.EnableExchangeRESTSupport @@ -222,7 +219,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.EnablePortfolioWatcher) + log.Debugf("\t Enable portfolio watcher: %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) @@ -230,8 +227,10 @@ func PrintSettings(s *Settings) { log.Debugf("\t Enable comms relayer: %v", s.EnableCommsRelayer) log.Debugf("\t Enable event manager: %v", s.EnableEventManager) log.Debugf("\t Event manager sleep delay: %v", s.EventManagerDelay) - log.Debugf("\t Enable ticker routine: %v", s.EnableTickerRoutine) - log.Debugf("\t Enable orderbook routine: %v", s.EnableOrderbookRoutine) + log.Debugf("\t Enable order manager: %v", s.EnableOrderManager) + log.Debugf("\t Enable exchange sync manager: %v", s.EnableExchangeSyncManager) + log.Debugf("\t Enable ticker syncing: %v", s.EnableTickerSyncing) + log.Debugf("\t Enable orderbook syncing: %v", s.EnableOrderbookSyncing) log.Debugf("\t Enable websocket routine: %v\n", s.EnableWebsocketRoutine) log.Debugf("\t Enable NTP client: %v", s.EnableNTPClient) log.Debugf("- FOREX SETTINGS:") @@ -266,16 +265,17 @@ func (e *Engine) Start() { // Sets up internet connectivity monitor var err error - e.Connectivity, err = connchecker.New(e.Config.ConnectionMonitor.DNSList, - e.Config.ConnectionMonitor.PublicDomainList, - e.Config.ConnectionMonitor.CheckInterval) - if err != nil { - log.Fatalf("Connectivity checker failure: %s", err) + if e.Settings.EnableConnectivityMonitor { + e.Connectivity, err = connchecker.New(e.Config.ConnectionMonitor.DNSList, + e.Config.ConnectionMonitor.PublicDomainList, + e.Config.ConnectionMonitor.CheckInterval) + if err != nil { + log.Fatalf("Connectivity checker failure: %s", err) + } } if e.Settings.EnableNTPClient { if e.Config.NTPClient.Level != -1 { - e.Config.CheckNTPConfig() NTPTime, errNTP := ntpclient.NTPClient(e.Config.NTPClient.Pool) currentTime := time.Now() if errNTP != nil { @@ -356,10 +356,6 @@ func (e *Engine) Start() { log.Warn("currency updater system failed to start", err) } - e.Portfolio = &portfolio.Portfolio - e.Portfolio.Seed(e.Config.Portfolio) - SeedExchangeAccountInfo(GetAllEnabledExchangeAccountInfo().Data) - e.CryptocurrencyDepositAddresses = GetExchangeCryptocurrencyDepositAddresses() if e.Settings.EnableGRPC { @@ -375,42 +371,31 @@ func (e *Engine) Start() { StartWebsocketHandler() } - if e.Settings.EnablePortfolioWatcher { - go portfolio.StartPortfolioWatcher() + if e.Settings.EnablePortfolioManager { + if err = e.PortfolioManager.Start(); err != nil { + log.Errorf("Fund manager unable to start: %v", err) + } } - /* + if e.Settings.EnableExchangeSyncManager { exchangeSyncCfg := CurrencyPairSyncerConfig{ - SyncTicker: true, - SyncOrderbook: true, + SyncTicker: e.Settings.EnableTickerSyncing, + SyncOrderbook: e.Settings.EnableOrderbookSyncing, SyncContinuously: true, NumWorkers: 15, } - - e.ExchangeCurrencyPairManager, err = NewCurrencyPairSyncer(exchangeSyncCfg) - if err != nil { - log.Warnf("Unable to initialise exchange currency pair syncer. Err: %s", err) - } else { - e.ExchangeCurrencyPairManager.Start() - } - */ - - go StartOrderManagerRoutine() - - if e.Settings.EnableTickerRoutine { - go TickerUpdaterRoutine() + e.ExchangeCurrencyPairManager, err = NewCurrencyPairSyncer(exchangeSyncCfg) + if err != nil { + log.Warnf("Unable to initialise exchange currency pair syncer. Err: %s", err) + } else { + go e.ExchangeCurrencyPairManager.Start() + } } - /* - if e.Settings.EnableOrderbookRoutine { - go OrderbookUpdaterRoutine() - } - - if e.Settings.EnableWebsocketRoutine { - go WebsocketRoutine() - } - */ + if e.Settings.EnableOrderManager { + go StartOrderManagerRoutine() + } if e.Settings.EnableEventManager { go events.EventManger() @@ -428,9 +413,14 @@ func (e *Engine) Stop() { e.Config.Portfolio = portfolio.Portfolio } + if e.PortfolioManager.Started() { + if err := e.PortfolioManager.Stop(); err != nil { + log.Errorf("Fund manager unable to stop. Error: %v", err) + } + } + if !e.Settings.EnableDryRun { err := e.Config.SaveConfig(e.Settings.ConfigFile) - if err != nil { log.Error("Unable to save config.") } else { diff --git a/engine/engine_types.go b/engine/engine_types.go index ca80ba22..32728c93 100644 --- a/engine/engine_types.go +++ b/engine/engine_types.go @@ -14,17 +14,20 @@ type Settings struct { EnableAllExchanges bool EnableAllPairs bool EnableCoinmarketcapAnalysis bool - EnablePortfolioWatcher bool + EnablePortfolioManager bool EnableGRPC bool EnableGRPCProxy bool EnableWebsocketRPC bool EnableDeprecatedRPC bool - EnableTickerRoutine bool - EnableOrderbookRoutine bool - EnableWebsocketRoutine bool EnableCommsRelayer bool + EnableExchangeSyncManager bool + EnableTickerSyncing bool + EnableOrderbookSyncing bool EnableEventManager bool + EnableOrderManager bool + EnableConnectivityMonitor bool EnableNTPClient bool + EnableWebsocketRoutine bool EventManagerDelay time.Duration Verbose bool diff --git a/engine/exchange.go b/engine/exchange.go index 55d66781..92b7aa50 100644 --- a/engine/exchange.go +++ b/engine/exchange.go @@ -269,8 +269,9 @@ func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error { // SetupExchanges sets up the exchanges used by the Bot func SetupExchanges() { var wg sync.WaitGroup - for x := range Bot.Config.Exchanges { - exch := &Bot.Config.Exchanges[x] + exchanges := Bot.Config.GetAllExchangeConfigs() + for x := range exchanges { + exch := exchanges[x] if CheckExchangeExists(exch.Name) { e := GetExchangeByName(exch.Name) if e == nil { diff --git a/engine/helpers.go b/engine/helpers.go index 10ef93ff..07c8b863 100644 --- a/engine/helpers.go +++ b/engine/helpers.go @@ -26,6 +26,14 @@ import ( "github.com/thrasher-/gocryptotrader/utils" ) +// IsOnline returns whether or not the engine has Internet connectivity +func IsOnline() bool { + if Bot.Connectivity == nil { + log.Warnf("IsOnline called but Bot.Connectivity is nil") + } + return Bot.Connectivity.IsConnected() +} + // GetAvailableExchanges returns a list of enabled exchanges func GetAvailableExchanges() []string { var enExchanges []string diff --git a/engine/portfolio.go b/engine/portfolio.go new file mode 100644 index 00000000..36d613d7 --- /dev/null +++ b/engine/portfolio.go @@ -0,0 +1,81 @@ +package engine + +import ( + "errors" + "sync/atomic" + "time" + + log "github.com/thrasher-/gocryptotrader/logger" + "github.com/thrasher-/gocryptotrader/portfolio" +) + +// vars for the fund manager package +var ( + PortfolioSleepDelay = time.Minute +) + +type portfolioManager struct { + started int32 + stopped int32 + shutdown chan struct{} +} + +func (p *portfolioManager) Started() bool { + return atomic.LoadInt32(&p.started) == 1 +} + +func (p *portfolioManager) Start() error { + if atomic.AddInt32(&p.started, 1) != 1 { + return errors.New("portfolio manager already started") + } + + log.Debugln("Portfolio manager starting...") + Bot.Portfolio = &portfolio.Portfolio + Bot.Portfolio.Seed(Bot.Config.Portfolio) + p.shutdown = make(chan struct{}) + go p.run() + return nil +} +func (p *portfolioManager) Stop() error { + if atomic.AddInt32(&p.stopped, 1) != 1 { + return errors.New("portfolio manager is already stopped") + } + + log.Debugln("Portfolio manager shutting down...") + close(p.shutdown) + return nil +} + +func (p *portfolioManager) run() { + log.Debugln("Portfolio manager started.") + tick := time.NewTicker(PortfolioSleepDelay) + defer func() { + log.Debugf("Portfolio manager shutdown.") + tick.Stop() + }() + + for { + select { + case <-p.shutdown: + return + + case <-tick.C: + p.processPortfolio() + } + } +} + +func (p *portfolioManager) processPortfolio() { + pf := portfolio.GetPortfolio() + data := pf.GetPortfolioGroupedCoin() + for key, value := range data { + success := pf.UpdatePortfolio(value, key) + if success { + log.Debugf( + "Portfolio manager: Successfully updated address balance for %s address(es) %s\n", + key, value, + ) + } + } + SeedExchangeAccountInfo(GetAllEnabledExchangeAccountInfo().Data) +} diff --git a/engine/restful_server.go b/engine/restful_server.go index cb25f7bb..6139d3c0 100644 --- a/engine/restful_server.go +++ b/engine/restful_server.go @@ -6,6 +6,7 @@ import ( "github.com/thrasher-/gocryptotrader/config" log "github.com/thrasher-/gocryptotrader/logger" + "github.com/thrasher-/gocryptotrader/portfolio" ) // RESTfulJSONResponse outputs a JSON response of the response interface @@ -100,7 +101,8 @@ func RESTGetAllActiveOrderbooks(w http.ResponseWriter, r *http.Request) { // RESTGetPortfolio returns the Bot portfolio func RESTGetPortfolio(w http.ResponseWriter, r *http.Request) { - result := Bot.Portfolio.GetPortfolioSummary() + p := portfolio.GetPortfolio() + result := p.GetPortfolioSummary() err := RESTfulJSONResponse(w, result) if err != nil { RESTfulError(r.Method, err) diff --git a/engine/rpcserver.go b/engine/rpcserver.go index a20e7671..5131021f 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -466,14 +466,14 @@ func (s *RPCServer) GetPortfolioSummary(ctx context.Context, r *gctrpc.GetPortfo // AddPortfolioAddress adds an address to the portfolio manager func (s *RPCServer) AddPortfolioAddress(ctx context.Context, r *gctrpc.AddPortfolioAddressRequest) (*gctrpc.AddPortfolioAddressResponse, error) { - Bot.Portfolio.AddAddress(r.Address, r.Description, currency.NewCode(r.CoinType), r.Balance) - return &gctrpc.AddPortfolioAddressResponse{}, nil + err := Bot.Portfolio.AddAddress(r.Address, r.Description, currency.NewCode(r.CoinType), r.Balance) + return &gctrpc.AddPortfolioAddressResponse{}, err } // RemovePortfolioAddress removes an address from the portfolio manager func (s *RPCServer) RemovePortfolioAddress(ctx context.Context, r *gctrpc.RemovePortfolioAddressRequest) (*gctrpc.RemovePortfolioAddressResponse, error) { - Bot.Portfolio.RemoveAddress(r.Address, r.Description, currency.NewCode(r.CoinType)) - return &gctrpc.RemovePortfolioAddressResponse{}, nil + err := Bot.Portfolio.RemoveAddress(r.Address, r.Description, currency.NewCode(r.CoinType)) + return &gctrpc.RemovePortfolioAddressResponse{}, err } // GetForexProviders returns a list of available forex providers diff --git a/main.go b/main.go index a30aa448..1ce4b8fd 100644 --- a/main.go +++ b/main.go @@ -32,18 +32,21 @@ func main() { flag.BoolVar(&settings.EnableDryRun, "dryrun", false, "dry runs bot, doesn't save config file") flag.BoolVar(&settings.EnableAllExchanges, "enableallexchanges", false, "enables all exchanges") flag.BoolVar(&settings.EnableAllPairs, "enableallpairs", false, "enables all pairs for enabled exchanges") - flag.BoolVar(&settings.EnablePortfolioWatcher, "portfoliowatcher", true, "enables the portfolio watcher") + flag.BoolVar(&settings.EnablePortfolioManager, "portfoliomanager", true, "enables the portfolio manager") flag.BoolVar(&settings.EnableGRPC, "grpc", true, "enables the grpc server") flag.BoolVar(&settings.EnableGRPCProxy, "grpcproxy", true, "enables the grpc proxy server") flag.BoolVar(&settings.EnableWebsocketRPC, "websocketrpc", true, "enables the websocket RPC server") flag.BoolVar(&settings.EnableDeprecatedRPC, "deprecatedrpc", true, "enables the deprecated RPC server") flag.BoolVar(&settings.EnableCommsRelayer, "enablecommsrelayer", true, "enables available communications relayer") flag.BoolVar(&settings.Verbose, "verbose", false, "increases logging verbosity for GoCryptoTrader") - flag.BoolVar(&settings.EnableTickerRoutine, "tickerroutine", true, "enables the ticker routine for all loaded exchanges") - flag.BoolVar(&settings.EnableOrderbookRoutine, "orderbookroutine", true, "enables the orderbook routine for all loaded exchanges") + flag.BoolVar(&settings.EnableExchangeSyncManager, "syncmanager", true, "enables to exchange sync manager") + flag.BoolVar(&settings.EnableTickerSyncing, "tickersync", true, "enables ticker syncing for all enabled exchanges") + flag.BoolVar(&settings.EnableOrderbookSyncing, "orderbooksync", true, "enables orderbook syncing for all enabled exchanges") flag.BoolVar(&settings.EnableWebsocketRoutine, "websocketroutine", true, "enables the websocket routine for all loaded exchanges") flag.BoolVar(&settings.EnableCoinmarketcapAnalysis, "coinmarketcap", false, "overrides config and runs currency analysis") - flag.BoolVar(&settings.EnableEventManager, "enableeventmanager", true, "enables the event manager") + flag.BoolVar(&settings.EnableEventManager, "eventmanager", true, "enables the event manager") + flag.BoolVar(&settings.EnableOrderManager, "ordermanager", true, "enables the order manager") + flag.BoolVar(&settings.EnableConnectivityMonitor, "connectivitymonitor", true, "enables the connectivity monitor") flag.DurationVar(&settings.EventManagerDelay, "eventmanagerdelay", time.Duration(0), "sets the event managers sleep delay between event checking") flag.BoolVar(&settings.EnableNTPClient, "ntpclient", true, "enables the NTP client to check system clock drift") diff --git a/portfolio/portfolio.go b/portfolio/portfolio.go index c6751422..e8862592 100644 --- a/portfolio/portfolio.go +++ b/portfolio/portfolio.go @@ -149,10 +149,17 @@ func (p *Base) UpdateExchangeAddressBalance(exchangeName string, coinType curren } // AddAddress adds an address to the portfolio base -func (p *Base) AddAddress(address, description string, coinType currency.Code, balance float64) { +func (p *Base) AddAddress(address, description string, coinType currency.Code, balance float64) error { + if address == "" { + return errors.New("address is empty") + } + + if coinType.String() == "" { + return errors.New("coin type is empty") + } + if description == PortfolioAddressExchange { p.AddExchangeAddress(address, coinType, balance) - return } if !p.AddressExists(address) { p.Addresses = append( @@ -166,19 +173,30 @@ func (p *Base) AddAddress(address, description string, coinType currency.Code, b p.UpdateAddressBalance(address, balance) } } + return nil } // RemoveAddress removes an address when checked against the correct address and // coinType -func (p *Base) RemoveAddress(address, description string, coinType currency.Code) { +func (p *Base) RemoveAddress(address, description string, coinType currency.Code) error { + if address == "" { + return errors.New("address is empty") + } + + if coinType.String() == "" { + return errors.New("coin type is empty") + } + for x := range p.Addresses { if p.Addresses[x].Address == address && p.Addresses[x].CoinType == coinType && p.Addresses[x].Description == description { p.Addresses = append(p.Addresses[:x], p.Addresses[x+1:]...) - return + return nil } } + + return errors.New("portfolio item does not exist") } // UpdatePortfolio adds to the portfolio addresses by coin type diff --git a/portfolio/portfolio_test.go b/portfolio/portfolio_test.go index 225010ba..2d69a267 100644 --- a/portfolio/portfolio_test.go +++ b/portfolio/portfolio_test.go @@ -138,7 +138,19 @@ func TestUpdateAddressBalance(t *testing.T) { } func TestRemoveAddress(t *testing.T) { - newbase := Base{} + var newbase Base + if err := newbase.RemoveAddress("", "MEOW", currency.LTC); err == nil { + t.Error("invalid address should throw an error") + } + + if err := newbase.RemoveAddress("Gibson", "", currency.NewCode("")); err == nil { + t.Error("invalid coin type should throw an error") + } + + if err := newbase.RemoveAddress("HIDDENERINO", "MEOW", currency.LTC); err == nil { + t.Error("non-existent address should throw an error") + } + newbase.AddAddress("someaddr", currency.LTC.String(), currency.NewCode("LTCWALLETTEST"), @@ -187,12 +199,36 @@ func TestUpdateExchangeAddressBalance(t *testing.T) { } func TestAddAddress(t *testing.T) { - newbase := Base{} + var newbase Base + if err := newbase.AddAddress("", "MEOW", currency.LTC, 1); err == nil { + t.Error("invalid address should throw an error") + } + + if err := newbase.AddAddress("Gibson", "", currency.NewCode(""), 1); err == nil { + t.Error("invalid coin type should throw an error") + } + + // test adding an exchange address + err := newbase.AddAddress("COINUT", PortfolioAddressExchange, currency.LTC, 0) + if err != nil { + t.Errorf("failed to add address: %v", err) + } + + // add a test portfolio address and amount newbase.AddAddress("Gibson", currency.LTC.String(), currency.NewCode("LTCWALLETTEST"), 0.02) + // test updating the balance and make sure it's reflected + newbase.AddAddress("Gibson", currency.LTC.String(), + currency.NewCode("LTCWALLETTEST"), 0.05) + b, _ := newbase.GetAddressBalance("Gibson", "LTC", + currency.NewCode("LTCWALLETTEST")) + if b != 0.05 { + t.Error("invalid portfolio amount") + } + portfolio := GetPortfolio() portfolio.Seed(newbase) if !portfolio.AddressExists("Gibson") {