From e8b517ef0a6f6504fd7d51cb28cac283e8b5c0d4 Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Tue, 10 Sep 2019 17:07:00 +1000 Subject: [PATCH] Daily engine changes 1) Although gRPC does server side validation currently, validate basic things on gctcli before relaying the request to the gRPC server 2) Make pair format consistent for the exchange sycner 3) Fix OKEX ticker failure due to thinking futures info is authenticated 4) Start filling out config tests 5) Extend timeout for golangci config so that AppVeyor has time to complete (Travis is fine) 6) Add IsSupported exchange func for easy lookup --- .golangci.yml | 2 +- cmd/gctcli/commands.go | 381 ++++++++++++++++++++++-------------- cmd/gctcli/validation.go | 9 +- config/config.go | 8 +- config/config_test.go | 193 ++++++++++++++++++ engine/syncer.go | 36 ++-- exchanges/okex/okex.go | 2 +- exchanges/okex/okex_test.go | 4 +- exchanges/support.go | 12 ++ exchanges/support_test.go | 13 ++ main.go | 4 +- 11 files changed, 490 insertions(+), 174 deletions(-) create mode 100644 exchanges/support_test.go 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")