diff --git a/.golangci.yml b/.golangci.yml index 2fcc3b32..9357ce01 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,5 @@ run: - deadline: 40s + deadline: 1m0s issues-exit-code: 1 tests: true skip-dirs: diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 37ee55a3..943009a0 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "fmt" "strconv" @@ -82,12 +83,6 @@ func enableSubsystem(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var subsystemName string if c.IsSet("subsystem") { subsystemName = c.String("subsystem") @@ -95,6 +90,16 @@ func enableSubsystem(c *cli.Context) error { subsystemName = c.Args().First() } + if subsystemName == "" { + return errors.New("invalid subsystem supplied") + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.EnableSubsystem(context.Background(), &gctrpc.GenericSubsystemRequest{ @@ -129,12 +134,6 @@ func disableSubsystem(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var subsystemName string if c.IsSet("subsystem") { subsystemName = c.String("subsystem") @@ -142,6 +141,16 @@ func disableSubsystem(c *cli.Context) error { subsystemName = c.Args().First() } + if subsystemName == "" { + return errors.New("invalid subsystem supplied") + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.DisableSubsystem(context.Background(), &gctrpc.GenericSubsystemRequest{ @@ -268,12 +277,6 @@ func enableExchange(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string if c.IsSet("exchange") { exchangeName = c.String("exchange") @@ -281,6 +284,16 @@ func enableExchange(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.EnableExchange(context.Background(), &gctrpc.GenericExchangeNameRequest{ @@ -315,12 +328,6 @@ func disableExchange(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string if c.IsSet("exchange") { exchangeName = c.String("exchange") @@ -328,6 +335,16 @@ func disableExchange(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.DisableExchange(context.Background(), &gctrpc.GenericExchangeNameRequest{ @@ -362,12 +379,6 @@ func getExchangeOTPCode(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string if c.IsSet("exchange") { exchangeName = c.String("exchange") @@ -375,6 +386,16 @@ func getExchangeOTPCode(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetExchangeOTPCode(context.Background(), &gctrpc.GenericExchangeNameRequest{ @@ -434,12 +455,6 @@ func getExchangeInfo(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string if c.IsSet("exchange") { exchangeName = c.String("exchange") @@ -447,6 +462,16 @@ func getExchangeInfo(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetExchangeInfo(context.Background(), &gctrpc.GenericExchangeNameRequest{ @@ -489,12 +514,6 @@ func getTicker(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string var currencyPair string var assetType string @@ -505,23 +524,33 @@ func getTicker(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + if c.IsSet("pair") { currencyPair = c.String("pair") } else { currencyPair = c.Args().Get(1) } + if !validPair(currencyPair) { + return errInvalidPair + } + if c.IsSet("asset") { assetType = c.String("asset") } else { assetType = c.Args().Get(2) } - if !validPair(currencyPair) { - return errInvalidPair + conn, err := setupClient() + if err != nil { + return err } - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + defer conn.Close() + p := currency.NewPairDelimiter(currencyPair, pairDelimiter) client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetTicker(context.Background(), &gctrpc.GetTickerRequest{ @@ -545,7 +574,7 @@ func getTicker(c *cli.Context) error { var getTickersCommand = cli.Command{ Name: "gettickers", - Usage: "gets all tickers for all enabled exchanes and currency pairs", + Usage: "gets all tickers for all enabled exchanges and currency pairs", Action: getTickers, } @@ -593,12 +622,6 @@ func getOrderbook(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string var currencyPair string var assetType string @@ -609,23 +632,33 @@ func getOrderbook(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + if c.IsSet("pair") { currencyPair = c.String("pair") } else { currencyPair = c.Args().Get(1) } + if !validPair(currencyPair) { + return errInvalidPair + } + if c.IsSet("asset") { assetType = c.String("asset") } else { assetType = c.Args().Get(2) } - if !validPair(currencyPair) { - return errInvalidPair + conn, err := setupClient() + if err != nil { + return err } - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + defer conn.Close() + p := currency.NewPairDelimiter(currencyPair, pairDelimiter) client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetOrderbook(context.Background(), &gctrpc.GetOrderbookRequest{ @@ -649,7 +682,7 @@ func getOrderbook(c *cli.Context) error { var getOrderbooksCommand = cli.Command{ Name: "getorderbooks", - Usage: "gets all orderbooks for all enabled exchanes and currency pairs", + Usage: "gets all orderbooks for all enabled exchanges and currency pairs", Action: getOrderbooks, } @@ -689,12 +722,6 @@ func getAccountInfo(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchange string if c.IsSet("exchange") { exchange = c.String("exchange") @@ -702,6 +729,16 @@ func getAccountInfo(c *cli.Context) error { exchange = c.Args().First() } + if !validExchange(exchange) { + return errInvalidExchange + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetAccountInfo(context.Background(), &gctrpc.GetAccountInfoRequest{ @@ -1009,12 +1046,6 @@ var getOrdersCommand = cli.Command{ } func getOrders(c *cli.Context) error { - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string var assetType string var currencyPair string @@ -1025,6 +1056,10 @@ func getOrders(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + if c.IsSet("asset_type") { assetType = c.String("asset_type") } else { @@ -1040,8 +1075,14 @@ func getOrders(c *cli.Context) error { if !validPair(currencyPair) { return errInvalidPair } - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + + p := currency.NewPairDelimiter(currencyPair, pairDelimiter) client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetOrders(context.Background(), &gctrpc.GetOrdersRequest{ Exchange: exchangeName, @@ -1083,12 +1124,6 @@ func getOrder(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string var orderID string @@ -1098,12 +1133,22 @@ func getOrder(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + if c.IsSet("order_id") { orderID = c.String("order_id") } else { orderID = c.Args().Get(1) } + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetOrder(context.Background(), &gctrpc.GetOrderRequest{ Exchange: exchangeName, @@ -1120,7 +1165,7 @@ func getOrder(c *cli.Context) error { var submitOrderCommand = cli.Command{ Name: "submitorder", Usage: "submit order submits an exchange order", - ArgsUsage: " ", + ArgsUsage: " ", Action: submitOrder, Flags: []cli.Flag{ cli.StringFlag{ @@ -1128,7 +1173,7 @@ var submitOrderCommand = cli.Command{ Usage: "the exchange to submit the order for", }, cli.StringFlag{ - Name: "currency_pair", + Name: "pair", Usage: "the currency pair", }, cli.StringFlag{ @@ -1160,12 +1205,6 @@ func submitOrder(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string var currencyPair string var orderSide string @@ -1180,12 +1219,20 @@ func submitOrder(c *cli.Context) error { exchangeName = c.Args().First() } - if c.IsSet("currency_pair") { - currencyPair = c.String("currency_pair") + if !validExchange(exchangeName) { + return errInvalidExchange + } + + if c.IsSet("pair") { + currencyPair = c.String("pair") } else { currencyPair = c.Args().Get(1) } + if !validPair(currencyPair) { + return errInvalidPair + } + if c.IsSet("side") { orderSide = c.String("side") } else { @@ -1216,11 +1263,13 @@ func submitOrder(c *cli.Context) error { clientID = c.Args().Get(6) } - if !validPair(currencyPair) { - return errInvalidPair + conn, err := setupClient() + if err != nil { + return err } - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + defer conn.Close() + p := currency.NewPairDelimiter(currencyPair, pairDelimiter) client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.SubmitOrder(context.Background(), &gctrpc.SubmitOrderRequest{ Exchange: exchangeName, @@ -1246,7 +1295,7 @@ func submitOrder(c *cli.Context) error { var simulateOrderCommand = cli.Command{ Name: "simulateorder", Usage: "simulate order simulates an exchange order", - ArgsUsage: " ", + ArgsUsage: " ", Action: simulateOrder, Flags: []cli.Flag{ cli.StringFlag{ @@ -1254,7 +1303,7 @@ var simulateOrderCommand = cli.Command{ Usage: "the exchange to simulate the order for", }, cli.StringFlag{ - Name: "currency_pair", + Name: "pair", Usage: "the currency pair", }, cli.StringFlag{ @@ -1274,12 +1323,6 @@ func simulateOrder(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string var currencyPair string var orderSide string @@ -1291,12 +1334,20 @@ func simulateOrder(c *cli.Context) error { exchangeName = c.Args().First() } - if c.IsSet("currency_pair") { - currencyPair = c.String("currency_pair") + if !validExchange(exchangeName) { + return errInvalidExchange + } + + if c.IsSet("pair") { + currencyPair = c.String("pair") } else { currencyPair = c.Args().Get(1) } + if !validPair(currencyPair) { + return errInvalidPair + } + if c.IsSet("side") { orderSide = c.String("side") } else { @@ -1309,11 +1360,13 @@ func simulateOrder(c *cli.Context) error { amount, _ = strconv.ParseFloat(c.Args().Get(3), 64) } - if !validPair(currencyPair) { - return errInvalidPair + conn, err := setupClient() + if err != nil { + return err } - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + defer conn.Close() + p := currency.NewPairDelimiter(currencyPair, pairDelimiter) client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.SimulateOrder(context.Background(), &gctrpc.SimulateOrderRequest{ Exchange: exchangeName, @@ -1336,7 +1389,7 @@ func simulateOrder(c *cli.Context) error { var whaleBombCommand = cli.Command{ Name: "whalebomb", Usage: "whale bomb finds the amount required to reach a price target", - ArgsUsage: " ", + ArgsUsage: " ", Action: whaleBomb, Flags: []cli.Flag{ cli.StringFlag{ @@ -1344,7 +1397,7 @@ var whaleBombCommand = cli.Command{ Usage: "the exchange to whale bomb", }, cli.StringFlag{ - Name: "currency_pair", + Name: "pair", Usage: "the currency pair", }, cli.StringFlag{ @@ -1364,12 +1417,6 @@ func whaleBomb(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string var currencyPair string var orderSide string @@ -1381,12 +1428,20 @@ func whaleBomb(c *cli.Context) error { exchangeName = c.Args().First() } - if c.IsSet("currency_pair") { - currencyPair = c.String("currency_pair") + if !validExchange(exchangeName) { + return errInvalidExchange + } + + if c.IsSet("pair") { + currencyPair = c.String("pair") } else { currencyPair = c.Args().Get(1) } + if !validPair(currencyPair) { + return errInvalidPair + } + if c.IsSet("side") { orderSide = c.String("side") } else { @@ -1399,11 +1454,13 @@ func whaleBomb(c *cli.Context) error { price, _ = strconv.ParseFloat(c.Args().Get(3), 64) } - if !validPair(currencyPair) { - return errInvalidPair + conn, err := setupClient() + if err != nil { + return err } - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + defer conn.Close() + p := currency.NewPairDelimiter(currencyPair, pairDelimiter) client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.WhaleBomb(context.Background(), &gctrpc.WhaleBombRequest{ Exchange: exchangeName, @@ -1426,7 +1483,7 @@ func whaleBomb(c *cli.Context) error { var cancelOrderCommand = cli.Command{ Name: "cancelorder", Usage: "cancel order cancels an exchange order", - ArgsUsage: " ", + ArgsUsage: " ", Action: cancelOrder, Flags: []cli.Flag{ cli.StringFlag{ @@ -1442,7 +1499,7 @@ var cancelOrderCommand = cli.Command{ Usage: "the order id", }, cli.StringFlag{ - Name: "currency_pair", + Name: "pair", Usage: "the currency pair to cancel the order for", }, cli.StringFlag{ @@ -1466,12 +1523,6 @@ func cancelOrder(c *cli.Context) error { return nil } - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string var accountID string var orderID string @@ -1486,6 +1537,10 @@ func cancelOrder(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + if c.IsSet("order_id") { orderID = c.String("order_id") } else { @@ -1496,8 +1551,8 @@ func cancelOrder(c *cli.Context) error { accountID = c.String("account_id") } - if c.IsSet("currency_pair") { - currencyPair = c.String("currency_pair") + if c.IsSet("pair") { + currencyPair = c.String("pair") } if c.IsSet("asset_type") { @@ -1520,6 +1575,12 @@ func cancelOrder(c *cli.Context) error { p = currency.NewPairDelimiter(currencyPair, pairDelimiter) } + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.CancelOrder(context.Background(), &gctrpc.CancelOrderRequest{ Exchange: exchangeName, @@ -1556,12 +1617,6 @@ var cancelAllOrdersCommand = cli.Command{ } func cancelAllOrders(c *cli.Context) error { - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - var exchangeName string if c.IsSet("exchange") { exchangeName = c.String("exchange") @@ -1569,6 +1624,19 @@ func cancelAllOrders(c *cli.Context) error { exchangeName = c.Args().First() } + // exchange name is an optional param + if exchangeName != "" { + if !validExchange(exchangeName) { + return errInvalidExchange + } + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.CancelAllOrders(context.Background(), &gctrpc.CancelAllOrdersRequest{ Exchange: exchangeName, @@ -1607,7 +1675,7 @@ func getEvents(_ *cli.Context) error { var addEventCommand = cli.Command{ Name: "addevent", Usage: "adds an event", - ArgsUsage: " ", + ArgsUsage: " ", Action: addEvent, Flags: []cli.Flag{ cli.StringFlag{ @@ -1639,7 +1707,7 @@ var addEventCommand = cli.Command{ Usage: "the orderbook amount to trigger the event", }, cli.StringFlag{ - Name: "currency_pair", + Name: "pair", Usage: "the currency pair", }, cli.StringFlag{ @@ -1704,8 +1772,8 @@ func addEvent(c *cli.Context) error { orderbookAmount = c.Float64("orderbook_amount") } - if c.IsSet("currency_pair") { - currencyPair = c.String("currency_pair") + if c.IsSet("pair") { + currencyPair = c.String("pair") } else { return fmt.Errorf("currency pair is required") } @@ -1720,17 +1788,17 @@ func addEvent(c *cli.Context) error { return fmt.Errorf("action is required") } + if !validPair(currencyPair) { + return errInvalidPair + } + conn, err := setupClient() if err != nil { return err } defer conn.Close() - if !validPair(currencyPair) { - return errInvalidPair - } p := currency.NewPairDelimiter(currencyPair, pairDelimiter) - client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.AddEvent(context.Background(), &gctrpc.AddEventRequest{ Exchange: exchangeName, @@ -1831,6 +1899,10 @@ func getCryptocurrencyDepositAddresses(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + conn, err := setupClient() if err != nil { return err @@ -1880,6 +1952,10 @@ func getCryptocurrencyDepositAddress(c *cli.Context) error { exchangeName = c.Args().First() } + if !validExchange(exchangeName) { + return errInvalidExchange + } + if c.IsSet("cryptocurrency") { cryptocurrency = c.String("cryptocurrency") } else { @@ -2086,6 +2162,10 @@ func getExchangePairs(c *cli.Context) error { exchange = c.Args().First() } + if !validExchange(exchange) { + return errInvalidExchange + } + if c.IsSet("asset") { asset = c.String("asset") } else { @@ -2099,7 +2179,6 @@ func getExchangePairs(c *cli.Context) error { defer conn.Close() client := gctrpc.NewGoCryptoTraderClient(conn) - result, err := client.GetExchangePairs(context.Background(), &gctrpc.GetExchangePairsRequest{ Exchange: exchange, @@ -2150,12 +2229,20 @@ func enableExchangePair(c *cli.Context) error { exchange = c.Args().First() } + if !validExchange(exchange) { + return errInvalidExchange + } + if c.IsSet("pair") { pair = c.String("pair") } else { pair = c.Args().Get(1) } + if !validPair(pair) { + return errInvalidPair + } + if c.IsSet("asset") { asset = c.String("asset") } else { @@ -2168,10 +2255,6 @@ func enableExchangePair(c *cli.Context) error { } defer conn.Close() - if !validPair(pair) { - return errInvalidPair - } - p := currency.NewPairDelimiter(pair, pairDelimiter) client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.EnableExchangePair(context.Background(), @@ -2229,12 +2312,20 @@ func disableExchangePair(c *cli.Context) error { exchange = c.Args().First() } + if !validExchange(exchange) { + return errInvalidExchange + } + if c.IsSet("pair") { pair = c.String("pair") } else { pair = c.Args().Get(1) } + if !validPair(pair) { + return errInvalidPair + } + if c.IsSet("asset") { asset = c.String("asset") } else { @@ -2247,10 +2338,6 @@ func disableExchangePair(c *cli.Context) error { } defer conn.Close() - if !validPair(pair) { - return errInvalidPair - } - p := currency.NewPairDelimiter(pair, pairDelimiter) client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.DisableExchangePair(context.Background(), diff --git a/cmd/gctcli/validation.go b/cmd/gctcli/validation.go index b170d98b..fd3202b0 100644 --- a/cmd/gctcli/validation.go +++ b/cmd/gctcli/validation.go @@ -3,12 +3,19 @@ package main import ( "errors" "strings" + + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" ) var ( - errInvalidPair = errors.New("invalid currency pair supplied") + errInvalidPair = errors.New("invalid currency pair supplied") + errInvalidExchange = errors.New("invalid exchange supplied") ) func validPair(pair string) bool { return strings.Contains(pair, pairDelimiter) } + +func validExchange(exch string) bool { + return exchange.IsSupported(exch) +} diff --git a/config/config.go b/config/config.go index f38b9dbc..d8741fc3 100644 --- a/config/config.go +++ b/config/config.go @@ -446,10 +446,6 @@ func (c *Config) CheckExchangeAssetsConsistency(exchName string) { return } - if exchCfg.CurrencyPairs == nil { - return - } - exchangeAssetTypes, err := c.GetExchangeAssetTypes(exchName) if err != nil { return @@ -458,7 +454,9 @@ func (c *Config) CheckExchangeAssetsConsistency(exchName string) { storedAssetTypes := exchCfg.CurrencyPairs.GetAssetTypes() for x := range storedAssetTypes { if !exchangeAssetTypes.Contains(storedAssetTypes[x]) { - log.Warnf(log.ConfigMgr, "%s has non-needed stored asset type %v. Removing..\n", exchName, storedAssetTypes[x]) + log.Warnf(log.ConfigMgr, + "%s has non-needed stored asset type %v. Removing..\n", + exchName, storedAssetTypes[x]) exchCfg.CurrencyPairs.Delete(storedAssetTypes[x]) } } diff --git a/config/config_test.go b/config/config_test.go index 27f38894..70597fe4 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -15,6 +15,7 @@ const ( // Default number of enabled exchanges. Modify this whenever an exchange is // added or removed defaultEnabledExchanges = 28 + testFakeExchangeName = "Stampbit" ) func TestGetCurrencyConfig(t *testing.T) { @@ -143,6 +144,70 @@ func TestCheckClientBankAccounts(t *testing.T) { // TO-DO: Complete test coverage } +func TestPurgeExchangeCredentials(t *testing.T) { + t.Parallel() + var c Config + c.Exchanges = []ExchangeConfig{ + { + Name: "test", + API: APIConfig{ + AuthenticatedSupport: true, + AuthenticatedWebsocketSupport: true, + + CredentialsValidator: &APICredentialsValidatorConfig{ + RequiresKey: true, + RequiresSecret: true, + RequiresClientID: true, + }, + + Credentials: APICredentialsConfig{ + Key: "asdf123", + Secret: "secretp4ssw0rd", + ClientID: "1337", + OTPSecret: "otp", + PEMKey: "aaa", + }, + }, + }, + { + Name: "test123", + API: APIConfig{ + CredentialsValidator: &APICredentialsValidatorConfig{ + RequiresKey: true, + }, + Credentials: APICredentialsConfig{ + Key: "asdf", + Secret: DefaultAPISecret, + }, + }, + }, + } + + c.PurgeExchangeAPICredentials() + + exchCfg, err := c.GetExchangeConfig("test") + if err != nil { + t.Error(err) + } + + if exchCfg.API.Credentials.Key != DefaultAPIKey && + exchCfg.API.Credentials.ClientID != DefaultAPIClientID && + exchCfg.API.Credentials.Secret != DefaultAPISecret && + exchCfg.API.Credentials.OTPSecret != "" && + exchCfg.API.Credentials.PEMKey != "" { + t.Error("unexpected values") + } + + exchCfg, err = c.GetExchangeConfig("test123") + if err != nil { + t.Error(err) + } + + if exchCfg.API.Credentials.Key != "asdf" { + t.Error("unexpected values") + } +} + func TestGetCommunicationsConfig(t *testing.T) { cfg := GetConfig() err := cfg.LoadConfig(ConfigTestFile) @@ -224,6 +289,18 @@ func TestCheckCommunicationsConfig(t *testing.T) { t.Error("Test failed. CheckCommunicationsConfig error:", err) } + cfg.Communications.SMSGlobalConfig.From = "" + cfg.CheckCommunicationsConfig() + if cfg.Communications.SMSGlobalConfig.From != cfg.Name { + t.Error("Test failed. CheckCommunicationsConfig From value should of been set to the config name") + } + + cfg.Communications.SMSGlobalConfig.From = "aaaaaaaaaaaaaaaaaaa" + cfg.CheckCommunicationsConfig() + if cfg.Communications.SMSGlobalConfig.From != "aaaaaaaaaaa" { + t.Error("Test failed. CheckCommunicationsConfig From value should of been trimmed to 11 characters") + } + cfg.SMS = &SMSGlobalConfig{} cfg.CheckCommunicationsConfig() if cfg.SMS != nil { @@ -266,6 +343,122 @@ func TestCheckCommunicationsConfig(t *testing.T) { } } +func TestGetExchangeAssetTypes(t *testing.T) { + t.Parallel() + var c Config + _, err := c.GetExchangeAssetTypes("void") + if err == nil { + t.Error("err should of been thrown on a non-existent exchange") + } + + c.Exchanges = append(c.Exchanges, + ExchangeConfig{ + Name: testFakeExchangeName, + CurrencyPairs: ¤cy.PairsManager{ + AssetTypes: asset.Items{ + asset.Spot, + asset.Futures, + }, + }, + }, + ) + + var assets asset.Items + assets, err = c.GetExchangeAssetTypes(testFakeExchangeName) + if err != nil { + t.Error(err) + } + + if assets.JoinToString(",") != "spot,futures" { + t.Error("unexpected results") + } + + c.Exchanges[0].CurrencyPairs = nil + _, err = c.GetExchangeAssetTypes(testFakeExchangeName) + if err == nil { + t.Error("a nil pair manager should throw an error") + } +} + +func TestSupportsExchangeAssetType(t *testing.T) { + t.Parallel() + var c Config + _, err := c.SupportsExchangeAssetType("void", asset.Spot) + if err == nil { + t.Error("unexpected result for non-existent exchange") + } + + c.Exchanges = append(c.Exchanges, + ExchangeConfig{ + Name: testFakeExchangeName, + CurrencyPairs: ¤cy.PairsManager{ + AssetTypes: asset.Items{ + asset.Spot, + asset.Futures, + }, + }, + }, + ) + + supports, err := c.SupportsExchangeAssetType(testFakeExchangeName, asset.Spot) + if err != nil { + t.Error(err) + } + + if !supports { + t.Error("exchange should support spot asset item") + } + + _, err = c.SupportsExchangeAssetType(testFakeExchangeName, "asdf") + if err == nil { + t.Error("invalid asset item should throw an error") + } + + c.Exchanges[0].CurrencyPairs = nil + _, err = c.SupportsExchangeAssetType(testFakeExchangeName, asset.Spot) + if err == nil { + t.Error("a nil pair manager should throw an error") + } +} + +func TestCheckExchangeAssetsConsistency(t *testing.T) { + t.Parallel() + var c Config + // Test for non-existent exchange + c.CheckExchangeAssetsConsistency("void") + + c.Exchanges = append(c.Exchanges, + ExchangeConfig{ + Name: testFakeExchangeName, + }, + ) + + // Tests for nil currency pairs store but valid exchange name + c.CheckExchangeAssetsConsistency(testFakeExchangeName) + + // Simulate testing a diff between stored asset types (config loading) + // and pair store + c.Exchanges[0].CurrencyPairs = ¤cy.PairsManager{ + AssetTypes: asset.Items{ + asset.Spot, + asset.Futures, + asset.Index, + }, + } + c.Exchanges[0].CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) + c.Exchanges[0].CurrencyPairs.Pairs[asset.PerpetualContract] = ¤cy.PairStore{} + c.CheckExchangeAssetsConsistency(testFakeExchangeName) + + supports, err := c.SupportsExchangeAssetType(testFakeExchangeName, asset.PerpetualContract) + if err != nil { + t.Error(err) + } + + if supports { + t.Error("perpetual contract should of been removed from the pair manager") + } +} + func TestCheckPairConsistency(t *testing.T) { cfg := GetConfig() err := cfg.LoadConfig(ConfigTestFile) diff --git a/engine/syncer.go b/engine/syncer.go index ff84a516..d62c24a9 100644 --- a/engine/syncer.go +++ b/engine/syncer.go @@ -92,8 +92,8 @@ func (e *ExchangeCurrencyPairSyncer) add(c *CurrencyPairSyncAgent) { defer e.mux.Unlock() if e.Cfg.SyncTicker { - log.Debugf(log.SyncMgr, "%s: Added ticker sync item %v: using websocket: %v using REST: %v\n", c.Exchange, c.Pair.String(), - c.Ticker.IsUsingWebsocket, c.Ticker.IsUsingREST) + log.Debugf(log.SyncMgr, "%s: Added ticker sync item %v: using websocket: %v using REST: %v\n", + c.Exchange, FormatCurrency(c.Pair).String(), c.Ticker.IsUsingWebsocket, c.Ticker.IsUsingREST) if atomic.LoadInt32(&e.initSyncCompleted) != 1 { e.initSyncWG.Add(1) createdCounter++ @@ -101,8 +101,8 @@ func (e *ExchangeCurrencyPairSyncer) add(c *CurrencyPairSyncAgent) { } if e.Cfg.SyncOrderbook { - log.Debugf(log.SyncMgr, "%s: Added orderbook sync item %v: using websocket: %v using REST: %v\n", c.Exchange, c.Pair.String(), - c.Orderbook.IsUsingWebsocket, c.Orderbook.IsUsingREST) + log.Debugf(log.SyncMgr, "%s: Added orderbook sync item %v: using websocket: %v using REST: %v\n", + c.Exchange, FormatCurrency(c.Pair).String(), c.Orderbook.IsUsingWebsocket, c.Orderbook.IsUsingREST) if atomic.LoadInt32(&e.initSyncCompleted) != 1 { e.initSyncWG.Add(1) createdCounter++ @@ -110,8 +110,8 @@ func (e *ExchangeCurrencyPairSyncer) add(c *CurrencyPairSyncAgent) { } if e.Cfg.SyncTrades { - log.Debugf(log.SyncMgr, "%s: Added trade sync item %v: using websocket: %v using REST: %v\n", c.Exchange, c.Pair.String(), - c.Trade.IsUsingWebsocket, c.Trade.IsUsingREST) + log.Debugf(log.SyncMgr, "%s: Added trade sync item %v: using websocket: %v using REST: %v\n", + c.Exchange, FormatCurrency(c.Pair).String(), c.Trade.IsUsingWebsocket, c.Trade.IsUsingREST) if atomic.LoadInt32(&e.initSyncCompleted) != 1 { e.initSyncWG.Add(1) createdCounter++ @@ -218,7 +218,8 @@ func (e *ExchangeCurrencyPairSyncer) update(exchangeName string, p currency.Pair e.CurrencyPairs[x].Ticker.HaveData = true e.CurrencyPairs[x].Ticker.IsProcessing = false if atomic.LoadInt32(&e.initSyncCompleted) != 1 && !origHadData { - log.Debugf(log.SyncMgr, "%s ticker sync complete %v [%d/%d].\n", exchangeName, p, removedCounter, createdCounter) + log.Debugf(log.SyncMgr, "%s ticker sync complete %v [%d/%d].\n", + exchangeName, FormatCurrency(p).String(), removedCounter, createdCounter) removedCounter++ e.initSyncWG.Done() } @@ -232,7 +233,8 @@ func (e *ExchangeCurrencyPairSyncer) update(exchangeName string, p currency.Pair e.CurrencyPairs[x].Orderbook.HaveData = true e.CurrencyPairs[x].Orderbook.IsProcessing = false if atomic.LoadInt32(&e.initSyncCompleted) != 1 && !origHadData { - log.Debugf(log.SyncMgr, "%s orderbook sync complete %v [%d/%d].\n", exchangeName, p, removedCounter, createdCounter) + log.Debugf(log.SyncMgr, "%s orderbook sync complete %v [%d/%d].\n", + exchangeName, FormatCurrency(p).String(), removedCounter, createdCounter) removedCounter++ e.initSyncWG.Done() } @@ -246,7 +248,8 @@ func (e *ExchangeCurrencyPairSyncer) update(exchangeName string, p currency.Pair e.CurrencyPairs[x].Trade.HaveData = true e.CurrencyPairs[x].Trade.IsProcessing = false if atomic.LoadInt32(&e.initSyncCompleted) != 1 && !origHadData { - log.Debugf(log.SyncMgr, "%s trade sync complete %v [%d/%d].\n", exchangeName, p, removedCounter, createdCounter) + log.Debugf(log.SyncMgr, "%s trade sync complete %v [%d/%d].\n", + exchangeName, FormatCurrency(p).String(), removedCounter, createdCounter) removedCounter++ e.initSyncWG.Done() } @@ -346,7 +349,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() { c.Ticker.IsUsingWebsocket = false c.Ticker.IsUsingREST = true log.Warnf(log.SyncMgr, "%s %s: No ticker update after 10 seconds, switching from websocket to rest\n", - c.Exchange, c.Pair.String()) + c.Exchange, FormatCurrency(p).String()) e.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemTicker, false) } } @@ -374,7 +377,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() { e.mux.Unlock() } else { if e.Cfg.Verbose { - log.Debugf(log.OrderMgr, "%s Using recent batching cache\n", exchangeName) + log.Debugf(log.SyncMgr, "%s Using recent batching cache\n", exchangeName) } result, err = Bot.Exchanges[x].FetchTicker(c.Pair, c.AssetType) } @@ -408,7 +411,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() { c.Orderbook.IsUsingWebsocket = false c.Orderbook.IsUsingREST = true log.Warnf(log.SyncMgr, "%s %s: No orderbook update after 15 seconds, switching from websocket to rest\n", - c.Exchange, c.Pair.String()) + c.Exchange, FormatCurrency(c.Pair).String()) e.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemOrderbook, false) } } @@ -531,10 +534,10 @@ func (e *ExchangeCurrencyPairSyncer) Start() { } if atomic.CompareAndSwapInt32(&e.initSyncStarted, 0, 1) { - log.Debugln(log.SyncMgr, "Exchange CurrencyPairSyncer initial sync started.") + log.Debugf(log.SyncMgr, + "Exchange CurrencyPairSyncer initial sync started. %d items to process.\n", + createdCounter) e.initSyncStartTime = time.Now() - log.Debugln(log.SyncMgr, createdCounter) - log.Debugln(log.SyncMgr, removedCounter) } go func() { @@ -542,7 +545,8 @@ func (e *ExchangeCurrencyPairSyncer) Start() { if atomic.CompareAndSwapInt32(&e.initSyncCompleted, 0, 1) { log.Debugf(log.SyncMgr, "Exchange CurrencyPairSyncer initial sync is complete.\n") completedTime := time.Now() - log.Debugf(log.SyncMgr, "Exchange CurrencyPairSyncer initiial sync took %v [%v sync items].\n", completedTime.Sub(e.initSyncStartTime), createdCounter) + log.Debugf(log.SyncMgr, "Exchange CurrencyPairSyncer initiial sync took %v [%v sync items].\n", + completedTime.Sub(e.initSyncStartTime), createdCounter) if !e.Cfg.SyncContinuously { log.Debugln(log.SyncMgr, "Exchange CurrencyPairSyncer stopping.") diff --git a/exchanges/okex/okex.go b/exchanges/okex/okex.go index 334bcf13..c452f490 100644 --- a/exchanges/okex/okex.go +++ b/exchanges/okex/okex.go @@ -207,7 +207,7 @@ func (o *OKEX) GetFuturesOrderBook(request okgroup.GetFuturesOrderBookRequest) ( // GetAllFuturesTokenInfo Get the last traded price, best bid/ask price, 24 hour trading volume and more info of all contracts. func (o *OKEX) GetAllFuturesTokenInfo() (resp []okgroup.GetFuturesTokenInfoResponse, _ error) { requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupInstruments, okgroup.OKGroupTicker) - return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true) + return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false) } // GetFuturesTokenInfoForCurrency Get the last traded price, best bid/ask price, 24 hour trading volume and more info of a contract. diff --git a/exchanges/okex/okex_test.go b/exchanges/okex/okex_test.go index 5febea19..a8b5d911 100644 --- a/exchanges/okex/okex_test.go +++ b/exchanges/okex/okex_test.go @@ -1045,7 +1045,9 @@ func TestGetAllFuturesTokenInfo(t *testing.T) { TestSetDefaults(t) t.Parallel() _, err := o.GetAllFuturesTokenInfo() - testStandardErrorHandling(t, err) + if err != nil { + t.Error(err) + } } // TestGetAllFuturesTokenInfo API endpoint test diff --git a/exchanges/support.go b/exchanges/support.go index 010fa626..16eae343 100644 --- a/exchanges/support.go +++ b/exchanges/support.go @@ -1,5 +1,17 @@ package exchange +import "strings" + +// IsSupported returns whether or not a specific exchange is supported +func IsSupported(exchangeName string) bool { + for x := range Exchanges { + if strings.EqualFold(exchangeName, Exchanges[x]) { + return true + } + } + return false +} + // Exchanges stores a list of supported exchanges var Exchanges = []string{ "anx", diff --git a/exchanges/support_test.go b/exchanges/support_test.go new file mode 100644 index 00000000..978ab4e8 --- /dev/null +++ b/exchanges/support_test.go @@ -0,0 +1,13 @@ +package exchange + +import "testing" + +func TestIsSupported(t *testing.T) { + if ok := IsSupported("BiTStaMp"); !ok { + t.Error("supported exchange should be valid") + } + + if ok := IsSupported("meowexch"); ok { + t.Error("non-supported exchange should be in valid") + } +} diff --git a/main.go b/main.go index 112737ab..b4c3b55e 100644 --- a/main.go +++ b/main.go @@ -62,9 +62,9 @@ func main() { flag.BoolVar(&settings.EnableOpenExchangeRates, "openexchangerates", false, "overrides config and sets up foreign exchange Open Exchange Rates") // Exchange tuning settings - flag.BoolVar(&settings.EnableExchangeAutoPairUpdates, "exchangeautopairupdates", true, "enables automatic available currency pair updates for supported exchanges") + flag.BoolVar(&settings.EnableExchangeAutoPairUpdates, "exchangeautopairupdates", false, "enables automatic available currency pair updates for supported exchanges") flag.BoolVar(&settings.DisableExchangeAutoPairUpdates, "exchangedisableautopairupdates", false, "disables exchange auto pair updates") - flag.BoolVar(&settings.EnableExchangeWebsocketSupport, "exchangewebsocketsupport", true, "enables Websocket support for exchanges") + flag.BoolVar(&settings.EnableExchangeWebsocketSupport, "exchangewebsocketsupport", false, "enables Websocket support for exchanges") flag.BoolVar(&settings.EnableExchangeRESTSupport, "exchangerestsupport", true, "enables REST support for exchanges") flag.BoolVar(&settings.EnableExchangeVerbose, "exchangeverbose", false, "increases exchange logging verbosity") flag.BoolVar(&settings.ExchangePurgeCredentials, "exchangepurgecredentials", false, "purges the stored exchange API credentials")